feat: implement tray in linux
This commit is contained in:
parent
884a223449
commit
220d056760
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -4380,12 +4380,14 @@ dependencies = [
|
|||||||
"flexi_logger",
|
"flexi_logger",
|
||||||
"flutter_rust_bridge",
|
"flutter_rust_bridge",
|
||||||
"flutter_rust_bridge_codegen",
|
"flutter_rust_bridge_codegen",
|
||||||
|
"gtk",
|
||||||
"hbb_common",
|
"hbb_common",
|
||||||
"hound",
|
"hound",
|
||||||
"impersonate_system",
|
"impersonate_system",
|
||||||
"include_dir",
|
"include_dir",
|
||||||
"jni",
|
"jni",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"libappindicator",
|
||||||
"libc",
|
"libc",
|
||||||
"libpulse-binding",
|
"libpulse-binding",
|
||||||
"libpulse-simple-binding",
|
"libpulse-simple-binding",
|
||||||
|
@ -112,7 +112,8 @@ mouce = { git="https://github.com/fufesou/mouce.git" }
|
|||||||
evdev = { git="https://github.com/fufesou/evdev" }
|
evdev = { git="https://github.com/fufesou/evdev" }
|
||||||
dbus = "0.9"
|
dbus = "0.9"
|
||||||
dbus-crossroads = "0.5"
|
dbus-crossroads = "0.5"
|
||||||
|
gtk = "0.15"
|
||||||
|
libappindicator = "0.7"
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
android_logger = "0.11"
|
android_logger = "0.11"
|
||||||
|
@ -432,7 +432,8 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
updateUrl = await bind.mainGetSoftwareUpdateUrl();
|
updateUrl = await bind.mainGetSoftwareUpdateUrl();
|
||||||
if (updateUrl.isNotEmpty) setState(() {});
|
if (updateUrl.isNotEmpty) setState(() {});
|
||||||
});
|
});
|
||||||
initTray();
|
// disable this tray because we use tray function provided by rust now
|
||||||
|
// initTray();
|
||||||
trayManager.addListener(this);
|
trayManager.addListener(this);
|
||||||
windowManager.addListener(this);
|
windowManager.addListener(this);
|
||||||
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
|
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
|
||||||
|
@ -90,10 +90,6 @@ class PlatformFFI {
|
|||||||
/// Init the FFI class, loads the native Rust core library.
|
/// Init the FFI class, loads the native Rust core library.
|
||||||
Future<void> init(String appType) async {
|
Future<void> init(String appType) async {
|
||||||
_appType = appType;
|
_appType = appType;
|
||||||
// if (isDesktop) {
|
|
||||||
// // TODO
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
final dylib = Platform.isAndroid
|
final dylib = Platform.isAndroid
|
||||||
? DynamicLibrary.open('librustdesk.so')
|
? DynamicLibrary.open('librustdesk.so')
|
||||||
: Platform.isLinux
|
: Platform.isLinux
|
||||||
|
@ -148,7 +148,7 @@ pub fn core_main() -> Option<Vec<String>> {
|
|||||||
return None;
|
return None;
|
||||||
} else if args[0] == "--server" {
|
} else if args[0] == "--server" {
|
||||||
log::info!("start --server");
|
log::info!("start --server");
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
crate::start_server(true);
|
crate::start_server(true);
|
||||||
return None;
|
return None;
|
||||||
@ -158,6 +158,11 @@ pub fn core_main() -> Option<Vec<String>> {
|
|||||||
std::thread::spawn(move || crate::start_server(true));
|
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.
|
// 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" {
|
} else if args[0] == "--import-config" {
|
||||||
if args.len() == 2 {
|
if args.len() == 2 {
|
||||||
let filepath;
|
let filepath;
|
||||||
|
@ -41,7 +41,7 @@ mod lang;
|
|||||||
mod license;
|
mod license;
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
mod port_forward;
|
mod port_forward;
|
||||||
#[cfg(windows)]
|
|
||||||
mod tray;
|
mod tray;
|
||||||
|
|
||||||
mod ui_cm_interface;
|
mod ui_cm_interface;
|
||||||
|
110
src/tray.rs
110
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::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex, RwLock},
|
||||||
};
|
|
||||||
use trayicon::{MenuBuilder, TrayIconBuilder};
|
|
||||||
use winit::{
|
|
||||||
event::Event,
|
|
||||||
event_loop::{ControlFlow, EventLoop},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
@ -15,6 +15,7 @@ enum Events {
|
|||||||
StartService,
|
StartService,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
pub fn start_tray(options: Arc<Mutex<HashMap<String, String>>>) {
|
pub fn start_tray(options: Arc<Mutex<HashMap<String, String>>>) {
|
||||||
let event_loop = EventLoop::<Events>::with_user_event();
|
let event_loop = EventLoop::<Events>::with_user_event();
|
||||||
let proxy = event_loop.create_proxy();
|
let proxy = event_loop.create_proxy();
|
||||||
@ -76,3 +77,100 @@ pub fn start_tray(options: Arc<Mutex<HashMap<String, String>>>) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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<Mutex<HashMap<String, String>>>) {
|
||||||
|
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<Mutex<HashMap<String, String>>>, 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<AppIndicator> {
|
||||||
|
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<Mutex<HashMap<String, String>>>) -> bool {
|
||||||
|
if let Some(v) = options.lock().unwrap().get("stop-service") {
|
||||||
|
debug!("service stopped: {}", v);
|
||||||
|
v.is_empty()
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user