Merge pull request #2583 from asur4s/master
refactor: legacy keyboard mode && release key
This commit is contained in:
commit
a39e009fb4
@ -154,7 +154,7 @@ packages:
|
|||||||
name: characters
|
name: characters
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.1"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -175,7 +175,7 @@ packages:
|
|||||||
name: clock
|
name: clock
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.1"
|
||||||
code_builder:
|
code_builder:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -621,14 +621,14 @@ packages:
|
|||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.4"
|
version: "0.1.5"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.0"
|
version: "1.8.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -705,7 +705,7 @@ packages:
|
|||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.1"
|
version: "1.8.2"
|
||||||
path_drawing:
|
path_drawing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -266,7 +266,7 @@ pub fn get_keyboard_mode_enum() -> KeyboardMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[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) {
|
if get_key_state(enigo::Key::CapsLock) {
|
||||||
key_event.modifiers.push(ControlKey::CapsLock.into());
|
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")))]
|
#[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;
|
return key_event;
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,7 @@ struct Input {
|
|||||||
y: i32,
|
y: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const KEY_RDEV_START: u64 = 999;
|
||||||
const KEY_CHAR_START: u64 = 9999;
|
const KEY_CHAR_START: u64 = 9999;
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
@ -280,13 +281,17 @@ pub fn mouse_move_relative(x: i32, y: i32) {
|
|||||||
en.mouse_move_relative(x, y);
|
en.mouse_move_relative(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(windows)]
|
||||||
fn modifier_sleep() {
|
fn modifier_sleep() {
|
||||||
// sleep for a while, this is only for keying in rdp in peer so far
|
// 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));
|
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]
|
#[inline]
|
||||||
fn get_modifier_state(key: Key, en: &mut Enigo) -> bool {
|
fn get_modifier_state(key: Key, en: &mut Enigo) -> bool {
|
||||||
// https://github.com/rustdesk/rustdesk/issues/332
|
// 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() {
|
pub fn fix_key_down_timeout_loop() {
|
||||||
std::thread::spawn(move || 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);
|
fix_key_down_timeout(false);
|
||||||
});
|
});
|
||||||
if let Err(err) = ctrlc::set_handler(move || {
|
if let Err(err) = ctrlc::set_handler(move || {
|
||||||
@ -356,38 +361,61 @@ pub fn fix_key_down_timeout_at_exit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_layout(key: u32) -> Key {
|
fn record_key_is_control_key(record_key: u64) -> bool {
|
||||||
Key::Layout(std::char::from_u32(key).unwrap_or('\0'))
|
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<Key> {
|
||||||
|
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) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
let cloned = (*KEYS_DOWN.lock().unwrap()).clone();
|
let cloned = (*key_down).clone();
|
||||||
for (key, value) in cloned.into_iter() {
|
drop(key_down);
|
||||||
if force || value.elapsed().as_millis() >= 360_000 {
|
|
||||||
KEYS_DOWN.lock().unwrap().remove(&key);
|
for (record_key, time) in cloned.into_iter() {
|
||||||
let key = if key < KEY_CHAR_START {
|
if force || time.elapsed().as_millis() >= 360_000 {
|
||||||
if let Some(key) = KEY_MAP.get(&(key as _)) {
|
record_pressed_key(record_key, false);
|
||||||
Some(*key)
|
release_record_key(record_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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -505,6 +533,7 @@ pub fn handle_mouse_(evt: &MouseEvent) {
|
|||||||
if key != &Key::CapsLock && key != &Key::NumLock {
|
if key != &Key::CapsLock && key != &Key::NumLock {
|
||||||
if !get_modifier_state(key.clone(), &mut en) {
|
if !get_modifier_state(key.clone(), &mut en) {
|
||||||
en.key_down(key.clone()).ok();
|
en.key_down(key.clone()).ok();
|
||||||
|
#[cfg(windows)]
|
||||||
modifier_sleep();
|
modifier_sleep();
|
||||||
to_release.push(key);
|
to_release.push(key);
|
||||||
}
|
}
|
||||||
@ -643,7 +672,370 @@ pub async fn lock_screen() {
|
|||||||
super::video_service::switch_to_primary().await;
|
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> {
|
||||||
|
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<Key>) {
|
||||||
|
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<Key>) {
|
||||||
|
#[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<ControlKey>, 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<Key>) {
|
||||||
|
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<ControlKey>) -> 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<Key> = 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! {
|
lazy_static::lazy_static! {
|
||||||
|
static ref MODIFIER_MAP: HashMap<i32, Key> = [
|
||||||
|
(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<i32, Key> =
|
static ref KEY_MAP: HashMap<i32, Key> =
|
||||||
[
|
[
|
||||||
(ControlKey::Alt, Key::Alt),
|
(ControlKey::Alt, Key::Alt),
|
||||||
@ -736,317 +1128,3 @@ lazy_static::lazy_static! {
|
|||||||
(ControlKey::Delete, true),
|
(ControlKey::Delete, true),
|
||||||
].iter().map(|(a, b)| (a.value(), b.clone())).collect();
|
].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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user