This commit is contained in:
rustdesk
2023-02-09 21:28:42 +08:00
parent 900d0e8be9
commit f7643077d3
12 changed files with 569 additions and 398 deletions

View File

@@ -1,11 +1,5 @@
#[cfg(any(target_os = "linux", target_os = "windows"))]
#[cfg(any(target_os = "windows"))]
use super::ui_interface::get_option_opt;
#[cfg(target_os = "linux")]
use hbb_common::log::{debug, error, info};
#[cfg(target_os = "linux")]
use libappindicator::AppIndicator;
#[cfg(target_os = "linux")]
use std::env::temp_dir;
#[cfg(target_os = "windows")]
use std::sync::{Arc, Mutex};
#[cfg(target_os = "windows")]
@@ -83,119 +77,10 @@ pub fn start_tray() {
});
}
/// 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() {
use std::time::Duration;
use glib::{clone, Continue};
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);
return;
}
if let Some(mut appindicator) = get_default_app_indicator() {
let mut menu = gtk::Menu::new();
let stoped = is_service_stopped();
// start/stop service
let label = if stoped {
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 |_| {
let _lock = crate::ui_interface::SENDER.lock().unwrap();
change_service_state();
});
menu.append(&menu_item_service);
// show tray item
menu.show_all();
appindicator.set_menu(&mut menu);
// start event loop
info!("Setting tray event loop");
// 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)
}),
);
gtk::main();
} else {
error!("Tray process exit now");
}
}
#[cfg(target_os = "linux")]
fn change_service_state() {
if is_service_stopped() {
debug!("Now try to start service");
crate::ipc::set_option("stop-service", "");
} else {
debug!("Now try to stop service");
crate::ipc::set_option("stop-service", "Y");
}
}
#[cfg(target_os = "linux")]
#[inline]
fn update_tray_service_item(item: &gtk::MenuItem) {
use gtk::traits::GtkMenuItemExt;
if is_service_stopped() {
item.set_label(&crate::client::translate("Start Service".to_owned()));
} else {
item.set_label(&crate::client::translate("Stop service".to_owned()));
}
}
#[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();
// 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();
}
}
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.
#[inline]
#[cfg(any(target_os = "linux", target_os = "windows"))]
#[cfg(any(target_os = "windows"))]
fn is_service_stopped() -> bool {
if let Some(v) = get_option_opt("stop-service") {
v == "Y"
@@ -204,47 +89,68 @@ fn is_service_stopped() -> bool {
}
}
#[cfg(target_os = "macos")]
pub fn make_tray() {
extern "C" {
fn BackingScaleFactor() -> f32;
}
let f = unsafe { BackingScaleFactor() };
use tray_item::TrayItem;
let mode = dark_light::detect();
let icon_path = match mode {
dark_light::Mode::Dark => {
// still show big overflow icon in my test, so still use x1 png.
// let's do it with objc with svg support later.
// or use another tray crate, or find out in tauri (it has tray support)
if f > 2. {
"mac-tray-light-x2.png"
} else {
"mac-tray-light.png"
}
}
dark_light::Mode::Light => {
if f > 2. {
"mac-tray-dark-x2.png"
} else {
"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();
/// 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() {}
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));
}
}
#[cfg(target_os = "macos")]
pub fn start_tray() {
use hbb_common::{allow_err, log};
allow_err!(make_tray());
}
#[cfg(target_os = "macos")]
pub fn make_tray() -> hbb_common::ResultType<()> {
// https://github.com/tauri-apps/tray-icon/blob/dev/examples/tao.rs
use hbb_common::anyhow::Context;
use tao::event_loop::{ControlFlow, EventLoopBuilder};
use tray_icon::{TrayEvent, TrayIconBuilder};
let mode = dark_light::detect();
const LIGHT: &[u8] = include_bytes!("../res/mac-tray-light-x2.png");
const DARK: &[u8] = include_bytes!("../res/mac-tray-dark-x2.png");
let icon = match mode {
dark_light::Mode::Dark => DARK,
_ => LIGHT,
};
let (icon_rgba, icon_width, icon_height) = {
let image = image::load_from_memory(icon)
.context("Failed to open icon path")?
.into_rgba8();
let (width, height) = image.dimensions();
let rgba = image.into_raw();
(rgba, width, height)
};
let icon = tray_icon::icon::Icon::from_rgba(icon_rgba, icon_width, icon_height)
.context("Failed to open icon")?;
let event_loop = EventLoopBuilder::new().build();
let _tray_icon = Some(
TrayIconBuilder::new()
.with_tooltip(format!(
"{} {}",
crate::get_app_name(),
crate::lang::translate("Service is running".to_owned())
))
.with_icon(icon)
.build()?,
);
let tray_channel = TrayEvent::receiver();
let mut docker_hiden = false;
event_loop.run(move |_event, _, control_flow| {
if !docker_hiden {
crate::platform::macos::hide_dock();
docker_hiden = true;
}
*control_flow = ControlFlow::Poll;
if tray_channel.try_recv().is_ok() {
crate::platform::macos::handle_application_should_open_untitled_file();
}
});
}