From 220d05676087e5b3958359785d4c631d7056af13 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Fri, 4 Nov 2022 19:20:51 +0800 Subject: [PATCH 1/4] feat: implement tray in linux --- Cargo.lock | 2 + Cargo.toml | 3 +- .../lib/desktop/pages/desktop_home_page.dart | 3 +- flutter/lib/models/native_model.dart | 4 - src/core_main.rs | 7 +- src/lib.rs | 2 +- src/tray.rs | 110 +++++++++++++++++- 7 files changed, 117 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0bc360fe7..3844a46cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4380,12 +4380,14 @@ dependencies = [ "flexi_logger", "flutter_rust_bridge", "flutter_rust_bridge_codegen", + "gtk", "hbb_common", "hound", "impersonate_system", "include_dir", "jni", "lazy_static", + "libappindicator", "libc", "libpulse-binding", "libpulse-simple-binding", diff --git a/Cargo.toml b/Cargo.toml index f9e519545..c950c8723 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -112,7 +112,8 @@ mouce = { git="https://github.com/fufesou/mouce.git" } evdev = { git="https://github.com/fufesou/evdev" } dbus = "0.9" dbus-crossroads = "0.5" - +gtk = "0.15" +libappindicator = "0.7" [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.11" diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index eb9e0a483..a1c69e5a3 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -432,7 +432,8 @@ class _DesktopHomePageState extends State updateUrl = await bind.mainGetSoftwareUpdateUrl(); if (updateUrl.isNotEmpty) setState(() {}); }); - initTray(); + // disable this tray because we use tray function provided by rust now + // initTray(); trayManager.addListener(this); windowManager.addListener(this); rustDeskWinManager.setMethodHandler((call, fromWindowId) async { diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index 5c724f59b..3af6f4fd7 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -90,10 +90,6 @@ class PlatformFFI { /// Init the FFI class, loads the native Rust core library. Future init(String appType) async { _appType = appType; - // if (isDesktop) { - // // TODO - // return; - // } final dylib = Platform.isAndroid ? DynamicLibrary.open('librustdesk.so') : Platform.isLinux diff --git a/src/core_main.rs b/src/core_main.rs index 46088da5c..b89f4a2b1 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -148,7 +148,7 @@ pub fn core_main() -> Option> { return None; } else if args[0] == "--server" { log::info!("start --server"); - #[cfg(not(target_os = "macos"))] + #[cfg(target_os = "windows")] { crate::start_server(true); return None; @@ -158,6 +158,11 @@ pub fn core_main() -> Option> { std::thread::spawn(move || crate::start_server(true)); // to-do: for flutter, starting tray not ready yet, or we can reuse sciter's tray implementation. } + #[cfg(all(target_os = "linux", feature = "flutter"))] + { + std::thread::spawn(move || crate::start_server(true)); + crate::tray::start_tray(crate::ui_interface::OPTIONS.clone()); + } } else if args[0] == "--import-config" { if args.len() == 2 { let filepath; diff --git a/src/lib.rs b/src/lib.rs index a5041e9f8..58dc50b04 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,7 +41,7 @@ mod lang; mod license; #[cfg(not(any(target_os = "android", target_os = "ios")))] mod port_forward; -#[cfg(windows)] + mod tray; mod ui_cm_interface; diff --git a/src/tray.rs b/src/tray.rs index a71ade329..c96eda684 100644 --- a/src/tray.rs +++ b/src/tray.rs @@ -1,11 +1,11 @@ +use hbb_common::log::{debug, error, info}; +use lazy_static::lazy_static; +#[cfg(target_os = "linux")] +use libappindicator::AppIndicator; +use std::env::temp_dir; use std::{ collections::HashMap, - sync::{Arc, Mutex}, -}; -use trayicon::{MenuBuilder, TrayIconBuilder}; -use winit::{ - event::Event, - event_loop::{ControlFlow, EventLoop}, + sync::{Arc, Mutex, RwLock}, }; #[derive(Clone, Eq, PartialEq, Debug)] @@ -15,6 +15,7 @@ enum Events { StartService, } +#[cfg(target_os = "windows")] pub fn start_tray(options: Arc>>) { let event_loop = EventLoop::::with_user_event(); let proxy = event_loop.create_proxy(); @@ -76,3 +77,100 @@ pub fn start_tray(options: Arc>>) { } }); } + +/// Start a tray icon in Linux +/// +/// [Block] +/// This function will block current execution, show the tray icon and handle events. +#[cfg(target_os = "linux")] +pub fn start_tray(options: Arc>>) { + use std::time::Duration; + + use gtk::traits::{GtkMenuItemExt, MenuShellExt, WidgetExt}; + info!("configuring tray"); + // init gtk context + if let Err(err) = gtk::init() { + error!("Error when starting the tray: {}", err); + gtk::main_quit(); + return; + } + if let Some(mut appindicator) = get_default_app_indicator() { + let mut menu = gtk::Menu::new(); + let running = get_service_status(options.clone()); + // start/stop service + let label = if !running { + crate::client::translate("Start Service".to_owned()) + } else { + crate::client::translate("Stop service".to_owned()) + }; + let menu_item_service = gtk::MenuItem::with_label(label.as_str()); + menu_item_service.connect_activate(move |item| { + let lock = crate::ui_interface::SENDER.lock().unwrap(); + update_tray_service_item(options.clone(), item); + }); + menu.append(&menu_item_service); + // show tray item + menu.show_all(); + appindicator.set_menu(&mut menu); + // start event loop + info!("Setting tray event loop"); + gtk::main(); + } else { + eprintln!("tray process exit now"); + } +} + +#[cfg(target_os = "linux")] +fn update_tray_service_item(options: Arc>>, item: >k::MenuItem) { + use gtk::{ + traits::{GtkMenuItemExt, ListBoxRowExt}, + MenuItem, + }; + if get_service_status(options.clone()) { + debug!("Now try to stop service"); + item.set_label(&crate::client::translate("Start Service".to_owned())); + crate::ipc::set_option("stop-service", "Y"); + } else { + debug!("Now try to start service"); + item.set_label(&crate::client::translate("Stop service".to_owned())); + crate::ipc::set_option("stop-service", ""); + } +} + +#[cfg(target_os = "linux")] +fn get_default_app_indicator() -> Option { + use libappindicator::AppIndicatorStatus; + use std::io::Write; + + let icon = include_bytes!("../res/icon.png"); + // appindicator does not support icon buffer, so we write it to tmp folder + let mut icon_path = temp_dir(); + icon_path.push("RustDesk"); + icon_path.push("rustdesk.png"); + match std::fs::File::create(icon_path.clone()) { + Ok(mut f) => { + f.write_all(icon).unwrap(); + } + Err(err) => { + error!("Error when writing icon to {:?}: {}", icon_path, err); + return None; + } + } + debug!("write temp icon complete"); + let mut appindicator = AppIndicator::new("RustDesk", icon_path.to_str().unwrap_or("rustdesk")); + appindicator.set_label("RustDesk", "A remote control software."); + appindicator.set_status(AppIndicatorStatus::Active); + Some(appindicator) +} + +/// Get service status +/// Return [`true`] if service is running, [`false`] otherwise. +#[inline] +fn get_service_status(options: Arc>>) -> bool { + if let Some(v) = options.lock().unwrap().get("stop-service") { + debug!("service stopped: {}", v); + v.is_empty() + } else { + true + } +} From 821f042fd90f54a3b56c206fbb5845d263d32998 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Fri, 4 Nov 2022 19:28:30 +0800 Subject: [PATCH 2/4] opt: add join handler to prevent server stopped --- src/core_main.rs | 6 ++++-- src/tray.rs | 3 +-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core_main.rs b/src/core_main.rs index b89f4a2b1..2baff12ad 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -158,10 +158,12 @@ pub fn core_main() -> Option> { std::thread::spawn(move || crate::start_server(true)); // to-do: for flutter, starting tray not ready yet, or we can reuse sciter's tray implementation. } - #[cfg(all(target_os = "linux", feature = "flutter"))] + #[cfg(all(target_os = "linux"))] { - std::thread::spawn(move || crate::start_server(true)); + let handler = std::thread::spawn(move || crate::start_server(true)); crate::tray::start_tray(crate::ui_interface::OPTIONS.clone()); + // revent server exit when encountering errors from tray + handler.join(); } } else if args[0] == "--import-config" { if args.len() == 2 { diff --git a/src/tray.rs b/src/tray.rs index c96eda684..4f45dc7bb 100644 --- a/src/tray.rs +++ b/src/tray.rs @@ -91,7 +91,6 @@ pub fn start_tray(options: Arc>>) { // init gtk context if let Err(err) = gtk::init() { error!("Error when starting the tray: {}", err); - gtk::main_quit(); return; } if let Some(mut appindicator) = get_default_app_indicator() { @@ -116,7 +115,7 @@ pub fn start_tray(options: Arc>>) { info!("Setting tray event loop"); gtk::main(); } else { - eprintln!("tray process exit now"); + error!("Tray process exit now"); } } From c4a2325d946c35e23c184fcdb077534101e8849c Mon Sep 17 00:00:00 2001 From: Kingtous Date: Fri, 4 Nov 2022 19:43:54 +0800 Subject: [PATCH 3/4] fix: windows compile --- build.py | 3 +++ src/tray.rs | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/build.py b/build.py index fffb22180..3e7ce853e 100755 --- a/build.py +++ b/build.py @@ -268,6 +268,9 @@ def build_flutter_arch_manjaro(version, features): def build_flutter_windows(version, features): os.system(f'cargo build --features {features} --lib --release') + if not os.path.exists("target/release/librustdesk.dll"): + print("cargo build failed, please check rust source code.") + exit(-1) os.chdir('flutter') os.system('flutter build windows --release') os.chdir('..') diff --git a/src/tray.rs b/src/tray.rs index 4f45dc7bb..10688bf5f 100644 --- a/src/tray.rs +++ b/src/tray.rs @@ -1,5 +1,4 @@ use hbb_common::log::{debug, error, info}; -use lazy_static::lazy_static; #[cfg(target_os = "linux")] use libappindicator::AppIndicator; use std::env::temp_dir; @@ -7,6 +6,11 @@ use std::{ collections::HashMap, sync::{Arc, Mutex, RwLock}, }; +use trayicon::{MenuBuilder, TrayIconBuilder}; +use winit::{ + event::Event, + event_loop::{ControlFlow, EventLoop}, +}; #[derive(Clone, Eq, PartialEq, Debug)] enum Events { From e3234193063418bf5a316b0c8434359528518359 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Fri, 4 Nov 2022 19:56:17 +0800 Subject: [PATCH 4/4] fix: add tray window cfg --- src/tray.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tray.rs b/src/tray.rs index 10688bf5f..91f8b0e0e 100644 --- a/src/tray.rs +++ b/src/tray.rs @@ -6,7 +6,9 @@ use std::{ collections::HashMap, sync::{Arc, Mutex, RwLock}, }; +#[cfg(target_os = "windows")] use trayicon::{MenuBuilder, TrayIconBuilder}; +#[cfg(target_os = "windows")] use winit::{ event::Event, event_loop::{ControlFlow, EventLoop},