working on windows service install/uninstall

This commit is contained in:
rustdesk
2023-06-05 20:27:48 +08:00
parent f1cc42769c
commit b07ac438f5
17 changed files with 280 additions and 688 deletions

View File

@@ -127,9 +127,6 @@ pub fn core_main() -> Option<Vec<String>> {
log::error!("Failed to before-uninstall: {}", err);
}
return None;
} else if args[0] == "--update" {
hbb_common::allow_err!(platform::update_me());
return None;
} else if args[0] == "--reinstall" {
hbb_common::allow_err!(platform::uninstall_me(false));
hbb_common::allow_err!(platform::install_me(

View File

@@ -40,8 +40,6 @@ use common::*;
pub mod cli;
#[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))]
pub mod core_main;
#[cfg(all(windows, feature = "hbbs"))]
mod hbbs;
mod lang;
#[cfg(windows)]
mod license;

View File

@@ -722,7 +722,7 @@ pub fn is_share_rdp() -> bool {
}
pub fn set_share_rdp(enable: bool) {
let (subkey, _, _, _, _) = get_install_info();
let (subkey, _, _, _) = get_install_info();
let cmd = format!(
"reg add {} /f /v share_rdp /t REG_SZ /d \"{}\"",
subkey,
@@ -815,11 +815,11 @@ fn get_valid_subkey() -> String {
return get_subkey(&app_name, false);
}
pub fn get_install_info() -> (String, String, String, String, String) {
pub fn get_install_info() -> (String, String, String, String) {
get_install_info_with_subkey(get_valid_subkey())
}
fn get_default_install_info() -> (String, String, String, String, String) {
fn get_default_install_info() -> (String, String, String, String) {
get_install_info_with_subkey(get_subkey(&crate::get_app_name(), false))
}
@@ -841,7 +841,6 @@ fn get_default_install_path() -> String {
}
pub fn check_update_broker_process() -> ResultType<()> {
// let (_, path, _, _) = get_install_info();
let process_exe = privacy_win_mag::INJECTED_PROCESS_EXE;
let origin_process_exe = privacy_win_mag::ORIGIN_PROCESS_EXE;
@@ -876,19 +875,17 @@ pub fn check_update_broker_process() -> ResultType<()> {
let cmds = format!(
"
chcp 65001
taskkill /F /IM {broker_exe}
taskkill /F /IM {process_exe}
copy /Y \"{origin_process_exe}\" \"{cur_exe}\"
",
broker_exe = process_exe,
origin_process_exe = origin_process_exe,
cur_exe = cur_exe.to_string_lossy().to_string(),
cur_exe = cur_exe.to_string_lossy(),
);
run_cmds(cmds, false, "update_broker")?;
Ok(())
}
fn get_install_info_with_subkey(subkey: String) -> (String, String, String, String, String) {
fn get_install_info_with_subkey(subkey: String) -> (String, String, String, String) {
let mut path = get_reg_of(&subkey, "InstallLocation");
if path.is_empty() {
path = get_default_install_path();
@@ -899,8 +896,7 @@ fn get_install_info_with_subkey(subkey: String) -> (String, String, String, Stri
crate::get_app_name()
);
let exe = format!("{}\\{}.exe", path, crate::get_app_name());
let dll = format!("{}\\sciter.dll", path);
(subkey, path, start_menu, exe, dll)
(subkey, path, start_menu, exe)
}
pub fn copy_raw_cmd(src_raw: &str, _raw: &str, _path: &str) -> String {
@@ -923,15 +919,14 @@ pub fn copy_exe_cmd(src_exe: &str, exe: &str, path: &str) -> String {
{main_exe}
copy /Y \"{ORIGIN_PROCESS_EXE}\" \"{path}\\{broker_exe}\"
",
main_exe = main_exe,
path = path,
ORIGIN_PROCESS_EXE = privacy_win_mag::ORIGIN_PROCESS_EXE,
broker_exe = privacy_win_mag::INJECTED_PROCESS_EXE,
)
}
/* // update_me has bad compatibility, so disable it.
pub fn update_me() -> ResultType<()> {
let (_, path, _, exe, _dll) = get_install_info();
let (_, path, _, exe) = get_install_info();
let src_exe = std::env::current_exe()?.to_str().unwrap_or("").to_owned();
let cmds = format!(
"
@@ -949,16 +944,14 @@ pub fn update_me() -> ResultType<()> {
lic = register_licence(),
cur_pid = get_current_pid(),
);
std::thread::sleep(std::time::Duration::from_millis(1000));
run_cmds(cmds, false, "update")?;
std::thread::sleep(std::time::Duration::from_millis(2000));
std::process::Command::new(&exe).arg("--tray").spawn().ok();
std::process::Command::new(&exe).spawn().ok();
run_after_run_cmds(false)?;
std::process::Command::new(&exe)
.args(&["--remove", &src_exe])
.spawn()?;
Ok(())
}
*/
fn get_after_install(exe: &str) -> String {
let app_name = crate::get_app_name();
@@ -986,24 +979,21 @@ fn get_after_install(exe: &str) -> String {
reg add HKEY_CLASSES_ROOT\\{ext}\\shell\\open /f
reg add HKEY_CLASSES_ROOT\\{ext}\\shell\\open\\command /f
reg add HKEY_CLASSES_ROOT\\{ext}\\shell\\open\\command /f /ve /t REG_SZ /d \"\\\"{exe}\\\" \\\"%%1\\\"\"
sc create {app_name} binpath= \"\\\"{exe}\\\" --service\" start= auto DisplayName= \"{app_name} Service\"
netsh advfirewall firewall add rule name=\"{app_name} Service\" dir=in action=allow program=\"{exe}\" enable=yes
sc start {app_name}
{create_service}
reg add HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System /f /v SoftwareSASGeneration /t REG_DWORD /d 1
", ext=ext, exe=exe, app_name=app_name)
", create_service=get_create_service(&exe))
}
pub fn install_me(options: &str, path: String, silent: bool, debug: bool) -> ResultType<()> {
let uninstall_str = get_uninstall(false);
let mut path = path.trim_end_matches('\\').to_owned();
let (subkey, _path, start_menu, exe, dll) = get_default_install_info();
let (subkey, _path, start_menu, exe) = get_default_install_info();
let mut exe = exe;
let mut _dll = dll;
if path.is_empty() {
path = _path;
} else {
exe = exe.replace(&_path, &path);
_dll = _dll.replace(&_path, &path);
}
let mut version_major = "0";
let mut version_minor = "0";
@@ -1018,6 +1008,7 @@ pub fn install_me(options: &str, path: String, silent: bool, debug: bool) -> Res
if versions.len() > 2 {
version_build = versions[2];
}
let app_name = crate::get_app_name();
let tmp_path = std::env::temp_dir().to_string_lossy().to_string();
let mk_shortcut = write_cmds(
@@ -1029,10 +1020,7 @@ sLinkFile = \"{tmp_path}\\{app_name}.lnk\"
Set oLink = oWS.CreateShortcut(sLinkFile)
oLink.TargetPath = \"{exe}\"
oLink.Save
",
tmp_path = tmp_path,
app_name = crate::get_app_name(),
exe = exe,
"
),
"vbs",
"mk_shortcut",
@@ -1051,10 +1039,7 @@ Set oLink = oWS.CreateShortcut(sLinkFile)
oLink.Arguments = \"--uninstall\"
oLink.IconLocation = \"msiexec.exe\"
oLink.Save
",
tmp_path = tmp_path,
app_name = crate::get_app_name(),
exe = exe,
"
),
"vbs",
"uninstall_shortcut",
@@ -1062,27 +1047,7 @@ oLink.Save
.to_str()
.unwrap_or("")
.to_owned();
let tray_shortcut = write_cmds(
format!(
"
Set oWS = WScript.CreateObject(\"WScript.Shell\")
sLinkFile = \"{tmp_path}\\{app_name} Tray.lnk\"
Set oLink = oWS.CreateShortcut(sLinkFile)
oLink.TargetPath = \"{exe}\"
oLink.Arguments = \"--tray\"
oLink.Save
",
tmp_path = tmp_path,
app_name = crate::get_app_name(),
exe = exe,
),
"vbs",
"tray_shortcut",
)?
.to_str()
.unwrap_or("")
.to_owned();
let tray_shortcut = get_tray_shortcut(&exe, &tmp_path)?;
let mut shortcuts = Default::default();
if options.contains("desktopicon") {
shortcuts = format!(
@@ -1093,15 +1058,11 @@ oLink.Save
}
if options.contains("startmenu") {
shortcuts = format!(
"{}
"{shortcuts}
md \"{start_menu}\"
copy /Y \"{tmp_path}\\{app_name}.lnk\" \"{start_menu}\\\"
copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{start_menu}\\\"
",
shortcuts,
start_menu = start_menu,
tmp_path = tmp_path,
app_name = crate::get_app_name(),
"
);
}
@@ -1119,12 +1080,7 @@ if exist \"{tray_shortcut}\" del /f /q \"{tray_shortcut}\"
if exist \"{tmp_path}\\{app_name}.lnk\" del /f /q \"{tmp_path}\\{app_name}.lnk\"
if exist \"{tmp_path}\\Uninstall {app_name}.lnk\" del /f /q \"{tmp_path}\\Uninstall {app_name}.lnk\"
if exist \"{tmp_path}\\{app_name} Tray.lnk\" del /f /q \"{tmp_path}\\{app_name} Tray.lnk\"
",
mk_shortcut = mk_shortcut,
uninstall_shortcut = uninstall_shortcut,
tray_shortcut = tray_shortcut,
tmp_path = tmp_path,
app_name = crate::get_app_name(),
"
);
let src_exe = std::env::current_exe()?.to_str().unwrap_or("").to_string();
@@ -1145,11 +1101,12 @@ reg add {subkey} /f /v DisplayIcon /t REG_SZ /d \"{exe}\"
reg add {subkey} /f /v DisplayName /t REG_SZ /d \"{app_name}\"
reg add {subkey} /f /v DisplayVersion /t REG_SZ /d \"{version}\"
reg add {subkey} /f /v Version /t REG_SZ /d \"{version}\"
reg add {subkey} /f /v BuildDate /t REG_SZ /d \"{build_date}\"
reg add {subkey} /f /v InstallLocation /t REG_SZ /d \"{path}\"
reg add {subkey} /f /v Publisher /t REG_SZ /d \"{app_name}\"
reg add {subkey} /f /v VersionMajor /t REG_DWORD /d {major}
reg add {subkey} /f /v VersionMinor /t REG_DWORD /d {minor}
reg add {subkey} /f /v VersionBuild /t REG_DWORD /d {build}
reg add {subkey} /f /v VersionMajor /t REG_DWORD /d {version_major}
reg add {subkey} /f /v VersionMinor /t REG_DWORD /d {version_minor}
reg add {subkey} /f /v VersionBuild /t REG_DWORD /d {version_build}
reg add {subkey} /f /v UninstallString /t REG_SZ /d \"\\\"{exe}\\\" --uninstall\"
reg add {subkey} /f /v EstimatedSize /t REG_DWORD /d {size}
reg add {subkey} /f /v WindowsInstaller /t REG_DWORD /d 0
@@ -1161,32 +1118,14 @@ copy /Y \"{tmp_path}\\{app_name} Tray.lnk\" \"%PROGRAMDATA%\\Microsoft\\Windows\
{shortcuts}
copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{path}\\\"
{dels}
sc create {app_name} binpath= \"\\\"{exe}\\\" --import-config \\\"{config_path}\\\"\" start= auto DisplayName= \"{app_name} Service\"
sc start {app_name}
sc stop {app_name}
sc delete {app_name}
{import_config}
{install_cert}
{after_install}
{sleep}
",
uninstall_str=uninstall_str,
path=path,
exe=exe,
subkey=subkey,
app_name=crate::get_app_name(),
version=crate::VERSION,
major=version_major,
minor=version_minor,
build=version_build,
size=size,
mk_shortcut=mk_shortcut,
uninstall_shortcut=uninstall_shortcut,
tray_shortcut=tray_shortcut,
tmp_path=tmp_path,
shortcuts=shortcuts,
config_path=Config::file().to_str().unwrap_or(""),
build_date=crate::BUILD_DATE,
lic=register_licence(),
install_cert=install_cert,
after_install=get_after_install(&exe),
sleep=if debug {
"timeout 300"
@@ -1199,19 +1138,15 @@ sc delete {app_name}
&dels
},
copy_exe = copy_exe_cmd(&src_exe, &exe, &path),
import_config = get_import_config(&exe),
);
run_cmds(cmds, debug, "install")?;
std::thread::sleep(std::time::Duration::from_millis(2000));
if !silent {
std::process::Command::new(&exe).spawn()?;
std::process::Command::new(&exe).arg("--tray").spawn()?;
std::thread::sleep(std::time::Duration::from_millis(1000));
}
run_after_run_cmds(silent)?;
Ok(())
}
pub fn run_after_install() -> ResultType<()> {
let (_, _, _, exe, _) = get_install_info();
let (_, _, _, exe) = get_install_info();
run_cmds(get_after_install(&exe), true, "after_install")
}
@@ -1235,12 +1170,10 @@ fn get_before_uninstall(kill_self: bool) -> String {
taskkill /F /IM {broker_exe}
taskkill /F /IM {app_name}.exe{filter}
reg delete HKEY_CLASSES_ROOT\\.{ext} /f
reg delete HKEY_CLASSES_ROOT\\{ext} /f
netsh advfirewall firewall delete rule name=\"{app_name} Service\"
",
app_name = app_name,
broker_exe = WIN_MAG_INJECTED_PROCESS_EXE,
ext = ext,
filter = filter,
)
}
@@ -1251,8 +1184,7 @@ fn get_uninstall(kill_self: bool) -> String {
uninstall_cert_cmd = format!("\"{}\" --uninstall-cert", exe_path);
}
}
let (subkey, path, start_menu, _, _) = get_install_info();
let (subkey, path, start_menu, _) = get_install_info();
format!(
"
{before_uninstall}
@@ -1264,11 +1196,7 @@ fn get_uninstall(kill_self: bool) -> String {
if exist \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\" del /f /q \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\"
",
before_uninstall=get_before_uninstall(kill_self),
uninstall_cert_cmd = uninstall_cert_cmd,
subkey=subkey,
app_name = crate::get_app_name(),
path = path,
start_menu = start_menu,
)
}
@@ -1277,6 +1205,7 @@ pub fn uninstall_me(kill_self: bool) -> ResultType<()> {
}
fn write_cmds(cmds: String, ext: &str, tip: &str) -> ResultType<std::path::PathBuf> {
let mut cmds = cmds;
let mut tmp = std::env::temp_dir();
// When dir contains these characters, the bat file will not execute in elevated mode.
if vec!["&", "@", "^"]
@@ -1289,9 +1218,20 @@ fn write_cmds(cmds: String, ext: &str, tip: &str) -> ResultType<std::path::PathB
}
tmp.push(format!("{}_{}.{}", crate::get_app_name(), tip, ext));
let mut file = std::fs::File::create(&tmp)?;
if ext == "bat" {
let tmp2 = get_undone_file(&tmp);
std::fs::File::create(&tmp2).ok();
cmds = format!(
"
{cmds}
if exist \"{path}\" del /f /q \"{path}\"
",
path = tmp2.to_string_lossy()
);
}
// in case cmds mixed with \r\n and \n, make sure all ending with \r\n
// in some windows, \r\n required for cmd file to run
let cmds = cmds.replace("\r\n", "\n").replace("\n", "\r\n");
cmds = cmds.replace("\r\n", "\n").replace("\n", "\r\n");
if ext == "vbs" {
let mut v: Vec<u16> = cmds.encode_utf16().collect();
// utf8 -> utf16le which vbs support it only
@@ -1310,8 +1250,18 @@ fn to_le(v: &mut [u16]) -> &[u8] {
unsafe { v.align_to().1 }
}
fn get_undone_file(tmp: &PathBuf) -> PathBuf {
let mut tmp1 = tmp.clone();
tmp1.set_file_name(format!(
"{}.undone",
tmp.file_name().unwrap().to_string_lossy()
));
tmp1
}
fn run_cmds(cmds: String, show: bool, tip: &str) -> ResultType<()> {
let tmp = write_cmds(cmds, "bat", tip)?;
let tmp2 = get_undone_file(&tmp);
let tmp_fn = tmp.to_str().unwrap_or("");
let res = runas::Command::new("cmd")
.args(&["/C", &tmp_fn])
@@ -1322,6 +1272,10 @@ fn run_cmds(cmds: String, show: bool, tip: &str) -> ResultType<()> {
allow_err!(std::fs::remove_file(tmp));
}
let _ = res?;
if tmp2.exists() {
allow_err!(std::fs::remove_file(tmp2));
bail!("{} failed", tip);
}
Ok(())
}
@@ -1357,7 +1311,7 @@ pub fn is_installed() -> bool {
service::ServiceAccess,
service_manager::{ServiceManager, ServiceManagerAccess},
};
let (_, _, _, exe, _) = get_install_info();
let (_, _, _, exe) = get_install_info();
if !std::fs::metadata(exe).is_ok() {
return false;
}
@@ -1372,18 +1326,8 @@ pub fn is_installed() -> bool {
return false;
}
pub fn get_installed_version() -> String {
let (_, _, _, exe, _) = get_install_info();
if let Ok(output) = std::process::Command::new(exe).arg("--version").output() {
for line in String::from_utf8_lossy(&output.stdout).lines() {
return line.to_owned();
}
}
"".to_owned()
}
fn get_reg(name: &str) -> String {
let (subkey, _, _, _, _) = get_install_info();
pub fn get_reg(name: &str) -> String {
let (subkey, _, _, _) = get_install_info();
get_reg_of(&subkey, name)
}
@@ -1434,7 +1378,7 @@ pub fn bootstrap() {
}
fn register_licence() -> String {
let (subkey, _, _, _, _) = get_install_info();
let (subkey, _, _, _) = get_install_info();
if let Ok(lic) = get_license_from_exe_name() {
format!(
"
@@ -1442,7 +1386,6 @@ fn register_licence() -> String {
reg add {subkey} /f /v Host /t REG_SZ /d \"{host}\"
reg add {subkey} /f /v Api /t REG_SZ /d \"{api}\"
",
subkey = subkey,
key = &lic.key,
host = &lic.host,
api = &lic.api,
@@ -1469,9 +1412,7 @@ Set oLink = oWS.CreateShortcut(sLinkFile)
oLink.TargetPath = \"{exe}\"
oLink.Arguments = \"--connect {id}\"
oLink.Save
",
exe = exe,
id = id,
"
),
"vbs",
"connect_shortcut",
@@ -2181,6 +2122,117 @@ pub fn is_process_consent_running() -> ResultType<bool> {
Ok(output.status.success() && !output.stdout.is_empty())
}
pub fn uninstall_service(show_new_window: bool) -> ResultType<()> {
let filter = format!(" /FI \"PID ne {}\"", get_current_pid());
Config::set_option("stop-service".into(), "Y".into());
let cmds = format!(
"
chcp 65001
sc stop {app_name}
sc delete {app_name}
if exist \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\" del /f /q \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\"
taskkill /F /IM {app_name}.exe{filter}
",
app_name = crate::get_app_name(),
);
if let Err(err) = run_cmds(cmds, false, "uninstall") {
Config::set_option("stop-service".into(), "".into());
bail!(err);
}
run_after_run_cmds(!show_new_window)?;
std::process::exit(0);
}
pub fn install_service() -> ResultType<()> {
let (_, _, _, exe) = get_install_info();
let tmp_path = std::env::temp_dir().to_string_lossy().to_string();
let tray_shortcut = get_tray_shortcut(&exe, &tmp_path)?;
let filter = format!(" /FI \"PID ne {}\"", get_current_pid());
Config::set_option("stop-service".into(), "".into());
let cmds = format!(
"
chcp 65001
cscript \"{tray_shortcut}\"
copy /Y \"{tmp_path}\\{app_name} Tray.lnk\" \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\\"
{import_config}
{create_service}
if exist \"{tray_shortcut}\" del /f /q \"{tray_shortcut}\"
taskkill /F /IM {app_name}.exe{filter}
",
app_name = crate::get_app_name(),
import_config = get_import_config(&exe),
create_service = get_create_service(&exe),
);
if let Err(err) = run_cmds(cmds, false, "install") {
Config::set_option("stop-service".into(), "Y".into());
bail!(err);
}
run_after_run_cmds(false)?;
std::process::exit(0);
}
pub fn get_tray_shortcut(exe: &str, tmp_path: &str) -> ResultType<String> {
Ok(write_cmds(
format!(
"
Set oWS = WScript.CreateObject(\"WScript.Shell\")
sLinkFile = \"{tmp_path}\\{app_name} Tray.lnk\"
Set oLink = oWS.CreateShortcut(sLinkFile)
oLink.TargetPath = \"{exe}\"
oLink.Arguments = \"--tray\"
oLink.Save
",
app_name = crate::get_app_name(),
),
"vbs",
"tray_shortcut",
)?
.to_str()
.unwrap_or("")
.to_owned())
}
fn get_import_config(exe: &str) -> String {
format!("
sc create {app_name} binpath= \"\\\"{exe}\\\" --import-config \\\"{config_path}\\\"\" start= auto DisplayName= \"{app_name} Service\"
sc start {app_name}
sc stop {app_name}
sc delete {app_name}
",
app_name = crate::get_app_name(),
config_path=Config::file().to_str().unwrap_or(""),
)
}
fn get_create_service(exe: &str) -> String {
let stop = Config::get_option("stop-service") == "Y";
if stop {
format!("
if exist \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\" del /f /q \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\"
", app_name = crate::get_app_name())
} else {
format!("
sc create {app_name} binpath= \"\\\"{exe}\\\" --service\" start= auto DisplayName= \"{app_name} Service\"
sc start {app_name}
",
app_name = crate::get_app_name())
}
}
fn run_after_run_cmds(silent: bool) -> ResultType<()> {
let (_, _, _, exe) = get_install_info();
std::thread::sleep(std::time::Duration::from_millis(2000));
if !silent {
std::process::Command::new(&exe).spawn()?;
}
if Config::get_option("stop-service") != "Y" {
std::process::Command::new(&exe).arg("--tray").spawn()?;
}
std::thread::sleep(std::time::Duration::from_millis(1000));
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -1,94 +1,3 @@
#[cfg(target_os = "windows")]
use super::ui_interface::get_option_opt;
#[cfg(target_os = "windows")]
use std::sync::{Arc, Mutex};
#[cfg(target_os = "windows")]
use trayicon::{MenuBuilder, TrayIconBuilder};
#[cfg(target_os = "windows")]
use winit::{
event::Event,
event_loop::{ControlFlow, EventLoop},
};
#[cfg(target_os = "windows")]
#[derive(Clone, Eq, PartialEq, Debug)]
enum Events {
DoubleClickTrayIcon,
StopService,
StartService,
}
#[cfg(target_os = "windows")]
pub fn start_tray() {
let event_loop = EventLoop::<Events>::with_user_event();
let proxy = event_loop.create_proxy();
let icon = include_bytes!("../res/tray-icon.ico");
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();
event_loop.run(move |event, _, control_flow| {
if get_option_opt("ipc-closed").is_some() {
*control_flow = ControlFlow::Exit;
return;
} else {
*control_flow = ControlFlow::Wait;
}
let stopped = is_service_stopped();
let state = if stopped { 2 } else { 1 };
let old = *old_state.lock().unwrap();
if state != old {
hbb_common::log::info!("State changed");
let mut m = MenuBuilder::new();
if state == 2 {
m = m.item(
&crate::client::translate("Start Service".to_owned()),
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;
}
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", "");
}
},
_ => (),
}
});
}
/// Check if service is stoped.
/// Return [`true`] if service is stoped, [`false`] otherwise.
#[inline]
#[cfg(target_os = "windows")]
fn is_service_stopped() -> bool {
if let Some(v) = get_option_opt("stop-service") {
v == "Y"
} else {
false
}
}
/// Start a tray icon in Linux
///
/// [Block]
@@ -96,13 +5,13 @@ fn is_service_stopped() -> bool {
#[cfg(target_os = "linux")]
pub fn start_tray() {}
#[cfg(target_os = "macos")]
#[cfg(any(target_os = "macos", target_os = "windows"))]
pub fn start_tray() {
use hbb_common::{allow_err, log};
allow_err!(make_tray());
}
#[cfg(target_os = "macos")]
#[cfg(any(target_os = "macos", target_os = "windows"))]
pub fn make_tray() -> hbb_common::ResultType<()> {
// https://github.com/tauri-apps/tray-icon/blob/dev/examples/tao.rs
use hbb_common::anyhow::Context;
@@ -111,13 +20,21 @@ pub fn make_tray() -> hbb_common::ResultType<()> {
menu::{Menu, MenuEvent, MenuItem},
ClickEvent, 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 => LIGHT,
_ => DARK,
};
let icon;
#[cfg(target_os = "macos")]
{
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");
icon = match mode {
dark_light::Mode::Dark => LIGHT,
_ => DARK,
};
}
#[cfg(target_os = "windows")]
{
icon = include_bytes!("../res/tray-icon.ico");
}
let (icon_rgba, icon_width, icon_height) = {
let image = image::load_from_memory(icon)
.context("Failed to open icon path")?
@@ -153,6 +70,7 @@ pub fn make_tray() -> hbb_common::ResultType<()> {
event_loop.run(move |_event, _, control_flow| {
if !docker_hiden {
#[cfg(target_os = "macos")]
crate::platform::macos::hide_dock();
docker_hiden = true;
}
@@ -160,14 +78,20 @@ pub fn make_tray() -> hbb_common::ResultType<()> {
if let Ok(event) = menu_channel.try_recv() {
if event.id == quit_i.id() {
#[cfg(target_os = "macos")]
crate::platform::macos::uninstall(false);
#[cfg(any(target_os = "linux", target_os = "windows"))]
crate::platform::uninstall_service(false).ok();
}
println!("{event:?}");
}
if let Ok(event) = tray_channel.try_recv() {
if event.event == ClickEvent::Double {
#[cfg(target_os = "macos")]
crate::platform::macos::handle_application_should_open_untitled_file();
#[cfg(target_os = "windows")]
crate::run_me(Vec::<&str>::new()).ok();
}
}
});

View File

@@ -74,28 +74,7 @@ pub fn install_me(_options: String, _path: String, _silent: bool, _debug: bool)
#[inline]
pub fn update_me(_path: String) {
#[cfg(target_os = "linux")]
{
allow_err!(crate::platform::linux::exec_privileged(&[
"apt", "install", "-f", &_path
]));
allow_err!(std::fs::remove_file(&_path));
allow_err!(crate::run_me(Vec::<&str>::new()));
}
#[cfg(windows)]
{
let mut path = _path;
if path.is_empty() {
if let Ok(tmp) = std::env::current_exe() {
path = tmp.to_string_lossy().to_string();
}
}
std::process::Command::new(path)
.arg("--update")
.spawn()
.ok();
std::process::exit(0);
}
goto_install();
}
#[inline]
@@ -296,11 +275,24 @@ pub fn set_options(m: HashMap<String, String>) {
#[inline]
pub fn set_option(key: String, value: String) {
let mut options = OPTIONS.lock().unwrap();
#[cfg(target_os = "macos")]
if &key == "stop-service" {
let is_stop = value == "Y";
if is_stop && crate::platform::macos::uninstall(true) {
return;
#[cfg(target_os = "macos")]
{
let is_stop = value == "Y";
if is_stop && crate::platform::macos::uninstall(true) {
return;
}
}
#[cfg(any(target_os = "windows"))]
{
if crate::platform::is_installed() {
if value == "Y" {
crate::platform::install_service().ok();
} else {
crate::platform::uninstall_service(true).ok();
}
return;
}
}
}
if value.is_empty() {
@@ -396,10 +388,8 @@ pub fn is_installed_lower_version() -> bool {
return false;
#[cfg(windows)]
{
let installed_version = crate::platform::windows::get_installed_version();
let a = hbb_common::get_version_number(crate::VERSION);
let b = hbb_common::get_version_number(&installed_version);
return a > b;
let b = crate::platform::windows::get_reg("BuildDate");
return crate::BUILD_DATE.cmp(&b).is_gt();
}
}