2021-03-29 15:59:14 +08:00
|
|
|
use super::*;
|
2022-11-09 15:04:24 +08:00
|
|
|
#[cfg(target_os = "linux")]
|
2022-09-05 23:50:42 +08:00
|
|
|
use crate::common::IS_X11;
|
2021-03-29 15:59:14 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
use dispatch::Queue;
|
2021-07-27 20:10:04 +08:00
|
|
|
use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable};
|
2022-09-11 21:46:53 +08:00
|
|
|
use hbb_common::{config::COMPRESS_LEVEL, get_time, protobuf::EnumOrUnknown};
|
2023-03-25 23:44:20 +08:00
|
|
|
use rdev::{self, EventType, Key as RdevKey, KeyCode, RawKey};
|
2022-12-29 17:11:10 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
use rdev::{CGEventSourceStateID, CGEventTapLocation, VirtualInput};
|
2021-08-12 01:25:32 +08:00
|
|
|
use std::{
|
|
|
|
convert::TryFrom,
|
2022-11-09 15:04:24 +08:00
|
|
|
ops::Sub,
|
2021-08-12 01:25:32 +08:00
|
|
|
sync::atomic::{AtomicBool, Ordering},
|
2023-03-11 17:18:13 +08:00
|
|
|
thread,
|
2023-04-08 19:07:24 +08:00
|
|
|
time::{self, Duration, Instant},
|
2021-08-12 01:25:32 +08:00
|
|
|
};
|
2023-04-02 14:31:30 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
use winapi::um::winuser::{
|
|
|
|
ActivateKeyboardLayout, GetForegroundWindow, GetKeyboardLayout, GetWindowThreadProcessId,
|
|
|
|
VkKeyScanW,
|
|
|
|
};
|
2021-03-29 15:59:14 +08:00
|
|
|
|
2022-11-11 14:28:35 +08:00
|
|
|
const INVALID_CURSOR_POS: i32 = i32::MIN;
|
|
|
|
|
2023-04-02 14:31:30 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
lazy_static::lazy_static! {
|
|
|
|
static ref LAST_HKL: Arc<Mutex<u32>> = Arc::new(Mutex::new(0));
|
|
|
|
}
|
|
|
|
|
2021-03-29 15:59:14 +08:00
|
|
|
#[derive(Default)]
|
|
|
|
struct StateCursor {
|
|
|
|
hcursor: u64,
|
|
|
|
cursor_data: Arc<Message>,
|
|
|
|
cached_cursor_data: HashMap<u64, Arc<Message>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl super::service::Reset for StateCursor {
|
|
|
|
fn reset(&mut self) {
|
|
|
|
*self = Default::default();
|
|
|
|
crate::platform::reset_input_cache();
|
2022-03-07 16:19:10 +08:00
|
|
|
fix_key_down_timeout(true);
|
2021-03-29 15:59:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct StatePos {
|
|
|
|
cursor_pos: (i32, i32),
|
|
|
|
}
|
|
|
|
|
2022-11-11 14:28:35 +08:00
|
|
|
impl Default for StatePos {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
cursor_pos: (INVALID_CURSOR_POS, INVALID_CURSOR_POS),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-29 15:59:14 +08:00
|
|
|
impl super::service::Reset for StatePos {
|
|
|
|
fn reset(&mut self) {
|
2022-11-11 14:28:35 +08:00
|
|
|
self.cursor_pos = (INVALID_CURSOR_POS, INVALID_CURSOR_POS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl StatePos {
|
2022-11-11 14:53:37 +08:00
|
|
|
#[inline]
|
2022-11-11 14:28:35 +08:00
|
|
|
fn is_valid(&self) -> bool {
|
2022-11-11 14:53:37 +08:00
|
|
|
self.cursor_pos.0 != INVALID_CURSOR_POS
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn is_moved(&self, x: i32, y: i32) -> bool {
|
|
|
|
self.is_valid() && (self.cursor_pos.0 != x || self.cursor_pos.1 != y)
|
2021-03-29 15:59:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-09 16:22:31 +08:00
|
|
|
#[derive(Default, Clone, Copy)]
|
2021-03-29 15:59:14 +08:00
|
|
|
struct Input {
|
|
|
|
conn: i32,
|
|
|
|
time: i64,
|
2022-11-09 16:22:31 +08:00
|
|
|
x: i32,
|
|
|
|
y: i32,
|
2021-03-29 15:59:14 +08:00
|
|
|
}
|
|
|
|
|
2022-01-14 01:15:03 +08:00
|
|
|
const KEY_CHAR_START: u64 = 9999;
|
2021-03-29 15:59:14 +08:00
|
|
|
|
|
|
|
#[derive(Clone, Default)]
|
|
|
|
pub struct MouseCursorSub {
|
|
|
|
inner: ConnInner,
|
|
|
|
cached: HashMap<u64, Arc<Message>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ConnInner> for MouseCursorSub {
|
|
|
|
fn from(inner: ConnInner) -> Self {
|
|
|
|
Self {
|
|
|
|
inner,
|
|
|
|
cached: HashMap::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Subscriber for MouseCursorSub {
|
|
|
|
#[inline]
|
|
|
|
fn id(&self) -> i32 {
|
|
|
|
self.inner.id()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn send(&mut self, msg: Arc<Message>) {
|
2022-07-14 17:20:01 +08:00
|
|
|
if let Some(message::Union::CursorData(cd)) = &msg.union {
|
2021-03-29 15:59:14 +08:00
|
|
|
if let Some(msg) = self.cached.get(&cd.id) {
|
|
|
|
self.inner.send(msg.clone());
|
|
|
|
} else {
|
|
|
|
self.inner.send(msg.clone());
|
|
|
|
let mut tmp = Message::new();
|
|
|
|
// only send id out, require client side cache also
|
|
|
|
tmp.set_cursor_id(cd.id);
|
|
|
|
self.cached.insert(cd.id, Arc::new(tmp));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.inner.send(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-08 20:14:43 +08:00
|
|
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
2023-03-25 17:47:39 +08:00
|
|
|
struct LockModesHandler {
|
|
|
|
caps_lock_changed: bool,
|
|
|
|
num_lock_changed: bool,
|
|
|
|
}
|
|
|
|
|
2023-04-08 21:24:29 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
2023-03-25 23:44:20 +08:00
|
|
|
struct LockModesHandler;
|
|
|
|
|
2023-03-25 17:47:39 +08:00
|
|
|
impl LockModesHandler {
|
2023-03-25 23:44:20 +08:00
|
|
|
#[inline]
|
2023-03-26 00:02:04 +08:00
|
|
|
fn is_modifier_enabled(key_event: &KeyEvent, modifier: ControlKey) -> bool {
|
|
|
|
key_event.modifiers.contains(&modifier.into())
|
2023-03-25 23:44:20 +08:00
|
|
|
}
|
|
|
|
|
2023-04-08 21:24:29 +08:00
|
|
|
#[inline]
|
|
|
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
|
|
|
fn new_handler(key_event: &KeyEvent, _is_numpad_key: bool) -> Self {
|
|
|
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
|
|
|
{
|
|
|
|
Self::new(key_event, _is_numpad_key)
|
|
|
|
}
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
{
|
|
|
|
Self::new(key_event)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-08 20:14:43 +08:00
|
|
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
2023-04-08 19:48:27 +08:00
|
|
|
fn new(key_event: &KeyEvent, is_numpad_key: bool) -> Self {
|
2023-03-25 17:47:39 +08:00
|
|
|
let mut en = ENIGO.lock().unwrap();
|
2023-03-25 23:44:20 +08:00
|
|
|
let event_caps_enabled = Self::is_modifier_enabled(key_event, ControlKey::CapsLock);
|
2023-03-25 17:47:39 +08:00
|
|
|
let local_caps_enabled = en.get_key_state(enigo::Key::CapsLock);
|
|
|
|
let caps_lock_changed = event_caps_enabled != local_caps_enabled;
|
|
|
|
if caps_lock_changed {
|
2023-03-25 23:44:20 +08:00
|
|
|
en.key_click(enigo::Key::CapsLock);
|
2023-03-25 17:47:39 +08:00
|
|
|
}
|
|
|
|
|
2023-04-08 22:54:36 +08:00
|
|
|
let mut num_lock_changed = false;
|
|
|
|
if is_numpad_key {
|
|
|
|
let event_num_enabled = Self::is_modifier_enabled(key_event, ControlKey::NumLock);
|
|
|
|
let local_num_enabled = en.get_key_state(enigo::Key::NumLock);
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
let disable_numlock = false;
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
let disable_numlock = is_numlock_disabled(key_event);
|
|
|
|
num_lock_changed = event_num_enabled != local_num_enabled && !disable_numlock;
|
|
|
|
}
|
2023-03-25 17:47:39 +08:00
|
|
|
if num_lock_changed {
|
2023-03-25 23:44:20 +08:00
|
|
|
en.key_click(enigo::Key::NumLock);
|
2023-03-25 17:47:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Self {
|
|
|
|
caps_lock_changed,
|
|
|
|
num_lock_changed,
|
|
|
|
}
|
|
|
|
}
|
2023-03-25 23:44:20 +08:00
|
|
|
|
2023-04-08 21:24:29 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
fn new(key_event: &KeyEvent) -> Self {
|
2023-03-25 23:44:20 +08:00
|
|
|
let event_caps_enabled = Self::is_modifier_enabled(key_event, ControlKey::CapsLock);
|
2023-03-26 12:22:13 +08:00
|
|
|
// Do not use the following code to detect `local_caps_enabled`.
|
|
|
|
// Because the state of get_key_state will not affect simuation of `VIRTUAL_INPUT_STATE` in this file.
|
|
|
|
//
|
|
|
|
// let local_caps_enabled = VirtualInput::get_key_state(
|
|
|
|
// CGEventSourceStateID::CombinedSessionState,
|
|
|
|
// rdev::kVK_CapsLock,
|
|
|
|
// );
|
|
|
|
let local_caps_enabled = unsafe {
|
|
|
|
let _lock = VIRTUAL_INPUT_MTX.lock();
|
|
|
|
VIRTUAL_INPUT_STATE
|
|
|
|
.as_ref()
|
|
|
|
.map_or(false, |input| input.capslock_down)
|
|
|
|
};
|
2023-03-25 23:44:20 +08:00
|
|
|
if event_caps_enabled && !local_caps_enabled {
|
2023-03-26 12:22:13 +08:00
|
|
|
press_capslock();
|
2023-03-25 23:44:20 +08:00
|
|
|
} else if !event_caps_enabled && local_caps_enabled {
|
2023-03-26 12:22:13 +08:00
|
|
|
release_capslock();
|
2023-03-25 23:44:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Self {}
|
|
|
|
}
|
2023-03-25 17:47:39 +08:00
|
|
|
}
|
|
|
|
|
2023-04-08 20:14:43 +08:00
|
|
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
2023-03-25 17:47:39 +08:00
|
|
|
impl Drop for LockModesHandler {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
let mut en = ENIGO.lock().unwrap();
|
|
|
|
if self.caps_lock_changed {
|
2023-03-25 23:44:20 +08:00
|
|
|
en.key_click(enigo::Key::CapsLock);
|
2023-03-25 17:47:39 +08:00
|
|
|
}
|
|
|
|
if self.num_lock_changed {
|
2023-03-25 23:44:20 +08:00
|
|
|
en.key_click(enigo::Key::NumLock);
|
2023-03-25 17:47:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-08 19:48:27 +08:00
|
|
|
#[inline]
|
|
|
|
#[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
|
2023-04-08 22:06:24 +08:00
|
|
|
if is_legacy_mode(&key_event) {
|
2023-04-08 19:48:27 +08:00
|
|
|
has_numpad_key(key_event)
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-29 15:59:14 +08:00
|
|
|
pub const NAME_CURSOR: &'static str = "mouse_cursor";
|
|
|
|
pub const NAME_POS: &'static str = "mouse_pos";
|
|
|
|
pub type MouseCursorService = ServiceTmpl<MouseCursorSub>;
|
|
|
|
|
|
|
|
pub fn new_cursor() -> MouseCursorService {
|
|
|
|
let sp = MouseCursorService::new(NAME_CURSOR, true);
|
|
|
|
sp.repeat::<StateCursor, _>(33, run_cursor);
|
|
|
|
sp
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_pos() -> GenericService {
|
|
|
|
let sp = GenericService::new(NAME_POS, false);
|
|
|
|
sp.repeat::<StatePos, _>(33, run_pos);
|
|
|
|
sp
|
|
|
|
}
|
|
|
|
|
2023-03-11 17:18:13 +08:00
|
|
|
#[inline]
|
2022-11-09 15:04:24 +08:00
|
|
|
fn update_last_cursor_pos(x: i32, y: i32) {
|
2022-11-14 17:54:39 +08:00
|
|
|
let mut lock = LATEST_SYS_CURSOR_POS.lock().unwrap();
|
2022-11-09 15:04:24 +08:00
|
|
|
if lock.1 .0 != x || lock.1 .1 != y {
|
|
|
|
(lock.0, lock.1) = (Instant::now(), (x, y))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-29 15:59:14 +08:00
|
|
|
fn run_pos(sp: GenericService, state: &mut StatePos) -> ResultType<()> {
|
2023-03-11 17:18:13 +08:00
|
|
|
let (_, (x, y)) = *LATEST_SYS_CURSOR_POS.lock().unwrap();
|
2023-03-11 17:48:35 +08:00
|
|
|
if x == INVALID_CURSOR_POS || y == INVALID_CURSOR_POS {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
2023-03-11 17:18:13 +08:00
|
|
|
if state.is_moved(x, y) {
|
|
|
|
let mut msg_out = Message::new();
|
|
|
|
msg_out.set_cursor_position(CursorPosition {
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
let exclude = {
|
|
|
|
let now = get_time();
|
|
|
|
let lock = LATEST_PEER_INPUT_CURSOR.lock().unwrap();
|
|
|
|
if now - lock.time < 300 {
|
|
|
|
lock.conn
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
}
|
|
|
|
};
|
|
|
|
sp.send_without(msg_out, exclude);
|
2021-03-29 15:59:14 +08:00
|
|
|
}
|
2023-03-11 17:18:13 +08:00
|
|
|
state.cursor_pos = (x, y);
|
2021-03-29 15:59:14 +08:00
|
|
|
|
|
|
|
sp.snapshot(|sps| {
|
|
|
|
let mut msg_out = Message::new();
|
|
|
|
msg_out.set_cursor_position(CursorPosition {
|
|
|
|
x: state.cursor_pos.0,
|
|
|
|
y: state.cursor_pos.1,
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
sps.send(msg_out);
|
|
|
|
Ok(())
|
|
|
|
})?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run_cursor(sp: MouseCursorService, state: &mut StateCursor) -> ResultType<()> {
|
|
|
|
if let Some(hcursor) = crate::get_cursor()? {
|
|
|
|
if hcursor != state.hcursor {
|
|
|
|
let msg;
|
|
|
|
if let Some(cached) = state.cached_cursor_data.get(&hcursor) {
|
|
|
|
super::log::trace!("Cursor data cached, hcursor: {}", hcursor);
|
|
|
|
msg = cached.clone();
|
|
|
|
} else {
|
|
|
|
let mut data = crate::get_cursor_data(hcursor)?;
|
2022-07-23 23:20:39 +08:00
|
|
|
data.colors =
|
|
|
|
hbb_common::compress::compress(&data.colors[..], COMPRESS_LEVEL).into();
|
2021-03-29 15:59:14 +08:00
|
|
|
let mut tmp = Message::new();
|
|
|
|
tmp.set_cursor_data(data);
|
|
|
|
msg = Arc::new(tmp);
|
|
|
|
state.cached_cursor_data.insert(hcursor, msg.clone());
|
|
|
|
super::log::trace!("Cursor data updated, hcursor: {}", hcursor);
|
|
|
|
}
|
|
|
|
state.hcursor = hcursor;
|
|
|
|
sp.send_shared(msg.clone());
|
|
|
|
state.cursor_data = msg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sp.snapshot(|sps| {
|
|
|
|
sps.send_shared(state.cursor_data.clone());
|
|
|
|
Ok(())
|
|
|
|
})?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-02-08 13:31:49 +08:00
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
|
|
|
enum KeysDown {
|
|
|
|
RdevKey(RawKey),
|
2023-02-08 14:14:13 +08:00
|
|
|
EnigoKey(u64),
|
2023-02-08 13:31:49 +08:00
|
|
|
}
|
|
|
|
|
2021-03-29 15:59:14 +08:00
|
|
|
lazy_static::lazy_static! {
|
2022-03-08 15:42:58 +08:00
|
|
|
static ref ENIGO: Arc<Mutex<Enigo>> = {
|
|
|
|
Arc::new(Mutex::new(Enigo::new()))
|
|
|
|
};
|
2023-02-08 13:31:49 +08:00
|
|
|
static ref KEYS_DOWN: Arc<Mutex<HashMap<KeysDown, Instant>>> = Default::default();
|
2022-11-14 17:54:39 +08:00
|
|
|
static ref LATEST_PEER_INPUT_CURSOR: Arc<Mutex<Input>> = Default::default();
|
2023-03-11 17:48:35 +08:00
|
|
|
static ref LATEST_SYS_CURSOR_POS: Arc<Mutex<(Instant, (i32, i32))>> = Arc::new(Mutex::new((Instant::now().sub(MOUSE_MOVE_PROTECTION_TIMEOUT), (INVALID_CURSOR_POS, INVALID_CURSOR_POS))));
|
2021-03-29 15:59:14 +08:00
|
|
|
}
|
2021-08-12 01:25:32 +08:00
|
|
|
static EXITING: AtomicBool = AtomicBool::new(false);
|
2021-03-29 15:59:14 +08:00
|
|
|
|
2022-11-09 15:04:24 +08:00
|
|
|
const MOUSE_MOVE_PROTECTION_TIMEOUT: Duration = Duration::from_millis(1_000);
|
2022-11-14 15:05:44 +08:00
|
|
|
// Actual diff of (x,y) is (1,1) here. But 5 may be tolerant.
|
2022-11-09 15:04:24 +08:00
|
|
|
const MOUSE_ACTIVE_DISTANCE: i32 = 5;
|
|
|
|
|
2023-03-11 17:18:13 +08:00
|
|
|
static RECORD_CURSOR_POS_RUNNING: AtomicBool = AtomicBool::new(false);
|
|
|
|
|
|
|
|
pub fn try_start_record_cursor_pos() {
|
|
|
|
if RECORD_CURSOR_POS_RUNNING.load(Ordering::SeqCst) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
RECORD_CURSOR_POS_RUNNING.store(true, Ordering::SeqCst);
|
|
|
|
thread::spawn(|| {
|
|
|
|
let interval = time::Duration::from_millis(33);
|
|
|
|
loop {
|
|
|
|
if !RECORD_CURSOR_POS_RUNNING.load(Ordering::SeqCst) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
let now = time::Instant::now();
|
|
|
|
if let Some((x, y)) = crate::get_cursor_pos() {
|
|
|
|
update_last_cursor_pos(x, y);
|
|
|
|
}
|
|
|
|
let elapsed = now.elapsed();
|
|
|
|
if elapsed < interval {
|
|
|
|
thread::sleep(interval - elapsed);
|
|
|
|
}
|
|
|
|
}
|
2023-03-11 17:48:35 +08:00
|
|
|
update_last_cursor_pos(INVALID_CURSOR_POS, INVALID_CURSOR_POS);
|
2023-03-11 17:18:13 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn try_stop_record_cursor_pos() {
|
|
|
|
let count_lock = CONN_COUNT.lock().unwrap();
|
|
|
|
if *count_lock > 0 {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
RECORD_CURSOR_POS_RUNNING.store(false, Ordering::SeqCst);
|
|
|
|
}
|
|
|
|
|
2021-03-29 15:59:14 +08:00
|
|
|
// mac key input must be run in main thread, otherwise crash on >= osx 10.15
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
lazy_static::lazy_static! {
|
|
|
|
static ref QUEUE: Queue = Queue::main();
|
|
|
|
static ref IS_SERVER: bool = std::env::args().nth(1) == Some("--server".to_owned());
|
|
|
|
}
|
|
|
|
|
2023-03-26 12:22:13 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
struct VirtualInputState {
|
|
|
|
virtual_input: VirtualInput,
|
|
|
|
capslock_down: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
impl VirtualInputState {
|
|
|
|
fn new() -> Option<Self> {
|
|
|
|
VirtualInput::new(CGEventSourceStateID::Private, CGEventTapLocation::Session)
|
|
|
|
.map(|virtual_input| Self {
|
|
|
|
virtual_input,
|
|
|
|
capslock_down: false,
|
|
|
|
})
|
|
|
|
.ok()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn simulate(&self, event_type: &EventType) -> ResultType<()> {
|
|
|
|
Ok(self.virtual_input.simulate(&event_type)?)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-29 19:10:25 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
2022-12-29 18:16:06 +08:00
|
|
|
static mut VIRTUAL_INPUT_MTX: Mutex<()> = Mutex::new(());
|
2022-12-29 19:10:25 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
2023-03-26 12:22:13 +08:00
|
|
|
static mut VIRTUAL_INPUT_STATE: Option<VirtualInputState> = None;
|
2022-12-29 17:11:10 +08:00
|
|
|
|
2022-11-25 15:22:44 +08:00
|
|
|
// First call set_uinput() will create keyboard and mouse clients.
|
|
|
|
// The clients are ipc connections that must live shorter than tokio runtime.
|
2022-12-26 01:21:13 +08:00
|
|
|
// Thus this function must not be called in a temporary runtime.
|
2022-07-07 01:27:21 +08:00
|
|
|
#[cfg(target_os = "linux")]
|
2022-11-30 10:28:03 +08:00
|
|
|
pub async fn setup_uinput(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> {
|
2022-07-07 01:27:21 +08:00
|
|
|
// Keyboard and mouse both open /dev/uinput
|
|
|
|
// TODO: Make sure there's no race
|
2022-11-30 10:28:03 +08:00
|
|
|
set_uinput_resolution(minx, maxx, miny, maxy).await?;
|
2022-11-25 15:22:44 +08:00
|
|
|
|
2022-11-30 10:28:03 +08:00
|
|
|
let keyboard = super::uinput::client::UInputKeyboard::new().await?;
|
|
|
|
log::info!("UInput keyboard created");
|
|
|
|
let mouse = super::uinput::client::UInputMouse::new().await?;
|
|
|
|
log::info!("UInput mouse created");
|
2022-11-25 15:22:44 +08:00
|
|
|
|
2022-11-30 10:28:03 +08:00
|
|
|
ENIGO
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.set_custom_keyboard(Box::new(keyboard));
|
|
|
|
ENIGO.lock().unwrap().set_custom_mouse(Box::new(mouse));
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
pub async fn update_mouse_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> {
|
|
|
|
set_uinput_resolution(minx, maxx, miny, maxy).await?;
|
|
|
|
|
2022-12-09 15:53:51 +08:00
|
|
|
std::thread::spawn(|| {
|
|
|
|
if let Some(mouse) = ENIGO.lock().unwrap().get_custom_mouse() {
|
|
|
|
if let Some(mouse) = mouse
|
|
|
|
.as_mut_any()
|
|
|
|
.downcast_mut::<super::uinput::client::UInputMouse>()
|
|
|
|
{
|
|
|
|
allow_err!(mouse.send_refresh());
|
|
|
|
} else {
|
|
|
|
log::error!("failed downcast uinput mouse");
|
|
|
|
}
|
2022-11-30 10:28:03 +08:00
|
|
|
}
|
2022-12-09 15:53:51 +08:00
|
|
|
});
|
2022-11-25 15:22:44 +08:00
|
|
|
|
2022-07-07 01:27:21 +08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(target_os = "linux")]
|
2022-11-30 10:28:03 +08:00
|
|
|
async fn set_uinput_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> {
|
2022-07-21 00:50:08 +08:00
|
|
|
super::uinput::client::set_resolution(minx, maxx, miny, maxy).await
|
2022-07-07 01:27:21 +08:00
|
|
|
}
|
|
|
|
|
2021-03-29 15:59:14 +08:00
|
|
|
pub fn is_left_up(evt: &MouseEvent) -> bool {
|
|
|
|
let buttons = evt.mask >> 3;
|
|
|
|
let evt_type = evt.mask & 0x7;
|
|
|
|
return buttons == 1 && evt_type == 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
pub fn mouse_move_relative(x: i32, y: i32) {
|
|
|
|
crate::platform::windows::try_change_desktop();
|
|
|
|
let mut en = ENIGO.lock().unwrap();
|
|
|
|
en.mouse_move_relative(x, y);
|
|
|
|
}
|
|
|
|
|
2022-12-14 16:57:28 +08:00
|
|
|
#[cfg(windows)]
|
2021-03-29 15:59:14 +08:00
|
|
|
fn modifier_sleep() {
|
|
|
|
// sleep for a while, this is only for keying in rdp in peer so far
|
|
|
|
std::thread::sleep(std::time::Duration::from_nanos(1));
|
|
|
|
}
|
|
|
|
|
2022-12-14 16:57:28 +08:00
|
|
|
#[inline]
|
2022-12-29 14:28:15 +08:00
|
|
|
#[cfg(not(target_os = "macos"))]
|
2022-12-14 16:57:28 +08:00
|
|
|
fn is_pressed(key: &Key, en: &mut Enigo) -> bool {
|
|
|
|
get_modifier_state(key.clone(), en)
|
|
|
|
}
|
|
|
|
|
2023-03-25 23:44:20 +08:00
|
|
|
#[inline]
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
fn key_sleep() {
|
|
|
|
std::thread::sleep(Duration::from_millis(20));
|
|
|
|
}
|
|
|
|
|
2021-06-01 13:05:04 +08:00
|
|
|
#[inline]
|
2021-07-27 20:10:04 +08:00
|
|
|
fn get_modifier_state(key: Key, en: &mut Enigo) -> bool {
|
2022-03-07 20:16:28 +08:00
|
|
|
// https://github.com/rustdesk/rustdesk/issues/332
|
2021-12-24 00:27:27 +08:00
|
|
|
// on Linux, if RightAlt is down, RightAlt status is false, Alt status is true
|
|
|
|
// but on Windows, both are true
|
2021-06-01 13:05:04 +08:00
|
|
|
let x = en.get_key_state(key.clone());
|
|
|
|
match key {
|
2021-07-27 20:10:04 +08:00
|
|
|
Key::Shift => x || en.get_key_state(Key::RightShift),
|
|
|
|
Key::Control => x || en.get_key_state(Key::RightControl),
|
|
|
|
Key::Alt => x || en.get_key_state(Key::RightAlt),
|
|
|
|
Key::Meta => x || en.get_key_state(Key::RWin),
|
2021-12-24 00:27:27 +08:00
|
|
|
Key::RightShift => x || en.get_key_state(Key::Shift),
|
|
|
|
Key::RightControl => x || en.get_key_state(Key::Control),
|
|
|
|
Key::RightAlt => x || en.get_key_state(Key::Alt),
|
|
|
|
Key::RWin => x || en.get_key_state(Key::Meta),
|
2021-06-01 13:05:04 +08:00
|
|
|
_ => x,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-29 15:59:14 +08:00
|
|
|
pub fn handle_mouse(evt: &MouseEvent, conn: i32) {
|
2022-11-10 10:27:13 +08:00
|
|
|
if !active_mouse_(conn) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let evt_type = evt.mask & 0x7;
|
|
|
|
if evt_type == 0 {
|
|
|
|
let time = get_time();
|
|
|
|
*LATEST_PEER_INPUT_CURSOR.lock().unwrap() = Input {
|
|
|
|
time,
|
|
|
|
conn,
|
|
|
|
x: evt.x,
|
|
|
|
y: evt.y,
|
|
|
|
};
|
|
|
|
}
|
2021-05-20 17:19:37 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
if !*IS_SERVER {
|
|
|
|
// having GUI, run main GUI thread, otherwise crash
|
|
|
|
let evt = evt.clone();
|
2022-11-16 13:15:05 +08:00
|
|
|
QUEUE.exec_async(move || handle_mouse_(&evt));
|
2021-05-20 17:19:37 +08:00
|
|
|
return;
|
|
|
|
}
|
2022-11-10 10:27:13 +08:00
|
|
|
#[cfg(windows)]
|
|
|
|
crate::portable_service::client::handle_mouse(evt);
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
handle_mouse_(evt);
|
2021-05-20 17:19:37 +08:00
|
|
|
}
|
|
|
|
|
2021-08-09 04:21:42 +08:00
|
|
|
pub fn fix_key_down_timeout_loop() {
|
2021-07-27 20:10:04 +08:00
|
|
|
std::thread::spawn(move || loop {
|
2022-12-15 19:51:50 +08:00
|
|
|
std::thread::sleep(std::time::Duration::from_millis(10_000));
|
2021-08-09 04:21:42 +08:00
|
|
|
fix_key_down_timeout(false);
|
|
|
|
});
|
2021-08-12 01:25:32 +08:00
|
|
|
if let Err(err) = ctrlc::set_handler(move || {
|
|
|
|
fix_key_down_timeout_at_exit();
|
|
|
|
std::process::exit(0); // will call atexit on posix, but not on Windows
|
|
|
|
}) {
|
|
|
|
log::error!("Failed to set Ctrl-C handler: {}", err);
|
2021-08-09 04:21:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-12 01:25:32 +08:00
|
|
|
pub fn fix_key_down_timeout_at_exit() {
|
|
|
|
if EXITING.load(Ordering::SeqCst) {
|
2021-08-09 04:21:42 +08:00
|
|
|
return;
|
|
|
|
}
|
2021-08-12 01:25:32 +08:00
|
|
|
EXITING.store(true, Ordering::SeqCst);
|
2021-08-09 04:21:42 +08:00
|
|
|
fix_key_down_timeout(true);
|
|
|
|
log::info!("fix_key_down_timeout_at_exit");
|
|
|
|
}
|
|
|
|
|
2023-03-25 23:44:20 +08:00
|
|
|
#[inline]
|
2023-03-19 11:55:35 +08:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
pub fn clear_remapped_keycode() {
|
|
|
|
ENIGO.lock().unwrap().tfc_clear_remapped();
|
|
|
|
}
|
|
|
|
|
2022-01-14 01:15:03 +08:00
|
|
|
#[inline]
|
2022-12-15 19:51:50 +08:00
|
|
|
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 {
|
2023-02-08 13:31:49 +08:00
|
|
|
record_key < KEY_CHAR_START
|
2022-12-15 19:51:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-29 15:40:05 +08:00
|
|
|
pub fn release_device_modifiers() {
|
2023-03-29 12:49:22 +08:00
|
|
|
let mut en = ENIGO.lock().unwrap();
|
|
|
|
for modifier in [
|
|
|
|
Key::Shift,
|
|
|
|
Key::Control,
|
|
|
|
Key::Alt,
|
|
|
|
Key::Meta,
|
|
|
|
Key::RightShift,
|
|
|
|
Key::RightControl,
|
|
|
|
Key::RightAlt,
|
|
|
|
Key::RWin,
|
|
|
|
] {
|
|
|
|
if get_modifier_state(modifier, &mut en) {
|
|
|
|
en.key_up(modifier);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-15 19:51:50 +08:00
|
|
|
#[inline]
|
2023-02-08 13:31:49 +08:00
|
|
|
fn release_record_key(record_key: KeysDown) {
|
2023-02-08 14:14:13 +08:00
|
|
|
let func = move || match record_key {
|
|
|
|
KeysDown::RdevKey(raw_key) => {
|
|
|
|
simulate_(&EventType::KeyRelease(RdevKey::RawKey(raw_key)));
|
|
|
|
}
|
|
|
|
KeysDown::EnigoKey(key) => {
|
|
|
|
if let Some(key) = record_key_to_key(key) {
|
|
|
|
ENIGO.lock().unwrap().key_up(key);
|
|
|
|
log::debug!("Fixed {:?} timeout", key);
|
2023-02-08 13:31:49 +08:00
|
|
|
}
|
2022-12-15 19:51:50 +08:00
|
|
|
}
|
|
|
|
};
|
2022-12-29 17:11:10 +08:00
|
|
|
|
2022-12-15 19:51:50 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
QUEUE.exec_async(func);
|
|
|
|
#[cfg(not(target_os = "macos"))]
|
|
|
|
func();
|
2022-01-14 01:15:03 +08:00
|
|
|
}
|
|
|
|
|
2021-08-09 04:21:42 +08:00
|
|
|
fn fix_key_down_timeout(force: bool) {
|
2022-12-15 19:51:50 +08:00
|
|
|
let key_down = KEYS_DOWN.lock().unwrap();
|
|
|
|
if key_down.is_empty() {
|
2021-08-09 04:21:42 +08:00
|
|
|
return;
|
|
|
|
}
|
2022-12-15 19:51:50 +08:00
|
|
|
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);
|
2021-07-27 20:10:04 +08:00
|
|
|
}
|
2021-08-09 04:21:42 +08:00
|
|
|
}
|
2021-07-27 20:10:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// e.g. current state of ctrl is down, but ctrl not in modifier, we should change ctrl to up, to make modifier state sync between remote and local
|
|
|
|
#[inline]
|
|
|
|
fn fix_modifier(
|
2022-07-14 17:20:01 +08:00
|
|
|
modifiers: &[EnumOrUnknown<ControlKey>],
|
2021-07-27 20:10:04 +08:00
|
|
|
key0: ControlKey,
|
|
|
|
key1: Key,
|
|
|
|
en: &mut Enigo,
|
|
|
|
) {
|
2022-07-14 17:20:01 +08:00
|
|
|
if get_modifier_state(key1, en) && !modifiers.contains(&EnumOrUnknown::new(key0)) {
|
2022-03-07 20:16:28 +08:00
|
|
|
#[cfg(windows)]
|
|
|
|
if key0 == ControlKey::Control && get_modifier_state(Key::Alt, en) {
|
|
|
|
// AltGr case
|
|
|
|
return;
|
|
|
|
}
|
2021-07-27 20:10:04 +08:00
|
|
|
en.key_up(key1);
|
|
|
|
log::debug!("Fixed {:?}", key1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-14 17:20:01 +08:00
|
|
|
fn fix_modifiers(modifiers: &[EnumOrUnknown<ControlKey>], en: &mut Enigo, ck: i32) {
|
2021-07-27 20:10:04 +08:00
|
|
|
if ck != ControlKey::Shift.value() {
|
|
|
|
fix_modifier(modifiers, ControlKey::Shift, Key::Shift, en);
|
|
|
|
}
|
|
|
|
if ck != ControlKey::RShift.value() {
|
|
|
|
fix_modifier(modifiers, ControlKey::Shift, Key::RightShift, en);
|
|
|
|
}
|
|
|
|
if ck != ControlKey::Alt.value() {
|
|
|
|
fix_modifier(modifiers, ControlKey::Alt, Key::Alt, en);
|
|
|
|
}
|
|
|
|
if ck != ControlKey::RAlt.value() {
|
|
|
|
fix_modifier(modifiers, ControlKey::Alt, Key::RightAlt, en);
|
|
|
|
}
|
|
|
|
if ck != ControlKey::Control.value() {
|
|
|
|
fix_modifier(modifiers, ControlKey::Control, Key::Control, en);
|
|
|
|
}
|
|
|
|
if ck != ControlKey::RControl.value() {
|
|
|
|
fix_modifier(modifiers, ControlKey::Control, Key::RightControl, en);
|
|
|
|
}
|
|
|
|
if ck != ControlKey::Meta.value() {
|
|
|
|
fix_modifier(modifiers, ControlKey::Meta, Key::Meta, en);
|
|
|
|
}
|
|
|
|
if ck != ControlKey::RWin.value() {
|
|
|
|
fix_modifier(modifiers, ControlKey::Meta, Key::RWin, en);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-09 16:35:08 +08:00
|
|
|
fn active_mouse_(conn: i32) -> bool {
|
2022-11-09 16:22:31 +08:00
|
|
|
// out of time protection
|
2022-11-14 17:54:39 +08:00
|
|
|
if LATEST_SYS_CURSOR_POS.lock().unwrap().0.elapsed() > MOUSE_MOVE_PROTECTION_TIMEOUT {
|
2022-11-09 15:04:24 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-11-09 16:22:31 +08:00
|
|
|
// last conn input may be protected
|
2022-11-14 17:54:39 +08:00
|
|
|
if LATEST_PEER_INPUT_CURSOR.lock().unwrap().conn != conn {
|
2022-11-09 16:22:31 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-01-09 15:31:17 +08:00
|
|
|
let in_active_dist = |a: i32, b: i32| -> bool { (a - b).abs() < MOUSE_ACTIVE_DISTANCE };
|
2022-11-14 15:05:44 +08:00
|
|
|
|
2022-11-14 18:39:49 +08:00
|
|
|
// Check if input is in valid range
|
2022-11-09 16:22:31 +08:00
|
|
|
match crate::get_cursor_pos() {
|
|
|
|
Some((x, y)) => {
|
2022-11-14 17:54:39 +08:00
|
|
|
let (last_in_x, last_in_y) = {
|
|
|
|
let lock = LATEST_PEER_INPUT_CURSOR.lock().unwrap();
|
|
|
|
(lock.x, lock.y)
|
|
|
|
};
|
2023-01-09 15:31:17 +08:00
|
|
|
let mut can_active = in_active_dist(last_in_x, x) && in_active_dist(last_in_y, y);
|
2022-11-14 18:39:49 +08:00
|
|
|
// The cursor may not have been moved to last input position if system is busy now.
|
|
|
|
// While this is not a common case, we check it again after some time later.
|
2022-11-14 15:05:44 +08:00
|
|
|
if !can_active {
|
2022-11-14 18:39:49 +08:00
|
|
|
// 10 micros may be enough for system to move cursor.
|
|
|
|
// We do not care about the situation which system is too slow(more than 10 micros is required).
|
2022-11-14 15:05:44 +08:00
|
|
|
std::thread::sleep(std::time::Duration::from_micros(10));
|
2022-11-14 18:39:49 +08:00
|
|
|
// Sleep here can also somehow suppress delay accumulation.
|
2022-11-14 15:05:44 +08:00
|
|
|
if let Some((x2, y2)) = crate::get_cursor_pos() {
|
2023-01-09 15:31:17 +08:00
|
|
|
can_active = in_active_dist(last_in_x, x2) && in_active_dist(last_in_y, y2);
|
2022-11-14 15:05:44 +08:00
|
|
|
}
|
|
|
|
}
|
2022-11-09 16:35:08 +08:00
|
|
|
if !can_active {
|
2022-11-14 17:54:39 +08:00
|
|
|
let mut lock = LATEST_PEER_INPUT_CURSOR.lock().unwrap();
|
|
|
|
lock.x = INVALID_CURSOR_POS / 2;
|
|
|
|
lock.y = INVALID_CURSOR_POS / 2;
|
2022-11-09 16:35:08 +08:00
|
|
|
}
|
2022-11-09 16:35:08 +08:00
|
|
|
can_active
|
2022-11-09 16:22:31 +08:00
|
|
|
}
|
2022-11-09 15:04:24 +08:00
|
|
|
None => true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-10 10:27:13 +08:00
|
|
|
pub fn handle_mouse_(evt: &MouseEvent) {
|
2021-08-12 01:25:32 +08:00
|
|
|
if EXITING.load(Ordering::SeqCst) {
|
2021-08-09 04:21:42 +08:00
|
|
|
return;
|
|
|
|
}
|
2022-11-09 15:04:24 +08:00
|
|
|
|
2021-03-29 15:59:14 +08:00
|
|
|
#[cfg(windows)]
|
|
|
|
crate::platform::windows::try_change_desktop();
|
|
|
|
let buttons = evt.mask >> 3;
|
|
|
|
let evt_type = evt.mask & 0x7;
|
|
|
|
let mut en = ENIGO.lock().unwrap();
|
|
|
|
#[cfg(not(target_os = "macos"))]
|
|
|
|
let mut to_release = Vec::new();
|
2021-08-08 01:44:37 +08:00
|
|
|
if evt_type == 1 {
|
2022-03-08 12:08:18 +08:00
|
|
|
fix_modifiers(&evt.modifiers[..], &mut en, 0);
|
2021-08-08 01:44:37 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
en.reset_flag();
|
|
|
|
for ref ck in evt.modifiers.iter() {
|
|
|
|
if let Some(key) = KEY_MAP.get(&ck.value()) {
|
2021-03-29 15:59:14 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
en.add_flag(key);
|
|
|
|
#[cfg(not(target_os = "macos"))]
|
2021-07-27 20:10:04 +08:00
|
|
|
if key != &Key::CapsLock && key != &Key::NumLock {
|
2021-06-01 13:05:04 +08:00
|
|
|
if !get_modifier_state(key.clone(), &mut en) {
|
2021-03-29 15:59:14 +08:00
|
|
|
en.key_down(key.clone()).ok();
|
2022-12-14 16:57:28 +08:00
|
|
|
#[cfg(windows)]
|
2021-03-29 15:59:14 +08:00
|
|
|
modifier_sleep();
|
|
|
|
to_release.push(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
match evt_type {
|
|
|
|
0 => {
|
|
|
|
en.mouse_move_to(evt.x, evt.y);
|
|
|
|
}
|
|
|
|
1 => match buttons {
|
2023-01-05 17:14:44 +08:00
|
|
|
0x01 => {
|
2021-03-29 15:59:14 +08:00
|
|
|
allow_err!(en.mouse_down(MouseButton::Left));
|
|
|
|
}
|
2023-01-05 17:14:44 +08:00
|
|
|
0x02 => {
|
2021-03-29 15:59:14 +08:00
|
|
|
allow_err!(en.mouse_down(MouseButton::Right));
|
|
|
|
}
|
2023-01-05 17:14:44 +08:00
|
|
|
0x04 => {
|
2021-03-29 15:59:14 +08:00
|
|
|
allow_err!(en.mouse_down(MouseButton::Middle));
|
|
|
|
}
|
2023-01-05 17:14:44 +08:00
|
|
|
0x08 => {
|
|
|
|
allow_err!(en.mouse_down(MouseButton::Back));
|
|
|
|
}
|
|
|
|
0x10 => {
|
|
|
|
allow_err!(en.mouse_down(MouseButton::Forward));
|
|
|
|
}
|
2021-03-29 15:59:14 +08:00
|
|
|
_ => {}
|
|
|
|
},
|
|
|
|
2 => match buttons {
|
2023-01-05 17:14:44 +08:00
|
|
|
0x01 => {
|
2021-03-29 15:59:14 +08:00
|
|
|
en.mouse_up(MouseButton::Left);
|
|
|
|
}
|
2023-01-05 17:14:44 +08:00
|
|
|
0x02 => {
|
2021-03-29 15:59:14 +08:00
|
|
|
en.mouse_up(MouseButton::Right);
|
|
|
|
}
|
2023-01-05 17:14:44 +08:00
|
|
|
0x04 => {
|
2021-03-29 15:59:14 +08:00
|
|
|
en.mouse_up(MouseButton::Middle);
|
|
|
|
}
|
2023-01-05 17:14:44 +08:00
|
|
|
0x08 => {
|
|
|
|
en.mouse_up(MouseButton::Back);
|
|
|
|
}
|
|
|
|
0x10 => {
|
|
|
|
en.mouse_up(MouseButton::Forward);
|
|
|
|
}
|
2021-03-29 15:59:14 +08:00
|
|
|
_ => {}
|
|
|
|
},
|
2022-11-20 22:46:27 +08:00
|
|
|
3 | 4 => {
|
2021-03-29 15:59:14 +08:00
|
|
|
#[allow(unused_mut)]
|
|
|
|
let mut x = evt.x;
|
|
|
|
#[allow(unused_mut)]
|
|
|
|
let mut y = evt.y;
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
{
|
|
|
|
x = -x;
|
|
|
|
y = -y;
|
|
|
|
}
|
2022-10-30 20:44:33 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
2022-11-07 01:25:36 +08:00
|
|
|
{
|
|
|
|
// TODO: support track pad on win.
|
|
|
|
let is_track_pad = evt
|
|
|
|
.modifiers
|
|
|
|
.contains(&EnumOrUnknown::new(ControlKey::Scroll));
|
|
|
|
|
|
|
|
// fix shift + scroll(down/up)
|
|
|
|
if !is_track_pad
|
|
|
|
&& evt
|
|
|
|
.modifiers
|
|
|
|
.contains(&EnumOrUnknown::new(ControlKey::Shift))
|
|
|
|
{
|
|
|
|
x = y;
|
|
|
|
y = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if x != 0 {
|
|
|
|
en.mouse_scroll_x(x, is_track_pad);
|
|
|
|
}
|
|
|
|
if y != 0 {
|
|
|
|
en.mouse_scroll_y(y, is_track_pad);
|
|
|
|
}
|
2021-03-29 15:59:14 +08:00
|
|
|
}
|
2022-11-07 01:25:36 +08:00
|
|
|
|
|
|
|
#[cfg(not(target_os = "macos"))]
|
|
|
|
{
|
|
|
|
if x != 0 {
|
|
|
|
en.mouse_scroll_x(x);
|
|
|
|
}
|
|
|
|
if y != 0 {
|
|
|
|
en.mouse_scroll_y(y);
|
|
|
|
}
|
2021-03-29 15:59:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
#[cfg(not(target_os = "macos"))]
|
|
|
|
for key in to_release {
|
|
|
|
en.key_up(key.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_enter(evt: &KeyEvent) -> bool {
|
2022-07-14 17:20:01 +08:00
|
|
|
if let Some(key_event::Union::ControlKey(ck)) = evt.union {
|
2021-03-29 15:59:14 +08:00
|
|
|
if ck.value() == ControlKey::Return.value() || ck.value() == ControlKey::NumpadEnter.value()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-07-07 01:27:21 +08:00
|
|
|
pub async fn lock_screen() {
|
2022-04-24 02:37:27 +08:00
|
|
|
cfg_if::cfg_if! {
|
|
|
|
if #[cfg(target_os = "linux")] {
|
|
|
|
// xdg_screensaver lock not work on Linux from our service somehow
|
|
|
|
// loginctl lock-session also not work, they both work run rustdesk from cmd
|
|
|
|
std::thread::spawn(|| {
|
|
|
|
let mut key_event = KeyEvent::new();
|
2022-07-23 23:20:39 +08:00
|
|
|
|
2022-04-24 02:37:27 +08:00
|
|
|
key_event.set_chr('l' as _);
|
|
|
|
key_event.modifiers.push(ControlKey::Meta.into());
|
2022-07-19 16:04:23 +08:00
|
|
|
key_event.mode = KeyboardMode::Legacy.into();
|
|
|
|
|
|
|
|
key_event.down = true;
|
2022-04-24 02:37:27 +08:00
|
|
|
handle_key(&key_event);
|
2022-07-19 16:04:23 +08:00
|
|
|
|
2022-04-24 02:37:27 +08:00
|
|
|
key_event.down = false;
|
|
|
|
handle_key(&key_event);
|
|
|
|
});
|
|
|
|
} else if #[cfg(target_os = "macos")] {
|
|
|
|
// CGSession -suspend not real lock screen, it is user switch
|
|
|
|
std::thread::spawn(|| {
|
|
|
|
let mut key_event = KeyEvent::new();
|
2022-07-23 23:20:39 +08:00
|
|
|
|
2022-04-24 02:37:27 +08:00
|
|
|
key_event.set_chr('q' as _);
|
|
|
|
key_event.modifiers.push(ControlKey::Meta.into());
|
|
|
|
key_event.modifiers.push(ControlKey::Control.into());
|
2022-07-19 16:04:23 +08:00
|
|
|
key_event.mode = KeyboardMode::Legacy.into();
|
|
|
|
|
|
|
|
key_event.down = true;
|
2022-04-24 02:37:27 +08:00
|
|
|
handle_key(&key_event);
|
|
|
|
key_event.down = false;
|
|
|
|
handle_key(&key_event);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
crate::platform::lock_screen();
|
|
|
|
}
|
|
|
|
}
|
2022-07-07 01:27:21 +08:00
|
|
|
super::video_service::switch_to_primary().await;
|
2022-04-24 02:37:27 +08:00
|
|
|
}
|
|
|
|
|
2021-03-29 15:59:14 +08:00
|
|
|
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));
|
2023-03-25 23:44:20 +08:00
|
|
|
key_sleep();
|
2021-03-29 15:59:14 +08:00
|
|
|
return;
|
|
|
|
}
|
2022-11-10 10:27:13 +08:00
|
|
|
#[cfg(windows)]
|
|
|
|
crate::portable_service::client::handle_key(evt);
|
|
|
|
#[cfg(not(windows))]
|
2021-03-29 15:59:14 +08:00
|
|
|
handle_key_(evt);
|
2022-12-29 18:16:06 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
2023-03-25 23:44:20 +08:00
|
|
|
key_sleep();
|
2021-03-29 15:59:14 +08:00
|
|
|
}
|
|
|
|
|
2022-12-29 19:10:25 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
2022-12-29 17:11:10 +08:00
|
|
|
#[inline]
|
|
|
|
fn reset_input() {
|
|
|
|
unsafe {
|
2022-12-29 18:16:06 +08:00
|
|
|
let _lock = VIRTUAL_INPUT_MTX.lock();
|
2023-03-26 12:22:13 +08:00
|
|
|
VIRTUAL_INPUT_STATE = VirtualInputState::new();
|
2022-12-29 17:11:10 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
pub fn reset_input_ondisconn() {
|
|
|
|
if !*IS_SERVER {
|
|
|
|
QUEUE.exec_async(reset_input);
|
|
|
|
} else {
|
|
|
|
reset_input();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-25 23:44:20 +08:00
|
|
|
fn sim_rdev_rawkey_position(code: KeyCode, keydown: bool) {
|
2022-12-15 17:16:05 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
let rawkey = RawKey::ScanCode(code);
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
let rawkey = RawKey::LinuxXorgKeycode(code);
|
2022-12-18 15:12:34 +08:00
|
|
|
// // to-do: test android
|
|
|
|
// #[cfg(target_os = "android")]
|
|
|
|
// let rawkey = RawKey::LinuxConsoleKeycode(code);
|
2022-12-15 17:16:05 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
let rawkey = RawKey::MacVirtualKeycode(code);
|
|
|
|
|
2023-02-08 13:31:49 +08:00
|
|
|
// map mode(1): Send keycode according to the peer platform.
|
|
|
|
record_pressed_key(KeysDown::RdevKey(rawkey), keydown);
|
|
|
|
|
|
|
|
let event_type = if keydown {
|
|
|
|
EventType::KeyPress(RdevKey::RawKey(rawkey))
|
|
|
|
} else {
|
|
|
|
EventType::KeyRelease(RdevKey::RawKey(rawkey))
|
|
|
|
};
|
|
|
|
simulate_(&event_type);
|
|
|
|
}
|
|
|
|
|
2023-02-08 14:14:13 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
2023-02-08 13:31:49 +08:00
|
|
|
fn sim_rdev_rawkey_virtual(code: u32, keydown: bool) {
|
|
|
|
let rawkey = RawKey::WinVirtualKeycode(code);
|
|
|
|
record_pressed_key(KeysDown::RdevKey(rawkey), keydown);
|
2022-12-29 17:11:10 +08:00
|
|
|
let event_type = if keydown {
|
|
|
|
EventType::KeyPress(RdevKey::RawKey(rawkey))
|
|
|
|
} else {
|
|
|
|
EventType::KeyRelease(RdevKey::RawKey(rawkey))
|
|
|
|
};
|
|
|
|
simulate_(&event_type);
|
2022-12-15 17:16:05 +08:00
|
|
|
}
|
|
|
|
|
2022-12-29 17:11:10 +08:00
|
|
|
#[inline]
|
2023-03-26 12:22:13 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
2022-12-29 17:11:10 +08:00
|
|
|
fn simulate_(event_type: &EventType) {
|
|
|
|
unsafe {
|
2022-12-29 18:16:06 +08:00
|
|
|
let _lock = VIRTUAL_INPUT_MTX.lock();
|
2023-03-26 12:22:13 +08:00
|
|
|
if let Some(input) = &VIRTUAL_INPUT_STATE {
|
|
|
|
let _ = input.simulate(&event_type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
fn press_capslock() {
|
|
|
|
let caps_key = RdevKey::RawKey(rdev::RawKey::MacVirtualKeycode(rdev::kVK_CapsLock));
|
|
|
|
unsafe {
|
|
|
|
let _lock = VIRTUAL_INPUT_MTX.lock();
|
|
|
|
if let Some(input) = &mut VIRTUAL_INPUT_STATE {
|
|
|
|
if input.simulate(&EventType::KeyPress(caps_key)).is_ok() {
|
|
|
|
input.capslock_down = true;
|
|
|
|
key_sleep();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
#[inline]
|
|
|
|
fn release_capslock() {
|
|
|
|
let caps_key = RdevKey::RawKey(rdev::RawKey::MacVirtualKeycode(rdev::kVK_CapsLock));
|
|
|
|
unsafe {
|
|
|
|
let _lock = VIRTUAL_INPUT_MTX.lock();
|
|
|
|
if let Some(input) = &mut VIRTUAL_INPUT_STATE {
|
|
|
|
if input.simulate(&EventType::KeyRelease(caps_key)).is_ok() {
|
|
|
|
input.capslock_down = false;
|
|
|
|
key_sleep();
|
|
|
|
}
|
2022-12-29 17:11:10 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "macos"))]
|
|
|
|
#[inline]
|
|
|
|
fn simulate_(event_type: &EventType) {
|
|
|
|
match rdev::simulate(&event_type) {
|
2022-07-18 16:54:54 +08:00
|
|
|
Ok(()) => (),
|
|
|
|
Err(_simulate_error) => {
|
2022-07-26 10:30:26 +08:00
|
|
|
log::error!("Could not send {:?}", &event_type);
|
2022-07-18 16:54:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-15 19:51:50 +08:00
|
|
|
#[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'))
|
2022-12-14 16:57:28 +08:00
|
|
|
}
|
|
|
|
|
2022-12-14 06:40:22 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
fn has_numpad_key(key_event: &KeyEvent) -> bool {
|
|
|
|
key_event
|
2022-07-18 17:19:33 +08:00
|
|
|
.modifiers
|
|
|
|
.iter()
|
2022-12-14 06:40:22 +08:00
|
|
|
.filter(|&&ck| NUMPAD_KEY_MAP.get(&ck.value()).is_some())
|
|
|
|
.count()
|
|
|
|
!= 0
|
|
|
|
}
|
2022-07-18 17:19:33 +08:00
|
|
|
|
2022-10-29 13:37:20 +08:00
|
|
|
fn map_keyboard_mode(evt: &KeyEvent) {
|
|
|
|
#[cfg(windows)]
|
|
|
|
crate::platform::windows::try_change_desktop();
|
|
|
|
|
2022-09-04 11:30:41 +08:00
|
|
|
// Wayland
|
2022-09-06 14:02:12 +08:00
|
|
|
#[cfg(target_os = "linux")]
|
2023-01-10 14:11:49 +08:00
|
|
|
if !*IS_X11 {
|
2022-09-04 11:30:41 +08:00
|
|
|
let mut en = ENIGO.lock().unwrap();
|
|
|
|
let code = evt.chr() as u16;
|
|
|
|
|
|
|
|
if evt.down {
|
2022-09-05 19:52:38 +08:00
|
|
|
en.key_down(enigo::Key::Raw(code)).ok();
|
2022-09-04 11:30:41 +08:00
|
|
|
} else {
|
|
|
|
en.key_up(enigo::Key::Raw(code));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-03-25 23:44:20 +08:00
|
|
|
sim_rdev_rawkey_position(evt.chr() as _, evt.down);
|
2022-07-18 11:34:08 +08:00
|
|
|
}
|
|
|
|
|
2022-12-14 16:57:28 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
fn add_flags_to_enigo(en: &mut Enigo, key_event: &KeyEvent) {
|
2022-08-31 18:54:31 +08:00
|
|
|
// When long-pressed the command key, then press and release
|
2022-08-13 08:12:45 +08:00
|
|
|
// the Tab key, there should be CGEventFlagCommand in the flag.
|
2022-12-14 16:57:28 +08:00
|
|
|
en.reset_flag();
|
2022-12-15 19:51:50 +08:00
|
|
|
for ck in key_event.modifiers.iter() {
|
2022-08-31 18:54:31 +08:00
|
|
|
if let Some(key) = KEY_MAP.get(&ck.value()) {
|
2022-08-13 08:12:45 +08:00
|
|
|
en.add_flag(key);
|
|
|
|
}
|
|
|
|
}
|
2022-12-14 16:57:28 +08:00
|
|
|
}
|
2022-09-05 23:50:42 +08:00
|
|
|
|
2022-12-14 16:57:28 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-12-18 15:07:53 +08:00
|
|
|
#[cfg(target_os = "linux")]
|
2022-12-15 19:51:50 +08:00
|
|
|
fn is_altgr_pressed() -> bool {
|
2023-02-08 14:14:13 +08:00
|
|
|
let altgr_rawkey = RawKey::LinuxXorgKeycode(ControlKey::RAlt.value() as _);
|
2022-12-14 16:57:28 +08:00
|
|
|
KEYS_DOWN
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
2023-02-08 14:14:13 +08:00
|
|
|
.get(&KeysDown::RdevKey(altgr_rawkey))
|
2022-12-14 16:57:28 +08:00
|
|
|
.is_some()
|
|
|
|
}
|
|
|
|
|
2022-12-29 14:28:15 +08:00
|
|
|
#[cfg(not(target_os = "macos"))]
|
2022-12-14 16:57:28 +08:00
|
|
|
fn press_modifiers(en: &mut Enigo, key_event: &KeyEvent, to_release: &mut Vec<Key>) {
|
|
|
|
for ref ck in key_event.modifiers.iter() {
|
2022-12-15 19:51:50 +08:00
|
|
|
if let Some(key) = control_key_value_to_key(ck.value()) {
|
|
|
|
if !is_pressed(&key, en) {
|
2022-03-08 12:08:18 +08:00
|
|
|
#[cfg(target_os = "linux")]
|
2022-12-15 19:51:50 +08:00
|
|
|
if key == Key::Alt && is_altgr_pressed() {
|
2022-12-14 16:57:28 +08:00
|
|
|
continue;
|
2021-03-29 15:59:14 +08:00
|
|
|
}
|
2022-12-14 16:57:28 +08:00
|
|
|
en.key_down(key.clone()).ok();
|
|
|
|
to_release.push(key.clone());
|
|
|
|
#[cfg(windows)]
|
|
|
|
modifier_sleep();
|
2021-03-29 15:59:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-12-14 16:57:28 +08:00
|
|
|
}
|
2022-09-04 14:29:14 +08:00
|
|
|
|
2022-12-29 14:28:15 +08:00
|
|
|
fn sync_modifiers(en: &mut Enigo, key_event: &KeyEvent, _to_release: &mut Vec<Key>) {
|
2022-12-14 16:57:28 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
2022-12-15 19:51:50 +08:00
|
|
|
add_flags_to_enigo(en, key_event);
|
2022-12-14 16:57:28 +08:00
|
|
|
|
|
|
|
if key_event.down {
|
|
|
|
release_unpressed_modifiers(en, key_event);
|
|
|
|
#[cfg(not(target_os = "macos"))]
|
2022-12-29 14:28:15 +08:00
|
|
|
press_modifiers(en, key_event, _to_release);
|
2022-12-14 16:57:28 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn process_control_key(en: &mut Enigo, ck: &EnumOrUnknown<ControlKey>, down: bool) {
|
2022-12-15 19:51:50 +08:00
|
|
|
if let Some(key) = control_key_value_to_key(ck.value()) {
|
2022-12-14 16:57:28 +08:00
|
|
|
if down {
|
2022-12-15 19:51:50 +08:00
|
|
|
en.key_down(key).ok();
|
2022-12-14 16:57:28 +08:00
|
|
|
} else {
|
2022-12-15 19:51:50 +08:00
|
|
|
en.key_up(key);
|
2021-03-29 15:59:14 +08:00
|
|
|
}
|
2022-12-14 16:57:28 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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) {
|
2022-12-15 19:51:50 +08:00
|
|
|
let key = char_value_to_key(chr);
|
2022-12-14 16:57:28 +08:00
|
|
|
|
|
|
|
if down {
|
|
|
|
if en.key_down(key).is_ok() {
|
|
|
|
} else {
|
2021-03-29 15:59:14 +08:00
|
|
|
if let Ok(chr) = char::try_from(chr) {
|
2022-12-14 16:57:28 +08:00
|
|
|
let mut s = chr.to_string();
|
|
|
|
if need_to_uppercase(en) {
|
|
|
|
s = s.to_uppercase();
|
|
|
|
}
|
|
|
|
en.key_sequence(&s);
|
|
|
|
};
|
2021-03-29 15:59:14 +08:00
|
|
|
}
|
2022-12-14 16:57:28 +08:00
|
|
|
} else {
|
|
|
|
en.key_up(key);
|
2021-03-29 15:59:14 +08:00
|
|
|
}
|
2022-12-14 16:57:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-12-29 14:28:15 +08:00
|
|
|
#[cfg(not(target_os = "macos"))]
|
2022-12-14 16:57:28 +08:00
|
|
|
fn release_keys(en: &mut Enigo, to_release: &Vec<Key>) {
|
2021-03-29 15:59:14 +08:00
|
|
|
for key in to_release {
|
2022-09-05 23:50:42 +08:00
|
|
|
en.key_up(key.clone());
|
2022-08-02 18:47:29 +08:00
|
|
|
}
|
2022-07-28 11:01:42 +08:00
|
|
|
}
|
2022-07-26 10:30:26 +08:00
|
|
|
|
2023-02-08 13:31:49 +08:00
|
|
|
fn record_pressed_key(record_key: KeysDown, down: bool) {
|
2022-12-15 19:51:50 +08:00
|
|
|
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() {
|
2023-03-30 16:44:09 +08:00
|
|
|
std::thread::spawn(|| {
|
|
|
|
lock_screen_2();
|
|
|
|
});
|
2022-12-15 19:51:50 +08:00
|
|
|
res = true;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2022-12-14 16:57:28 +08:00
|
|
|
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 {
|
2022-12-15 19:51:50 +08:00
|
|
|
Some(key_event::Union::ControlKey(ck)) => {
|
|
|
|
if is_function_key(&ck) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let record_key = ck.value() as u64;
|
2023-02-08 13:31:49 +08:00
|
|
|
record_pressed_key(KeysDown::EnigoKey(record_key), down);
|
2022-12-15 19:51:50 +08:00
|
|
|
process_control_key(&mut en, &ck, down)
|
|
|
|
}
|
|
|
|
Some(key_event::Union::Chr(chr)) => {
|
|
|
|
let record_key = chr as u64 + KEY_CHAR_START;
|
2023-02-08 13:31:49 +08:00
|
|
|
record_pressed_key(KeysDown::EnigoKey(record_key), down);
|
2022-12-15 19:51:50 +08:00
|
|
|
process_chr(&mut en, chr, down)
|
|
|
|
}
|
2022-12-14 16:57:28 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-02-08 09:48:04 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
2023-02-13 16:26:14 +08:00
|
|
|
fn translate_process_code(code: u32, down: bool) {
|
2023-02-08 09:48:04 +08:00
|
|
|
crate::platform::windows::try_change_desktop();
|
2023-02-13 16:26:14 +08:00
|
|
|
match code >> 16 {
|
2023-03-25 23:44:20 +08:00
|
|
|
0 => sim_rdev_rawkey_position(code as _, down),
|
2023-02-13 16:26:14 +08:00
|
|
|
vk_code => sim_rdev_rawkey_virtual(vk_code, down),
|
|
|
|
};
|
2023-02-08 09:48:04 +08:00
|
|
|
}
|
|
|
|
|
2023-04-02 14:31:30 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
fn check_update_input_layout() {
|
|
|
|
unsafe {
|
|
|
|
let foreground_thread_id =
|
|
|
|
GetWindowThreadProcessId(GetForegroundWindow(), std::ptr::null_mut());
|
|
|
|
let layout = GetKeyboardLayout(foreground_thread_id);
|
|
|
|
let layout_u32 = layout as u32;
|
|
|
|
let mut last_layout_lock = LAST_HKL.lock().unwrap();
|
|
|
|
if *last_layout_lock == 0 || *last_layout_lock != layout_u32 {
|
|
|
|
let res = ActivateKeyboardLayout(layout, 0);
|
|
|
|
if res == layout {
|
|
|
|
*last_layout_lock = layout_u32;
|
|
|
|
} else {
|
|
|
|
log::error!("Failed to call ActivateKeyboardLayout, {}", layout_u32);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-02 22:27:11 +08:00
|
|
|
fn translate_keyboard_mode(evt: &KeyEvent) {
|
2023-04-02 14:31:30 +08:00
|
|
|
// --server could not detect the input layout change.
|
|
|
|
// This is a temporary workaround.
|
2023-04-02 14:39:54 +08:00
|
|
|
//
|
2023-04-02 14:31:30 +08:00
|
|
|
// There may be a better way to detect and handle the input layout change.
|
2023-04-02 14:39:54 +08:00
|
|
|
// while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
|
|
|
|
// {
|
|
|
|
// ...
|
|
|
|
// if (msg.message == WM_INPUTLANGCHANGE)
|
|
|
|
// {
|
|
|
|
// // handle WM_INPUTLANGCHANGE message here
|
|
|
|
// check_update_input_layout();
|
|
|
|
// }
|
|
|
|
// TranslateMessage(&msg);
|
|
|
|
// DispatchMessage(&msg);
|
|
|
|
// ...
|
|
|
|
// }
|
2023-04-02 14:31:30 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
check_update_input_layout();
|
|
|
|
|
2023-02-14 15:42:02 +08:00
|
|
|
match &evt.union {
|
2023-02-10 09:03:19 +08:00
|
|
|
Some(key_event::Union::Seq(seq)) => {
|
2023-03-19 11:55:35 +08:00
|
|
|
// Fr -> US
|
|
|
|
// client: Shift + & => 1(send to remote)
|
|
|
|
// remote: Shift + 1 => !
|
|
|
|
//
|
|
|
|
// Try to release shift first.
|
|
|
|
// remote: Shift + 1 => 1
|
|
|
|
let mut en = ENIGO.lock().unwrap();
|
|
|
|
|
2023-03-24 21:51:56 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
en.key_sequence(seq);
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
2023-03-19 11:55:35 +08:00
|
|
|
{
|
2023-03-24 21:51:56 +08:00
|
|
|
if get_modifier_state(Key::Shift, &mut en) {
|
|
|
|
simulate_(&EventType::KeyRelease(RdevKey::ShiftLeft));
|
|
|
|
}
|
|
|
|
if get_modifier_state(Key::RightShift, &mut en) {
|
|
|
|
simulate_(&EventType::KeyRelease(RdevKey::ShiftRight));
|
|
|
|
}
|
2023-03-19 11:55:35 +08:00
|
|
|
for chr in seq.chars() {
|
2023-03-24 21:51:56 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
rdev::simulate_char(chr).ok();
|
|
|
|
#[cfg(target_os = "linux")]
|
2023-03-19 11:55:35 +08:00
|
|
|
en.key_click(Key::Layout(chr));
|
|
|
|
}
|
|
|
|
}
|
2023-02-10 09:03:19 +08:00
|
|
|
}
|
2023-03-11 17:18:13 +08:00
|
|
|
Some(key_event::Union::Chr(..)) => {
|
2023-02-08 09:48:04 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
2023-02-13 16:26:14 +08:00
|
|
|
translate_process_code(evt.chr(), evt.down);
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
2023-03-25 23:44:20 +08:00
|
|
|
sim_rdev_rawkey_position(evt.chr() as _, evt.down);
|
2023-02-02 22:27:11 +08:00
|
|
|
}
|
2023-02-13 14:55:57 +08:00
|
|
|
Some(key_event::Union::Unicode(..)) => {
|
|
|
|
// Do not handle unicode for now.
|
|
|
|
}
|
2023-04-01 18:09:53 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
Some(key_event::Union::Win2winHotkey(code)) => {
|
|
|
|
simulate_win2win_hotkey(*code, evt.down);
|
|
|
|
}
|
2023-02-02 22:27:11 +08:00
|
|
|
_ => {
|
|
|
|
log::debug!("Unreachable. Unexpected key event {:?}", &evt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-01 18:09:53 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
fn simulate_win2win_hotkey(code: u32, down: bool) {
|
|
|
|
let unicode: u16 = (code & 0x0000FFFF) as u16;
|
2023-04-01 19:30:22 +08:00
|
|
|
if down {
|
2023-04-01 18:09:53 +08:00
|
|
|
// Try convert unicode to virtual keycode first.
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscanw
|
2023-04-02 14:31:30 +08:00
|
|
|
let res = unsafe { VkKeyScanW(unicode) };
|
2023-04-01 18:09:53 +08:00
|
|
|
if res as u16 != 0xFFFF {
|
|
|
|
let vk = res & 0x00FF;
|
|
|
|
let flag = res >> 8;
|
|
|
|
let modifiers = [rdev::Key::ShiftLeft, rdev::Key::ControlLeft, rdev::Key::Alt];
|
|
|
|
let mod_len = modifiers.len();
|
|
|
|
for pos in 0..mod_len {
|
|
|
|
if flag & (0x0001 << pos) != 0 {
|
|
|
|
allow_err!(rdev::simulate(&EventType::KeyPress(modifiers[pos])));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
allow_err!(rdev::simulate_code(Some(vk as _), None, true));
|
|
|
|
allow_err!(rdev::simulate_code(Some(vk as _), None, false));
|
|
|
|
for pos in 0..mod_len {
|
|
|
|
let rpos = mod_len - 1 - pos;
|
|
|
|
if flag & (0x0001 << rpos) != 0 {
|
|
|
|
allow_err!(rdev::simulate(&EventType::KeyRelease(modifiers[rpos])));
|
|
|
|
}
|
|
|
|
}
|
2023-04-01 19:30:22 +08:00
|
|
|
return;
|
2023-04-01 18:09:53 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-01 19:30:22 +08:00
|
|
|
let keycode: u16 = ((code >> 16) & 0x0000FFFF) as u16;
|
|
|
|
allow_err!(rdev::simulate_code(Some(keycode), None, down));
|
2023-04-01 18:09:53 +08:00
|
|
|
}
|
|
|
|
|
2023-04-05 14:30:47 +08:00
|
|
|
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
|
2023-04-08 20:14:43 +08:00
|
|
|
fn skip_led_sync_control_key(_key: &ControlKey) -> bool {
|
2023-04-05 13:51:59 +08:00
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2023-04-08 17:56:04 +08:00
|
|
|
// LockModesHandler should not be created when single meta is pressing and releasing.
|
|
|
|
// Because the drop function may insert "CapsLock Click" and "NumLock Click", which breaks single meta click.
|
|
|
|
// https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1496936687
|
|
|
|
// https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1500415822
|
|
|
|
// https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1500773473
|
2023-04-05 14:30:47 +08:00
|
|
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
2023-04-08 17:56:04 +08:00
|
|
|
fn skip_led_sync_control_key(key: &ControlKey) -> bool {
|
2023-04-08 19:07:24 +08:00
|
|
|
matches!(
|
|
|
|
key,
|
|
|
|
ControlKey::Control
|
|
|
|
| ControlKey::RControl
|
|
|
|
| ControlKey::Meta
|
|
|
|
| ControlKey::Shift
|
|
|
|
| ControlKey::RShift
|
|
|
|
| ControlKey::Alt
|
|
|
|
| ControlKey::RAlt
|
|
|
|
| ControlKey::Tab
|
|
|
|
| ControlKey::Return
|
2023-04-08 19:48:27 +08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2023-04-08 21:26:19 +08:00
|
|
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
2023-04-08 19:48:27 +08:00
|
|
|
fn is_numpad_control_key(key: &ControlKey) -> bool {
|
|
|
|
matches!(
|
|
|
|
key,
|
|
|
|
ControlKey::Numpad0
|
2023-04-08 19:07:24 +08:00
|
|
|
| ControlKey::Numpad1
|
|
|
|
| ControlKey::Numpad2
|
|
|
|
| ControlKey::Numpad3
|
|
|
|
| ControlKey::Numpad4
|
|
|
|
| ControlKey::Numpad5
|
|
|
|
| ControlKey::Numpad6
|
|
|
|
| ControlKey::Numpad7
|
|
|
|
| ControlKey::Numpad8
|
|
|
|
| ControlKey::Numpad9
|
|
|
|
| ControlKey::NumpadEnter
|
|
|
|
)
|
2023-04-08 17:56:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
|
2023-04-08 20:14:43 +08:00
|
|
|
fn skip_led_sync_rdev_key(_key: &RdevKey) -> bool {
|
2023-04-05 13:51:59 +08:00
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2023-04-08 17:56:04 +08:00
|
|
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
|
|
|
fn skip_led_sync_rdev_key(key: &RdevKey) -> bool {
|
2023-04-08 19:48:27 +08:00
|
|
|
matches!(
|
|
|
|
key,
|
|
|
|
RdevKey::ControlLeft
|
|
|
|
| RdevKey::ControlRight
|
|
|
|
| RdevKey::MetaLeft
|
|
|
|
| RdevKey::MetaRight
|
|
|
|
| RdevKey::ShiftLeft
|
|
|
|
| RdevKey::ShiftRight
|
|
|
|
| RdevKey::Alt
|
|
|
|
| RdevKey::AltGr
|
|
|
|
| RdevKey::Tab
|
|
|
|
| RdevKey::Return
|
|
|
|
)
|
2023-04-08 17:56:04 +08:00
|
|
|
}
|
|
|
|
|
2023-04-08 22:06:24 +08:00
|
|
|
#[inline]
|
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
|
|
fn is_legacy_mode(evt: &KeyEvent) -> bool {
|
|
|
|
evt.mode.enum_value_or(KeyboardMode::Legacy) == KeyboardMode::Legacy
|
|
|
|
}
|
|
|
|
|
2022-11-10 10:27:13 +08:00
|
|
|
pub fn handle_key_(evt: &KeyEvent) {
|
2022-07-18 11:34:08 +08:00
|
|
|
if EXITING.load(Ordering::SeqCst) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-04-08 20:14:43 +08:00
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
2023-04-08 17:56:04 +08:00
|
|
|
let mut _lock_mode_handler = None;
|
2023-04-08 20:14:43 +08:00
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
2023-04-08 22:06:24 +08:00
|
|
|
match &evt.union {
|
|
|
|
Some(key_event::Union::Unicode(..)) | Some(key_event::Union::Seq(..)) => {
|
2023-04-08 21:24:29 +08:00
|
|
|
_lock_mode_handler = Some(LockModesHandler::new_handler(&evt, false));
|
2023-03-25 23:44:20 +08:00
|
|
|
}
|
2023-04-08 22:06:24 +08:00
|
|
|
Some(key_event::Union::ControlKey(ck)) => {
|
2023-04-08 17:56:04 +08:00
|
|
|
let key = ck.enum_value_or(ControlKey::Unknown);
|
|
|
|
if !skip_led_sync_control_key(&key) {
|
2023-04-08 21:24:29 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
let is_numpad_key = false;
|
|
|
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
|
|
|
let is_numpad_key = is_numpad_control_key(&key);
|
|
|
|
_lock_mode_handler = Some(LockModesHandler::new_handler(&evt, is_numpad_key));
|
2023-04-08 17:56:04 +08:00
|
|
|
}
|
|
|
|
}
|
2023-04-08 22:06:24 +08:00
|
|
|
Some(key_event::Union::Chr(code)) => {
|
|
|
|
if is_legacy_mode(&evt) {
|
|
|
|
_lock_mode_handler = Some(LockModesHandler::new_handler(evt, false));
|
|
|
|
} else {
|
|
|
|
let key = crate::keyboard::keycode_to_rdev_key(*code);
|
|
|
|
if !skip_led_sync_rdev_key(&key) {
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
let is_numpad_key = false;
|
|
|
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
|
|
|
let is_numpad_key = crate::keyboard::is_numpad_rdev_key(&key);
|
|
|
|
_lock_mode_handler = Some(LockModesHandler::new_handler(evt, is_numpad_key));
|
|
|
|
}
|
2023-03-25 18:37:05 +08:00
|
|
|
}
|
|
|
|
}
|
2023-04-08 17:56:04 +08:00
|
|
|
_ => {}
|
2023-03-25 18:37:05 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
match evt.mode.unwrap() {
|
|
|
|
KeyboardMode::Map => {
|
2022-07-26 10:30:26 +08:00
|
|
|
map_keyboard_mode(evt);
|
2022-07-18 11:34:08 +08:00
|
|
|
}
|
2022-07-28 11:01:42 +08:00
|
|
|
KeyboardMode::Translate => {
|
2023-02-02 22:27:11 +08:00
|
|
|
translate_keyboard_mode(evt);
|
2022-07-28 11:01:42 +08:00
|
|
|
}
|
2022-07-18 11:34:08 +08:00
|
|
|
_ => {
|
2022-07-26 10:30:26 +08:00
|
|
|
legacy_keyboard_mode(evt);
|
2022-07-18 11:34:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-07 01:27:21 +08:00
|
|
|
#[tokio::main(flavor = "current_thread")]
|
|
|
|
async fn lock_screen_2() {
|
|
|
|
lock_screen().await;
|
|
|
|
}
|
|
|
|
|
2021-06-25 19:42:51 +08:00
|
|
|
#[tokio::main(flavor = "current_thread")]
|
2021-03-29 15:59:14 +08:00
|
|
|
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(())
|
|
|
|
}
|
2022-12-14 16:57:28 +08:00
|
|
|
|
|
|
|
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),
|
|
|
|
(ControlKey::Backspace, Key::Backspace),
|
|
|
|
(ControlKey::CapsLock, Key::CapsLock),
|
|
|
|
(ControlKey::Control, Key::Control),
|
|
|
|
(ControlKey::Delete, Key::Delete),
|
|
|
|
(ControlKey::DownArrow, Key::DownArrow),
|
|
|
|
(ControlKey::End, Key::End),
|
|
|
|
(ControlKey::Escape, Key::Escape),
|
|
|
|
(ControlKey::F1, Key::F1),
|
|
|
|
(ControlKey::F10, Key::F10),
|
|
|
|
(ControlKey::F11, Key::F11),
|
|
|
|
(ControlKey::F12, Key::F12),
|
|
|
|
(ControlKey::F2, Key::F2),
|
|
|
|
(ControlKey::F3, Key::F3),
|
|
|
|
(ControlKey::F4, Key::F4),
|
|
|
|
(ControlKey::F5, Key::F5),
|
|
|
|
(ControlKey::F6, Key::F6),
|
|
|
|
(ControlKey::F7, Key::F7),
|
|
|
|
(ControlKey::F8, Key::F8),
|
|
|
|
(ControlKey::F9, Key::F9),
|
|
|
|
(ControlKey::Home, Key::Home),
|
|
|
|
(ControlKey::LeftArrow, Key::LeftArrow),
|
|
|
|
(ControlKey::Meta, Key::Meta),
|
|
|
|
(ControlKey::Option, Key::Option),
|
|
|
|
(ControlKey::PageDown, Key::PageDown),
|
|
|
|
(ControlKey::PageUp, Key::PageUp),
|
|
|
|
(ControlKey::Return, Key::Return),
|
|
|
|
(ControlKey::RightArrow, Key::RightArrow),
|
|
|
|
(ControlKey::Shift, Key::Shift),
|
|
|
|
(ControlKey::Space, Key::Space),
|
|
|
|
(ControlKey::Tab, Key::Tab),
|
|
|
|
(ControlKey::UpArrow, Key::UpArrow),
|
|
|
|
(ControlKey::Numpad0, Key::Numpad0),
|
|
|
|
(ControlKey::Numpad1, Key::Numpad1),
|
|
|
|
(ControlKey::Numpad2, Key::Numpad2),
|
|
|
|
(ControlKey::Numpad3, Key::Numpad3),
|
|
|
|
(ControlKey::Numpad4, Key::Numpad4),
|
|
|
|
(ControlKey::Numpad5, Key::Numpad5),
|
|
|
|
(ControlKey::Numpad6, Key::Numpad6),
|
|
|
|
(ControlKey::Numpad7, Key::Numpad7),
|
|
|
|
(ControlKey::Numpad8, Key::Numpad8),
|
|
|
|
(ControlKey::Numpad9, Key::Numpad9),
|
|
|
|
(ControlKey::Cancel, Key::Cancel),
|
|
|
|
(ControlKey::Clear, Key::Clear),
|
|
|
|
(ControlKey::Menu, Key::Alt),
|
|
|
|
(ControlKey::Pause, Key::Pause),
|
|
|
|
(ControlKey::Kana, Key::Kana),
|
|
|
|
(ControlKey::Hangul, Key::Hangul),
|
|
|
|
(ControlKey::Junja, Key::Junja),
|
|
|
|
(ControlKey::Final, Key::Final),
|
|
|
|
(ControlKey::Hanja, Key::Hanja),
|
|
|
|
(ControlKey::Kanji, Key::Kanji),
|
|
|
|
(ControlKey::Convert, Key::Convert),
|
|
|
|
(ControlKey::Select, Key::Select),
|
|
|
|
(ControlKey::Print, Key::Print),
|
|
|
|
(ControlKey::Execute, Key::Execute),
|
|
|
|
(ControlKey::Snapshot, Key::Snapshot),
|
|
|
|
(ControlKey::Insert, Key::Insert),
|
|
|
|
(ControlKey::Help, Key::Help),
|
|
|
|
(ControlKey::Sleep, Key::Sleep),
|
|
|
|
(ControlKey::Separator, Key::Separator),
|
|
|
|
(ControlKey::Scroll, Key::Scroll),
|
|
|
|
(ControlKey::NumLock, Key::NumLock),
|
|
|
|
(ControlKey::RWin, Key::RWin),
|
|
|
|
(ControlKey::Apps, Key::Apps),
|
|
|
|
(ControlKey::Multiply, Key::Multiply),
|
|
|
|
(ControlKey::Add, Key::Add),
|
|
|
|
(ControlKey::Subtract, Key::Subtract),
|
|
|
|
(ControlKey::Decimal, Key::Decimal),
|
|
|
|
(ControlKey::Divide, Key::Divide),
|
|
|
|
(ControlKey::Equals, Key::Equals),
|
|
|
|
(ControlKey::NumpadEnter, Key::NumpadEnter),
|
|
|
|
(ControlKey::RAlt, Key::RightAlt),
|
|
|
|
(ControlKey::RControl, Key::RightControl),
|
|
|
|
(ControlKey::RShift, Key::RightShift),
|
|
|
|
].iter().map(|(a, b)| (a.value(), b.clone())).collect();
|
|
|
|
static ref NUMPAD_KEY_MAP: HashMap<i32, bool> =
|
|
|
|
[
|
|
|
|
(ControlKey::Home, true),
|
|
|
|
(ControlKey::UpArrow, true),
|
|
|
|
(ControlKey::PageUp, true),
|
|
|
|
(ControlKey::LeftArrow, true),
|
|
|
|
(ControlKey::RightArrow, true),
|
|
|
|
(ControlKey::End, true),
|
|
|
|
(ControlKey::DownArrow, true),
|
|
|
|
(ControlKey::PageDown, true),
|
|
|
|
(ControlKey::Insert, true),
|
|
|
|
(ControlKey::Delete, true),
|
|
|
|
].iter().map(|(a, b)| (a.value(), b.clone())).collect();
|
|
|
|
}
|