rustdesk/src/keyboard.rs
fufesou a76fd86f19 remove warns
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-04-20 13:36:44 +08:00

1074 lines
34 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#[cfg(feature = "flutter")]
use crate::flutter::{CUR_SESSION_ID, SESSIONS};
#[cfg(target_os = "windows")]
use crate::platform::windows::{get_char_from_vk, get_unicode_from_vk};
#[cfg(not(any(feature = "flutter", feature = "cli")))]
use crate::ui::CUR_SESSION;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use crate::{client::get_key_state, common::GrabState};
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use hbb_common::log;
use hbb_common::message_proto::*;
#[cfg(any(target_os = "windows", target_os = "macos"))]
use rdev::KeyCode;
use rdev::{Event, EventType, Key};
#[cfg(any(target_os = "windows", target_os = "macos"))]
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use std::time::SystemTime;
use std::{
collections::{HashMap, HashSet},
sync::{Arc, Mutex},
};
#[cfg(windows)]
static mut IS_ALT_GR: bool = false;
#[allow(dead_code)]
const OS_LOWER_WINDOWS: &str = "windows";
#[allow(dead_code)]
const OS_LOWER_LINUX: &str = "linux";
#[allow(dead_code)]
const OS_LOWER_MACOS: &str = "macos";
#[cfg(any(target_os = "windows", target_os = "macos"))]
static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false);
lazy_static::lazy_static! {
static ref TO_RELEASE: Arc<Mutex<HashSet<Key>>> = Arc::new(Mutex::new(HashSet::<Key>::new()));
static ref MODIFIERS_STATE: Mutex<HashMap<Key, bool>> = {
let mut m = HashMap::new();
m.insert(Key::ShiftLeft, false);
m.insert(Key::ShiftRight, false);
m.insert(Key::ControlLeft, false);
m.insert(Key::ControlRight, false);
m.insert(Key::Alt, false);
m.insert(Key::AltGr, false);
m.insert(Key::MetaLeft, false);
m.insert(Key::MetaRight, false);
Mutex::new(m)
};
}
pub mod client {
use super::*;
pub fn get_keyboard_mode() -> String {
#[cfg(not(any(feature = "flutter", feature = "cli")))]
if let Some(session) = CUR_SESSION.lock().unwrap().as_ref() {
return session.get_keyboard_mode();
}
#[cfg(feature = "flutter")]
if let Some(session) = SESSIONS
.read()
.unwrap()
.get(&*CUR_SESSION_ID.read().unwrap())
{
return session.get_keyboard_mode();
}
"legacy".to_string()
}
pub fn start_grab_loop() {
super::start_grab_loop();
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn change_grab_status(state: GrabState) {
match state {
GrabState::Ready => {}
GrabState::Run => {
#[cfg(windows)]
update_grab_get_key_name();
#[cfg(any(target_os = "windows", target_os = "macos"))]
KEYBOARD_HOOKED.swap(true, Ordering::SeqCst);
#[cfg(target_os = "linux")]
rdev::enable_grab();
}
GrabState::Wait => {
release_remote_keys();
#[cfg(any(target_os = "windows", target_os = "macos"))]
KEYBOARD_HOOKED.swap(false, Ordering::SeqCst);
#[cfg(target_os = "linux")]
rdev::disable_grab();
}
GrabState::Exit => {
#[cfg(target_os = "linux")]
rdev::exit_grab_listen();
}
}
}
pub fn process_event(event: &Event, lock_modes: Option<i32>) -> KeyboardMode {
let keyboard_mode = get_keyboard_mode_enum();
if is_long_press(&event) {
return keyboard_mode;
}
for key_event in event_to_key_events(&event, keyboard_mode, lock_modes) {
send_key_event(&key_event);
}
keyboard_mode
}
pub fn get_modifiers_state(
alt: bool,
ctrl: bool,
shift: bool,
command: bool,
) -> (bool, bool, bool, bool) {
let modifiers_lock = MODIFIERS_STATE.lock().unwrap();
let ctrl = *modifiers_lock.get(&Key::ControlLeft).unwrap()
|| *modifiers_lock.get(&Key::ControlRight).unwrap()
|| ctrl;
let shift = *modifiers_lock.get(&Key::ShiftLeft).unwrap()
|| *modifiers_lock.get(&Key::ShiftRight).unwrap()
|| shift;
let command = *modifiers_lock.get(&Key::MetaLeft).unwrap()
|| *modifiers_lock.get(&Key::MetaRight).unwrap()
|| command;
let alt = *modifiers_lock.get(&Key::Alt).unwrap()
|| *modifiers_lock.get(&Key::AltGr).unwrap()
|| alt;
(alt, ctrl, shift, command)
}
pub fn legacy_modifiers(
key_event: &mut KeyEvent,
alt: bool,
ctrl: bool,
shift: bool,
command: bool,
) {
if alt
&& !crate::is_control_key(&key_event, &ControlKey::Alt)
&& !crate::is_control_key(&key_event, &ControlKey::RAlt)
{
key_event.modifiers.push(ControlKey::Alt.into());
}
if shift
&& !crate::is_control_key(&key_event, &ControlKey::Shift)
&& !crate::is_control_key(&key_event, &ControlKey::RShift)
{
key_event.modifiers.push(ControlKey::Shift.into());
}
if ctrl
&& !crate::is_control_key(&key_event, &ControlKey::Control)
&& !crate::is_control_key(&key_event, &ControlKey::RControl)
{
key_event.modifiers.push(ControlKey::Control.into());
}
if command
&& !crate::is_control_key(&key_event, &ControlKey::Meta)
&& !crate::is_control_key(&key_event, &ControlKey::RWin)
{
key_event.modifiers.push(ControlKey::Meta.into());
}
}
pub fn event_lock_screen() -> KeyEvent {
let mut key_event = KeyEvent::new();
key_event.set_control_key(ControlKey::LockScreen);
key_event.down = true;
key_event.mode = KeyboardMode::Legacy.into();
key_event
}
#[inline]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn lock_screen() {
send_key_event(&event_lock_screen());
}
pub fn event_ctrl_alt_del() -> KeyEvent {
let mut key_event = KeyEvent::new();
if get_peer_platform() == "Windows" {
key_event.set_control_key(ControlKey::CtrlAltDel);
key_event.down = true;
} else {
key_event.set_control_key(ControlKey::Delete);
legacy_modifiers(&mut key_event, true, true, false, false);
key_event.press = true;
}
key_event.mode = KeyboardMode::Legacy.into();
key_event
}
#[inline]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn ctrl_alt_del() {
send_key_event(&event_ctrl_alt_del());
}
}
#[cfg(windows)]
pub fn update_grab_get_key_name() {
match get_keyboard_mode_enum() {
KeyboardMode::Map => rdev::set_get_key_unicode(false),
KeyboardMode::Translate => rdev::set_get_key_unicode(true),
_ => {}
};
}
#[cfg(target_os = "windows")]
static mut IS_0X021D_DOWN: bool = false;
#[cfg(target_os = "macos")]
static mut IS_LEFT_OPTION_DOWN: bool = false;
pub fn start_grab_loop() {
std::env::set_var("KEYBOARD_ONLY", "y");
#[cfg(any(target_os = "windows", target_os = "macos"))]
std::thread::spawn(move || {
let try_handle_keyboard = move |event: Event, key: Key, is_press: bool| -> Option<Event> {
// fix #2211CAPS LOCK don't work
if key == Key::CapsLock || key == Key::NumLock {
return Some(event);
}
let mut _keyboard_mode = KeyboardMode::Map;
let _scan_code = event.position_code;
let _code = event.platform_code as KeyCode;
let res = if KEYBOARD_HOOKED.load(Ordering::SeqCst) {
_keyboard_mode = client::process_event(&event, None);
if is_press {
None
} else {
Some(event)
}
} else {
Some(event)
};
#[cfg(target_os = "windows")]
match _scan_code {
0x1D | 0x021D => rdev::set_modifier(Key::ControlLeft, is_press),
0xE01D => rdev::set_modifier(Key::ControlRight, is_press),
0x2A => rdev::set_modifier(Key::ShiftLeft, is_press),
0x36 => rdev::set_modifier(Key::ShiftRight, is_press),
0x38 => rdev::set_modifier(Key::Alt, is_press),
// Right Alt
0xE038 => rdev::set_modifier(Key::AltGr, is_press),
0xE05B => rdev::set_modifier(Key::MetaLeft, is_press),
0xE05C => rdev::set_modifier(Key::MetaRight, is_press),
_ => {}
}
#[cfg(target_os = "windows")]
unsafe {
// AltGr
if _scan_code == 0x021D {
IS_0X021D_DOWN = is_press;
}
}
#[cfg(target_os = "macos")]
unsafe {
if _code == rdev::kVK_Option {
IS_LEFT_OPTION_DOWN = is_press;
}
}
return res;
};
let func = move |event: Event| match event.event_type {
EventType::KeyPress(key) => try_handle_keyboard(event, key, true),
EventType::KeyRelease(key) => try_handle_keyboard(event, key, false),
_ => Some(event),
};
#[cfg(target_os = "macos")]
rdev::set_is_main_thread(false);
#[cfg(target_os = "windows")]
rdev::set_event_popup(false);
if let Err(error) = rdev::grab(func) {
log::error!("rdev Error: {:?}", error)
}
});
#[cfg(target_os = "linux")]
if let Err(err) = rdev::start_grab_listen(move |event: Event| match event.event_type {
EventType::KeyPress(key) | EventType::KeyRelease(key) => {
if let Key::Unknown(keycode) = key {
log::error!("rdev get unknown key, keycode is : {:?}", keycode);
} else {
client::process_event(&event, None);
}
None
}
_ => Some(event),
}) {
log::error!("Failed to init rdev grab thread: {:?}", err);
};
}
pub fn is_long_press(event: &Event) -> bool {
let keys = MODIFIERS_STATE.lock().unwrap();
match event.event_type {
EventType::KeyPress(k) => {
if let Some(&state) = keys.get(&k) {
if state == true {
return true;
}
}
}
_ => {}
};
return false;
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn release_remote_keys() {
// todo!: client quit suddenly, how to release keys?
let to_release = TO_RELEASE.lock().unwrap().clone();
TO_RELEASE.lock().unwrap().clear();
for key in to_release {
let event_type = EventType::KeyRelease(key);
let event = event_type_to_event(event_type);
// to-do: BUG
// Release events should be sent to the corresponding sessions, instead of current session.
client::process_event(&event, None);
}
}
pub fn get_keyboard_mode_enum() -> KeyboardMode {
match client::get_keyboard_mode().as_str() {
"map" => KeyboardMode::Map,
"translate" => KeyboardMode::Translate,
"legacy" => KeyboardMode::Legacy,
_ => {
// Set "map" as default mode if version >= 1.2.0.
if crate::is_peer_version_ge("1.2.0") {
KeyboardMode::Map
} else {
KeyboardMode::Legacy
}
}
}
}
#[inline]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn is_modifier(key: &rdev::Key) -> bool {
matches!(
key,
Key::ShiftLeft
| Key::ShiftRight
| Key::ControlLeft
| Key::ControlRight
| Key::MetaLeft
| Key::MetaRight
| Key::Alt
| Key::AltGr
)
}
#[inline]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn is_numpad_rdev_key(key: &rdev::Key) -> bool {
matches!(
key,
Key::Kp0
| Key::Kp1
| Key::Kp2
| Key::Kp3
| Key::Kp4
| Key::Kp5
| Key::Kp6
| Key::Kp7
| Key::Kp8
| Key::Kp9
| Key::KpMinus
| Key::KpMultiply
| Key::KpDivide
| Key::KpPlus
| Key::KpDecimal
)
}
#[inline]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn is_letter_rdev_key(key: &rdev::Key) -> bool {
matches!(
key,
Key::KeyA
| Key::KeyB
| Key::KeyC
| Key::KeyD
| Key::KeyE
| Key::KeyF
| Key::KeyG
| Key::KeyH
| Key::KeyI
| Key::KeyJ
| Key::KeyK
| Key::KeyL
| Key::KeyM
| Key::KeyN
| Key::KeyO
| Key::KeyP
| Key::KeyQ
| Key::KeyR
| Key::KeyS
| Key::KeyT
| Key::KeyU
| Key::KeyV
| Key::KeyW
| Key::KeyX
| Key::KeyY
| Key::KeyZ
)
}
#[inline]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn is_numpad_key(event: &Event) -> bool {
matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if is_numpad_rdev_key(&key))
}
#[inline]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn is_letter_key(event: &Event) -> bool {
matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if is_letter_rdev_key(&key))
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn parse_add_lock_modes_modifiers(
key_event: &mut KeyEvent,
lock_modes: i32,
is_numpad_key: bool,
is_letter_key: bool,
) {
const CAPS_LOCK: i32 = 1;
const NUM_LOCK: i32 = 2;
// const SCROLL_LOCK: i32 = 3;
if is_letter_key && (lock_modes & (1 << CAPS_LOCK) != 0) {
key_event.modifiers.push(ControlKey::CapsLock.into());
}
if is_numpad_key && lock_modes & (1 << NUM_LOCK) != 0 {
key_event.modifiers.push(ControlKey::NumLock.into());
}
// if lock_modes & (1 << SCROLL_LOCK) != 0 {
// key_event.modifiers.push(ControlKey::ScrollLock.into());
// }
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn add_lock_modes_modifiers(key_event: &mut KeyEvent, is_numpad_key: bool, is_letter_key: bool) {
if is_letter_key && get_key_state(enigo::Key::CapsLock) {
key_event.modifiers.push(ControlKey::CapsLock.into());
}
if is_numpad_key && get_key_state(enigo::Key::NumLock) {
key_event.modifiers.push(ControlKey::NumLock.into());
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn convert_numpad_keys(key: Key) -> Key {
if get_key_state(enigo::Key::NumLock) {
return key;
}
match key {
Key::Kp0 => Key::Insert,
Key::KpDecimal => Key::Delete,
Key::Kp1 => Key::End,
Key::Kp2 => Key::DownArrow,
Key::Kp3 => Key::PageDown,
Key::Kp4 => Key::LeftArrow,
Key::Kp5 => Key::Clear,
Key::Kp6 => Key::RightArrow,
Key::Kp7 => Key::Home,
Key::Kp8 => Key::UpArrow,
Key::Kp9 => Key::PageUp,
_ => key,
}
}
fn update_modifiers_state(event: &Event) {
// for mouse
let mut keys = MODIFIERS_STATE.lock().unwrap();
match event.event_type {
EventType::KeyPress(k) => {
if keys.contains_key(&k) {
keys.insert(k, true);
}
}
EventType::KeyRelease(k) => {
if keys.contains_key(&k) {
keys.insert(k, false);
}
}
_ => {}
};
}
pub fn event_to_key_events(
event: &Event,
keyboard_mode: KeyboardMode,
_lock_modes: Option<i32>,
) -> Vec<KeyEvent> {
let mut key_event = KeyEvent::new();
update_modifiers_state(event);
match event.event_type {
EventType::KeyPress(key) => {
TO_RELEASE.lock().unwrap().insert(key);
}
EventType::KeyRelease(key) => {
TO_RELEASE.lock().unwrap().remove(&key);
}
_ => {}
}
let mut peer = get_peer_platform().to_lowercase();
peer.retain(|c| !c.is_whitespace());
key_event.mode = keyboard_mode.into();
#[cfg(not(any(target_os = "android", target_os = "ios")))]
let mut key_events;
#[cfg(any(target_os = "android", target_os = "ios"))]
let key_events;
key_events = match keyboard_mode {
KeyboardMode::Map => match map_keyboard_mode(peer.as_str(), event, key_event) {
Some(event) => [event].to_vec(),
None => Vec::new(),
},
KeyboardMode::Translate => translate_keyboard_mode(peer.as_str(), event, key_event),
_ => {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
legacy_keyboard_mode(event, key_event)
}
#[cfg(any(target_os = "android", target_os = "ios"))]
{
Vec::new()
}
}
};
#[cfg(not(any(target_os = "android", target_os = "ios")))]
let is_numpad_key = is_numpad_key(&event);
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if keyboard_mode != KeyboardMode::Translate || is_numpad_key {
let is_letter_key = is_letter_key(&event);
for key_event in &mut key_events {
if let Some(lock_modes) = _lock_modes {
parse_add_lock_modes_modifiers(key_event, lock_modes, is_numpad_key, is_letter_key);
} else {
add_lock_modes_modifiers(key_event, is_numpad_key, is_letter_key);
}
}
}
key_events
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn event_type_to_event(event_type: EventType) -> Event {
Event {
event_type,
time: SystemTime::now(),
unicode: None,
platform_code: 0,
position_code: 0,
}
}
pub fn send_key_event(key_event: &KeyEvent) {
#[cfg(not(any(feature = "flutter", feature = "cli")))]
if let Some(session) = CUR_SESSION.lock().unwrap().as_ref() {
session.send_key_event(key_event);
}
#[cfg(feature = "flutter")]
if let Some(session) = SESSIONS
.read()
.unwrap()
.get(&*CUR_SESSION_ID.read().unwrap())
{
session.send_key_event(key_event);
}
}
pub fn get_peer_platform() -> String {
#[cfg(not(any(feature = "flutter", feature = "cli")))]
if let Some(session) = CUR_SESSION.lock().unwrap().as_ref() {
return session.peer_platform();
}
#[cfg(feature = "flutter")]
if let Some(session) = SESSIONS
.read()
.unwrap()
.get(&*CUR_SESSION_ID.read().unwrap())
{
return session.peer_platform();
}
"Windows".to_string()
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn legacy_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Vec<KeyEvent> {
let mut events = Vec::new();
// legacy mode(0): Generate characters locally, look for keycode on other side.
let (mut key, down_or_up) = match event.event_type {
EventType::KeyPress(key) => (key, true),
EventType::KeyRelease(key) => (key, false),
_ => {
return events;
}
};
let peer = get_peer_platform();
let is_win = peer == "Windows";
if is_win {
key = convert_numpad_keys(key);
}
let alt = get_key_state(enigo::Key::Alt);
#[cfg(windows)]
let ctrl = {
let mut tmp = get_key_state(enigo::Key::Control) || get_key_state(enigo::Key::RightControl);
unsafe {
if IS_ALT_GR {
if alt || key == Key::AltGr {
if tmp {
tmp = false;
}
} else {
IS_ALT_GR = false;
}
}
}
tmp
};
#[cfg(not(windows))]
let ctrl = get_key_state(enigo::Key::Control) || get_key_state(enigo::Key::RightControl);
let shift = get_key_state(enigo::Key::Shift) || get_key_state(enigo::Key::RightShift);
#[cfg(windows)]
let command = crate::platform::windows::get_win_key_state();
#[cfg(not(windows))]
let command = get_key_state(enigo::Key::Meta);
let control_key = match key {
Key::Alt => Some(ControlKey::Alt),
Key::AltGr => Some(ControlKey::RAlt),
Key::Backspace => Some(ControlKey::Backspace),
Key::ControlLeft => {
// when pressing AltGr, an extra VK_LCONTROL with a special
// scancode with bit 9 set is sent, let's ignore this.
#[cfg(windows)]
if (event.position_code >> 8) == 0xE0 {
unsafe {
IS_ALT_GR = true;
}
return events;
}
Some(ControlKey::Control)
}
Key::ControlRight => Some(ControlKey::RControl),
Key::DownArrow => Some(ControlKey::DownArrow),
Key::Escape => Some(ControlKey::Escape),
Key::F1 => Some(ControlKey::F1),
Key::F10 => Some(ControlKey::F10),
Key::F11 => Some(ControlKey::F11),
Key::F12 => Some(ControlKey::F12),
Key::F2 => Some(ControlKey::F2),
Key::F3 => Some(ControlKey::F3),
Key::F4 => Some(ControlKey::F4),
Key::F5 => Some(ControlKey::F5),
Key::F6 => Some(ControlKey::F6),
Key::F7 => Some(ControlKey::F7),
Key::F8 => Some(ControlKey::F8),
Key::F9 => Some(ControlKey::F9),
Key::LeftArrow => Some(ControlKey::LeftArrow),
Key::MetaLeft => Some(ControlKey::Meta),
Key::MetaRight => Some(ControlKey::RWin),
Key::Return => Some(ControlKey::Return),
Key::RightArrow => Some(ControlKey::RightArrow),
Key::ShiftLeft => Some(ControlKey::Shift),
Key::ShiftRight => Some(ControlKey::RShift),
Key::Space => Some(ControlKey::Space),
Key::Tab => Some(ControlKey::Tab),
Key::UpArrow => Some(ControlKey::UpArrow),
Key::Delete => {
if is_win && ctrl && alt {
client::ctrl_alt_del();
return events;
}
Some(ControlKey::Delete)
}
Key::Apps => Some(ControlKey::Apps),
Key::Cancel => Some(ControlKey::Cancel),
Key::Clear => Some(ControlKey::Clear),
Key::Kana => Some(ControlKey::Kana),
Key::Hangul => Some(ControlKey::Hangul),
Key::Junja => Some(ControlKey::Junja),
Key::Final => Some(ControlKey::Final),
Key::Hanja => Some(ControlKey::Hanja),
Key::Hanji => Some(ControlKey::Hanja),
Key::Convert => Some(ControlKey::Convert),
Key::Print => Some(ControlKey::Print),
Key::Select => Some(ControlKey::Select),
Key::Execute => Some(ControlKey::Execute),
Key::PrintScreen => Some(ControlKey::Snapshot),
Key::Help => Some(ControlKey::Help),
Key::Sleep => Some(ControlKey::Sleep),
Key::Separator => Some(ControlKey::Separator),
Key::KpReturn => Some(ControlKey::NumpadEnter),
Key::Kp0 => Some(ControlKey::Numpad0),
Key::Kp1 => Some(ControlKey::Numpad1),
Key::Kp2 => Some(ControlKey::Numpad2),
Key::Kp3 => Some(ControlKey::Numpad3),
Key::Kp4 => Some(ControlKey::Numpad4),
Key::Kp5 => Some(ControlKey::Numpad5),
Key::Kp6 => Some(ControlKey::Numpad6),
Key::Kp7 => Some(ControlKey::Numpad7),
Key::Kp8 => Some(ControlKey::Numpad8),
Key::Kp9 => Some(ControlKey::Numpad9),
Key::KpDivide => Some(ControlKey::Divide),
Key::KpMultiply => Some(ControlKey::Multiply),
Key::KpDecimal => Some(ControlKey::Decimal),
Key::KpMinus => Some(ControlKey::Subtract),
Key::KpPlus => Some(ControlKey::Add),
Key::CapsLock | Key::NumLock | Key::ScrollLock => {
return events;
}
Key::Home => Some(ControlKey::Home),
Key::End => Some(ControlKey::End),
Key::Insert => Some(ControlKey::Insert),
Key::PageUp => Some(ControlKey::PageUp),
Key::PageDown => Some(ControlKey::PageDown),
Key::Pause => Some(ControlKey::Pause),
_ => None,
};
if let Some(k) = control_key {
key_event.set_control_key(k);
} else {
let name = event
.unicode
.as_ref()
.and_then(|unicode| unicode.name.clone());
let mut chr = match &name {
Some(ref s) => {
if s.len() <= 2 {
// exclude chinese characters
s.chars().next().unwrap_or('\0')
} else {
'\0'
}
}
_ => '\0',
};
if chr == '·' {
// special for Chinese
chr = '`';
}
if chr == '\0' {
chr = match key {
Key::Num1 => '1',
Key::Num2 => '2',
Key::Num3 => '3',
Key::Num4 => '4',
Key::Num5 => '5',
Key::Num6 => '6',
Key::Num7 => '7',
Key::Num8 => '8',
Key::Num9 => '9',
Key::Num0 => '0',
Key::KeyA => 'a',
Key::KeyB => 'b',
Key::KeyC => 'c',
Key::KeyD => 'd',
Key::KeyE => 'e',
Key::KeyF => 'f',
Key::KeyG => 'g',
Key::KeyH => 'h',
Key::KeyI => 'i',
Key::KeyJ => 'j',
Key::KeyK => 'k',
Key::KeyL => 'l',
Key::KeyM => 'm',
Key::KeyN => 'n',
Key::KeyO => 'o',
Key::KeyP => 'p',
Key::KeyQ => 'q',
Key::KeyR => 'r',
Key::KeyS => 's',
Key::KeyT => 't',
Key::KeyU => 'u',
Key::KeyV => 'v',
Key::KeyW => 'w',
Key::KeyX => 'x',
Key::KeyY => 'y',
Key::KeyZ => 'z',
Key::Comma => ',',
Key::Dot => '.',
Key::SemiColon => ';',
Key::Quote => '\'',
Key::LeftBracket => '[',
Key::RightBracket => ']',
Key::Slash => '/',
Key::BackSlash => '\\',
Key::Minus => '-',
Key::Equal => '=',
Key::BackQuote => '`',
_ => '\0',
}
}
if chr != '\0' {
if chr == 'l' && is_win && command {
client::lock_screen();
return events;
}
key_event.set_chr(chr as _);
} else {
log::error!("Unknown key {:?}", &event);
return events;
}
}
let (alt, ctrl, shift, command) = client::get_modifiers_state(alt, ctrl, shift, command);
client::legacy_modifiers(&mut key_event, alt, ctrl, shift, command);
if down_or_up == true {
key_event.down = true;
}
events.push(key_event);
events
}
pub fn map_keyboard_mode(_peer: &str, event: &Event, mut key_event: KeyEvent) -> Option<KeyEvent> {
match event.event_type {
EventType::KeyPress(..) => {
key_event.down = true;
}
EventType::KeyRelease(..) => {
key_event.down = false;
}
_ => return None,
};
#[cfg(target_os = "windows")]
let keycode = match _peer {
OS_LOWER_WINDOWS => {
// https://github.com/rustdesk/rustdesk/issues/1371
// Filter scancodes that are greater than 255 and the hight word is not 0xE0.
if event.position_code > 255 && (event.position_code >> 8) != 0xE0 {
return None;
}
event.position_code
}
OS_LOWER_MACOS => {
if hbb_common::config::LocalConfig::get_kb_layout_type() == "ISO" {
rdev::win_scancode_to_macos_iso_code(event.position_code)?
} else {
rdev::win_scancode_to_macos_code(event.position_code)?
}
}
_ => rdev::win_scancode_to_linux_code(event.position_code)?,
};
#[cfg(target_os = "macos")]
let keycode = match _peer {
OS_LOWER_WINDOWS => rdev::macos_code_to_win_scancode(event.platform_code as _)?,
OS_LOWER_MACOS => event.platform_code as _,
_ => rdev::macos_code_to_linux_code(event.platform_code as _)?,
};
#[cfg(target_os = "linux")]
let keycode = match _peer {
OS_LOWER_WINDOWS => rdev::linux_code_to_win_scancode(event.position_code as _)?,
OS_LOWER_MACOS => {
if hbb_common::config::LocalConfig::get_kb_layout_type() == "ISO" {
rdev::linux_code_to_macos_iso_code(event.position_code as _)?
} else {
rdev::linux_code_to_macos_code(event.position_code as _)?
}
}
_ => event.position_code as _,
};
#[cfg(any(target_os = "android", target_os = "ios"))]
let keycode = 0;
key_event.set_chr(keycode as _);
Some(key_event)
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &mut Vec<KeyEvent>) {
match &event.unicode {
Some(unicode_info) => {
if let Some(name) = &unicode_info.name {
if name.len() > 0 {
let mut evt = key_event.clone();
evt.set_seq(name.to_string());
evt.down = true;
events.push(evt);
}
}
}
None =>
{
#[cfg(target_os = "windows")]
if _peer == OS_LOWER_LINUX {
if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } {
if let Some(chr) = get_char_from_vk(event.platform_code as u32) {
let mut evt = key_event.clone();
evt.set_seq(chr.to_string());
evt.down = true;
events.push(evt);
}
}
}
}
}
}
#[cfg(target_os = "windows")]
fn try_file_win2win_hotkey(
peer: &str,
event: &Event,
key_event: &KeyEvent,
events: &mut Vec<KeyEvent>,
) {
if peer == OS_LOWER_WINDOWS && is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } {
let mut down = false;
let win2win_hotkey = match event.event_type {
EventType::KeyPress(..) => {
down = true;
if let Some(unicode) = get_unicode_from_vk(event.platform_code as u32) {
Some((unicode as u32 & 0x0000FFFF) | (event.platform_code << 16))
} else {
None
}
}
EventType::KeyRelease(..) => Some(event.platform_code << 16),
_ => None,
};
if let Some(code) = win2win_hotkey {
let mut evt = key_event.clone();
evt.set_win2win_hotkey(code);
evt.down = down;
events.push(evt);
}
}
}
#[cfg(target_os = "windows")]
fn is_hot_key_modifiers_down() -> bool {
if rdev::get_modifier(Key::ControlLeft) || rdev::get_modifier(Key::ControlRight) {
return true;
}
if rdev::get_modifier(Key::Alt) || rdev::get_modifier(Key::AltGr) {
return true;
}
if rdev::get_modifier(Key::MetaLeft) || rdev::get_modifier(Key::MetaRight) {
return true;
}
return false;
}
#[inline]
#[cfg(any(target_os = "linux", target_os = "windows"))]
fn is_altgr(event: &Event) -> bool {
#[cfg(target_os = "linux")]
if event.platform_code == 0xFE03 {
true
} else {
false
}
#[cfg(target_os = "windows")]
if unsafe { IS_0X021D_DOWN } && event.position_code == 0xE038 {
true
} else {
false
}
}
#[inline]
#[cfg(any(target_os = "linux", target_os = "windows"))]
fn is_press(event: &Event) -> bool {
matches!(event.event_type, EventType::KeyPress(_))
}
// https://github.com/fufesou/rustdesk/wiki/Keyboard-mode----Translate-Mode
pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -> Vec<KeyEvent> {
let mut events: Vec<KeyEvent> = Vec::new();
if let Some(unicode_info) = &event.unicode {
if unicode_info.is_dead {
#[cfg(target_os = "macos")]
if peer != OS_LOWER_MACOS && unsafe { IS_LEFT_OPTION_DOWN } {
// try clear dead key state
// rdev::clear_dead_key_state();
} else {
return events;
}
#[cfg(not(target_os = "macos"))]
return events;
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if is_numpad_key(&event) {
if let Some(evt) = map_keyboard_mode(peer, event, key_event) {
events.push(evt);
}
return events;
}
#[cfg(target_os = "macos")]
// ignore right option key
if event.platform_code == rdev::kVK_RightOption as u32 {
return events;
}
#[cfg(any(target_os = "linux", target_os = "windows"))]
if is_altgr(event) {
return events;
}
#[cfg(target_os = "windows")]
if event.position_code == 0x021D {
return events;
}
#[cfg(target_os = "windows")]
try_file_win2win_hotkey(peer, event, &key_event, &mut events);
#[cfg(any(target_os = "linux", target_os = "windows"))]
if events.is_empty() && is_press(event) {
try_fill_unicode(peer, event, &key_event, &mut events);
}
// If AltGr is down, no need to send events other than unicode.
#[cfg(target_os = "windows")]
unsafe {
if IS_0X021D_DOWN {
return events;
}
}
#[cfg(target_os = "macos")]
if !unsafe { IS_LEFT_OPTION_DOWN } {
try_fill_unicode(peer, event, &key_event, &mut events);
}
if events.is_empty() {
if let Some(evt) = map_keyboard_mode(peer, event, key_event) {
events.push(evt);
}
}
events
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn keycode_to_rdev_key(keycode: u32) -> Key {
#[cfg(target_os = "windows")]
return rdev::win_key_from_scancode(keycode);
#[cfg(target_os = "linux")]
return rdev::linux_key_from_code(keycode);
#[cfg(target_os = "macos")]
return rdev::macos_key_from_code(keycode.try_into().unwrap_or_default());
}