rustdesk/src/tray.rs

215 lines
6.7 KiB
Rust
Raw Normal View History

use super::ui_interface::get_option_opt;
#[cfg(target_os = "linux")]
use hbb_common::log::{debug, error, info};
2022-11-04 19:20:51 +08:00
#[cfg(target_os = "linux")]
use libappindicator::AppIndicator;
#[cfg(target_os = "linux")]
2022-11-04 19:20:51 +08:00
use std::env::temp_dir;
#[cfg(target_os = "windows")]
use std::sync::{Arc, Mutex};
2022-11-04 19:56:17 +08:00
#[cfg(target_os = "windows")]
2022-05-12 17:35:25 +08:00
use trayicon::{MenuBuilder, TrayIconBuilder};
2022-11-04 19:56:17 +08:00
#[cfg(target_os = "windows")]
2022-05-12 17:35:25 +08:00
use winit::{
event::Event,
event_loop::{ControlFlow, EventLoop},
};
#[cfg(target_os = "windows")]
2022-05-12 17:35:25 +08:00
#[derive(Clone, Eq, PartialEq, Debug)]
enum Events {
DoubleClickTrayIcon,
StopService,
StartService,
}
2022-11-04 19:20:51 +08:00
#[cfg(target_os = "windows")]
pub fn start_tray() {
2022-05-12 17:35:25 +08:00
let event_loop = EventLoop::<Events>::with_user_event();
let proxy = event_loop.create_proxy();
2022-09-18 11:22:30 +08:00
let icon = include_bytes!("../res/tray-icon.ico");
2022-05-12 17:35:25 +08:00
let mut tray_icon = TrayIconBuilder::new()
.sender_winit(proxy)
.icon_from_buffer(icon)
.tooltip("RustDesk")
.on_double_click(Events::DoubleClickTrayIcon)
.build()
.unwrap();
let old_state = Arc::new(Mutex::new(0));
let _sender = crate::ui_interface::SENDER.lock().unwrap();
2022-05-12 17:35:25 +08:00
event_loop.run(move |event, _, control_flow| {
if get_option_opt("ipc-closed").is_some() {
2022-05-12 17:35:25 +08:00
*control_flow = ControlFlow::Exit;
return;
} else {
*control_flow = ControlFlow::Wait;
}
let stopped = is_service_stoped();
let state = if stopped { 2 } else { 1 };
2022-05-12 17:35:25 +08:00
let old = *old_state.lock().unwrap();
if state != old {
2022-05-12 17:35:25 +08:00
hbb_common::log::info!("State changed");
let mut m = MenuBuilder::new();
if state == 2 {
2022-05-12 17:35:25 +08:00
m = m.item(
&crate::client::translate("Start Service".to_owned()),
2022-05-12 17:35:25 +08:00
Events::StartService,
);
} else {
m = m.item(
&crate::client::translate("Stop service".to_owned()),
Events::StopService,
);
}
tray_icon.set_menu(&m).ok();
*old_state.lock().unwrap() = state;
2022-05-12 17:35:25 +08:00
}
match event {
Event::UserEvent(e) => match e {
Events::DoubleClickTrayIcon => {
crate::run_me(Vec::<&str>::new()).ok();
}
Events::StopService => {
crate::ipc::set_option("stop-service", "Y");
}
Events::StartService => {
crate::ipc::set_option("stop-service", "");
}
},
_ => (),
}
});
}
2022-11-04 19:20:51 +08:00
/// 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() {
2022-11-04 19:20:51 +08:00
use gtk::traits::{GtkMenuItemExt, MenuShellExt, WidgetExt};
2022-11-04 19:20:51 +08:00
info!("configuring tray");
// init gtk context
if let Err(err) = gtk::init() {
error!("Error when starting the tray: {}", err);
return;
}
if let Some(mut appindicator) = get_default_app_indicator() {
let mut menu = gtk::Menu::new();
let stoped = is_service_stoped();
2022-11-04 19:20:51 +08:00
// start/stop service
let label = if stoped {
2022-11-04 19:20:51 +08:00
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(item);
2022-11-04 19:20:51 +08:00
});
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 {
error!("Tray process exit now");
2022-11-04 19:20:51 +08:00
}
}
#[cfg(target_os = "linux")]
fn update_tray_service_item(item: &gtk::MenuItem) {
use gtk::traits::GtkMenuItemExt;
if is_service_stoped() {
2022-11-04 19:20:51 +08:00
debug!("Now try to start service");
item.set_label(&crate::client::translate("Stop service".to_owned()));
crate::ipc::set_option("stop-service", "");
} else {
debug!("Now try to stop service");
item.set_label(&crate::client::translate("Start Service".to_owned()));
crate::ipc::set_option("stop-service", "Y");
2022-11-04 19:20:51 +08:00
}
}
#[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();
2022-11-19 20:06:17 +08:00
// set .png icon file to be writable
// this ensures successful file rewrite when switching between x11 and wayland.
let mut perm = f.metadata().unwrap().permissions();
if perm.readonly() {
perm.set_readonly(false);
f.set_permissions(perm).unwrap();
}
2022-11-04 19:20:51 +08:00
}
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)
}
/// Check if service is stoped.
/// Return [`true`] if service is stoped, [`false`] otherwise.
2022-11-04 19:20:51 +08:00
#[inline]
fn is_service_stoped() -> bool {
if let Some(v) = get_option_opt("stop-service") {
v == "Y"
2022-11-04 19:20:51 +08:00
} else {
false
2022-11-04 19:20:51 +08:00
}
}
2022-11-18 18:36:25 +08:00
#[cfg(target_os = "macos")]
pub fn make_tray() {
use tray_item::TrayItem;
let mode = dark_light::detect();
let mut icon_path = "";
match mode {
dark_light::Mode::Dark => {
icon_path = "mac-tray-light.png";
},
dark_light::Mode::Light => {
icon_path = "mac-tray-dark.png";
},
}
if let Ok(mut tray) = TrayItem::new(&crate::get_app_name(), icon_path) {
tray.add_label(&format!(
"{} {}",
crate::get_app_name(),
crate::lang::translate("Service is running".to_owned())
))
.ok();
let inner = tray.inner_mut();
inner.add_quit_item(&crate::lang::translate("Quit".to_owned()));
inner.display();
} else {
loop {
std::thread::sleep(std::time::Duration::from_secs(3));
}
}
}