From 5dab7bd9a218caf48ae2ae9f1fa63a827e19d8c1 Mon Sep 17 00:00:00 2001
From: Asura <todaymofish@gmail.com>
Date: Sun, 17 Jul 2022 20:34:08 -0700
Subject: [PATCH] Refactor simulate to support switching keyboard modes

---
 libs/enigo/src/linux.rs            | 43 +++-----------
 libs/enigo/src/macos/macos_impl.rs | 32 -----------
 libs/enigo/src/win/win_impl.rs     | 32 -----------
 src/server/connection.rs           |  9 +--
 src/server/input_service.rs        | 90 +++++++++++++++++++++++++-----
 5 files changed, 84 insertions(+), 122 deletions(-)

diff --git a/libs/enigo/src/linux.rs b/libs/enigo/src/linux.rs
index 7f2afc3dc..67af71a62 100644
--- a/libs/enigo/src/linux.rs
+++ b/libs/enigo/src/linux.rs
@@ -3,7 +3,6 @@ use libc;
 use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};
 
 use self::libc::{c_char, c_int, c_void, useconds_t};
-use rdev::{simulate, EventType, EventType::*, Key as RdevKey, SimulateError};
 use std::{borrow::Cow, ffi::CString, io::prelude::*, ptr, sync::mpsc, thread, time};
 const CURRENT_WINDOW: c_int = 0;
 const DEFAULT_DELAY: u64 = 12000;
@@ -104,27 +103,7 @@ impl Enigo {
         self.tx.send((PyMsg::Char('\0'), true)).ok();
     }
 
-    fn send_rdev(&mut self, key: &Key, is_press: bool) -> bool {
-        if let Key::Raw(keycode) = key {
-            let event_type = match is_press {
-                // todo: Acccodding to client type
-                true => Box::leak(Box::new(EventType::KeyPress(RdevKey::Unknown(
-                    (*keycode).into(),
-                )))),
-                false => Box::leak(Box::new(EventType::KeyRelease(RdevKey::Unknown(
-                    (*keycode).into(),
-                )))),
-            };
-
-            match simulate(event_type) {
-                Ok(()) => true,
-                Err(SimulateError) => false,
-            }
-        } else {
-            false
-        }
-    }
-
+ 
     #[inline]
     fn send_pynput(&mut self, key: &Key, is_press: bool) -> bool {
         if unsafe { PYNPUT_EXIT || !PYNPUT_REDAY } {
@@ -459,12 +438,9 @@ impl KeyboardControllable for Enigo {
         if self.xdo.is_null() {
             return Ok(());
         }
-        if self.send_rdev(&key, true) {
-            return Ok(());
-        }
-        if self.send_pynput(&key, true) {
-            return Ok(());
-        }
+        // if self.send_pynput(&key, true) {
+        //     return Ok(());
+        // }
         let string = CString::new(&*keysequence(key))?;
         unsafe {
             xdo_send_keysequence_window_down(
@@ -480,14 +456,9 @@ impl KeyboardControllable for Enigo {
         if self.xdo.is_null() {
             return;
         }
-        // todo
-        let keyboard_mode = 1;
-        if keyboard_mode == 1 && self.send_rdev(&key, false) {
-            return;
-        }
-        if self.send_pynput(&key, false) {
-            return;
-        }
+        // if self.send_pynput(&key, false) {
+        //     return;
+        // }
         if let Ok(string) = CString::new(&*keysequence(key)) {
             unsafe {
                 xdo_send_keysequence_window_up(
diff --git a/libs/enigo/src/macos/macos_impl.rs b/libs/enigo/src/macos/macos_impl.rs
index df494b9cb..520c9dca1 100644
--- a/libs/enigo/src/macos/macos_impl.rs
+++ b/libs/enigo/src/macos/macos_impl.rs
@@ -1,5 +1,4 @@
 use core_graphics;
-use rdev::{simulate, EventType, EventType::*, Key as RdevKey, SimulateError};
 // TODO(dustin): use only the things i need
 
 use self::core_graphics::display::*;
@@ -354,11 +353,6 @@ impl KeyboardControllable for Enigo {
     }
 
     fn key_down(&mut self, key: Key) -> crate::ResultType {
-        let keyboard_mode = 1;
-        if keyboard_mode == 1 {
-            self.send_rdev(&key, true);
-            return Ok(());
-        };
         let code = self.key_to_keycode(key);
         if code == u16::MAX {
             return Err("".into()); 
@@ -374,11 +368,6 @@ impl KeyboardControllable for Enigo {
     }
 
     fn key_up(&mut self, key: Key) {
-        let keyboard_mode = 1;
-        if keyboard_mode == 1 {
-            self.send_rdev(&key, true);
-            return Ok(());
-        };
         if let Some(src) = self.event_source.as_ref() {
             if let Ok(event) =
                 CGEvent::new_keyboard_event(src.clone(), self.key_to_keycode(key), false)
@@ -431,27 +420,6 @@ impl Enigo {
         (x, (display_height as i32) - y_inv)
     }
 
-    fn send_rdev(&mut self, key: &Key, is_press: bool) -> bool {
-        if let Key::Raw(keycode) = key {
-            let event_type = match is_press {
-                // todo: Acccodding to client type
-                true => Box::leak(Box::new(EventType::KeyPress(RdevKey::Unknown(
-                    (*keycode).into(),
-                )))),
-                false => Box::leak(Box::new(EventType::KeyRelease(RdevKey::Unknown(
-                    (*keycode).into(),
-                )))),
-            };
-
-            match simulate(event_type) {
-                Ok(()) => true,
-                Err(SimulateError) => false,
-            }
-        } else {
-            false
-        }
-    }
-
     fn key_to_keycode(&mut self, key: Key) -> CGKeyCode {
         #[allow(deprecated)]
         // I mean duh, we still need to support deprecated keys until they're removed
diff --git a/libs/enigo/src/win/win_impl.rs b/libs/enigo/src/win/win_impl.rs
index b8f3d0fa3..56fc4caef 100644
--- a/libs/enigo/src/win/win_impl.rs
+++ b/libs/enigo/src/win/win_impl.rs
@@ -2,7 +2,6 @@ use self::winapi::ctypes::c_int;
 use self::winapi::shared::{basetsd::ULONG_PTR, minwindef::*, windef::*};
 use self::winapi::um::winbase::*;
 use self::winapi::um::winuser::*;
-use rdev::{simulate, EventType, Key as RdevKey, SimulateError};
 use winapi;
 
 use crate::win::keycodes::*;
@@ -198,11 +197,6 @@ impl KeyboardControllable for Enigo {
     }
 
     fn key_down(&mut self, key: Key) -> crate::ResultType {
-        let keyboard_mode = 1;
-        if keyboard_mode == 1 {
-            self.send_rdev(&key, true);
-            return Ok(());
-        };
         let code = self.key_to_keycode(key);
         if code == 0 || code == 65535 {
             return Err("".into());
@@ -218,11 +212,6 @@ impl KeyboardControllable for Enigo {
     }
 
     fn key_up(&mut self, key: Key) {
-        let keyboard_mode = 1;
-        if keyboard_mode == 1 {
-            self.send_rdev(&key, false);
-            return;
-        };
         keybd_event(KEYEVENTF_KEYUP, self.key_to_keycode(key), 0);
     }
 
@@ -283,27 +272,6 @@ impl Enigo {
         keybd_event(KEYEVENTF_UNICODE | KEYEVENTF_KEYUP, 0, unicode_char);
     }
 
-    fn send_rdev(&mut self, key: &Key, is_press: bool) -> bool {
-        if let Key::Raw(keycode) = key {
-            let event_type = match is_press {
-                // todo: Acccodding to client type
-                true => Box::leak(Box::new(EventType::KeyPress(RdevKey::Unknown(
-                    (*keycode).into(),
-                )))),
-                false => Box::leak(Box::new(EventType::KeyRelease(RdevKey::Unknown(
-                    (*keycode).into(),
-                )))),
-            };
-
-            match simulate(event_type) {
-                Ok(()) => true,
-                Err(SimulateError) => false,
-            }
-        } else {
-            false
-        }
-    }
-
     fn key_to_keycode(&self, key: Key) -> u16 {
         unsafe {
             LAYOUT = std::ptr::null_mut();
diff --git a/src/server/connection.rs b/src/server/connection.rs
index 682c7d928..48fab0c2d 100644
--- a/src/server/connection.rs
+++ b/src/server/connection.rs
@@ -419,15 +419,8 @@ impl Connection {
                         handle_mouse(&msg, id);
                     }
                     MessageInput::Key((mut msg, press)) => {
-                        if press {
-                            msg.down = true;
-                        }
+                        // todo: press and down have similar meanings.
                         handle_key(&msg);
-                        let keyboard_mode = 1;
-                        if press && keyboard_mode != 1{
-                            msg.down = false;
-                            handle_key(&msg);
-                        }
                     }
                     MessageInput::BlockOn => {
                         if crate::platform::block_input(true) {
diff --git a/src/server/input_service.rs b/src/server/input_service.rs
index 8e8e2abb4..f041b7e86 100644
--- a/src/server/input_service.rs
+++ b/src/server/input_service.rs
@@ -3,6 +3,7 @@ use super::*;
 use dispatch::Queue;
 use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable};
 use hbb_common::{config::COMPRESS_LEVEL, protobuf::ProtobufEnumOrUnknown};
+use rdev::{simulate, EventType, EventType::*, Key as RdevKey, SimulateError};
 use std::{
     convert::TryFrom,
     sync::atomic::{AtomicBool, Ordering},
@@ -578,24 +579,27 @@ pub fn handle_key(evt: &KeyEvent) {
     handle_key_(evt);
 }
 
-fn handle_key_(evt: &KeyEvent) {
-    if EXITING.load(Ordering::SeqCst) {
-        return;
+fn map_keyboard_map(evt: &KeyEvent) {
+    // map mode(1): Send keycode according to the peer platform.
+    let event_type = match evt.down {
+        true => EventType::KeyPress(RdevKey::Unknown(evt.get_chr())),
+        false => EventType::KeyRelease(RdevKey::Unknown(evt.get_chr())),
+    };
+
+    match simulate(&event_type) {
+        Ok(()) => (),
+        Err(_simulate_error) => {
+            // todo
+            log::error!("rdev could not send {:?}", event_type);
+        }
     }
+    return;
+}
+
+fn legacy_keyboard_map(evt: &KeyEvent) {
     #[cfg(windows)]
     crate::platform::windows::try_change_desktop();
     let mut en = ENIGO.lock().unwrap();
-    let keyboard_mode = 1;
-    if keyboard_mode == 1 {
-        if let Some(key_event::Union::chr(chr)) = evt.union {
-            if evt.down {
-                en.key_down(Key::Raw(chr.try_into().unwrap()));
-            } else {
-                en.key_up(Key::Raw(chr.try_into().unwrap()));
-            }
-        }
-        return;
-    }
     // disable numlock if press home etc when numlock is on,
     // because we will get numpad value (7,8,9 etc) if not
     #[cfg(windows)]
@@ -740,9 +744,67 @@ fn handle_key_(evt: &KeyEvent) {
     }
 }
 
+fn handle_key_(evt: &KeyEvent) {
+    if EXITING.load(Ordering::SeqCst) {
+        return;
+    }
+
+    match evt.mode {
+        1 => {
+            map_keyboard_map(evt);
+        }
+        3 => {
+            legacy_keyboard_map(evt);
+        }
+        _ => {
+            map_keyboard_map(evt);
+        }
+    }
+}
+
 #[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, simulate, Event, EventType, Key};
+    use std::sync::mpsc;
+    use std::thread;
+
+    #[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(49);
+        evt.mode = 3;
+
+        // 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))
+        }
+    }
+}