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
|
||||
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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<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) {
|
||||
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> {
|
||||
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! {
|
||||
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> =
|
||||
[
|
||||
(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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user