diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 681334f33..bcbad530c 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -154,7 +154,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.1" charcode: dependency: transitive description: @@ -175,7 +175,7 @@ packages: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" code_builder: dependency: transitive description: @@ -621,14 +621,14 @@ packages: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.1.5" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0" mime: dependency: transitive description: @@ -705,7 +705,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" path_drawing: dependency: transitive description: diff --git a/src/keyboard.rs b/src/keyboard.rs index 5159eae80..99e1c1455 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -266,7 +266,7 @@ pub fn get_keyboard_mode_enum() -> KeyboardMode { } #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub fn add_numlock_capslock_state(key_event: &mut KeyEvent) { +pub fn add_numlock_capslock_status(key_event: &mut KeyEvent) { if get_key_state(enigo::Key::CapsLock) { key_event.modifiers.push(ControlKey::CapsLock.into()); } @@ -343,7 +343,7 @@ pub fn event_to_key_event(event: &Event) -> KeyEvent { } }; #[cfg(not(any(target_os = "android", target_os = "ios")))] - add_numlock_capslock_state(&mut key_event); + add_numlock_capslock_status(&mut key_event); return key_event; } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 695c6f3d5..66d2b0414 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -69,6 +69,7 @@ struct Input { y: i32, } +const KEY_RDEV_START: u64 = 999; const KEY_CHAR_START: u64 = 9999; #[derive(Clone, Default)] @@ -280,13 +281,17 @@ pub fn mouse_move_relative(x: i32, y: i32) { en.mouse_move_relative(x, y); } -#[cfg(not(target_os = "macos"))] +#[cfg(windows)] fn modifier_sleep() { // sleep for a while, this is only for keying in rdp in peer so far - #[cfg(windows)] std::thread::sleep(std::time::Duration::from_nanos(1)); } +#[inline] +fn is_pressed(key: &Key, en: &mut Enigo) -> bool { + get_modifier_state(key.clone(), en) +} + #[inline] fn get_modifier_state(key: Key, en: &mut Enigo) -> bool { // https://github.com/rustdesk/rustdesk/issues/332 @@ -335,7 +340,7 @@ pub fn handle_mouse(evt: &MouseEvent, conn: i32) { pub fn fix_key_down_timeout_loop() { std::thread::spawn(move || loop { - std::thread::sleep(std::time::Duration::from_millis(1_000)); + std::thread::sleep(std::time::Duration::from_millis(10_000)); fix_key_down_timeout(false); }); if let Err(err) = ctrlc::set_handler(move || { @@ -356,38 +361,61 @@ pub fn fix_key_down_timeout_at_exit() { } #[inline] -fn get_layout(key: u32) -> Key { - Key::Layout(std::char::from_u32(key).unwrap_or('\0')) +fn record_key_is_control_key(record_key: u64) -> bool { + record_key < KEY_CHAR_START +} + +#[inline] +fn record_key_is_chr(record_key: u64) -> bool { + KEY_RDEV_START <= record_key && record_key < KEY_CHAR_START +} + +#[inline] +fn record_key_is_rdev_layout(record_key: u64) -> bool { + KEY_CHAR_START <= record_key +} + +#[inline] +fn record_key_to_key(record_key: u64) -> Option { + if record_key_is_control_key(record_key) { + control_key_value_to_key(record_key as _) + } else if record_key_is_chr(record_key) { + let chr: u32 = (record_key - KEY_CHAR_START) as _; + Some(char_value_to_key(chr)) + } else { + None + } +} + +#[inline] +fn release_record_key(record_key: u64) { + let func = move || { + if record_key_is_rdev_layout(record_key) { + rdev_key_down_or_up(RdevKey::Unknown((record_key - KEY_RDEV_START) as _), false); + } else if let Some(key) = record_key_to_key(record_key) { + ENIGO.lock().unwrap().key_up(key); + log::debug!("Fixed {:?} timeout", key); + } + }; + + #[cfg(target_os = "macos")] + QUEUE.exec_async(func); + #[cfg(not(target_os = "macos"))] + func(); } fn fix_key_down_timeout(force: bool) { - if KEYS_DOWN.lock().unwrap().is_empty() { + let key_down = KEYS_DOWN.lock().unwrap(); + if key_down.is_empty() { return; } - let cloned = (*KEYS_DOWN.lock().unwrap()).clone(); - for (key, value) in cloned.into_iter() { - if force || value.elapsed().as_millis() >= 360_000 { - KEYS_DOWN.lock().unwrap().remove(&key); - let key = if key < KEY_CHAR_START { - if let Some(key) = KEY_MAP.get(&(key as _)) { - Some(*key) - } else { - None - } - } else { - Some(get_layout((key - KEY_CHAR_START) as _)) - }; - if let Some(key) = key { - let func = move || { - let mut en = ENIGO.lock().unwrap(); - en.key_up(key); - log::debug!("Fixed {:?} timeout", key); - }; - #[cfg(target_os = "macos")] - QUEUE.exec_async(func); - #[cfg(not(target_os = "macos"))] - func(); - } + let cloned = (*key_down).clone(); + drop(key_down); + + for (record_key, time) in cloned.into_iter() { + if force || time.elapsed().as_millis() >= 360_000 { + record_pressed_key(record_key, false); + release_record_key(record_key); } } } @@ -505,6 +533,7 @@ pub fn handle_mouse_(evt: &MouseEvent) { if key != &Key::CapsLock && key != &Key::NumLock { if !get_modifier_state(key.clone(), &mut en) { en.key_down(key.clone()).ok(); + #[cfg(windows)] modifier_sleep(); to_release.push(key); } @@ -643,7 +672,370 @@ pub async fn lock_screen() { super::video_service::switch_to_primary().await; } +pub fn handle_key(evt: &KeyEvent) { + #[cfg(target_os = "macos")] + if !*IS_SERVER { + // having GUI, run main GUI thread, otherwise crash + let evt = evt.clone(); + QUEUE.exec_async(move || handle_key_(&evt)); + return; + } + #[cfg(windows)] + crate::portable_service::client::handle_key(evt); + #[cfg(not(windows))] + handle_key_(evt); +} + +fn rdev_key_down_or_up(key: RdevKey, down_or_up: bool) { + let event_type = match down_or_up { + true => EventType::KeyPress(key), + false => EventType::KeyRelease(key), + }; + match simulate(&event_type) { + Ok(()) => (), + Err(_simulate_error) => { + log::error!("Could not send {:?}", &event_type); + } + } + #[cfg(target_os = "macos")] + std::thread::sleep(Duration::from_millis(20)); +} + +fn is_modifier_in_key_event(control_key: ControlKey, key_event: &KeyEvent) -> bool { + key_event + .modifiers + .iter() + .position(|&m| m == control_key.into()) + .is_some() +} + +#[inline] +fn control_key_value_to_key(value: i32) -> Option { + KEY_MAP.get(&value).and_then(|k| Some(*k)) +} + +#[inline] +fn char_value_to_key(value: u32) -> Key { + Key::Layout(std::char::from_u32(value).unwrap_or('\0')) +} + +fn is_not_same_status(client_locking: bool, remote_locking: bool) -> bool { + client_locking != remote_locking +} + +#[cfg(target_os = "windows")] +fn has_numpad_key(key_event: &KeyEvent) -> bool { + key_event + .modifiers + .iter() + .filter(|&&ck| NUMPAD_KEY_MAP.get(&ck.value()).is_some()) + .count() + != 0 +} + +#[cfg(target_os = "windows")] +fn is_rdev_numpad_key(key_event: &KeyEvent) -> bool { + let code = key_event.chr(); + let key = rdev::get_win_key(code, 0); + match key { + RdevKey::Home + | RdevKey::UpArrow + | RdevKey::PageUp + | RdevKey::LeftArrow + | RdevKey::RightArrow + | RdevKey::End + | RdevKey::DownArrow + | RdevKey::PageDown + | RdevKey::Insert + | RdevKey::Delete => true, + _ => false, + } +} + +#[cfg(target_os = "windows")] +fn is_numlock_disabled(key_event: &KeyEvent) -> bool { + // disable numlock if press home etc when numlock is on, + // because we will get numpad value (7,8,9 etc) if not + match key_event.mode.unwrap() { + KeyboardMode::Map => is_rdev_numpad_key(key_event), + _ => has_numpad_key(key_event), + } +} + +fn click_capslock(en: &mut Enigo) { + #[cfg(not(targe_os = "macos"))] + en.key_click(enigo::Key::CapsLock); + #[cfg(target_os = "macos")] + en.key_down(enigo::Key::CapsLock); +} + +fn click_numlock(en: &mut Enigo) { + // without numlock in macos + #[cfg(not(target_os = "macos"))] + en.key_click(enigo::Key::NumLock); +} + +fn sync_numlock_capslock_status(key_event: &KeyEvent) { + let mut en = ENIGO.lock().unwrap(); + + let client_caps_locking = is_modifier_in_key_event(ControlKey::CapsLock, key_event); + let client_num_locking = is_modifier_in_key_event(ControlKey::NumLock, key_event); + let remote_caps_locking = en.get_key_state(enigo::Key::CapsLock); + let remote_num_locking = en.get_key_state(enigo::Key::NumLock); + + let need_click_capslock = is_not_same_status(client_caps_locking, remote_caps_locking); + let need_click_numlock = is_not_same_status(client_num_locking, remote_num_locking); + + #[cfg(not(target_os = "windows"))] + let disable_numlock = false; + #[cfg(target_os = "windows")] + let disable_numlock = is_numlock_disabled(key_event); + + if need_click_capslock { + click_capslock(&mut en); + } + + if need_click_numlock && !disable_numlock { + click_numlock(&mut en); + } +} + +fn map_keyboard_mode(evt: &KeyEvent) { + // map mode(1): Send keycode according to the peer platform. + record_pressed_key(evt.chr() as u64 + KEY_CHAR_START, evt.down); + + #[cfg(windows)] + crate::platform::windows::try_change_desktop(); + + // Wayland + #[cfg(target_os = "linux")] + if !*IS_X11.lock().unwrap() { + let mut en = ENIGO.lock().unwrap(); + let code = evt.chr() as u16; + + if evt.down { + en.key_down(enigo::Key::Raw(code)).ok(); + } else { + en.key_up(enigo::Key::Raw(code)); + } + return; + } + + rdev_key_down_or_up(RdevKey::Unknown(evt.chr()), evt.down); + return; +} + +#[cfg(target_os = "macos")] +fn add_flags_to_enigo(en: &mut Enigo, key_event: &KeyEvent) { + // When long-pressed the command key, then press and release + // the Tab key, there should be CGEventFlagCommand in the flag. + en.reset_flag(); + for ck in key_event.modifiers.iter() { + if let Some(key) = KEY_MAP.get(&ck.value()) { + en.add_flag(key); + } + } +} + +fn get_control_key_value(key_event: &KeyEvent) -> i32 { + if let Some(key_event::Union::ControlKey(ck)) = key_event.union { + ck.value() + } else { + -1 + } +} + +fn release_unpressed_modifiers(en: &mut Enigo, key_event: &KeyEvent) { + let ck_value = get_control_key_value(key_event); + fix_modifiers(&key_event.modifiers[..], en, ck_value); +} + +fn is_altgr_pressed() -> bool { + KEYS_DOWN + .lock() + .unwrap() + .get(&(ControlKey::RAlt.value() as _)) + .is_some() +} + +fn press_modifiers(en: &mut Enigo, key_event: &KeyEvent, to_release: &mut Vec) { + for ref ck in key_event.modifiers.iter() { + if let Some(key) = control_key_value_to_key(ck.value()) { + if !is_pressed(&key, en) { + #[cfg(target_os = "linux")] + if key == Key::Alt && is_altgr_pressed() { + continue; + } + en.key_down(key.clone()).ok(); + to_release.push(key.clone()); + #[cfg(windows)] + modifier_sleep(); + } + } + } +} + +fn sync_modifiers(en: &mut Enigo, key_event: &KeyEvent, to_release: &mut Vec) { + #[cfg(target_os = "macos")] + add_flags_to_enigo(en, key_event); + + if key_event.down { + release_unpressed_modifiers(en, key_event); + #[cfg(not(target_os = "macos"))] + press_modifiers(en, key_event, to_release); + } +} + +fn process_control_key(en: &mut Enigo, ck: &EnumOrUnknown, down: bool) { + if let Some(key) = control_key_value_to_key(ck.value()) { + if down { + en.key_down(key).ok(); + } else { + en.key_up(key); + } + } +} + +#[inline] +fn need_to_uppercase(en: &mut Enigo) -> bool { + get_modifier_state(Key::Shift, en) || get_modifier_state(Key::CapsLock, en) +} + +fn process_chr(en: &mut Enigo, chr: u32, down: bool) { + let key = char_value_to_key(chr); + + if down { + if en.key_down(key).is_ok() { + } else { + if let Ok(chr) = char::try_from(chr) { + let mut s = chr.to_string(); + if need_to_uppercase(en) { + s = s.to_uppercase(); + } + en.key_sequence(&s); + }; + } + } else { + en.key_up(key); + } +} + +fn process_unicode(en: &mut Enigo, chr: u32) { + if let Ok(chr) = char::try_from(chr) { + en.key_sequence(&chr.to_string()); + } +} + +fn process_seq(en: &mut Enigo, sequence: &str) { + en.key_sequence(&sequence); +} + +fn release_keys(en: &mut Enigo, to_release: &Vec) { + for key in to_release { + en.key_up(key.clone()); + } +} + +fn record_pressed_key(record_key: u64, down: bool) { + let mut key_down = KEYS_DOWN.lock().unwrap(); + if down { + key_down.insert(record_key, Instant::now()); + } else { + key_down.remove(&record_key); + } +} + +fn is_function_key(ck: &EnumOrUnknown) -> bool { + let mut res = false; + if ck.value() == ControlKey::CtrlAltDel.value() { + // have to spawn new thread because send_sas is tokio_main, the caller can not be tokio_main. + std::thread::spawn(|| { + allow_err!(send_sas()); + }); + res = true; + } else if ck.value() == ControlKey::LockScreen.value() { + lock_screen_2(); + res = true; + } + return res; +} + +fn legacy_keyboard_mode(evt: &KeyEvent) { + #[cfg(windows)] + crate::platform::windows::try_change_desktop(); + let mut to_release: Vec = Vec::new(); + + let mut en = ENIGO.lock().unwrap(); + sync_modifiers(&mut en, &evt, &mut to_release); + + let down = evt.down; + match evt.union { + Some(key_event::Union::ControlKey(ck)) => { + if is_function_key(&ck) { + return; + } + let record_key = ck.value() as u64; + record_pressed_key(record_key, down); + process_control_key(&mut en, &ck, down) + } + Some(key_event::Union::Chr(chr)) => { + let record_key = chr as u64 + KEY_CHAR_START; + record_pressed_key(record_key, down); + process_chr(&mut en, chr, down) + } + Some(key_event::Union::Unicode(chr)) => process_unicode(&mut en, chr), + Some(key_event::Union::Seq(ref seq)) => process_seq(&mut en, seq), + _ => {} + } + + #[cfg(not(target_os = "macos"))] + release_keys(&mut en, &to_release); +} + +pub fn handle_key_(evt: &KeyEvent) { + if EXITING.load(Ordering::SeqCst) { + return; + } + + if evt.down { + sync_numlock_capslock_status(evt) + } + match evt.mode.unwrap() { + KeyboardMode::Map => { + map_keyboard_mode(evt); + } + KeyboardMode::Translate => { + legacy_keyboard_mode(evt); + } + _ => { + legacy_keyboard_mode(evt); + } + } +} + +#[tokio::main(flavor = "current_thread")] +async fn lock_screen_2() { + lock_screen().await; +} + +#[tokio::main(flavor = "current_thread")] +async fn send_sas() -> ResultType<()> { + let mut stream = crate::ipc::connect(1000, crate::POSTFIX_SERVICE).await?; + timeout(1000, stream.send(&crate::ipc::Data::SAS)).await??; + Ok(()) +} + lazy_static::lazy_static! { + static ref MODIFIER_MAP: HashMap = [ + (ControlKey::Alt, Key::Alt), + (ControlKey::RAlt, Key::RightAlt), + (ControlKey::Control, Key::Control), + (ControlKey::RControl, Key::RightControl), + (ControlKey::Shift, Key::Shift), + (ControlKey::RShift, Key::RightShift), + (ControlKey::Meta, Key::Meta), + (ControlKey::RWin, Key::RWin), + ].iter().map(|(a, b)| (a.value(), b.clone())).collect(); static ref KEY_MAP: HashMap = [ (ControlKey::Alt, Key::Alt), @@ -736,317 +1128,3 @@ lazy_static::lazy_static! { (ControlKey::Delete, true), ].iter().map(|(a, b)| (a.value(), b.clone())).collect(); } - -pub fn handle_key(evt: &KeyEvent) { - #[cfg(target_os = "macos")] - if !*IS_SERVER { - // having GUI, run main GUI thread, otherwise crash - let evt = evt.clone(); - QUEUE.exec_async(move || handle_key_(&evt)); - return; - } - #[cfg(windows)] - crate::portable_service::client::handle_key(evt); - #[cfg(not(windows))] - handle_key_(evt); -} - -fn rdev_key_down_or_up(key: RdevKey, down_or_up: bool) { - let event_type = match down_or_up { - true => EventType::KeyPress(key), - false => EventType::KeyRelease(key), - }; - match simulate(&event_type) { - Ok(()) => (), - Err(_simulate_error) => { - log::error!("Could not send {:?}", &event_type); - } - } - #[cfg(target_os = "macos")] - std::thread::sleep(Duration::from_millis(20)); -} - -fn sync_status(evt: &KeyEvent) { - let mut en = ENIGO.lock().unwrap(); - - // remote caps status - let caps_locking = evt - .modifiers - .iter() - .position(|&r| r == ControlKey::CapsLock.into()) - .is_some(); - // remote numpad status - let num_locking = evt - .modifiers - .iter() - .position(|&r| r == ControlKey::NumLock.into()) - .is_some(); - - let click_capslock = (caps_locking && !en.get_key_state(enigo::Key::CapsLock)) - || (!caps_locking && en.get_key_state(enigo::Key::CapsLock)); - let click_numlock = (num_locking && !en.get_key_state(enigo::Key::NumLock)) - || (!num_locking && en.get_key_state(enigo::Key::NumLock)); - #[cfg(windows)] - let click_numlock = { - let code = evt.chr(); - let key = rdev::get_win_key(code, 0); - match key { - RdevKey::Home - | RdevKey::UpArrow - | RdevKey::PageUp - | RdevKey::LeftArrow - | RdevKey::RightArrow - | RdevKey::End - | RdevKey::DownArrow - | RdevKey::PageDown - | RdevKey::Insert - | RdevKey::Delete => en.get_key_state(enigo::Key::NumLock), - _ => click_numlock, - } - }; - - if click_capslock { - #[cfg(not(target_os = "macos"))] - en.key_click(enigo::Key::CapsLock); - #[cfg(target_os = "macos")] - en.key_down(enigo::Key::CapsLock); - } - - if click_numlock { - #[cfg(not(target_os = "macos"))] - en.key_click(enigo::Key::NumLock); - } -} - -fn map_keyboard_mode(evt: &KeyEvent) { - // map mode(1): Send keycode according to the peer platform. - #[cfg(windows)] - crate::platform::windows::try_change_desktop(); - - // Wayland - #[cfg(target_os = "linux")] - if !*IS_X11.lock().unwrap() { - let mut en = ENIGO.lock().unwrap(); - let code = evt.chr() as u16; - - if evt.down { - en.key_down(enigo::Key::Raw(code)).ok(); - } else { - en.key_up(enigo::Key::Raw(code)); - } - return; - } - - rdev_key_down_or_up(RdevKey::Unknown(evt.chr()), evt.down); - return; -} - -fn legacy_keyboard_mode(evt: &KeyEvent) { - #[cfg(windows)] - crate::platform::windows::try_change_desktop(); - let mut en = ENIGO.lock().unwrap(); - // disable numlock if press home etc when numlock is on, - // because we will get numpad value (7,8,9 etc) if not - #[cfg(windows)] - let mut _disable_numlock = false; - #[cfg(target_os = "macos")] - en.reset_flag(); - // When long-pressed the command key, then press and release - // the Tab key, there should be CGEventFlagCommand in the flag. - #[cfg(target_os = "macos")] - for ck in evt.modifiers.iter() { - if let Some(key) = KEY_MAP.get(&ck.value()) { - en.add_flag(key); - } - } - #[cfg(not(target_os = "macos"))] - let mut to_release = Vec::new(); - - if evt.down { - let ck = if let Some(key_event::Union::ControlKey(ck)) = evt.union { - ck.value() - } else { - -1 - }; - fix_modifiers(&evt.modifiers[..], &mut en, ck); - for ref ck in evt.modifiers.iter() { - if let Some(key) = KEY_MAP.get(&ck.value()) { - #[cfg(target_os = "linux")] - if key == &Key::Alt && !get_modifier_state(key.clone(), &mut en) { - // for AltGr on Linux - if KEYS_DOWN - .lock() - .unwrap() - .get(&(ControlKey::RAlt.value() as _)) - .is_some() - { - continue; - } - } - #[cfg(not(target_os = "macos"))] - if !get_modifier_state(key.clone(), &mut en) { - en.key_down(key.clone()).ok(); - modifier_sleep(); - to_release.push(key); - } - } - } - } - - match evt.union { - Some(key_event::Union::ControlKey(ck)) => { - if let Some(key) = KEY_MAP.get(&ck.value()) { - #[cfg(windows)] - if let Some(_) = NUMPAD_KEY_MAP.get(&ck.value()) { - _disable_numlock = en.get_key_state(Key::NumLock); - if _disable_numlock { - en.key_down(Key::NumLock).ok(); - en.key_up(Key::NumLock); - } - } - if evt.down { - en.key_down(key.clone()).ok(); - KEYS_DOWN - .lock() - .unwrap() - .insert(ck.value() as _, Instant::now()); - } else { - en.key_up(key.clone()); - KEYS_DOWN.lock().unwrap().remove(&(ck.value() as _)); - } - } else if ck.value() == ControlKey::CtrlAltDel.value() { - // have to spawn new thread because send_sas is tokio_main, the caller can not be tokio_main. - std::thread::spawn(|| { - allow_err!(send_sas()); - }); - } else if ck.value() == ControlKey::LockScreen.value() { - lock_screen_2(); - } - } - Some(key_event::Union::Chr(chr)) => { - if evt.down { - if en.key_down(get_layout(chr)).is_ok() { - KEYS_DOWN - .lock() - .unwrap() - .insert(chr as u64 + KEY_CHAR_START, Instant::now()); - } else { - if let Ok(chr) = char::try_from(chr) { - let mut x = chr.to_string(); - if get_modifier_state(Key::Shift, &mut en) - || get_modifier_state(Key::CapsLock, &mut en) - { - x = x.to_uppercase(); - } - en.key_sequence(&x); - } - } - KEYS_DOWN - .lock() - .unwrap() - .insert(chr as u64 + KEY_CHAR_START, Instant::now()); - } else { - en.key_up(get_layout(chr)); - KEYS_DOWN - .lock() - .unwrap() - .remove(&(chr as u64 + KEY_CHAR_START)); - } - } - Some(key_event::Union::Unicode(chr)) => { - if let Ok(chr) = char::try_from(chr) { - en.key_sequence(&chr.to_string()); - } - } - Some(key_event::Union::Seq(ref seq)) => { - en.key_sequence(&seq); - } - _ => {} - } - #[cfg(not(target_os = "macos"))] - for key in to_release { - en.key_up(key.clone()); - } -} - -pub fn handle_key_(evt: &KeyEvent) { - if EXITING.load(Ordering::SeqCst) { - return; - } - - if evt.down { - sync_status(evt) - } - match evt.mode.unwrap() { - KeyboardMode::Map => { - map_keyboard_mode(evt); - } - KeyboardMode::Translate => { - legacy_keyboard_mode(evt); - } - _ => { - legacy_keyboard_mode(evt); - } - } -} - -#[tokio::main(flavor = "current_thread")] -async fn lock_screen_2() { - lock_screen().await; -} - -#[tokio::main(flavor = "current_thread")] -async fn send_sas() -> ResultType<()> { - let mut stream = crate::ipc::connect(1000, crate::POSTFIX_SERVICE).await?; - timeout(1000, stream.send(&crate::ipc::Data::SAS)).await??; - Ok(()) -} - -#[cfg(test)] -mod test { - use super::*; - use rdev::{listen, Event, EventType, Key}; - use std::sync::mpsc; - - #[test] - fn test_handle_key() { - // listen - let (tx, rx) = mpsc::channel(); - std::thread::spawn(move || { - std::env::set_var("KEYBOARD_ONLY", "y"); - let func = move |event: Event| { - tx.send(event).ok(); - }; - if let Err(error) = listen(func) { - println!("Error: {:?}", error); - } - }); - // set key/char base on char - let mut evt = KeyEvent::new(); - evt.set_chr(66); - evt.mode = KeyboardMode::Legacy.into(); - - evt.modifiers.push(ControlKey::CapsLock.into()); - - // press - evt.down = true; - handle_key(&evt); - if let Ok(listen_evt) = rx.recv() { - assert_eq!(listen_evt.event_type, EventType::KeyPress(Key::Num1)) - } - // release - evt.down = false; - handle_key(&evt); - if let Ok(listen_evt) = rx.recv() { - assert_eq!(listen_evt.event_type, EventType::KeyRelease(Key::Num1)) - } - } - #[test] - fn test_get_key_state() { - let mut en = ENIGO.lock().unwrap(); - println!( - "[*] test_get_key_state: {:?}", - en.get_key_state(enigo::Key::NumLock) - ); - } -}