diff --git a/Cargo.lock b/Cargo.lock index e6942ef72..8d05bd3ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4093,6 +4093,7 @@ dependencies = [ "serde_derive", "serde_json 1.0.79", "sha2", + "simple_rc", "sys-locale", "sysinfo", "tray-item", @@ -4446,6 +4447,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f054c6c1a6e95179d6f23ed974060dcefb2d9388bb7256900badad682c499de4" +[[package]] +name = "simple_rc" +version = "0.1.0" +dependencies = [ + "confy", + "hbb_common", + "serde 1.0.136", + "serde_derive", + "walkdir", +] + [[package]] name = "siphasher" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index 4b5b5dd29..821de77a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ path = "src/naming.rs" inline = [] hbbs = [] cli = [] +with_rc = ["simple_rc"] use_samplerate = ["samplerate"] use_rubato = ["rubato"] use_dasp = ["dasp"] @@ -103,7 +104,7 @@ jni = "0.19.0" flutter_rust_bridge = "1.30.0" [workspace] -members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display"] +members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/simple_rc"] [package.metadata.winres] LegalCopyright = "Copyright © 2022 Purslane, Inc." @@ -117,6 +118,7 @@ winapi = { version = "0.3", features = [ "winnt" ] } [build-dependencies] cc = "1.0" hbb_common = { path = "libs/hbb_common" } +simple_rc = { path = "libs/simple_rc", optional = true } flutter_rust_bridge_codegen = "1.30.0" [dev-dependencies] diff --git a/build.rs b/build.rs index 0f734715a..4f7821012 100644 --- a/build.rs +++ b/build.rs @@ -27,6 +27,20 @@ fn build_manifest() { } } +#[cfg(all(windows, feature = "with_rc"))] +fn build_rc_source() { + use simple_rc::{generate_with_conf, Config, ConfigItem}; + generate_with_conf(&Config { + outfile: "src/rc.rs".to_owned(), + confs: vec![ConfigItem { + inc: "resources".to_owned(), + exc: vec![], + suppressed_front: "resources".to_owned(), + }], + }) + .unwrap(); +} + fn install_oboe() { let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); if target_os != "android" { @@ -87,6 +101,8 @@ fn main() { gen_flutter_rust_bridge(); return; } + #[cfg(all(windows, feature = "with_rc"))] + build_rc_source(); #[cfg(all(windows, feature = "inline"))] build_manifest(); #[cfg(windows)] diff --git a/libs/simple_rc/Cargo.toml b/libs/simple_rc/Cargo.toml new file mode 100644 index 000000000..89304524d --- /dev/null +++ b/libs/simple_rc/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "simple_rc" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde_derive = "1.0" +serde = "1.0" +walkdir = "2" +confy = { git = "https://github.com/open-trade/confy" } +hbb_common = { path = "../hbb_common" } diff --git a/libs/simple_rc/examples/generate.rs b/libs/simple_rc/examples/generate.rs new file mode 100644 index 000000000..2de39961a --- /dev/null +++ b/libs/simple_rc/examples/generate.rs @@ -0,0 +1,23 @@ +extern crate simple_rc; + +use simple_rc::*; + +fn main() { + { + const CONF_FILE: &str = "simple_rc.toml"; + generate(CONF_FILE).unwrap(); + } + + { + generate_with_conf(&Config { + outfile: "src/rc.rs".to_owned(), + confs: vec![ConfigItem { + inc: "D:/projects/windows/RustDeskTempTopMostWindow/x64/Release/xxx".to_owned(), + // exc: vec!["*.dll".to_owned(), "*.exe".to_owned()], + exc: vec![], + suppressed_front: "D:/projects/windows".to_owned(), + }], + }) + .unwrap(); + } +} diff --git a/libs/simple_rc/simple_rc.toml b/libs/simple_rc/simple_rc.toml new file mode 100644 index 000000000..bef976967 --- /dev/null +++ b/libs/simple_rc/simple_rc.toml @@ -0,0 +1,12 @@ +# The output source file +outfile = "src/rc.rs" + +# The resource config list. +[[confs]] +# The file or director to integrate. +inc = "D:/projects/windows/RustDeskTempTopMostWindow/x64/Release/xxx" +# The exclusions. +exc = ["*.dll", "*.exe"] +# The front path that will ignore for extracting. +# The following config will make base output path to be "RustDeskTempTopMostWindow/x64/Release/xxx". +suppressed_front = "D:/projects/windows" diff --git a/libs/simple_rc/src/lib.rs b/libs/simple_rc/src/lib.rs new file mode 100644 index 000000000..e59e0493f --- /dev/null +++ b/libs/simple_rc/src/lib.rs @@ -0,0 +1,208 @@ +use hbb_common::{bail, ResultType}; +use serde_derive::{Deserialize, Serialize}; +use std::{collections::HashMap, fs::File, io::prelude::*, path::Path}; +use walkdir::WalkDir; + +//mod rc; + +#[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)] +pub struct ConfigItem { + // include directory or file + pub inc: String, + // exclude files + pub exc: Vec, + // out_path = origin_path - suppressed_front + pub suppressed_front: String, +} + +#[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)] +pub struct Config { + // output source file + pub outfile: String, + // config items + pub confs: Vec, +} + +pub fn get_outin_files<'a>(item: &'a ConfigItem) -> ResultType> { + let mut outin_filemap = HashMap::new(); + + for entry in WalkDir::new(&item.inc).follow_links(true) { + let path = entry?.into_path(); + if path.is_file() { + let mut exclude = false; + for excfile in item.exc.iter() { + if excfile.starts_with("*.") { + if let Some(ext) = path.extension().and_then(|x| x.to_str()) { + if excfile.ends_with(&format!(".{}", ext)) { + exclude = true; + break; + } + } + } else { + if path.ends_with(Path::new(excfile)) { + exclude = true; + break; + } + } + } + if exclude { + continue; + } + + let mut suppressed_front = item.suppressed_front.clone(); + if !suppressed_front.is_empty() && suppressed_front.ends_with('/') { + suppressed_front.push('/'); + } + let outpath = path.strip_prefix(Path::new(&suppressed_front))?; + let outfile = if outpath.is_absolute() { + match outpath + .file_name() + .and_then(|f| f.to_str()) + .map(|f| f.to_string()) + { + None => { + bail!("Failed to get filename of {}", outpath.display()); + } + Some(s) => s, + } + } else { + match outpath.to_str() { + None => { + bail!("Failed to convert {} to string", outpath.display()); + } + // Simple replace \ to / here. + // A better way is to use lib [path-slash](https://github.com/rhysd/path-slash) + Some(s) => s.to_string().replace("\\", "/"), + } + }; + let infile = match path.canonicalize()?.to_str() { + None => { + bail!("Failed to get file path of {}", path.display()); + } + Some(s) => s.to_string(), + }; + if let Some(_) = outin_filemap.insert(outfile.clone(), infile) { + bail!("outfile {} is set before", outfile); + } + } + } + + Ok(outin_filemap) +} + +pub fn generate(conf_file: &str) -> ResultType<()> { + let conf = confy::load_path(conf_file)?; + generate_with_conf(&conf)?; + Ok(()) +} + +pub fn generate_with_conf<'a>(conf: &'a Config) -> ResultType<()> { + let mut outfile = File::create(&conf.outfile)?; + + outfile.write( + br##"use hbb_common::{bail, ResultType}; +use std::{ + fs::{self, File}, + io::prelude::*, + path::Path, +}; + +"##, + )?; + + outfile.write(b"#[allow(dead_code)]\n")?; + outfile.write(b"pub fn extract_resources(root_path: &str) -> ResultType<()> {\n")?; + outfile.write(b" let mut resources: Vec<(&str, &[u8])> = Vec::new();\n")?; + + let mut outin_files = HashMap::new(); + for item in conf.confs.iter() { + for (o, i) in get_outin_files(item)?.into_iter() { + if let Some(_) = outin_files.insert(o.clone(), i) { + bail!("outfile {} is set before", o); + } + } + } + + let mut count = 1; + for (o, i) in outin_files.iter() { + let mut infile = File::open(&i)?; + let mut buffer = Vec::::new(); + infile.read_to_end(&mut buffer)?; + + let var_outfile = format!("outfile_{}", count); + let var_outdata = format!("outdata_{}", count); + + write!(outfile, " let {} = \"{}\";\n", var_outfile, o)?; + write!(outfile, " let {}: &[u8] = &[\n ", var_outdata)?; + + let mut line_num = 20; + for v in buffer { + if line_num == 0 { + write!(outfile, "\n ")?; + line_num = 20; + } + write!(outfile, "{:#04x}, ", v)?; + line_num -= 1; + } + write!(outfile, "\n ];\n")?; + + write!( + outfile, + " resources.push(({}, &{}));\n", + var_outfile, var_outdata + )?; + + count += 1; + } + + outfile.write(b" do_extract(root_path, resources)?;\n")?; + outfile.write(b" Ok(())\n")?; + outfile.write(b"}\n")?; + + outfile.write( + br##" +#[allow(dead_code)] +fn do_extract(root_path: &str, resources: Vec<(&str, &[u8])>) -> ResultType<()> { + let mut root_path = root_path.replace("\\", "/"); + if !root_path.ends_with('/') { + root_path.push('/'); + } + let root_path = Path::new(&root_path); + for (outfile, data) in resources { + let outfile_path = root_path.join(outfile); + match outfile_path.parent().and_then(|p| p.to_str()) { + None => { + bail!("Failed to get parent of {}", outfile_path.display()); + } + Some(p) => { + fs::create_dir_all(p)?; + let mut of = File::create(outfile_path)?; + of.write_all(data)?; + of.flush()?; + } + } + } + Ok(()) +} +"##, + )?; + + outfile.flush()?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + let result = 2 + 2; + assert_eq!(result, 4); + } + + // #[test] + // fn test_extract() { + // use super::*; + // rc::extract_resources("D:").unwrap(); + // } +} diff --git a/libs/virtual_display/examples/idd_controller.rs b/libs/virtual_display/examples/idd_controller.rs index 92710ddf9..7d5677724 100644 --- a/libs/virtual_display/examples/idd_controller.rs +++ b/libs/virtual_display/examples/idd_controller.rs @@ -1,12 +1,14 @@ #[cfg(windows)] use virtual_display::win10::{idd, DRIVER_INSTALL_PATH}; +#[cfg(windows)] use std::{ ffi::CStr, io::{self, Read}, path::Path, }; +#[cfg(windows)] fn prompt_input() -> u8 { println!("Press key execute:"); println!(" 1. 'x' 1. exit"); @@ -24,6 +26,7 @@ fn prompt_input() -> u8 { .unwrap_or(0) } +#[cfg(windows)] unsafe fn plug_in(index: idd::UINT, edid: idd::UINT) { println!("Plug in monitor begin"); if idd::FALSE == idd::MonitorPlugIn(index, edid, 25) { @@ -48,6 +51,7 @@ unsafe fn plug_in(index: idd::UINT, edid: idd::UINT) { } } +#[cfg(windows)] unsafe fn plug_out(index: idd::UINT) { println!("Plug out monitor begin"); if idd::FALSE == idd::MonitorPlugOut(index) { @@ -58,88 +62,91 @@ unsafe fn plug_out(index: idd::UINT) { } fn main() { - let abs_path = Path::new(DRIVER_INSTALL_PATH).canonicalize().unwrap(); + #[cfg(windows)] + { + let abs_path = Path::new(DRIVER_INSTALL_PATH).canonicalize().unwrap(); - unsafe { - let invalid_device = 0 as idd::HSWDEVICE; - let mut h_sw_device = invalid_device; + unsafe { + let invalid_device = 0 as idd::HSWDEVICE; + let mut h_sw_device = invalid_device; - let full_inf_path: Vec = abs_path - .to_string_lossy() - .as_ref() - .encode_utf16() - .chain(Some(0).into_iter()) - .collect(); + let full_inf_path: Vec = abs_path + .to_string_lossy() + .as_ref() + .encode_utf16() + .chain(Some(0).into_iter()) + .collect(); - loop { - match prompt_input() as char { - 'x' => break, - 'i' => { - println!("Install or update driver begin, {}", abs_path.display()); - let mut reboot_required = idd::FALSE; - if idd::InstallUpdate(full_inf_path.as_ptr() as _, &mut reboot_required) - == idd::FALSE - { - println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap()); - } else { - println!( - "Install or update driver done, reboot is {} required", - if reboot_required == idd::FALSE { - "not" - } else { - "" - } - ); + loop { + match prompt_input() as char { + 'x' => break, + 'i' => { + println!("Install or update driver begin, {}", abs_path.display()); + let mut reboot_required = idd::FALSE; + if idd::InstallUpdate(full_inf_path.as_ptr() as _, &mut reboot_required) + == idd::FALSE + { + println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap()); + } else { + println!( + "Install or update driver done, reboot is {} required", + if reboot_required == idd::FALSE { + "not" + } else { + "" + } + ); + } } - } - 'u' => { - println!("Uninstall driver begin {}", abs_path.display()); - let mut reboot_required = idd::FALSE; - if idd::Uninstall(full_inf_path.as_ptr() as _, &mut reboot_required) - == idd::FALSE - { - println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap()); - } else { - println!( - "Uninstall driver done, reboot is {} required", - if reboot_required == idd::FALSE { - "not" - } else { - "" - } - ); + 'u' => { + println!("Uninstall driver begin {}", abs_path.display()); + let mut reboot_required = idd::FALSE; + if idd::Uninstall(full_inf_path.as_ptr() as _, &mut reboot_required) + == idd::FALSE + { + println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap()); + } else { + println!( + "Uninstall driver done, reboot is {} required", + if reboot_required == idd::FALSE { + "not" + } else { + "" + } + ); + } } - } - 'c' => { - println!("Create device begin"); - if h_sw_device != invalid_device { - println!("Device created before"); - continue; + 'c' => { + println!("Create device begin"); + if h_sw_device != invalid_device { + println!("Device created before"); + continue; + } + if idd::FALSE == idd::DeviceCreate(&mut h_sw_device) { + println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap()); + idd::DeviceClose(h_sw_device); + h_sw_device = invalid_device; + } else { + println!("Create device done"); + } } - if idd::FALSE == idd::DeviceCreate(&mut h_sw_device) { - println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap()); + 'd' => { + println!("Close device begin"); idd::DeviceClose(h_sw_device); h_sw_device = invalid_device; - } else { - println!("Create device done"); + println!("Close device done"); } + '1' => plug_in(0, 0), + '2' => plug_in(1, 0), + '3' => plug_in(2, 0), + '4' => plug_out(0), + '5' => plug_out(1), + '6' => plug_out(2), + _ => {} } - 'd' => { - println!("Close device begin"); - idd::DeviceClose(h_sw_device); - h_sw_device = invalid_device; - println!("Close device done"); - } - '1' => plug_in(0, 0), - '2' => plug_in(1, 0), - '3' => plug_in(2, 0), - '4' => plug_out(0), - '5' => plug_out(1), - '6' => plug_out(2), - _ => {} } - } - idd::DeviceClose(h_sw_device); + idd::DeviceClose(h_sw_device); + } } } diff --git a/src/lib.rs b/src/lib.rs index 8dafb727e..93cd67738 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,3 +42,6 @@ mod tray; #[cfg(windows)] pub mod clipboard_file; + +#[cfg(all(windows, feature = "with_rc"))] +pub mod rc; diff --git a/src/main.rs b/src/main.rs index d802b8ae9..86e015c9b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ // Specify the Windows subsystem to eliminate console window. // Requires Rust 1.18. -//#![windows_subsystem = "windows"] +#![windows_subsystem = "windows"] use hbb_common::log; use librustdesk::*; @@ -104,6 +104,10 @@ fn main() { "".to_owned() )); return; + } else if args[0] == "--extract" { + #[cfg(feature = "with_rc")] + hbb_common::allow_err!(crate::rc::extract_resources(&args[1])); + return; } } if args[0] == "--remove" { diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 6f45ba8ff..7aa0851b5 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -10,7 +10,6 @@ use std::io::prelude::*; use std::{ ffi::OsString, fs, io, mem, - path::Path, sync::{Arc, Mutex}, time::{Duration, Instant}, }; @@ -856,7 +855,7 @@ fn get_install_info_with_subkey(subkey: String) -> (String, String, String, Stri } pub fn update_me() -> ResultType<()> { - let (_, _, _, exe) = 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!( " @@ -865,12 +864,14 @@ pub fn update_me() -> ResultType<()> { taskkill /F /IM {broker_exe} taskkill /F /IM {app_name}.exe copy /Y \"{src_exe}\" \"{exe}\" + \"{src_exe}\" --extract \"{path}\" sc start {app_name} {lic} ", src_exe = src_exe, exe = exe, broker_exe = crate::ui::win_privacy::INJECTED_PROCESS_EXE, + path = path, app_name = crate::get_app_name(), lic = register_licence(), ); @@ -1023,6 +1024,7 @@ chcp 65001 md \"{path}\" copy /Y \"{src_exe}\" \"{exe}\" copy /Y \"{ORIGIN_PROCESS_EXE}\" \"{path}\\{broker_exe}\" +\"{src_exe}\" --extract \"{path}\" reg add {subkey} /f reg add {subkey} /f /v DisplayIcon /t REG_SZ /d \"{exe}\" reg add {subkey} /f /v DisplayName /t REG_SZ /d \"{app_name}\" diff --git a/src/rc.rs b/src/rc.rs new file mode 100644 index 000000000..ef86caaa3 --- /dev/null +++ b/src/rc.rs @@ -0,0 +1,38 @@ +use hbb_common::{bail, ResultType}; +use std::{ + fs::{self, File}, + io::prelude::*, + path::Path, +}; + +#[allow(dead_code)] +pub fn extract_resources(root_path: &str) -> ResultType<()> { + let mut resources: Vec<(&str, &[u8])> = Vec::new(); + resources.push((outfile_4, &outdata_4)); + do_extract(root_path, resources)?; + Ok(()) +} + +#[allow(dead_code)] +fn do_extract(root_path: &str, resources: Vec<(&str, &[u8])>) -> ResultType<()> { + let mut root_path = root_path.replace("\\", "/"); + if !root_path.ends_with('/') { + root_path.push('/'); + } + let root_path = Path::new(&root_path); + for (outfile, data) in resources { + let outfile_path = root_path.join(outfile); + match outfile_path.parent().and_then(|p| p.to_str()) { + None => { + bail!("Failed to get parent of {}", outfile_path.display()); + } + Some(p) => { + fs::create_dir_all(p)?; + let mut of = File::create(outfile_path)?; + of.write_all(data)?; + of.flush()?; + } + } + } + Ok(()) +} diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 1529253d7..aaf0249e4 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -24,8 +24,8 @@ use clipboard::{ }; use enigo::{self, Enigo, KeyboardControllable}; use hbb_common::fs::{ - can_enable_overwrite_detection, get_string, new_send_confirm, - DigestCheckResult, RemoveJobMeta, get_job, + can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult, + RemoveJobMeta, }; use hbb_common::{ allow_err, @@ -48,7 +48,7 @@ use hbb_common::{config::TransferSerde, fs::TransferJobMeta}; use crate::clipboard_file::*; use crate::{ client::*, - common::{self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL} + common::{self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL}, }; type Video = AssetPtr; @@ -267,7 +267,8 @@ impl Handler { std::env::set_var("KEYBOARD_ONLY", "y"); // pass to rdev use rdev::{EventType::*, *}; let func = move |evt: Event| { - if !IS_IN.load(Ordering::SeqCst) || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) { + if !IS_IN.load(Ordering::SeqCst) || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) + { return; } let (key, down) = match evt.event_type { @@ -1660,7 +1661,12 @@ impl Remote { Data::AddJob((id, path, to, file_num, include_hidden, is_remote)) => { let od = can_enable_overwrite_detection(self.handler.lc.read().unwrap().version); if is_remote { - log::debug!("new write waiting job {}, write to {} from remote {}", id, to, path); + log::debug!( + "new write waiting job {}, write to {} from remote {}", + id, + to, + path + ); let mut job = fs::TransferJob::new_write( id, path.clone(), @@ -1708,15 +1714,27 @@ impl Remote { if let Some(job) = get_job(id, &mut self.write_jobs) { job.is_last_job = false; allow_err!( - peer.send(&fs::new_send(id, job.remote.clone(), job.file_num, job.show_hidden)) + peer.send(&fs::new_send( + id, + job.remote.clone(), + job.file_num, + job.show_hidden + )) .await ); } } else { if let Some(job) = get_job(id, &mut self.read_jobs) { job.is_last_job = false; - allow_err!(peer.send(&fs::new_receive(id, job.path.to_string_lossy().to_string(), - job.file_num, job.files.clone())).await); + allow_err!( + peer.send(&fs::new_receive( + id, + job.path.to_string_lossy().to_string(), + job.file_num, + job.files.clone() + )) + .await + ); } } } @@ -2024,6 +2042,9 @@ impl Remote { Some(message::Union::file_response(fr)) => { match fr.union { Some(file_response::Union::dir(fd)) => { + #[cfg(windows)] + let entries = fd.entries.to_vec(); + #[cfg(not(windows))] let mut entries = fd.entries.to_vec(); #[cfg(not(windows))] {