2022-12-29 14:28:15 +08:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
2022-11-10 21:25:12 +08:00
|
|
|
use super::ui_interface::get_option_opt;
|
2022-11-09 15:04:24 +08:00
|
|
|
#[cfg(target_os = "linux")]
|
2022-11-10 21:25:12 +08:00
|
|
|
use hbb_common::log::{debug, error, info};
|
2022-11-04 19:20:51 +08:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
use libappindicator::AppIndicator;
|
2022-11-09 15:04:24 +08:00
|
|
|
#[cfg(target_os = "linux")]
|
2022-11-04 19:20:51 +08:00
|
|
|
use std::env::temp_dir;
|
2022-11-10 23:26:09 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
2022-11-10 21:25:12 +08:00
|
|
|
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},
|
|
|
|
};
|
|
|
|
|
2022-11-10 23:26:09 +08:00
|
|
|
#[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")]
|
2022-11-10 21:25:12 +08:00
|
|
|
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));
|
2022-10-31 16:08:51 +08:00
|
|
|
let _sender = crate::ui_interface::SENDER.lock().unwrap();
|
2022-05-12 17:35:25 +08:00
|
|
|
event_loop.run(move |event, _, control_flow| {
|
2022-11-10 21:25:12 +08:00
|
|
|
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;
|
|
|
|
}
|
2022-12-29 14:28:15 +08:00
|
|
|
let stopped = is_service_stopped();
|
2022-11-10 23:26:09 +08:00
|
|
|
let state = if stopped { 2 } else { 1 };
|
2022-05-12 17:35:25 +08:00
|
|
|
let old = *old_state.lock().unwrap();
|
2022-11-10 23:26:09 +08:00
|
|
|
if state != old {
|
2022-05-12 17:35:25 +08:00
|
|
|
hbb_common::log::info!("State changed");
|
|
|
|
let mut m = MenuBuilder::new();
|
2022-11-10 23:26:09 +08:00
|
|
|
if state == 2 {
|
2022-05-12 17:35:25 +08:00
|
|
|
m = m.item(
|
2022-10-29 21:53:18 +08:00
|
|
|
&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();
|
2022-11-10 23:26:09 +08:00
|
|
|
*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")]
|
2022-11-10 23:26:09 +08:00
|
|
|
pub fn start_tray() {
|
2022-12-10 10:57:21 +08:00
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
use glib::{clone, Continue};
|
2022-11-04 19:20:51 +08:00
|
|
|
use gtk::traits::{GtkMenuItemExt, MenuShellExt, WidgetExt};
|
2022-11-10 23:26:09 +08:00
|
|
|
|
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();
|
2022-12-29 14:28:15 +08:00
|
|
|
let stoped = is_service_stopped();
|
2022-11-04 19:20:51 +08:00
|
|
|
// start/stop service
|
2022-11-10 23:26:09 +08:00
|
|
|
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());
|
2022-12-10 10:57:21 +08:00
|
|
|
menu_item_service.connect_activate(move |_| {
|
2022-11-10 23:26:09 +08:00
|
|
|
let _lock = crate::ui_interface::SENDER.lock().unwrap();
|
2022-12-10 10:57:21 +08:00
|
|
|
change_service_state();
|
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");
|
2022-12-10 10:57:21 +08:00
|
|
|
// check the connection status for every second
|
|
|
|
glib::timeout_add_local(
|
|
|
|
Duration::from_secs(1),
|
|
|
|
clone!(@strong menu_item_service as item => move || {
|
|
|
|
let _lock = crate::ui_interface::SENDER.lock().unwrap();
|
|
|
|
update_tray_service_item(&item);
|
|
|
|
// continue to trigger the next status check
|
|
|
|
Continue(true)
|
|
|
|
}),
|
|
|
|
);
|
2022-11-04 19:20:51 +08:00
|
|
|
gtk::main();
|
|
|
|
} else {
|
2022-11-04 19:28:30 +08:00
|
|
|
error!("Tray process exit now");
|
2022-11-04 19:20:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(target_os = "linux")]
|
2022-12-10 10:57:21 +08:00
|
|
|
fn change_service_state() {
|
2022-12-29 14:28:15 +08:00
|
|
|
if is_service_stopped() {
|
2022-11-04 19:20:51 +08:00
|
|
|
debug!("Now try to start service");
|
|
|
|
crate::ipc::set_option("stop-service", "");
|
2022-11-10 23:26:09 +08:00
|
|
|
} else {
|
|
|
|
debug!("Now try to stop service");
|
|
|
|
crate::ipc::set_option("stop-service", "Y");
|
2022-11-04 19:20:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-10 10:57:21 +08:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
#[inline]
|
|
|
|
fn update_tray_service_item(item: >k::MenuItem) {
|
|
|
|
use gtk::traits::GtkMenuItemExt;
|
|
|
|
|
2022-12-29 14:28:15 +08:00
|
|
|
if is_service_stopped() {
|
2022-12-10 10:57:21 +08:00
|
|
|
item.set_label(&crate::client::translate("Start Service".to_owned()));
|
|
|
|
} else {
|
|
|
|
item.set_label(&crate::client::translate("Stop service".to_owned()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2022-11-10 23:26:09 +08:00
|
|
|
/// Check if service is stoped.
|
|
|
|
/// Return [`true`] if service is stoped, [`false`] otherwise.
|
2022-11-04 19:20:51 +08:00
|
|
|
#[inline]
|
2022-12-29 14:28:15 +08:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
|
|
|
fn is_service_stopped() -> bool {
|
2022-11-10 23:26:09 +08:00
|
|
|
if let Some(v) = get_option_opt("stop-service") {
|
|
|
|
v == "Y"
|
2022-11-04 19:20:51 +08:00
|
|
|
} else {
|
2022-11-10 23:26:09 +08:00
|
|
|
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() {
|
2023-01-11 15:35:35 +08:00
|
|
|
extern "C" {
|
|
|
|
fn BackingScaleFactor() -> f32;
|
|
|
|
}
|
|
|
|
let f = unsafe { BackingScaleFactor() };
|
2022-11-18 18:36:25 +08:00
|
|
|
use tray_item::TrayItem;
|
|
|
|
let mode = dark_light::detect();
|
2023-01-11 15:35:35 +08:00
|
|
|
let icon_path = match mode {
|
2022-11-18 18:36:25 +08:00
|
|
|
dark_light::Mode::Dark => {
|
2023-01-11 15:35:35 +08:00
|
|
|
if f > 1. {
|
|
|
|
"mac-tray-light_x2.png";
|
|
|
|
} else {
|
|
|
|
"mac-tray-light.png";
|
|
|
|
}
|
2022-12-10 10:57:21 +08:00
|
|
|
}
|
2022-11-18 18:36:25 +08:00
|
|
|
dark_light::Mode::Light => {
|
2023-01-11 15:35:35 +08:00
|
|
|
if f > 1. {
|
|
|
|
"mac-tray-dark_x2.png";
|
|
|
|
} else {
|
|
|
|
"mac-tray-dark.png";
|
|
|
|
}
|
2022-12-10 10:57:21 +08:00
|
|
|
}
|
2023-01-11 15:35:35 +08:00
|
|
|
};
|
2022-11-18 18:36:25 +08:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|