From d825d30e53c6916c71dda69ab38c174b4ea88869 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 25 May 2021 12:01:27 +0800 Subject: [PATCH 01/24] fix linux restore button and clipboard initial sync issue --- src/common.rs | 28 ++++++++++++++++------------ src/server/clipboard_service.rs | 22 ++++++++++------------ src/ui/header.tis | 4 +++- src/ui/remote.rs | 22 ++++++++++++++++++++-- 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/src/common.rs b/src/common.rs index 87e0b98ff..bed64ecaf 100644 --- a/src/common.rs +++ b/src/common.rs @@ -50,6 +50,20 @@ pub fn valid_for_capslock(evt: &KeyEvent) -> bool { } } +pub fn create_clipboard_msg(content: String) -> Message { + let bytes = content.into_bytes(); + let compressed = compress_func(&bytes, COMPRESS_LEVEL); + let compress = compressed.len() < bytes.len(); + let content = if compress { compressed } else { bytes }; + let mut msg = Message::new(); + msg.set_clipboard(Clipboard { + compress, + content, + ..Default::default() + }); + msg +} + pub fn check_clipboard( ctx: &mut ClipboardContext, old: Option<&Arc>>, @@ -61,18 +75,8 @@ pub fn check_clipboard( let changed = content != *old.lock().unwrap(); if changed { log::info!("{} update found on {}", CLIPBOARD_NAME, side); - let bytes = content.clone().into_bytes(); - *old.lock().unwrap() = content; - let compressed = compress_func(&bytes, COMPRESS_LEVEL); - let compress = compressed.len() < bytes.len(); - let content = if compress { compressed } else { bytes }; - let mut msg = Message::new(); - msg.set_clipboard(Clipboard { - compress, - content, - ..Default::default() - }); - return Some(msg); + *old.lock().unwrap() = content.clone(); + return Some(create_clipboard_msg(content)); } } } diff --git a/src/server/clipboard_service.rs b/src/server/clipboard_service.rs index 5393d1f7d..25597d11c 100644 --- a/src/server/clipboard_service.rs +++ b/src/server/clipboard_service.rs @@ -6,7 +6,6 @@ pub use crate::common::{ struct State { ctx: Option, - initialized: bool, } impl Default for State { @@ -18,22 +17,18 @@ impl Default for State { None } }; - Self { - ctx, - initialized: false, - } + Self { ctx } } } impl super::service::Reset for State { fn reset(&mut self) { *CONTENT.lock().unwrap() = Default::default(); - self.initialized = false; } } pub fn new() -> GenericService { - let sp = GenericService::new(NAME, false); + let sp = GenericService::new(NAME, true); sp.repeat::(INTERVAL, run); sp } @@ -41,13 +36,16 @@ pub fn new() -> GenericService { fn run(sp: GenericService, state: &mut State) -> ResultType<()> { if let Some(ctx) = state.ctx.as_mut() { if let Some(msg) = check_clipboard(ctx, None) { - if !state.initialized { - state.initialized = true; - // ignore clipboard update before service start - return Ok(()); - } sp.send(msg); } + sp.snapshot(|sps| { + let txt = crate::CONTENT.lock().unwrap().clone(); + if !txt.is_empty() { + let msg_out = crate::create_clipboard_msg(txt); + sps.send_shared(Arc::new(msg_out)); + } + Ok(()) + })?; } Ok(()) } diff --git a/src/ui/header.tis b/src/ui/header.tis index 951236c89..96b722efb 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -36,7 +36,9 @@ function stateChanged() { cur_window_state = view.windowState; adjustBorder(); adaptDisplay(); - if (!is_linux) view.focus = handler; // this cause windows always topmost on linux + if (cur_window_state != View.WINDOW_MINIMIZED) { + view.focus = handler; // to make focus away from restore/maximize button, so that enter key work + } var fs = view.windowState == View.WINDOW_FULL_SCREEN; var el = $(#fullscreen); if (el) el.attributes.toggleClass("active", fs); diff --git a/src/ui/remote.rs b/src/ui/remote.rs index f18600fc9..359f11a4f 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -9,6 +9,7 @@ use hbb_common::{ fs, log, message_proto::*, protobuf::Message as _, + sleep, tokio::{ self, sync::mpsc, @@ -1145,7 +1146,7 @@ impl Remote { } fn start_clipboard(&mut self) -> Option> { - if self.handler.is_file_transfer() { + if self.handler.is_file_transfer() || self.handler.is_port_forward() { return None; } let (tx, rx) = std::sync::mpsc::channel(); @@ -1430,6 +1431,23 @@ impl Remote { } Some(login_response::Union::peer_info(pi)) => { self.handler.handle_peer_info(pi); + if !(self.handler.is_file_transfer() + || self.handler.is_port_forward() + || !*self.clipboard.read().unwrap() + || !*self.keyboard.read().unwrap() + || self.handler.lc.read().unwrap().disable_clipboard) + { + let txt = self.old_clipboard.lock().unwrap().clone(); + if !txt.is_empty() { + let msg_out = crate::create_clipboard_msg(txt); + let sender = self.sender.clone(); + tokio::spawn(async move { + // due to clipboard service interval time + sleep(common::CLIPBOARD_INTERVAL as f32 / 1_000.).await; + sender.send(Data::Message(msg_out)).ok(); + }); + } + } } _ => {} }, @@ -1587,7 +1605,7 @@ impl Interface for Handler { pi_sciter.set_item("sas_enabled", pi.sas_enabled); if self.is_file_transfer() { if pi.username.is_empty() { - self.on_error("No active user logged on, please connect and logon first."); + self.on_error("No active console user logged on, please connect and logon first."); return; } } else if !self.is_port_forward() { From 8708fb366a960a7ea4e64891bafbf564c9cc5d84 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 26 May 2021 12:42:21 +0800 Subject: [PATCH 02/24] righ key mapping and fix mac crash --- libs/enigo/src/lib.rs | 10 ++++-- libs/enigo/src/linux.rs | 5 ++- libs/enigo/src/macos/keycodes.rs | 1 + libs/enigo/src/macos/macos_impl.rs | 45 +++++++++++++++++++++--- libs/enigo/src/win/keycodes.rs | 5 +++ libs/enigo/src/win/win_impl.rs | 4 +++ libs/hbb_common/protos/message.proto | 5 ++- src/client.rs | 6 +++- src/server/input_service.rs | 7 ++-- src/ui/remote.rs | 51 ++++++++++++++++++++++++++++ 10 files changed, 128 insertions(+), 11 deletions(-) diff --git a/libs/enigo/src/lib.rs b/libs/enigo/src/lib.rs index bf289c8ed..e5a19750c 100644 --- a/libs/enigo/src/lib.rs +++ b/libs/enigo/src/lib.rs @@ -347,7 +347,7 @@ pub enum Key { /// Clear, /// - Menu, + Menu, // deprecated, use alt instead /// Pause, /// @@ -409,6 +409,12 @@ pub enum Key { /// NumpadEnter, /// + RightShift, + /// + RightControl, + /// + RightAlt, + /// /// Function, /// mac /// keyboard layout dependent key Layout(char), @@ -485,7 +491,7 @@ impl Enigo { /// ``` pub fn new() -> Self { #[cfg(any(target_os = "android", target_os = "ios"))] - return Enigo{}; + return Enigo {}; #[cfg(not(any(target_os = "android", target_os = "ios")))] Self::default() } diff --git a/libs/enigo/src/linux.rs b/libs/enigo/src/linux.rs index 0e8078b10..58f134a18 100644 --- a/libs/enigo/src/linux.rs +++ b/libs/enigo/src/linux.rs @@ -260,7 +260,7 @@ fn keysequence<'a>(key: Key) -> Cow<'a, str> { Key::Mute => "", Key::Scroll => "Scroll_Lock", Key::NumLock => "Num_Lock", - Key::RWin => "", + Key::RWin => "Super_R", Key::Apps => "", Key::Multiply => "KP_Multiply", Key::Add => "KP_Add", @@ -268,6 +268,9 @@ fn keysequence<'a>(key: Key) -> Cow<'a, str> { Key::Divide => "KP_Divide", Key::Equals => "KP_Equal", Key::NumpadEnter => "KP_Enter", + Key::RightShift => "Shift_R", + Key::RightControl => "Control_R", + Key::RightAlt => "Alt_R", Key::Command | Key::Super | Key::Windows | Key::Meta => "Super", diff --git a/libs/enigo/src/macos/keycodes.rs b/libs/enigo/src/macos/keycodes.rs index e946789b1..8dcd2191b 100644 --- a/libs/enigo/src/macos/keycodes.rs +++ b/libs/enigo/src/macos/keycodes.rs @@ -70,3 +70,4 @@ pub const kVK_ANSI_KeypadDivide: u16 = 0x4B; pub const kVK_ANSI_KeypadEnter: u16 = 0x4C; pub const kVK_ANSI_KeypadMinus: u16 = 0x4E; pub const kVK_ANSI_KeypadEquals: u16 = 0x51; +pub const kVK_RIGHT_COMMAND: u16 = 0x36; diff --git a/libs/enigo/src/macos/macos_impl.rs b/libs/enigo/src/macos/macos_impl.rs index 746526dcd..bb4875c08 100644 --- a/libs/enigo/src/macos/macos_impl.rs +++ b/libs/enigo/src/macos/macos_impl.rs @@ -142,6 +142,8 @@ pub const kCFStringEncodingUTF8: u32 = 134_217_984; #[link(name = "Carbon", kind = "framework")] extern "C" { fn TISCopyCurrentKeyboardInputSource() -> TISInputSourceRef; + fn TISCopyCurrentKeyboardLayoutInputSource() -> TISInputSourceRef; + fn TISCopyCurrentASCIICapableKeyboardLayoutInputSource() -> TISInputSourceRef; // extern void * // TISGetInputSourceProperty( @@ -583,6 +585,10 @@ impl Enigo { Key::Subtract => kVK_ANSI_KeypadMinus, Key::Equals => kVK_ANSI_KeypadEquals, Key::NumLock => kVK_ANSI_KeypadClear, + Key::RWin => kVK_RIGHT_COMMAND, + Key::RightShift => kVK_RightShift, + Key::RightControl => kVK_RightControl, + Key::RightAlt => kVK_RightOption, Key::Raw(raw_keycode) => raw_keycode, Key::Layout(c) => self.get_layoutdependent_keycode(c.to_string()), @@ -600,6 +606,7 @@ impl Enigo { } fn init_map(&mut self) { + println!("init_map"); self.keycode_to_string_map.insert("".to_owned(), 0); // loop through every keycode (0 - 127) for keycode in 0..128 { @@ -649,14 +656,44 @@ impl Enigo { fn create_string_for_key(&self, keycode: u16, modifier: u32) -> CFStringRef { let current_keyboard = unsafe { TISCopyCurrentKeyboardInputSource() }; - let layout_data = unsafe { - TISGetInputSourceProperty(current_keyboard, kTISPropertyUnicodeKeyLayoutData) - }; + let mut layout_data = std::ptr::null_mut(); + if !current_keyboard.is_null() { + layout_data = unsafe { + TISGetInputSourceProperty(current_keyboard, kTISPropertyUnicodeKeyLayoutData) + }; + } + if layout_data.is_null() { + // https://github.com/microsoft/vscode/issues/23833 + let current_keyboard = unsafe { TISCopyCurrentKeyboardLayoutInputSource() }; + if !current_keyboard.is_null() { + layout_data = unsafe { + TISGetInputSourceProperty(current_keyboard, kTISPropertyUnicodeKeyLayoutData) + }; + } + } + if layout_data.is_null() { + let current_keyboard = unsafe { TISCopyCurrentASCIICapableKeyboardLayoutInputSource() }; + if !current_keyboard.is_null() { + layout_data = unsafe { + TISGetInputSourceProperty(current_keyboard, kTISPropertyUnicodeKeyLayoutData) + }; + } + } + if layout_data.is_null() { + // to-do: try out manual mapping in https://github.com/stweil/OSXvnc + // we do see crash like this, also not easy to reproduce, no sure if it related +/* +0 rustdesk 0x000000010f921bc9 std::collections::hash::map::HashMap$LT$K$C$V$C$S$GT$::insert::h84e28c51a3292e7a + 473 +1 rustdesk 0x000000010f921884 enigo::macos::macos_impl::Enigo::key_to_keycode::h85ead82e9b1075ae + 1428 +2 rustdesk 0x000000010f922a8c _$LT$enigo..macos..macos_impl..Enigo$u20$as$u20$enigo..KeyboardControllable$GT$::key_down::h54f24da6d274b948 + 44 +*/ + return std::ptr::null() as _; + } let keyboard_layout = unsafe { CFDataGetBytePtr(layout_data) }; let mut keys_down: UInt32 = 0; - // let mut chars: *mut c_void;//[UniChar; 4]; let mut chars: u16 = 0; + // let mut chars: *mut c_void;//[UniChar; 4]; let mut real_length: UniCharCount = 0; unsafe { UCKeyTranslate( diff --git a/libs/enigo/src/win/keycodes.rs b/libs/enigo/src/win/keycodes.rs index 2dc99275a..58122cb83 100644 --- a/libs/enigo/src/win/keycodes.rs +++ b/libs/enigo/src/win/keycodes.rs @@ -7,9 +7,14 @@ pub const EVK_BACK: u16 = 0x08; pub const EVK_ESCAPE: u16 = 0x1b; pub const EVK_LWIN: u16 = 0x5b; pub const EVK_SHIFT: u16 = 0x10; +//pub const EVK_LSHIFT: u16 = 0xa0; +pub const EVK_RSHIFT: u16 = 0xa1; +//pub const EVK_LMENU: u16 = 0xa4; +pub const EVK_RMENU: u16 = 0xa5; pub const EVK_CAPITAL: u16 = 0x14; pub const EVK_MENU: u16 = 0x12; pub const EVK_LCONTROL: u16 = 0xa2; +pub const EVK_RCONTROL: u16 = 0xa3; pub const EVK_HOME: u16 = 0x24; pub const EVK_PRIOR: u16 = 0x21; pub const EVK_NEXT: u16 = 0x22; diff --git a/libs/enigo/src/win/win_impl.rs b/libs/enigo/src/win/win_impl.rs index df636e978..73917cde4 100644 --- a/libs/enigo/src/win/win_impl.rs +++ b/libs/enigo/src/win/win_impl.rs @@ -338,10 +338,14 @@ impl Enigo { Key::Divide => EVK_DIVIDE, Key::NumpadEnter => EVK_RETURN, Key::Equals => '=' as _, + Key::RightShift => EVK_RSHIFT, + Key::RightControl => EVK_RCONTROL, + Key::RightAlt => EVK_RMENU, Key::Raw(raw_keycode) => raw_keycode, Key::Layout(c) => self.get_layoutdependent_keycode(c.to_string()), Key::Super | Key::Command | Key::Windows | Key::Meta => EVK_LWIN, + _ => 0, } } diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 6a2a03299..a8e8d34b5 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -129,7 +129,7 @@ enum ControlKey { Numpad9 = 42; Cancel = 43; Clear = 44; - Menu = 45; + Menu = 45; // deprecated, use Alt instead Pause = 46; Kana = 47; Hangul = 48; @@ -157,6 +157,9 @@ enum ControlKey { Divide = 70; Equals = 71; NumpadEnter = 72; + RShift= 73; + RControl = 74; + RAlt = 75; CtrlAltDel = 100; LockScreen = 101; } diff --git a/src/client.rs b/src/client.rs index d2fea14c3..2fd6e1430 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1068,7 +1068,7 @@ lazy_static::lazy_static! { ("VK_RETURN", Key::ControlKey(ControlKey::Return)), ("VK_SHIFT", Key::ControlKey(ControlKey::Shift)), ("VK_CONTROL", Key::ControlKey(ControlKey::Control)), - ("VK_MENU", Key::ControlKey(ControlKey::Menu)), + ("VK_MENU", Key::ControlKey(ControlKey::Alt)), ("VK_PAUSE", Key::ControlKey(ControlKey::Pause)), ("VK_CAPITAL", Key::ControlKey(ControlKey::CapsLock)), ("VK_KANA", Key::ControlKey(ControlKey::Kana)), @@ -1107,6 +1107,10 @@ lazy_static::lazy_static! { ("VK_NUMPAD7", Key::ControlKey(ControlKey::Numpad7)), ("VK_NUMPAD8", Key::ControlKey(ControlKey::Numpad8)), ("VK_NUMPAD9", Key::ControlKey(ControlKey::Numpad9)), + ("RAlt", Key::ControlKey(ControlKey::RAlt)), + ("RWin", Key::ControlKey(ControlKey::RWin)), + ("RControl", Key::ControlKey(ControlKey::RControl)), + ("RShift", Key::ControlKey(ControlKey::RShift)), ("CTRL_ALT_DEL", Key::ControlKey(ControlKey::CtrlAltDel)), ("LOCK_SCREEN", Key::ControlKey(ControlKey::LockScreen)), ].iter().cloned().collect(); diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 363164ee8..957a01653 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -174,7 +174,6 @@ pub fn is_left_up(evt: &MouseEvent) -> bool { #[cfg(windows)] pub fn mouse_move_relative(x: i32, y: i32) { - #[cfg(windows)] crate::platform::windows::try_change_desktop(); let mut en = ENIGO.lock().unwrap(); en.mouse_move_relative(x, y); @@ -340,7 +339,7 @@ lazy_static::lazy_static! { (ControlKey::Numpad9, enigo::Key::Numpad9), (ControlKey::Cancel, enigo::Key::Cancel), (ControlKey::Clear, enigo::Key::Clear), - (ControlKey::Menu, enigo::Key::Menu), + (ControlKey::Menu, enigo::Key::Alt), (ControlKey::Pause, enigo::Key::Pause), (ControlKey::Kana, enigo::Key::Kana), (ControlKey::Hangul, enigo::Key::Hangul), @@ -368,6 +367,10 @@ lazy_static::lazy_static! { (ControlKey::Divide, enigo::Key::Divide), (ControlKey::Equals, enigo::Key::Equals), (ControlKey::NumpadEnter, enigo::Key::NumpadEnter), + (ControlKey::RAlt, enigo::Key::RightAlt), + (ControlKey::RWin, enigo::Key::RWin), + (ControlKey::RControl, enigo::Key::RightControl), + (ControlKey::RShift, enigo::Key::RightShift), ].iter().map(|(a, b)| (a.value(), b.clone())).collect(); static ref NUMPAD_KEY_MAP: HashMap = [ diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 359f11a4f..0af34704d 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -711,6 +711,7 @@ impl Handler { 0x4C => key_event.set_control_key(ControlKey::NumpadEnter), // numpad enter 0x69 => key_event.set_control_key(ControlKey::Snapshot), 0x72 => key_event.set_control_key(ControlKey::Help), + 0x6E => key_event.set_control_key(ControlKey::Apps), 0x47 => { key_event.set_control_key(if self.peer_platform() == "Mac OS" { ControlKey::Clear @@ -732,6 +733,7 @@ impl Handler { 0x91 => key_event.set_control_key(ControlKey::Scroll), 0x90 => key_event.set_control_key(ControlKey::NumLock), 0x5C => key_event.set_control_key(ControlKey::RWin), + 0x5B => key_event.set_control_key(ControlKey::Meta), 0x5D => key_event.set_control_key(ControlKey::Apps), 0xBE => key_event.set_chr('.' as _), 0xC0 => key_event.set_chr('`' as _), @@ -740,6 +742,44 @@ impl Handler { return None; } } + } else if cfg!(target_os = "linux") { + match code { + 65300 => key_event.set_control_key(ControlKey::Scroll), + 65421 => key_event.set_control_key(ControlKey::NumpadEnter), // numpad enter + 65407 => key_event.set_control_key(ControlKey::NumLock), + 65516 => key_event.set_control_key(ControlKey::RWin), + 65513 => key_event.set_control_key(ControlKey::Alt), + 65514 => key_event.set_control_key(ControlKey::RAlt), + 65508 => key_event.set_control_key(ControlKey::RControl), + 65506 => key_event.set_control_key(ControlKey::RShift), + 96 => key_event.set_chr('`' as _), + 46 => key_event.set_chr('.' as _), + 126 => key_event.set_chr('`' as _), + 33 => key_event.set_chr('1' as _), + 64 => key_event.set_chr('2' as _), + 35 => key_event.set_chr('3' as _), + 36 => key_event.set_chr('4' as _), + 37 => key_event.set_chr('5' as _), + 94 => key_event.set_chr('6' as _), + 38 => key_event.set_chr('7' as _), + 42 => key_event.set_chr('8' as _), + 40 => key_event.set_chr('9' as _), + 41 => key_event.set_chr('0' as _), + 95 => key_event.set_chr('-' as _), + 43 => key_event.set_chr('=' as _), + 123 => key_event.set_chr('[' as _), + 125 => key_event.set_chr(']' as _), + 124 => key_event.set_chr('\\' as _), + 58 => key_event.set_chr(';' as _), + 34 => key_event.set_chr('\'' as _), + 60 => key_event.set_chr(',' as _), + 62 => key_event.set_chr('.' as _), + 63 => key_event.set_chr('/' as _), + _ => { + log::error!("Unknown key code {}", code); + return None; + } + } } else { log::error!("Unknown key code {}", code); return None; @@ -820,6 +860,17 @@ impl Handler { extended, ); + let mut name = name; + + if extended { + match name.as_ref() { + "VK_CONTROL" => name = "RControl".to_owned(), + "VK_MENU" => name = "RAlt".to_owned(), + "VK_SHIFT" => name = "RShift".to_owned(), + _ => {} + } + } + if let Some(mut key_event) = self.get_key_event(down_or_up, &name, code) { // Linux has different repeated key down handling from mac and windows /* // below cause hang some time, not find why, so disable. so shift + repeat char not work for mac->linux, win->linux works fine, linux->linux not test yet From cac416bcc03db07e260bff63b8c957dd1e37ad79 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Thu, 27 May 2021 18:30:16 +0800 Subject: [PATCH 03/24] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f6224386e..fb46b93d8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # RustDesk | Your Remote Desktop Software -The best open-source remote desktop client software, written in Rust. Works out of the box, no configuration required. Great alternative to TeamViewer and AnyDesk! You have full control of your data, with no concerns about security. You can use our rendezvous/relay server, [set up your own](https://rustdesk.com/blog/id-relay-set/), or write your own rendezvous/relay server. +The best open-source remote desktop client software, written in Rust. Works out of the box, no configuration required. Great alternative to TeamViewer and AnyDesk! You have full control of your data, with no concerns about security. You can use our rendezvous/relay server, [set up your own](https://rustdesk.com/blog/id-relay-set/), or [write your own rendezvous/relay server](https://github.com/rustdesk/rustdesk-server-demo). [**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases) From 044f4cb12c4438ee4ef18d48fc73cd10575ca03f Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 27 May 2021 22:57:37 +0800 Subject: [PATCH 04/24] demo server --- src/demo-server.rs | 129 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 src/demo-server.rs diff --git a/src/demo-server.rs b/src/demo-server.rs new file mode 100644 index 000000000..694aa0272 --- /dev/null +++ b/src/demo-server.rs @@ -0,0 +1,129 @@ +use hbb_common::{ + bytes::BytesMut, + protobuf::Message as _, + rendezvous_proto::*, + tcp::{new_listener, FramedStream}, + tokio, + udp::FramedSocket, +}; + +#[tokio::main(basic_scheduler)] +async fn main() { + let mut socket = FramedSocket::new("0.0.0.0:21116").await.unwrap(); + let mut listener = new_listener("0.0.0.0:21116", false).await.unwrap(); + let mut rlistener = new_listener("0.0.0.0:21117", false).await.unwrap(); + let mut id_map = std::collections::HashMap::new(); + let relay_server = std::env::var("IP").unwrap(); + let mut saved_stream = None; + loop { + tokio::select! { + Some(Ok((bytes, addr))) = socket.next() => { + handle_udp(&mut socket, bytes, addr, &mut id_map).await; + } + Ok((stream, addr)) = listener.accept() => { + let mut stream = FramedStream::from(stream); + if let Some(Ok(bytes)) = stream.next_timeout(3000).await { + if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) { + match msg_in.union { + Some(rendezvous_message::Union::punch_hole_request(ph)) => { + println!("punch_hole_request {:?}", addr); + if let Some(addr) = id_map.get(&ph.id) { + let mut msg_out = RendezvousMessage::new(); + msg_out.set_request_relay(RequestRelay { + relay_server: relay_server.clone(), + ..Default::default() + }); + socket.send(&msg_out, addr.clone()).await.ok(); + saved_stream = Some(stream); + } + } + Some(rendezvous_message::Union::relay_response(_)) => { + println!("relay_response {:?}", addr); + let mut msg_out = RendezvousMessage::new(); + msg_out.set_relay_response(RelayResponse { + relay_server: relay_server.clone(), + ..Default::default() + }); + if let Some(mut stream) = saved_stream.take() { + stream.send(&msg_out).await.ok(); + if let Ok((stream_a, _)) = rlistener.accept().await { + let mut stream_a = FramedStream::from(stream_a); + stream_a.next_timeout(3_000).await; + if let Ok((stream_b, _)) = rlistener.accept().await { + let mut stream_b = FramedStream::from(stream_b); + stream_b.next_timeout(3_000).await; + relay(stream_a, stream_b, &mut socket, &mut id_map).await; + } + } + } + } + _ => {} + } + } + } + } + } + } +} + +async fn relay( + stream: FramedStream, + peer: FramedStream, + socket: &mut FramedSocket, + id_map: &mut std::collections::HashMap, +) { + let mut peer = peer; + let mut stream = stream; + peer.set_raw(); + stream.set_raw(); + loop { + tokio::select! { + Some(Ok((bytes, addr))) = socket.next() => { + handle_udp(socket, bytes, addr, id_map).await; + } + res = peer.next() => { + if let Some(Ok(bytes)) = res { + stream.send_bytes(bytes.into()).await.ok(); + } else { + break; + } + }, + res = stream.next() => { + if let Some(Ok(bytes)) = res { + peer.send_bytes(bytes.into()).await.ok(); + } else { + break; + } + }, + } + } +} + +async fn handle_udp( + socket: &mut FramedSocket, + bytes: BytesMut, + addr: std::net::SocketAddr, + id_map: &mut std::collections::HashMap, +) { + if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) { + match msg_in.union { + Some(rendezvous_message::Union::register_peer(rp)) => { + println!("register_peer {:?}", addr); + id_map.insert(rp.id, addr); + let mut msg_out = RendezvousMessage::new(); + msg_out.set_register_peer_response(RegisterPeerResponse::new()); + socket.send(&msg_out, addr).await.ok(); + } + Some(rendezvous_message::Union::register_pk(_)) => { + println!("register_pk {:?}", addr); + let mut msg_out = RendezvousMessage::new(); + msg_out.set_register_pk_response(RegisterPkResponse { + result: register_pk_response::Result::OK.into(), + ..Default::default() + }); + socket.send(&msg_out, addr).await.ok(); + } + _ => {} + } + } +} From 3be1971ca49a69d696a7a8ddd8948c0759e3c1e4 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 27 May 2021 22:57:52 +0800 Subject: [PATCH 05/24] moved to rustdesk-server-demo repo --- src/demo-server.rs | 129 --------------------------------------------- 1 file changed, 129 deletions(-) delete mode 100644 src/demo-server.rs diff --git a/src/demo-server.rs b/src/demo-server.rs deleted file mode 100644 index 694aa0272..000000000 --- a/src/demo-server.rs +++ /dev/null @@ -1,129 +0,0 @@ -use hbb_common::{ - bytes::BytesMut, - protobuf::Message as _, - rendezvous_proto::*, - tcp::{new_listener, FramedStream}, - tokio, - udp::FramedSocket, -}; - -#[tokio::main(basic_scheduler)] -async fn main() { - let mut socket = FramedSocket::new("0.0.0.0:21116").await.unwrap(); - let mut listener = new_listener("0.0.0.0:21116", false).await.unwrap(); - let mut rlistener = new_listener("0.0.0.0:21117", false).await.unwrap(); - let mut id_map = std::collections::HashMap::new(); - let relay_server = std::env::var("IP").unwrap(); - let mut saved_stream = None; - loop { - tokio::select! { - Some(Ok((bytes, addr))) = socket.next() => { - handle_udp(&mut socket, bytes, addr, &mut id_map).await; - } - Ok((stream, addr)) = listener.accept() => { - let mut stream = FramedStream::from(stream); - if let Some(Ok(bytes)) = stream.next_timeout(3000).await { - if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) { - match msg_in.union { - Some(rendezvous_message::Union::punch_hole_request(ph)) => { - println!("punch_hole_request {:?}", addr); - if let Some(addr) = id_map.get(&ph.id) { - let mut msg_out = RendezvousMessage::new(); - msg_out.set_request_relay(RequestRelay { - relay_server: relay_server.clone(), - ..Default::default() - }); - socket.send(&msg_out, addr.clone()).await.ok(); - saved_stream = Some(stream); - } - } - Some(rendezvous_message::Union::relay_response(_)) => { - println!("relay_response {:?}", addr); - let mut msg_out = RendezvousMessage::new(); - msg_out.set_relay_response(RelayResponse { - relay_server: relay_server.clone(), - ..Default::default() - }); - if let Some(mut stream) = saved_stream.take() { - stream.send(&msg_out).await.ok(); - if let Ok((stream_a, _)) = rlistener.accept().await { - let mut stream_a = FramedStream::from(stream_a); - stream_a.next_timeout(3_000).await; - if let Ok((stream_b, _)) = rlistener.accept().await { - let mut stream_b = FramedStream::from(stream_b); - stream_b.next_timeout(3_000).await; - relay(stream_a, stream_b, &mut socket, &mut id_map).await; - } - } - } - } - _ => {} - } - } - } - } - } - } -} - -async fn relay( - stream: FramedStream, - peer: FramedStream, - socket: &mut FramedSocket, - id_map: &mut std::collections::HashMap, -) { - let mut peer = peer; - let mut stream = stream; - peer.set_raw(); - stream.set_raw(); - loop { - tokio::select! { - Some(Ok((bytes, addr))) = socket.next() => { - handle_udp(socket, bytes, addr, id_map).await; - } - res = peer.next() => { - if let Some(Ok(bytes)) = res { - stream.send_bytes(bytes.into()).await.ok(); - } else { - break; - } - }, - res = stream.next() => { - if let Some(Ok(bytes)) = res { - peer.send_bytes(bytes.into()).await.ok(); - } else { - break; - } - }, - } - } -} - -async fn handle_udp( - socket: &mut FramedSocket, - bytes: BytesMut, - addr: std::net::SocketAddr, - id_map: &mut std::collections::HashMap, -) { - if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) { - match msg_in.union { - Some(rendezvous_message::Union::register_peer(rp)) => { - println!("register_peer {:?}", addr); - id_map.insert(rp.id, addr); - let mut msg_out = RendezvousMessage::new(); - msg_out.set_register_peer_response(RegisterPeerResponse::new()); - socket.send(&msg_out, addr).await.ok(); - } - Some(rendezvous_message::Union::register_pk(_)) => { - println!("register_pk {:?}", addr); - let mut msg_out = RendezvousMessage::new(); - msg_out.set_register_pk_response(RegisterPkResponse { - result: register_pk_response::Result::OK.into(), - ..Default::default() - }); - socket.send(&msg_out, addr).await.ok(); - } - _ => {} - } - } -} From ef6103da254e7fdc9b2d7e1b335f83b601908a1e Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Fri, 28 May 2021 13:26:48 +0800 Subject: [PATCH 06/24] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fb46b93d8..a8ac64a1a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # RustDesk | Your Remote Desktop Software -The best open-source remote desktop client software, written in Rust. Works out of the box, no configuration required. Great alternative to TeamViewer and AnyDesk! You have full control of your data, with no concerns about security. You can use our rendezvous/relay server, [set up your own](https://rustdesk.com/blog/id-relay-set/), or [write your own rendezvous/relay server](https://github.com/rustdesk/rustdesk-server-demo). +An open-source remote desktop client software, written in Rust. Works out of the box, no configuration required. Great alternative to TeamViewer and AnyDesk! You have full control of your data, with no concerns about security. You can use our rendezvous/relay server, [set up your own](https://rustdesk.com/blog/id-relay-set/), or [write your own rendezvous/relay server](https://github.com/rustdesk/rustdesk-server-demo). [**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases) From 5c541179dc9097a9b6c616a5215eb66fac15b237 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 31 May 2021 01:08:27 +0800 Subject: [PATCH 07/24] update_pk if mismatch --- src/server.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/server.rs b/src/server.rs index 8f13901b1..359c3378b 100644 --- a/src/server.rs +++ b/src/server.rs @@ -121,7 +121,10 @@ async fn create_tcp_connection_( let mut key = [0u8; secretbox::KEYBYTES]; key[..].copy_from_slice(&symmetric_key); stream.set_key(secretbox::Key(key)); - } else if !pk.asymmetric_value.is_empty() { + } else if pk.asymmetric_value.is_empty() { + // force a trial to update_pk to rendezvous server + Config::set_key_confirmed(false); + } else { bail!("Handshake failed: invalid public sign key length from peer"); } } else { From ffcbc2abaa92485fe674edd36cc4d11a3809551d Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 1 Jun 2021 13:05:04 +0800 Subject: [PATCH 08/24] more for right key --- src/server/input_service.rs | 17 +++++++++++++++-- src/ui/remote.rs | 20 ++++++++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 957a01653..879a7dea3 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -186,6 +186,19 @@ fn modifier_sleep() { std::thread::sleep(std::time::Duration::from_nanos(1)); } +#[cfg(not(target_os = "macos"))] +#[inline] +fn get_modifier_state(key: enigo::Key, en: &mut Enigo) -> bool { + let x = en.get_key_state(key.clone()); + match key { + enigo::Key::Shift => x || en.get_key_state(enigo::Key::RightShift), + enigo::Key::Control => x || en.get_key_state(enigo::Key::RightControl), + enigo::Key::Alt => x || en.get_key_state(enigo::Key::RightAlt), + enigo::Key::Meta => x || en.get_key_state(enigo::Key::RWin), + _ => x, + } +} + pub fn handle_mouse(evt: &MouseEvent, conn: i32) { #[cfg(target_os = "macos")] if !*IS_SERVER { @@ -220,7 +233,7 @@ fn handle_mouse_(evt: &MouseEvent, conn: i32) { en.add_flag(key); #[cfg(not(target_os = "macos"))] if key != &enigo::Key::CapsLock && key != &enigo::Key::NumLock { - if !en.get_key_state(key.clone()) { + if !get_modifier_state(key.clone(), &mut en) { en.key_down(key.clone()).ok(); modifier_sleep(); to_release.push(key); @@ -428,7 +441,7 @@ fn handle_key_(evt: &KeyEvent) { has_numlock = true; } } else { - if !en.get_key_state(key.clone()) { + if !get_modifier_state(key.clone(), &mut en) { en.key_down(key.clone()).ok(); modifier_sleep(); to_release.push(key); diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 0af34704d..17e6c8af1 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -899,16 +899,28 @@ impl Handler { } } */ - if alt && !crate::is_control_key(&key_event, &ControlKey::Alt) { + if alt + && !crate::is_control_key(&key_event, &ControlKey::Alt) + && !crate::is_control_key(&key_event, &ControlKey::RAlt) + { key_event.modifiers.push(ControlKey::Alt.into()); } - if shift && !crate::is_control_key(&key_event, &ControlKey::Shift) { + if shift + && !crate::is_control_key(&key_event, &ControlKey::Shift) + && !crate::is_control_key(&key_event, &ControlKey::RShift) + { key_event.modifiers.push(ControlKey::Shift.into()); } - if ctrl && !crate::is_control_key(&key_event, &ControlKey::Control) { + if ctrl + && !crate::is_control_key(&key_event, &ControlKey::Control) + && !crate::is_control_key(&key_event, &ControlKey::RControl) + { key_event.modifiers.push(ControlKey::Control.into()); } - if command && !crate::is_control_key(&key_event, &ControlKey::Meta) { + if command + && !crate::is_control_key(&key_event, &ControlKey::Meta) + && !crate::is_control_key(&key_event, &ControlKey::RWin) + { key_event.modifiers.push(ControlKey::Meta.into()); } if crate::is_control_key(&key_event, &ControlKey::CapsLock) { From 31e6108f4088e454c55a6dc3be898e8c17fe4c3f Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 2 Jun 2021 09:55:38 +0800 Subject: [PATCH 09/24] move confy here --- Cargo.lock | 30 +++- README.md | 2 + libs/confy/.gitignore | 14 ++ libs/confy/Cargo.toml | 26 +++ libs/confy/LICENSE | 21 +++ libs/confy/README.md | 39 +++++ libs/confy/examples/simple.rs | 29 ++++ libs/confy/src/lib.rs | 300 ++++++++++++++++++++++++++++++++++ libs/confy/src/utils.rs | 16 ++ libs/hbb_common/Cargo.toml | 2 +- 10 files changed, 477 insertions(+), 2 deletions(-) create mode 100644 libs/confy/.gitignore create mode 100644 libs/confy/Cargo.toml create mode 100644 libs/confy/LICENSE create mode 100644 libs/confy/README.md create mode 100644 libs/confy/examples/simple.rs create mode 100644 libs/confy/src/lib.rs create mode 100644 libs/confy/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 06a12f53b..069688c9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -461,10 +461,11 @@ dependencies = [ [[package]] name = "confy" version = "0.4.1" -source = "git+https://github.com/open-trade/confy#27fa12941291b44ccd856aef4a5452c1eb646047" dependencies = [ "directories", "serde 1.0.125", + "serde_derive", + "serde_yaml", "toml", ] @@ -1763,6 +1764,12 @@ dependencies = [ "num-traits 0.2.14", ] +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + [[package]] name = "lock_api" version = "0.3.4" @@ -3196,6 +3203,18 @@ dependencies = [ "serde 1.0.125", ] +[[package]] +name = "serde_yaml" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23" +dependencies = [ + "dtoa", + "linked-hash-map", + "serde 1.0.125", + "yaml-rust", +] + [[package]] name = "sha2" version = "0.9.3" @@ -4115,6 +4134,15 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "yansi" version = "0.5.0" diff --git a/README.md b/README.md index a8ac64a1a..8bb28514d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # RustDesk | Your Remote Desktop Software +Chat with us: [Discord](https://discord.gg/nDceKgxnkV) + An open-source remote desktop client software, written in Rust. Works out of the box, no configuration required. Great alternative to TeamViewer and AnyDesk! You have full control of your data, with no concerns about security. You can use our rendezvous/relay server, [set up your own](https://rustdesk.com/blog/id-relay-set/), or [write your own rendezvous/relay server](https://github.com/rustdesk/rustdesk-server-demo). [**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases) diff --git a/libs/confy/.gitignore b/libs/confy/.gitignore new file mode 100644 index 000000000..55e830d9e --- /dev/null +++ b/libs/confy/.gitignore @@ -0,0 +1,14 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +/target +**/*.rs.bk +Cargo.lock diff --git a/libs/confy/Cargo.toml b/libs/confy/Cargo.toml new file mode 100644 index 000000000..749ab44a8 --- /dev/null +++ b/libs/confy/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "confy" +version = "0.4.1" +authors = ["Katharina Fey "] +description = "Boilerplate-free configuration management" +license = "MIT/X11 OR Apache-2.0" +documentation = "https://docs.rs/confy" +repository = "https://github.com/rust-clique/confy" +edition = "2018" + +[dependencies] +serde = "^1.0" +toml = { version = "^0.5", optional = true } +directories = "^2.0" +serde_yaml = { version = "0.8", optional = true } + +[features] +default = ["toml_conf"] +toml_conf = ["toml"] +yaml_conf = ["serde_yaml"] + +[[example]] +name = "simple" + +[dev-dependencies] +serde_derive = "^1.0" diff --git a/libs/confy/LICENSE b/libs/confy/LICENSE new file mode 100644 index 000000000..74c2a263c --- /dev/null +++ b/libs/confy/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 rust-clique + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libs/confy/README.md b/libs/confy/README.md new file mode 100644 index 000000000..1b4cebb52 --- /dev/null +++ b/libs/confy/README.md @@ -0,0 +1,39 @@ +# confy + +Chat with us: [Discord](https://discord.gg/dwq4Zme) + +Zero-boilerplate configuration management. + +Focus on storing the right data, instead of worrying about how or where to store it. + +```rust +use serde_derive::{Serialize, Deserialize}; + +#[derive(Default, Debug, Serialize, Deserialize)] +struct MyConfig { + version: u8, + api_key: String, +} + +fn main() -> Result<(), ::std::io::Error> { + let cfg: MyConfig = confy::load("my-app-name")?; + dbg!(cfg); + Ok(()) +} +``` + +## Using yaml +Enabling the `yaml_conf` feature while disabling the default `toml_conf` +feature causes confy to use a YAML config file instead of TOML. + +``` +[dependencies.confy] +features = ["yaml_conf"] +default-features = false +``` + +## Breakings changes +Starting with version 0.4.0 the configuration file are stored in the expected place for your system. See the [`directories`] crates for more information. +Before version 0.4.0, the configuration file was written in the current directory. + +[`directories`]: https://crates.io/crates/directories diff --git a/libs/confy/examples/simple.rs b/libs/confy/examples/simple.rs new file mode 100644 index 000000000..f95bb6416 --- /dev/null +++ b/libs/confy/examples/simple.rs @@ -0,0 +1,29 @@ +//! The most simplest examples of how to use confy + +extern crate confy; + +#[macro_use] +extern crate serde_derive; + +#[derive(Debug, Serialize, Deserialize)] +struct ConfyConfig { + name: String, + comfy: bool, + foo: i64, +} + +impl Default for ConfyConfig { + fn default() -> Self { + ConfyConfig { + name: "Unknown".to_string(), + comfy: true, + foo: 42, + } + } +} + +fn main() -> Result<(), confy::ConfyError> { + let cfg: ConfyConfig = confy::load("confy_simple_app")?; + println!("{:#?}", cfg); + Ok(()) +} diff --git a/libs/confy/src/lib.rs b/libs/confy/src/lib.rs new file mode 100644 index 000000000..eaccaae9c --- /dev/null +++ b/libs/confy/src/lib.rs @@ -0,0 +1,300 @@ +//! Zero-boilerplate configuration management +//! +//! ## Why? +//! +//! There are a lot of different requirements when +//! selecting, loading and writing a config, +//! depending on the operating system and other +//! environment factors. +//! +//! In many applications this burden is left to you, +//! the developer of an application, to figure out +//! where to place the configuration files. +//! +//! This is where `confy` comes in. +//! +//! ## Idea +//! +//! `confy` takes care of figuring out operating system +//! specific and environment paths before reading and +//! writing a configuration. +//! +//! It gives you easy access to a configuration file +//! which is mirrored into a Rust `struct` via [serde]. +//! This way you only need to worry about the layout of +//! your configuration, not where and how to store it. +//! +//! [serde]: https://docs.rs/crates/serde +//! +//! `confy` uses the [`Default`] trait in Rust to automatically +//! create a new configuration, if none is available to read +//! from yet. +//! This means that you can simply assume your application +//! to have a configuration, which will be created with +//! default values of your choosing, without requiring +//! any special logic to handle creation. +//! +//! [`Default`]: https://doc.rust-lang.org/std/default/trait.Default.html +//! +//! ```rust,no_run +//! use serde_derive::{Serialize, Deserialize}; +//! +//! #[derive(Serialize, Deserialize)] +//! struct MyConfig { +//! version: u8, +//! api_key: String, +//! } +//! +//! /// `MyConfig` implements `Default` +//! impl ::std::default::Default for MyConfig { +//! fn default() -> Self { Self { version: 0, api_key: "".into() } } +//! } +//! +//! fn main() -> Result<(), confy::ConfyError> { +//! let cfg = confy::load("my-app-name")?; +//! Ok(()) +//! } +//! ``` +//! +//! Updating the configuration is then done via the [`store`] function. +//! +//! [`store`]: fn.store.html +//! + +mod utils; +use utils::*; + +use directories::ProjectDirs; +use serde::{de::DeserializeOwned, Serialize}; +use std::error::Error; +use std::fmt; +use std::fs::{self, File, OpenOptions}; +use std::io::{ErrorKind::NotFound, Write}; +use std::path::{Path, PathBuf}; + +#[cfg(not(any(feature = "toml_conf", feature = "yaml_conf")))] +compile_error!("Exactly one config language feature must be enabled to use \ +confy. Please enable one of either the `toml_conf` or `yaml_conf` \ +features."); + +#[cfg(all(feature = "toml_conf", feature = "yaml_conf"))] +compile_error!("Exactly one config language feature must be enabled to compile \ +confy. Please disable one of either the `toml_conf` or `yaml_conf` features. \ +NOTE: `toml_conf` is a default feature, so disabling it might mean switching off \ +default features for confy in your Cargo.toml"); + +#[cfg(all(feature = "toml_conf", not(feature = "yaml_conf")))] +const EXTENSION: &str = "toml"; + +#[cfg(feature = "yaml_conf")] +const EXTENSION: &str = "yml"; + +/// Load an application configuration from disk +/// +/// A new configuration file is created with default values if none +/// exists. +/// +/// Errors that are returned from this function are I/O related, +/// for example if the writing of the new configuration fails +/// or `confy` encounters an operating system or environment +/// that it does not support. +/// +/// **Note:** The type of configuration needs to be declared in some way +/// that is inferrable by the compiler. Also note that your +/// configuration needs to implement `Default`. +/// +/// ```rust,no_run +/// # use confy::ConfyError; +/// # use serde_derive::{Serialize, Deserialize}; +/// # fn main() -> Result<(), ConfyError> { +/// #[derive(Default, Serialize, Deserialize)] +/// struct MyConfig {} +/// +/// let cfg: MyConfig = confy::load("my-app-name")?; +/// # Ok(()) +/// # } +/// ``` +pub fn load(name: &str) -> Result { + let project = ProjectDirs::from("rs", "", name).ok_or(ConfyError::BadConfigDirectoryStr)?; + + let config_dir_str = get_configuration_directory_str(&project)?; + + let path: PathBuf = [config_dir_str, &format!("{}.{}", name, EXTENSION)].iter().collect(); + + load_path(path) +} + +/// Load an application configuration from a specified path. +/// +/// This is an alternate version of [`load`] that allows the specification of +/// an aritrary path instead of a system one. For more information on errors +/// and behavior, see [`load`]'s documentation. +/// +/// [`load`]: fn.load.html +pub fn load_path(path: impl AsRef) -> Result { + match File::open(&path) { + Ok(mut cfg) => { + let cfg_string = cfg + .get_string() + .map_err(ConfyError::ReadConfigurationFileError)?; + + #[cfg(feature = "toml_conf")] { + let cfg_data = toml::from_str(&cfg_string); + cfg_data.map_err(ConfyError::BadTomlData) + } + #[cfg(feature = "yaml_conf")] { + let cfg_data = serde_yaml::from_str(&cfg_string); + cfg_data.map_err(ConfyError::BadYamlData) + } + + } + Err(ref e) if e.kind() == NotFound => { + if let Some(parent) = path.as_ref().parent() { + fs::create_dir_all(parent) + .map_err(ConfyError::DirectoryCreationFailed)?; + } + store_path(path, T::default())?; + Ok(T::default()) + } + Err(e) => Err(ConfyError::GeneralLoadError(e)), + } +} + +/// The errors the confy crate can encounter. +#[derive(Debug)] +pub enum ConfyError { + #[cfg(feature = "toml_conf")] + BadTomlData(toml::de::Error), + + #[cfg(feature = "yaml_conf")] + BadYamlData(serde_yaml::Error), + + DirectoryCreationFailed(std::io::Error), + GeneralLoadError(std::io::Error), + BadConfigDirectoryStr, + + #[cfg(feature = "toml_conf")] + SerializeTomlError(toml::ser::Error), + + #[cfg(feature = "yaml_conf")] + SerializeYamlError(serde_yaml::Error), + + WriteConfigurationFileError(std::io::Error), + ReadConfigurationFileError(std::io::Error), + OpenConfigurationFileError(std::io::Error), +} + +impl fmt::Display for ConfyError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + + #[cfg(feature = "toml_conf")] + ConfyError::BadTomlData(e) => write!(f, "Bad TOML data: {}", e), + #[cfg(feature = "toml_conf")] + ConfyError::SerializeTomlError(_) => write!(f, "Failed to serialize configuration data into TOML."), + + #[cfg(feature = "yaml_conf")] + ConfyError::BadYamlData(e) => write!(f, "Bad YAML data: {}", e), + #[cfg(feature = "yaml_conf")] + ConfyError::SerializeYamlError(_) => write!(f, "Failed to serialize configuration data into YAML."), + + ConfyError::DirectoryCreationFailed(e) => write!(f, "Failed to create directory: {}", e), + ConfyError::GeneralLoadError(_) => write!(f, "Failed to load configuration file."), + ConfyError::BadConfigDirectoryStr => write!(f, "Failed to convert directory name to str."), + ConfyError::WriteConfigurationFileError(_) => write!(f, "Failed to write configuration file."), + ConfyError::ReadConfigurationFileError(_) => write!(f, "Failed to read configuration file."), + ConfyError::OpenConfigurationFileError(_) => write!(f, "Failed to open configuration file."), + } + } +} + +impl Error for ConfyError {} + +/// Save changes made to a configuration object +/// +/// This function will update a configuration, +/// with the provided values, and create a new one, +/// if none exists. +/// +/// You can also use this function to create a new configuration +/// with different initial values than which are provided +/// by your `Default` trait implementation, or if your +/// configuration structure _can't_ implement `Default`. +/// +/// ```rust,no_run +/// # use serde_derive::{Serialize, Deserialize}; +/// # use confy::ConfyError; +/// # fn main() -> Result<(), ConfyError> { +/// #[derive(Serialize, Deserialize)] +/// struct MyConf {} +/// +/// let my_cfg = MyConf {}; +/// confy::store("my-app-name", my_cfg)?; +/// # Ok(()) +/// # } +/// ``` +/// +/// Errors returned are I/O errors related to not being +/// able to write the configuration file or if `confy` +/// encounters an operating system or environment it does +/// not support. +pub fn store(name: &str, cfg: T) -> Result<(), ConfyError> { + let project = ProjectDirs::from("rs", "", name).ok_or(ConfyError::BadConfigDirectoryStr)?; + fs::create_dir_all(project.config_dir()).map_err(ConfyError::DirectoryCreationFailed)?; + + let config_dir_str = get_configuration_directory_str(&project)?; + + let path: PathBuf = [config_dir_str, &format!("{}.{}", name, EXTENSION)].iter().collect(); + + store_path(path, cfg) +} + +/// Save changes made to a configuration object at a specified path +/// +/// This is an alternate version of [`store`] that allows the specification of +/// an aritrary path instead of a system one. For more information on errors +/// and behavior, see [`store`]'s documentation. +/// +/// [`store`]: fn.store.html +pub fn store_path(path: impl AsRef, cfg: T) -> Result<(), ConfyError> { + let path = path.as_ref(); + let mut path_tmp = path.to_path_buf(); + use std::time::{SystemTime, UNIX_EPOCH}; + let mut i = 0; + loop { + i += 1; + path_tmp.set_extension(SystemTime::now().duration_since(UNIX_EPOCH).map(|x| x.as_nanos()).unwrap_or(i).to_string()); + if !path_tmp.exists() { + break; + } + } + let mut f = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(&path_tmp) + .map_err(ConfyError::OpenConfigurationFileError)?; + + let s; + #[cfg(feature = "toml_conf")] { + s = toml::to_string(&cfg).map_err(ConfyError::SerializeTomlError)?; + } + #[cfg(feature = "yaml_conf")] { + s = serde_yaml::to_string(&cfg).map_err(ConfyError::SerializeYamlError)?; + } + + f.write_all(s.as_bytes()) + .map_err(ConfyError::WriteConfigurationFileError)?; + std::fs::rename(path_tmp, path) + .map_err(ConfyError::WriteConfigurationFileError)?; + Ok(()) +} + +fn get_configuration_directory_str(project: &ProjectDirs) -> Result<&str, ConfyError> { + let config_dir_option = project.config_dir().to_str(); + + match config_dir_option { + Some(x) => Ok(x), + None => Err(ConfyError::BadConfigDirectoryStr), + } +} diff --git a/libs/confy/src/utils.rs b/libs/confy/src/utils.rs new file mode 100644 index 000000000..d2d4263e7 --- /dev/null +++ b/libs/confy/src/utils.rs @@ -0,0 +1,16 @@ +//! Some storage utilities + +use std::fs::File; +use std::io::{Error as IoError, Read}; + +pub trait CheckedStringRead { + fn get_string(&mut self) -> Result; +} + +impl CheckedStringRead for File { + fn get_string(&mut self) -> Result { + let mut s = String::new(); + self.read_to_string(&mut s)?; + Ok(s) + } +} diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index 6dc5319c8..5faff841a 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -24,7 +24,7 @@ rand = "0.7" serde_derive = "1.0" serde = "1.0" lazy_static = "1.4" -confy = { git = "https://github.com/open-trade/confy" } +confy = { path = "../confy" } dirs-next = "2.0" filetime = "0.2" sodiumoxide = "0.2" From 1da5c1bde999d79183cfd908ce4a77ff0ca6fbe4 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 4 Jun 2021 21:04:40 +0800 Subject: [PATCH 10/24] better linux support --- src/platform/linux.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 15c619378..5099c9717 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -187,8 +187,9 @@ pub fn start_os_service() { d = get_display(); } if d.is_empty() { - d = ":0".to_owned() + d = ":0".to_owned(); } + d = d.replace(&whoami::hostname(), "").replace("localhost", ""); log::info!("DISPLAY: {}", d); log::info!("XAUTHORITY: {}", auth); std::env::set_var("XAUTHORITY", auth); @@ -343,18 +344,24 @@ pub fn get_display_server() -> String { pub fn is_login_wayland() -> bool { if let Ok(contents) = std::fs::read_to_string("/etc/gdm3/custom.conf") { contents.contains("#WaylandEnable=false") + } else if let Ok(contents) = std::fs::read_to_string("/etc/gdm/custom.conf") { + contents.contains("#WaylandEnable=false") } else { false } } pub fn fix_login_wayland() { + let mut file = "/etc/gdm3/custom.conf".to_owned(); + if !std::path::Path::new(&file).exists() { + file = "/etc/gdm/custom.conf".to_owned(); + } match std::process::Command::new("pkexec") .args(vec![ "sed", "-i", "s/#WaylandEnable=false/WaylandEnable=false/g", - "/etc/gdm3/custom.conf", + &file ]) .output() { From 913568a5974162a4bfaa91b97f2fa8ce5a70066a Mon Sep 17 00:00:00 2001 From: killswitch Date: Fri, 4 Jun 2021 18:05:35 +0200 Subject: [PATCH 11/24] Add --needed parameter to pacman install command This avoids reinstalling already installed packages. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8bb28514d..ae6761dde 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb- ### Arch (Manjaro) ``` -sudo pacman -Syu unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio +sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio ``` ### Install vcpkg From 155357a7e9e614c7d427e7aa372828fc8838340f Mon Sep 17 00:00:00 2001 From: Duncan Bristow Date: Fri, 4 Jun 2021 16:27:53 -0700 Subject: [PATCH 12/24] Fixed typos, defaulted MacOS update prompt to use https --- src/client.rs | 2 +- src/ui/index.tis | 6 +++--- src/ui/port_forward.tis | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/client.rs b/src/client.rs index 2fd6e1430..d893614aa 100644 --- a/src/client.rs +++ b/src/client.rs @@ -135,7 +135,7 @@ impl Client { if ph.socket_addr.is_empty() { match ph.failure.enum_value_or_default() { punch_hole_response::Failure::ID_NOT_EXIST => { - bail!("ID not exist"); + bail!("ID does not exist"); } punch_hole_response::Failure::OFFLINE => { bail!("Remote desktop is offline"); diff --git a/src/ui/index.tis b/src/ui/index.tis index 63e77798c..e8321c0e7 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -115,7 +115,7 @@ function createNewConnect(id, type) { app.remote_id.value = formatId(id); if (!id) return; if (id == handler.get_id()) { - handler.msgbox("custom-error", "Error", "Sorry, it is yourself"); + handler.msgbox("custom-error", "Error", "You cannot connect to your own computer"); return; } handler.set_remote_id(id); @@ -421,7 +421,7 @@ class UpgradeMe: Reactor.Component { var update_or_download = is_osx ? "download" : "update"; return
{handler.get_app_name()} Status
-
Your installation is lower version.
+
An update is available for RustDesk.
Click to upgrade
; } @@ -444,7 +444,7 @@ class UpdateMe: Reactor.Component { event click $(#install-me) { if (is_osx) { - handler.open_url("http://rustdesk.com"); + handler.open_url("https://rustdesk.com"); return; } var url = software_update_url + '.' + handler.get_software_ext(); diff --git a/src/ui/port_forward.tis b/src/ui/port_forward.tis index 986d22de1..d30367710 100644 --- a/src/ui/port_forward.tis +++ b/src/ui/port_forward.tis @@ -22,7 +22,7 @@ class PortForward: Reactor.Component { return
{pfs.length ?
Listening ...
- Don't close this window while your are using tunnel + Don't close this window while you are using the tunnel
: ""} From 0ddfc32f0c3a1f389a55685b75ad073efd25a502 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 5 Jun 2021 19:53:34 +0800 Subject: [PATCH 13/24] refactor --- src/rendezvous_mediator.rs | 48 ++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 98c1a2642..cda13250c 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -109,46 +109,48 @@ impl RendezvousMediator { let mut old_latency = 0; let mut ema_latency = 0; loop { + let mut update_latency = || { + last_register_resp = SystemTime::now(); + let mut latency = last_register_resp + .duration_since(last_register_sent) + .map(|d| d.as_micros() as i64) + .unwrap_or(0); + if ema_latency == 0 { + ema_latency = latency; + } else { + ema_latency = latency / 30 + (ema_latency * 29 / 30); + latency = ema_latency; + } + let mut n = latency / 5; + if n < 3000 { + n = 3000; + } + if (latency - old_latency).abs() > n || old_latency <= 0 { + Config::update_latency(&host, latency); + log::debug!("Latency of {}: {}ms", host, latency as f64 / 1000.); + old_latency = latency; + } + fails = 0; + }; select! { Some(Ok((bytes, _))) = socket.next() => { if let Ok(msg_in) = Message::parse_from_bytes(&bytes) { match msg_in.union { Some(rendezvous_message::Union::register_peer_response(rpr)) => { + update_latency(); if rpr.request_pk { log::info!("request_pk received from {}", host); allow_err!(rz.register_pk(&mut socket).await); continue; } - last_register_resp = SystemTime::now(); - let mut latency = last_register_resp.duration_since(last_register_sent).map(|d| d.as_micros() as i64).unwrap_or(0); - if ema_latency == 0 { - ema_latency = latency; - } else { - ema_latency = latency / 30 + (ema_latency * 29 / 30); - latency = ema_latency; - } - let mut n = latency / 5; - if n < 3000 { - n = 3000; - } - if (latency - old_latency).abs() > n || old_latency <= 0 { - Config::update_latency(&host, latency); - log::debug!("Latency of {}: {}ms", host, latency as f64 / 1000.); - old_latency = latency; - } - fails = 0; } Some(rendezvous_message::Union::register_pk_response(rpr)) => { + update_latency(); match rpr.result.enum_value_or_default() { register_pk_response::Result::OK => { Config::set_key_confirmed(true); Config::set_host_key_confirmed(&rz.host_prefix, true); *SOLVING_PK_MISMATCH.lock().unwrap() = "".to_owned(); - last_register_resp = SystemTime::now(); - let latency = last_register_resp.duration_since(last_register_sent).map(|d| d.as_micros() as i64).unwrap_or(0); - Config::update_latency(&host, latency); - log::debug!("Latency of {}: {}ms", host, latency as f64 / 1000.); - fails = 0; } register_pk_response::Result::UUID_MISMATCH => { allow_err!(rz.handle_uuid_mismatch(&mut socket).await); From 34ce4022036d3b5aba7c2aab80a084549a418542 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 6 Jun 2021 16:27:02 +0800 Subject: [PATCH 14/24] https://github.com/rustdesk/rustdesk/issues/61 --- src/server/video_service.rs | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 5c466683e..ddf497b9a 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -46,11 +46,11 @@ pub fn new() -> GenericService { fn run(sp: GenericService) -> ResultType<()> { let fps = 30; let spf = time::Duration::from_secs_f32(1. / (fps as f32)); - let (n, current, display) = get_current_display()?; + let (ndisplay, current, display) = get_current_display()?; let (origin, width, height) = (display.origin(), display.width(), display.height()); log::debug!( "#displays={}, current={}, origin: {:?}, width={}, height={}", - n, + ndisplay, current, &origin, width, @@ -74,7 +74,11 @@ fn run(sp: GenericService) -> ResultType<()> { speed, }; let mut vpx; - match Encoder::new(&cfg, 1) { + let mut n = ((width * height) as f64 / (1920 * 1080) as f64).round() as u32; + if n < 1 { + n = 1; + } + match Encoder::new(&cfg, n) { Ok(x) => vpx = x, Err(err) => bail!("Failed to create encoder: {}", err), } @@ -113,6 +117,7 @@ fn run(sp: GenericService) -> ResultType<()> { let start = time::Instant::now(); let mut crc = (0, 0); let mut last_sent = time::Instant::now(); + let mut last_check_displays = time::Instant::now(); while sp.ok() { if *SWITCH.lock().unwrap() { bail!("SWITCH"); @@ -131,6 +136,14 @@ fn run(sp: GenericService) -> ResultType<()> { } } let now = time::Instant::now(); + if last_check_displays.elapsed().as_millis() > 1000 { + last_check_displays = now; + if ndisplay != get_display_num() { + log::info!("Displays changed"); + *SWITCH.lock().unwrap() = true; + bail!("SWITCH"); + } + } *LAST_ACTIVE.lock().unwrap() = now; if get_latency() < 1000 || last_sent.elapsed().as_millis() > 1000 { match c.frame(wait as _) { @@ -228,6 +241,14 @@ fn handle_one_frame( Ok(()) } +fn get_display_num() -> usize { + if let Ok(d) = Display::all() { + d.len() + } else { + 0 + } +} + pub fn get_displays() -> ResultType<(usize, Vec)> { // switch to primary display if long time (30 seconds) no users if LAST_ACTIVE.lock().unwrap().elapsed().as_secs() >= 30 { From ae9b6edf7ccc5dedf051f784baa5e63a0cb10f2b Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 7 Jun 2021 08:57:26 +0800 Subject: [PATCH 15/24] comment --- src/client.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/client.rs b/src/client.rs index d893614aa..e62eb19c1 100644 --- a/src/client.rs +++ b/src/client.rs @@ -318,6 +318,7 @@ impl Client { } } else { // fall back to non-secure connection in case pk mismatch + // to-do: pop up a warning dialog to let user choose if continue let mut msg_out = Message::new(); msg_out.set_public_key(PublicKey::new()); timeout(CONNECT_TIMEOUT, conn.send(&msg_out)).await??; From e8606b8d0c75140e38a050638c378c4f18b26855 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Mon, 7 Jun 2021 12:44:19 +0800 Subject: [PATCH 16/24] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ae6761dde..760493276 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Chat with us: [Discord](https://discord.gg/nDceKgxnkV) +[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) + An open-source remote desktop client software, written in Rust. Works out of the box, no configuration required. Great alternative to TeamViewer and AnyDesk! You have full control of your data, with no concerns about security. You can use our rendezvous/relay server, [set up your own](https://rustdesk.com/blog/id-relay-set/), or [write your own rendezvous/relay server](https://github.com/rustdesk/rustdesk-server-demo). [**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases) From 7daa08063793692a815ebd14b69d2dbcbda4af4d Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Mon, 7 Jun 2021 12:48:43 +0800 Subject: [PATCH 17/24] Update FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 8e2b0af94..1745f9ba6 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,2 @@ github: [rustdesk] +ko_fi: rustdesk From 369a9e080549a5ced477383140c8424ac07ad111 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 7 Jun 2021 13:44:21 +0800 Subject: [PATCH 18/24] CONTRIBUTING.md --- CONTRIBUTING.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 ++ 2 files changed, 51 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..fbcf6eaf9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,49 @@ +# Contributing to RustDesk + +RustDesk welcomes contribution from everyone. Here are the guidelines if you are +thinking of helping us: + + +## Contributions + +Contributions to RustDesk or its dependencies should be made in the form of GitHub +pull requests. Each pull request will be reviewed by a core contributor +(someone with permission to land patches) and either landed in the main tree or +given feedback for changes that would be required. All contributions should +follow this format, even those from core contributors. + +Should you wish to work on an issue, please claim it first by commenting on +the GitHub issue that you want to work on it. This is to prevent duplicated +efforts from contributors on the same issue. + +## Pull Request Checklist + +- Branch from the master branch and, if needed, rebase to the current master + branch before submitting your pull request. If it doesn't merge cleanly with + master you may be asked to rebase your changes. + +- Commits should be as small as possible, while ensuring that each commit is + correct independently (i.e., each commit should compile and pass tests). + +- Commits should be accompanied by a Developer Certificate of Origin + (http://developercertificate.org) sign-off, which indicates that you (and + your employer if applicable) agree to be bound by the terms of the + [project license](LICENSE.md). In git, this is the `-s` option to `git commit` + +- If your patch is not getting reviewed or you need a specific person to review + it, you can @-reply a reviewer asking for a review in the pull request or a + comment, or you can ask for a review via [email](mailto:info@rustdesk.com). + +- Add tests relevant to the fixed bug or new feature. + +For specific git instructions, see [GitHub workflow 101](https://github.com/servo/servo/wiki/Github-workflow). + +## Conduct + +We follow the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct). + + +## Communication + +RustDesk contributors frequent the [Discord](https://discord.gg/nDceKgxnkV). + diff --git a/README.md b/README.md index 760493276..a4840381c 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Chat with us: [Discord](https://discord.gg/nDceKgxnkV) An open-source remote desktop client software, written in Rust. Works out of the box, no configuration required. Great alternative to TeamViewer and AnyDesk! You have full control of your data, with no concerns about security. You can use our rendezvous/relay server, [set up your own](https://rustdesk.com/blog/id-relay-set/), or [write your own rendezvous/relay server](https://github.com/rustdesk/rustdesk-server-demo). +RustDesk welcomes contribution from everyone. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for help getting started. + [**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases) ## Dependencies From c844d99ff16092540f7b06c28d249fde1bc4ebca Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 8 Jun 2021 17:54:25 +0800 Subject: [PATCH 19/24] try multiple time to get X11 env --- src/platform/linux.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 5099c9717..838d2eae4 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -164,7 +164,7 @@ pub fn start_os_service() { uid = tmp; log::info!("uid of seat0: {}", uid); let gdm = format!("/run/user/{}/gdm/Xauthority", uid); - let mut auth = get_env("XAUTHORITY", &uid); + let mut auth = get_env_tries("XAUTHORITY", &uid, 10); if auth.is_empty() { auth = if std::path::Path::new(&gdm).exists() { gdm @@ -502,6 +502,17 @@ fn run_cmds(cmds: String) -> ResultType> { } } +fn get_env_tries(name: &str, uid: &str, n: usize) -> String { + for _ in 0..n { + let x = get_env(name, uid); + if !x.is_empty() { + return x; + } + std::thread::sleep(std::time::Duration::from_millis(300)); + } + "".to_owned() +} + fn get_env(name: &str, uid: &str) -> String { let cmd = format!("ps -u {} -o pid= | xargs -I__ cat /proc/__/environ 2>/dev/null | tr '\\0' '\\n' | grep -m1 '^{}=' | sed 's/{}=//g'", uid, name, name); log::debug!("Run: {}", &cmd); From da4669366b7efcf226133fd11b94c0a9b7d47277 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 10 Jun 2021 12:19:23 +0800 Subject: [PATCH 20/24] fix some rare case --- src/platform/linux.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 838d2eae4..c563fbec0 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -290,6 +290,7 @@ fn get_display() -> String { } // above not work for gdm user log::debug!("ls -l /tmp/.X11-unix/"); + let mut last = "".to_owned(); if let Ok(output) = std::process::Command::new("ls") .args(vec!["-l", "/tmp/.X11-unix/"]) .output() @@ -297,16 +298,18 @@ fn get_display() -> String { for line in String::from_utf8_lossy(&output.stdout).lines() { log::debug!(" {}", line); let mut iter = line.split_whitespace(); - if iter.nth(2) == Some(&user) { - if let Some(x) = iter.last() { - if x.starts_with("X") { - return x.replace("X", ":").to_owned(); + let user_field = iter.nth(2); + if let Some(x) = iter.last() { + if x.starts_with("X") { + last = x.replace("X", ":").to_owned(); + if user_field == Some(&user) { + return last; } } } } } - "".to_owned() + last } fn get_value_of_seat0(i: usize) -> String { From 912053d99d350b39d5f5585dc41ba1de0c2349dc Mon Sep 17 00:00:00 2001 From: Maxwell White Date: Thu, 10 Jun 2021 08:41:00 -0400 Subject: [PATCH 21/24] Improved readability of OS list --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a4840381c..f31a96d05 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,9 @@ RustDesk welcomes contribution from everyone. See [`CONTRIBUTING.md`](CONTRIBUT Desktop versions use [sciter](https://sciter.com/) for GUI, please download sciter dynamic library yourself. -[Windows](https://github.com/c-smile/sciter-sdk/blob/dc65744b66389cd5a0ff6bdb7c63a8b7b05a708b/bin.win/x64/sciter.dll) -[Linux](https://github.com/c-smile/sciter-sdk/raw/dc65744b66389cd5a0ff6bdb7c63a8b7b05a708b/bin.lnx/x64/libsciter-gtk.so) -[Osx](https://github.com/c-smile/sciter-sdk/raw/dc65744b66389cd5a0ff6bdb7c63a8b7b05a708b/bin.osx/sciter-osx-64.dylib) - +[Windows](https://github.com/c-smile/sciter-sdk/blob/dc65744b66389cd5a0ff6bdb7c63a8b7b05a708b/bin.win/x64/sciter.dll) | +[Linux](https://github.com/c-smile/sciter-sdk/raw/dc65744b66389cd5a0ff6bdb7c63a8b7b05a708b/bin.lnx/x64/libsciter-gtk.so) | +[macOS](https://github.com/c-smile/sciter-sdk/raw/dc65744b66389cd5a0ff6bdb7c63a8b7b05a708b/bin.osx/sciter-osx-64.dylib) ## Raw steps to build * Prepare your Rust development env and C++ build env @@ -100,5 +99,3 @@ RustDesk does not support Wayland. Check [this](https://docs.fedoraproject.org/e ![image](https://user-images.githubusercontent.com/71636191/113112857-3fbd5d80-923c-11eb-9836-768325faf906.png) ![image](https://user-images.githubusercontent.com/71636191/113112990-65e2fd80-923c-11eb-840e-349b4d6e340d.png) - - From a830922dea02a405b0516e4ece733d2e9a378c76 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 14 Jun 2021 13:16:20 +0800 Subject: [PATCH 22/24] https://github.com/rustdesk/rustdesk/issues/73 --- src/platform/linux.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index c563fbec0..9fbddb826 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -326,13 +326,32 @@ fn get_value_of_seat0(i: usize) -> String { } } } + + // some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73 + if let Ok(output) = std::process::Command::new("loginctl").output() { + for line in String::from_utf8_lossy(&output.stdout).lines() { + if let Some(sid) = line.split_whitespace().nth(0) { + let d = get_display_server_of_session(sid); + if is_active(sid) && d != "tty" { + if let Some(uid) = line.split_whitespace().nth(i) { + return uid.to_owned(); + } + } + } + } + } + return "".to_owned(); } pub fn get_display_server() -> String { let session = get_value_of_seat0(0); + get_display_server_of_session(&session) +} + +fn get_display_server_of_session(session: &str) -> String { if let Ok(output) = std::process::Command::new("loginctl") - .args(vec!["show-session", "-p", "Type", &session]) + .args(vec!["show-session", "-p", "Type", session]) .output() { String::from_utf8_lossy(&output.stdout) From a2890fa548e6935a1a9f33212401a3a0d35881a5 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 14 Jun 2021 15:29:36 +0800 Subject: [PATCH 23/24] https://github.com/rustdesk/rustdesk/issues/35 --- src/ui/header.tis | 19 ++++++++++++++++++- src/ui/remote.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/ui/header.tis b/src/ui/header.tis index 96b722efb..2c3f39131 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -135,8 +135,13 @@ class Header: Reactor.Component { return
  • Transfer File
  • -
  • TCP Tunneling
  • +
  • TCP Tunneling
  • +
    {keyboard_enabled && (pi.platform == "Linux" || pi.sas_enabled) ?
  • Insert Ctrl + Alt + Del
  • : ""} +
  • Insert Ctrl + Space
  • +
  • Insert Alt + Tab
  • + {false &&
  • Insert Win/Super + ...
  • } +
    {keyboard_enabled ?
  • Insert Lock
  • : ""} {false && pi.platform == "Windows" ?
  • Block user input
  • : ""} {handler.support_refresh() ?
  • Refresh
  • : ""} @@ -223,6 +228,18 @@ class Header: Reactor.Component { event click $(#ctrl-alt-del) { handler.ctrl_alt_del(); } + + event click $(#alt-tab) { + handler.alt_tab(); + } + + event click $(#ctrl-space) { + handler.ctrl_space(); + } + + event click $(#super-x) { + handler.super_x(); + } event click $(#lock-screen) { handler.lock_screen(); diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 17e6c8af1..b4dd201f1 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -63,6 +63,7 @@ pub struct Handler { id: String, args: Vec, lc: Arc>, + super_on: bool, } impl Deref for Handler { @@ -153,6 +154,9 @@ impl sciter::EventHandler for Handler { fn send_mouse(i32, i32, i32, bool, bool, bool, bool); fn key_down_or_up(bool, String, i32, bool, bool, bool, bool, bool); fn ctrl_alt_del(); + fn ctrl_space(); + fn alt_tab(); + fn super_x(); fn transfer_file(); fn tunnel(); fn lock_screen(); @@ -813,6 +817,20 @@ impl Handler { } } + fn super_x(&mut self) { + self.super_on = true; + } + + fn ctrl_space(&mut self) { + let key = "VK_SPACE".to_owned(); + self.key_down_or_up(3, key, 0, false, true, false, false, false); + } + + fn alt_tab(&mut self) { + let key = "VK_TAB".to_owned(); + self.key_down_or_up(3, key, 0, true, false, false, false, false); + } + fn lock_screen(&mut self) { let lock = "LOCK_SCREEN".to_owned(); self.key_down_or_up(1, lock, 0, false, false, false, false, false); @@ -860,6 +878,15 @@ impl Handler { extended, ); + let mut command = command; + if self.super_on { + command = true; + } + + if down_or_up == 0 { + self.super_on = false; + } + let mut name = name; if extended { From 673986d045b18fed0349fd2192e9a5b340945233 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 14 Jun 2021 17:51:45 +0800 Subject: [PATCH 24/24] https://github.com/rustdesk/rustdesk/issues/69 --- src/platform/windows.rs | 30 ++++++++++++++++++++++++++++++ src/ui.rs | 6 ++++++ src/ui/index.html | 15 +-------------- src/ui/index.tis | 18 ++++++++++++++++++ 4 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 0d64a8238..043c73321 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1037,3 +1037,33 @@ pub fn get_installed_version() -> String { } "".to_owned() } + + +pub fn create_shortcut(id: &str) -> ResultType<()> { + let exe = std::env::current_exe()?.to_str().unwrap_or("").to_owned(); + let shortcut = write_cmds( + format!( + " +Set oWS = WScript.CreateObject(\"WScript.Shell\") +strDesktop = oWS.SpecialFolders(\"Desktop\") +Set objFSO = CreateObject(\"Scripting.FileSystemObject\") +sLinkFile = objFSO.BuildPath(strDesktop, \"{id}.lnk\") +Set oLink = oWS.CreateShortcut(sLinkFile) + oLink.TargetPath = \"{exe}\" + oLink.Arguments = \"--connect {id}\" +oLink.Save + ", + exe = exe, + id = id, + ), + "vbs", + )? + .to_str() + .unwrap_or("") + .to_owned(); + std::process::Command::new("cscript") + .arg(&shortcut) + .output()?; + allow_err!(std::fs::remove_file(shortcut)); + Ok(()) +} diff --git a/src/ui.rs b/src/ui.rs index a8e1450ff..4f4506d00 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -486,6 +486,11 @@ impl UI { format!("{}.{}", p.to_string_lossy(), self.get_software_ext()) } + fn create_shortcut(&self, id: String) { + #[cfg(windows)] + crate::platform::windows::create_shortcut(&id).ok(); + } + fn open_url(&self, url: String) { #[cfg(windows)] let p = "explorer"; @@ -541,6 +546,7 @@ impl sciter::EventHandler for UI { fn get_software_store_path(); fn get_software_ext(); fn open_url(String); + fn create_shortcut(String); } } diff --git a/src/ui/index.html b/src/ui/index.html index 638e5d09d..c51c64d84 100644 --- a/src/ui/index.html +++ b/src/ui/index.html @@ -9,22 +9,9 @@ include "common.tis"; include "index.tis"; - - -
  • Connect
  • -
  • Transfer File
  • -
  • TCP Tunneling
  • -
  • RDP
  • -
  • Remove
  • - -
    - -
  • Refresh random password
  • -
  • Set your own password
  • -
    - \ No newline at end of file + diff --git a/src/ui/index.tis b/src/ui/index.tis index e8321c0e7..1af803470 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -103,6 +103,8 @@ event click $(menu#remote-context li) (evt, me) { } else if (action == "remove") { handler.remove_peer(id); app.recent_sessions.update(); + } else if (action == "shortcut") { + handler.create_shortcut(id); } else if (action == "rdp") { createNewConnect(id, "rdp"); } else if (action == "tunnel") { @@ -310,6 +312,22 @@ class App: Reactor.Component var is_can_screen_recording = handler.is_can_screen_recording(false); return
    + + +
  • Connect
  • +
  • Transfer File
  • +
  • TCP Tunneling
  • +
  • RDP
  • +
  • Remove
  • + {is_win &&
  • Create Desktop Shortcut
  • } + +
    + + +
  • Refresh random password
  • +
  • Set your own password
  • + +
    Your Desktop