Merge branch 'master' of github.com:asur4s/rustdesk

This commit is contained in:
Asura 2022-08-25 21:29:42 +08:00
commit ba17864fa1
36 changed files with 910 additions and 304 deletions

88
Cargo.lock generated
View File

@ -689,21 +689,6 @@ dependencies = [
"cc",
]
[[package]]
name = "cocoa"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "667fdc068627a2816b9ff831201dd9864249d6ee8d190b9532357f1fc0f61ea7"
dependencies = [
"bitflags",
"block",
"core-foundation 0.9.3",
"core-graphics 0.21.0",
"foreign-types",
"libc",
"objc",
]
[[package]]
name = "cocoa"
version = "0.24.0"
@ -821,18 +806,6 @@ dependencies = [
"libc",
]
[[package]]
name = "core-graphics"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52a67c4378cf203eace8fb6567847eb641fd6ff933c1145a115c6ee820ebb978"
dependencies = [
"bitflags",
"core-foundation 0.9.3",
"foreign-types",
"libc",
]
[[package]]
name = "core-graphics"
version = "0.22.3"
@ -1394,12 +1367,33 @@ dependencies = [
"log",
"objc",
"pkg-config",
"rdev",
"serde 1.0.139",
"serde_derive",
"unicode-segmentation",
"winapi 0.3.9",
]
[[package]]
name = "enum-map"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ddfe61e8040145222887d0d32a939c70c8cae681490d72fb868305e9b40ced8"
dependencies = [
"enum-map-derive",
]
[[package]]
name = "enum-map-derive"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00d1c54e25a57236a790ecf051c2befbb57740c9b86c4273eac378ba84d620d6"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "enum_dispatch"
version = "0.3.8"
@ -3921,15 +3915,17 @@ dependencies = [
[[package]]
name = "rdev"
version = "0.5.0-1"
source = "git+https://github.com/open-trade/rdev#a9b6ea462956f289b4a48e81f2ea7dda33cd8047"
version = "0.5.0-2"
source = "git+https://github.com/asur4s/rdev#895c8fb1a6106714793e8877d35d2b7a1c57ce9c"
dependencies = [
"cocoa 0.22.0",
"core-foundation 0.7.0",
"core-foundation-sys 0.7.0",
"core-graphics 0.19.2",
"cocoa",
"core-foundation 0.9.3",
"core-foundation-sys 0.8.3",
"core-graphics 0.22.3",
"enum-map",
"lazy_static",
"libc",
"widestring 1.0.2",
"winapi 0.3.9",
"x11",
]
@ -4158,7 +4154,7 @@ dependencies = [
"cfg-if 1.0.0",
"clap 3.2.12",
"clipboard",
"cocoa 0.24.0",
"cocoa",
"core-foundation 0.9.3",
"core-graphics 0.22.3",
"cpal",
@ -4186,6 +4182,7 @@ dependencies = [
"mouce",
"num_cpus",
"objc",
"once_cell",
"parity-tokio-ipc",
"rdev",
"repng",
@ -4205,6 +4202,7 @@ dependencies = [
"sys-locale",
"sysinfo",
"system_shutdown",
"tfc",
"tray-item",
"trayicon",
"uuid",
@ -4930,6 +4928,16 @@ version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "tfc"
version = "0.6.1"
source = "git+https://github.com/asur4s/The-Fat-Controller#25bfa7ef1cb0bd0b522cc4155dea6b99673bcfd4"
dependencies = [
"core-graphics 0.22.3",
"unicode-segmentation",
"winapi 0.3.9",
]
[[package]]
name = "thiserror"
version = "1.0.31"
@ -5143,7 +5151,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76863575f7842ed64fda361f417a787efa82811b4617267709066969cd4ccf3b"
dependencies = [
"cocoa 0.24.0",
"cocoa",
"core-graphics 0.22.3",
"gtk",
"libappindicator",
@ -5565,6 +5573,12 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
[[package]]
name = "widestring"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8"
[[package]]
name = "winapi"
version = "0.2.8"
@ -5638,7 +5652,7 @@ checksum = "0c643e10139d127d30d6d753398c8a6f0a43532e8370f6c9d29ebbff29b984ab"
dependencies = [
"bitflags",
"err-derive",
"widestring",
"widestring 0.4.3",
"winapi 0.3.9",
]
@ -5765,7 +5779,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b43cc931d58b99461188607efd7acb2a093e65fc621f54cad78517a6063e73a"
dependencies = [
"bitflags",
"cocoa 0.24.0",
"cocoa",
"core-foundation 0.9.3",
"core-graphics 0.22.3",
"core-video-sys",

View File

@ -72,7 +72,9 @@ sciter-rs = { git = "https://github.com/open-trade/rust-sciter", branch = "dyn"
sys-locale = "0.2"
enigo = { path = "libs/enigo", features = [ "with_serde" ] }
clipboard = { path = "libs/clipboard" }
rdev = { git = "https://github.com/open-trade/rdev" }
rdev = { git = "https://github.com/asur4s/rdev" }
tfc = { git = "https://github.com/asur4s/The-Fat-Controller" }
once_cell = "1.13.0"
ctrlc = "3.2"
arboard = "2.0"
#minreq = { version = "2.4", features = ["punycode", "https-native"] }
@ -105,6 +107,7 @@ async-process = "1.3"
mouce = { git="https://github.com/fufesou/mouce.git" }
evdev = { git="https://github.com/fufesou/evdev" }
[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.11"
jni = "0.19"

View File

@ -22,6 +22,7 @@ appveyor = { repository = "pythoneer/enigo-85xiy" }
serde = { version = "1.0", optional = true }
serde_derive = { version = "1.0", optional = true }
log = "0.4"
rdev = { git = "https://github.com/asur4s/rdev" }
hbb_common = { path = "../hbb_common" }
[features]

View File

@ -257,7 +257,7 @@ pub enum Key {
Backspace,
/// caps lock key
CapsLock,
#[deprecated(since = "0.0.12", note = "now renamed to Meta")]
// #[deprecated(since = "0.0.12", note = "now renamed to Meta")]
/// command key on macOS (super key on Linux, windows key on Windows)
Command,
/// control key
@ -314,14 +314,14 @@ pub enum Key {
Shift,
/// space key
Space,
#[deprecated(since = "0.0.12", note = "now renamed to Meta")]
// #[deprecated(since = "0.0.12", note = "now renamed to Meta")]
/// super key on linux (command key on macOS, windows key on Windows)
Super,
/// tab key (tabulator)
Tab,
/// up arrow key
UpArrow,
#[deprecated(since = "0.0.12", note = "now renamed to Meta")]
// #[deprecated(since = "0.0.12", note = "now renamed to Meta")]
/// windows key on Windows (super key on Linux, command key on macOS)
Windows,
///

View File

@ -167,12 +167,7 @@ impl KeyboardControllable for Enigo {
}
}
fn key_click(&mut self, key: Key) {
if self.is_x11 {
self.xdo.key_click(key)
} else {
if let Some(keyboard) = &mut self.uinput_keyboard {
keyboard.key_click(key)
}
}
self.key_down(key).ok();
self.key_up(key);
}
}

View File

@ -67,16 +67,16 @@ impl EnigoPynput {
Key::Space => "space",
Key::Tab => "Tab",
Key::UpArrow => "Up",
Key::Numpad0 => "0",
Key::Numpad1 => "1",
Key::Numpad2 => "2",
Key::Numpad3 => "3",
Key::Numpad4 => "4",
Key::Numpad5 => "5",
Key::Numpad6 => "6",
Key::Numpad7 => "7",
Key::Numpad8 => "8",
Key::Numpad9 => "9",
Key::Numpad0 => "KP_0",
Key::Numpad1 => "KP_1",
Key::Numpad2 => "KP_2",
Key::Numpad3 => "KP_3",
Key::Numpad4 => "KP_4",
Key::Numpad5 => "KP_5",
Key::Numpad6 => "KP_6",
Key::Numpad7 => "KP_7",
Key::Numpad8 => "KP_8",
Key::Numpad9 => "KP_9",
Key::Decimal => "KP_Decimal",
Key::Cancel => "Cancel",
Key::Clear => "Clear",
@ -110,7 +110,6 @@ impl EnigoPynput {
return true;
}
};
log::info!("send pynput: {:?}", &s);
return self.tx.send((PyMsg::Str(s), is_press)).is_ok();
}
}

View File

@ -370,4 +370,19 @@ impl KeyboardControllable for EnigoXdo {
}
}
}
fn key_sequence_parse(&mut self, sequence: &str)
where
Self: Sized,
{
self.key_sequence_parse_try(sequence)
.expect("Could not parse sequence");
}
fn key_sequence_parse_try(&mut self, sequence: &str) -> Result<(), crate::dsl::ParseError>
where
Self: Sized,
{
crate::dsl::eval(self, sequence)
}
}

View File

@ -1,5 +1,4 @@
use core_graphics;
// TODO(dustin): use only the things i need
use self::core_graphics::display::*;

View File

@ -1,9 +1,8 @@
use winapi;
use self::winapi::ctypes::c_int;
use self::winapi::shared::{basetsd::ULONG_PTR, minwindef::*, windef::*};
use self::winapi::um::winbase::*;
use self::winapi::um::winuser::*;
use winapi;
use crate::win::keycodes::*;
use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};
@ -200,7 +199,7 @@ impl KeyboardControllable for Enigo {
fn key_down(&mut self, key: Key) -> crate::ResultType {
let code = self.key_to_keycode(key);
if code == 0 || code == 65535 {
return Err("".into());
return Err("".into());
}
let res = keybd_event(0, code, 0);
if res == 0 {
@ -227,7 +226,8 @@ impl KeyboardControllable for Enigo {
}
impl Enigo {
/// Gets the (width, height) of the main display in screen coordinates (pixels).
/// Gets the (width, height) of the main display in screen coordinates
/// (pixels).
///
/// # Example
///

View File

@ -105,6 +105,13 @@ message MouseEvent {
repeated ControlKey modifiers = 4;
}
enum KeyboardMode{
Legacy = 0;
Map = 1;
Translate = 2;
Auto = 3;
}
enum ControlKey {
Unknown = 0;
Alt = 1;
@ -198,6 +205,7 @@ message KeyEvent {
string seq = 6;
}
repeated ControlKey modifiers = 8;
KeyboardMode mode = 9;
}
message CursorData {

View File

@ -145,7 +145,11 @@ class MyController(Controller):
or (keycode_flag == False and keycode == list(keycode_set)[0] and len(keycode_set) == 1):
deakkey_chr = str(key).replace("'", '')
keysym = DEAD_KEYS[deakkey_chr]
keycode, shift_state = self.keyboard_mapping[keysym][0]
# shift_state = 0
keycode, shift_state = list(
filter(lambda x: x[1] == 0,
self.keyboard_mapping[keysym])
)[0]
# If the key has a virtual key code, use that immediately with
# fake_input; fake input,being an X server extension, has access to

View File

@ -1,2 +0,0 @@
[toolchain]
channel = "1.62.0"

View File

@ -288,6 +288,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Ignore Battery Optimizations", "忽略电池优化"),
("android_open_battery_optimizations_tip", "如需关闭此功能请在接下来的RustDesk应用设置页面中找到并进入 [电源] 页面,取消勾选 [不受限制]"),
("Connection not allowed", "对方不允许连接"),
("Legacy mode", "传统模式"),
("Map mode", "11传输"),
("Translate mode", "翻译模式"),
("Use temporary password", "使用临时密码"),
("Use permanent password", "使用固定密码"),
("Use both passwords", "同时使用两种密码"),

View File

@ -288,6 +288,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Ignore Battery Optimizations", ""),
("android_open_battery_optimizations_tip", ""),
("Connection not allowed", ""),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
("Use temporary password", ""),
("Use permanent password", ""),
("Use both passwords", ""),

View File

@ -288,6 +288,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Ignore Battery Optimizations", ""),
("android_open_battery_optimizations_tip", ""),
("Connection not allowed", ""),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
("Use temporary password", ""),
("Use permanent password", ""),
("Use both passwords", ""),

View File

@ -288,6 +288,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Ignore Battery Optimizations", "Batterieoptimierung ignorieren"),
("android_open_battery_optimizations_tip", "Möchten Sie die Batterieopimierungs-Einstellungen öffnen?"),
("Connection not allowed", "Verbindung abgelehnt"),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
("Use temporary password", "Temporäres Passwort verwenden"),
("Use permanent password", "Dauerhaftes Passwort verwenden"),
("Use both passwords", "Beide Passwörter verwenden"),

View File

@ -288,6 +288,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Ignore Battery Optimizations", ""),
("android_open_battery_optimizations_tip", ""),
("Connection not allowed", ""),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
("Use temporary password", ""),
("Use permanent password", ""),
("Use both passwords", ""),

View File

@ -287,6 +287,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Keep RustDesk background service", "Dejar RustDesk como Servicio en 2do plano"),
("Ignore Battery Optimizations", "Ignorar optimizacioens de bateria"),
("android_open_battery_optimizations_tip", ""),
("Random Password After Session", ""),
("Keep", ""),
("Update", ""),
("Disable", ""),
("Onetime Password", ""),
("Verification Method", ""),
("Enable security password", ""),
("Enable random password", ""),
("Enable onetime password", ""),
("Disable onetime password", ""),
("Activate onetime password", ""),
("Set security password", ""),
("Connection not allowed", ""),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
("Connection not allowed", "Conexión no disponible"),
("Use temporary password", "Usar contraseña temporal"),
("Use permanent password", "Usar contraseña permamente"),

View File

@ -288,6 +288,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Ignore Battery Optimizations", ""),
("android_open_battery_optimizations_tip", ""),
("Connection not allowed", ""),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
("Use temporary password", ""),
("Use permanent password", ""),
("Use both passwords", ""),

View File

@ -288,6 +288,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Ignore Battery Optimizations", ""),
("android_open_battery_optimizations_tip", ""),
("Connection not allowed", ""),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
("Use temporary password", ""),
("Use permanent password", ""),
("Use both passwords", ""),

View File

@ -287,6 +287,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Keep RustDesk background service", "Pertahankan RustDesk berjalan pada background service"),
("Ignore Battery Optimizations", "Abaikan Pengoptimalan Baterai"),
("android_open_battery_optimizations_tip", ""),
("Random Password After Session", ""),
("Keep", ""),
("Update", ""),
("Disable", ""),
("Onetime Password", ""),
("Verification Method", ""),
("Enable security password", ""),
("Enable random password", ""),
("Enable onetime password", ""),
("Disable onetime password", ""),
("Activate onetime password", ""),
("Set security password", ""),
("Connection not allowed", ""),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
("Connection not allowed", "Koneksi tidak dijinkan"),
("Use temporary password", "Gunakan kata sandi sementara"),
("Use permanent password", "Gunakan kata sandi permanaen"),

View File

@ -299,5 +299,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Are you sure you want to restart", "Sei sicuro di voler riavviare?"),
("Restarting Remote Device", "Il dispositivo remoto si sta riavviando"),
("remote_restarting_tip", "Riavviare il dispositivo remoto"),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
].iter().cloned().collect();
}

View File

@ -288,6 +288,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Ignore Battery Optimizations", "バッテリーの最適化を無効にする"),
("android_open_battery_optimizations_tip", "この機能を使わない場合は、次のRestDeskアプリ設定ページから「バッテリー」に進み、「制限なし」の選択を外してください"),
("Connection not allowed", "接続が許可されていません"),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
("Use temporary password", "使い捨てのパスワードを使用"),
("Use permanent password", "固定のパスワードを使用"),
("Use both passwords", "どちらのパスワードも使用"),

View File

@ -288,6 +288,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Ignore Battery Optimizations", ""),
("android_open_battery_optimizations_tip", ""),
("Connection not allowed", ""),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
("Use temporary password", ""),
("Use permanent password", ""),
("Use both passwords", ""),

View File

@ -288,6 +288,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Ignore Battery Optimizations", "Игнорировать оптимизацию батареи"),
("android_open_battery_optimizations_tip", "Перейдите на следующую страницу настроек "),
("Connection not allowed", "Подключение не разрешено"),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
("Use temporary password", "Использовать временный пароль"),
("Use permanent password", "Использовать постоянный пароль"),
("Use both passwords", "Использовать оба пароля"),

View File

@ -288,6 +288,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Ignore Battery Optimizations", ""),
("android_open_battery_optimizations_tip", ""),
("Connection not allowed", ""),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
("Use temporary password", ""),
("Use permanent password", ""),
("Use both passwords", ""),

View File

@ -288,6 +288,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Ignore Battery Optimizations", ""),
("android_open_battery_optimizations_tip", ""),
("Connection not allowed", ""),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
("Use temporary password", ""),
("Use permanent password", ""),
("Use both passwords", ""),

View File

@ -287,6 +287,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Keep RustDesk background service", ""),
("Ignore Battery Optimizations", ""),
("android_open_battery_optimizations_tip", ""),
("Random Password After Session", ""),
("Keep", ""),
("Update", ""),
("Disable", ""),
("Onetime Password", ""),
("Verification Method", ""),
("Enable security password", ""),
("Enable random password", ""),
("Enable onetime password", ""),
("Disable onetime password", ""),
("Activate onetime password", ""),
("Set security password", ""),
("Connection not allowed", ""),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
("Connection not allowed", "bağlantıya izin verilmedi"),
("Use temporary password", "Geçici şifre kullan"),
("Use permanent password", "Kalıcı şifre kullan"),

View File

@ -288,6 +288,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Ignore Battery Optimizations", "忽略電池優化"),
("android_open_battery_optimizations_tip", "如需關閉此功能請在接下來的RustDesk應用設置頁面中找到並進入 [電源] 頁面,取消勾選 [不受限制]"),
("Connection not allowed", "對方不允許連接"),
("Legacy mode", "傳統模式"),
("Map mode", "11傳輸"),
("Translate mode", "翻譯模式"),
("Use temporary password", "使用臨時密碼"),
("Use permanent password", "使用固定密碼"),
("Use both passwords", "同時使用兩種密碼"),

View File

@ -288,6 +288,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Ignore Battery Optimizations", "Bỏ qua các tối ưu pin"),
("android_open_battery_optimizations_tip", "Nếu bạn muốn tắt tính năng này, vui lòng chuyển đến trang cài đặt ứng dụng RustDesk tiếp theo, tìm và nhập [Pin], Bỏ chọn [Không hạn chế]"),
("Connection not allowed", "Kết nối không đuợc phép"),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
("Use temporary password", "Sử dụng mật khẩu tạm thời"),
("Use permanent password", "Sử dụng mật khẩu vĩnh viễn"),
("Use both passwords", "Sử dụng cả hai mật khẩu"),

View File

@ -6,7 +6,7 @@ use crate::common::update_clipboard;
use crate::video_service;
#[cfg(any(target_os = "android", target_os = "ios"))]
use crate::{common::MOBILE_INFO2, mobile::connection_manager::start_channel};
use crate::{ipc, VERSION};
use crate::{ipc};
use hbb_common::{
config::Config,
fs,
@ -448,11 +448,12 @@ impl Connection {
handle_mouse(&msg, id);
}
MessageInput::Key((mut msg, press)) => {
if press {
// todo: press and down have similar meanings.
if press && msg.mode.unwrap() == KeyboardMode::Legacy {
msg.down = true;
}
handle_key(&msg);
if press {
if press && msg.mode.unwrap() == KeyboardMode::Legacy {
msg.down = false;
handle_key(&msg);
}

View File

@ -3,11 +3,13 @@ use super::*;
use dispatch::Queue;
use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable};
use hbb_common::{config::COMPRESS_LEVEL, protobuf::EnumOrUnknown};
use rdev::{simulate, EventType, Key as RdevKey};
use std::{
convert::TryFrom,
sync::atomic::{AtomicBool, Ordering},
time::Instant,
};
use tfc::{traits::*, Context};
#[derive(Default)]
struct StateCursor {
@ -145,7 +147,8 @@ fn run_cursor(sp: MouseCursorService, state: &mut StateCursor) -> ResultType<()>
msg = cached.clone();
} else {
let mut data = crate::get_cursor_data(hcursor)?;
data.colors = hbb_common::compress::compress(&data.colors[..], COMPRESS_LEVEL).into();
data.colors =
hbb_common::compress::compress(&data.colors[..], COMPRESS_LEVEL).into();
let mut tmp = Message::new();
tmp.set_cursor_data(data);
msg = Arc::new(tmp);
@ -177,6 +180,7 @@ lazy_static::lazy_static! {
};
static ref KEYS_DOWN: Arc<Mutex<HashMap<u64, Instant>>> = Default::default();
static ref LATEST_INPUT: Arc<Mutex<Input>> = Default::default();
static ref KBD_CONTEXT: Mutex<Context> = Mutex::new(Context::new().expect("kbd context error"));
}
static EXITING: AtomicBool = AtomicBool::new(false);
@ -466,10 +470,14 @@ pub async fn lock_screen() {
// loginctl lock-session also not work, they both work run rustdesk from cmd
std::thread::spawn(|| {
let mut key_event = KeyEvent::new();
key_event.down = true;
key_event.set_chr('l' as _);
key_event.modifiers.push(ControlKey::Meta.into());
key_event.mode = KeyboardMode::Legacy.into();
key_event.down = true;
handle_key(&key_event);
key_event.down = false;
handle_key(&key_event);
});
@ -477,10 +485,13 @@ pub async fn lock_screen() {
// CGSession -suspend not real lock screen, it is user switch
std::thread::spawn(|| {
let mut key_event = KeyEvent::new();
key_event.down = true;
key_event.set_chr('q' as _);
key_event.modifiers.push(ControlKey::Meta.into());
key_event.modifiers.push(ControlKey::Control.into());
key_event.mode = KeyboardMode::Legacy.into();
key_event.down = true;
handle_key(&key_event);
key_event.down = false;
handle_key(&key_event);
@ -597,19 +608,89 @@ pub fn handle_key(evt: &KeyEvent) {
handle_key_(evt);
}
fn handle_key_(evt: &KeyEvent) {
if EXITING.load(Ordering::SeqCst) {
return;
fn rdev_key_down_or_up(key: RdevKey, down_or_up: bool) {
let event_type = match down_or_up {
true => EventType::KeyPress(key),
false => EventType::KeyRelease(key),
};
let delay = std::time::Duration::from_millis(20);
match simulate(&event_type) {
Ok(()) => (),
Err(_simulate_error) => {
log::error!("Could not send {:?}", &event_type);
}
}
// Let ths OS catchup (at least MacOS)
std::thread::sleep(delay);
}
fn rdev_key_click(key: RdevKey) {
rdev_key_down_or_up(key, true);
rdev_key_down_or_up(key, false);
}
fn sync_status(evt: &KeyEvent) -> (bool, bool) {
let mut en = ENIGO.lock().unwrap();
// remote caps status
let caps_locking = evt
.modifiers
.iter()
.position(|&r| r == ControlKey::CapsLock.into())
.is_some();
// remote numpad status
let num_locking = evt
.modifiers
.iter()
.position(|&r| r == ControlKey::NumLock.into())
.is_some();
let click_capslock = (caps_locking && !en.get_key_state(enigo::Key::CapsLock))
|| (!caps_locking && en.get_key_state(enigo::Key::CapsLock));
let click_numlock = (num_locking && !en.get_key_state(enigo::Key::NumLock))
|| (!num_locking && en.get_key_state(enigo::Key::NumLock));
return (click_capslock, click_numlock);
}
fn map_keyboard_mode(evt: &KeyEvent) {
// map mode(1): Send keycode according to the peer platform.
let (click_capslock, click_numlock) = sync_status(evt);
if click_capslock {
rdev_key_click(RdevKey::CapsLock);
}
if click_numlock {
rdev_key_click(RdevKey::NumLock);
}
rdev_key_down_or_up(RdevKey::Unknown(evt.chr()), evt.down);
return;
}
fn legacy_keyboard_mode(evt: &KeyEvent) {
let (click_capslock, click_numlock) = sync_status(evt);
#[cfg(windows)]
crate::platform::windows::try_change_desktop();
let mut en = ENIGO.lock().unwrap();
if click_capslock {
en.key_click(Key::CapsLock);
}
if click_numlock {
en.key_click(Key::NumLock);
}
// disable numlock if press home etc when numlock is on,
// because we will get numpad value (7,8,9 etc) if not
#[cfg(windows)]
let mut disable_numlock = false;
#[cfg(target_os = "macos")]
en.reset_flag();
// When long-pressed the command key, then press and release
// the Tab key, there should be CGEventFlagCommand in the flag.
#[cfg(target_os = "macos")]
for ck in evt.modifiers.iter(){
if let Some(key) = KEY_MAP.get(&ck.value()){
en.add_flag(key);
}
}
#[cfg(not(target_os = "macos"))]
let mut to_release = Vec::new();
#[cfg(not(target_os = "macos"))]
@ -637,8 +718,6 @@ fn handle_key_(evt: &KeyEvent) {
continue;
}
}
#[cfg(target_os = "macos")]
en.add_flag(key);
#[cfg(not(target_os = "macos"))]
{
if key == &Key::CapsLock {
@ -748,6 +827,35 @@ fn handle_key_(evt: &KeyEvent) {
}
}
fn translate_keyboard_mode(evt: &KeyEvent) {
let chr = char::from_u32(evt.chr()).unwrap_or_default();
// down(true)->press && press(false)-> release
if evt.down && !evt.press {
KBD_CONTEXT.lock().unwrap().unicode_char(chr).expect("unicode_char_down error");
}
}
fn handle_key_(evt: &KeyEvent) {
if EXITING.load(Ordering::SeqCst) {
return;
}
match evt.mode.unwrap() {
KeyboardMode::Legacy => {
legacy_keyboard_mode(evt);
}
KeyboardMode::Map => {
map_keyboard_mode(evt);
}
KeyboardMode::Translate => {
translate_keyboard_mode(evt);
}
_ => {
legacy_keyboard_mode(evt);
}
}
}
#[tokio::main(flavor = "current_thread")]
async fn lock_screen_2() {
lock_screen().await;
@ -759,3 +867,52 @@ async fn send_sas() -> ResultType<()> {
timeout(1000, stream.send(&crate::ipc::Data::SAS)).await??;
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
use rdev::{listen, Event, EventType, Key};
use std::sync::mpsc;
#[test]
fn test_handle_key() {
// listen
let (tx, rx) = mpsc::channel();
std::thread::spawn(move || {
std::env::set_var("KEYBOARD_ONLY", "y");
let func = move |event: Event| {
tx.send(event).ok();
};
if let Err(error) = listen(func) {
println!("Error: {:?}", error);
}
});
// set key/char base on char
let mut evt = KeyEvent::new();
evt.set_chr(66);
evt.mode = KeyboardMode::Legacy.into();
evt.modifiers.push(ControlKey::CapsLock.into());
// press
evt.down = true;
handle_key(&evt);
if let Ok(listen_evt) = rx.recv() {
assert_eq!(listen_evt.event_type, EventType::KeyPress(Key::Num1))
}
// release
evt.down = false;
handle_key(&evt);
if let Ok(listen_evt) = rx.recv() {
assert_eq!(listen_evt.event_type, EventType::KeyRelease(Key::Num1))
}
}
#[test]
fn test_get_key_state() {
let mut en = ENIGO.lock().unwrap();
println!(
"[*] test_get_key_state: {:?}",
en.get_key_state(enigo::Key::NumLock)
);
}
}

View File

@ -1,5 +1,4 @@
use crate::ipc::{self, new_listener, Connection, Data};
use crate::VERSION;
#[cfg(windows)]
use clipboard::{
create_cliprdr_context, empty_clipboard, get_rx_clip_client, server_clip_file, set_conn_enabled,

View File

@ -155,6 +155,96 @@ var svg_send = <svg viewBox="0 0 448 448">
var svg_chat = <svg viewBox="0 0 511.07 511.07">
<path d="m74.39 480.54h-36.213l25.607-25.607c13.807-13.807 22.429-31.765 24.747-51.246-36.029-23.644-62.375-54.751-76.478-90.425-14.093-35.647-15.864-74.888-5.121-113.48 12.89-46.309 43.123-88.518 85.128-118.85 45.646-32.963 102.47-50.387 164.33-50.387 77.927 0 143.61 22.389 189.95 64.745 41.744 38.159 64.734 89.63 64.734 144.93 0 26.868-5.471 53.011-16.26 77.703-11.165 25.551-27.514 48.302-48.593 67.619-46.399 42.523-112.04 65-189.83 65-28.877 0-59.01-3.855-85.913-10.929-25.465 26.123-59.972 40.929-96.086 40.929zm182-420c-124.04 0-200.15 73.973-220.56 147.28-19.284 69.28 9.143 134.74 76.043 175.12l7.475 4.511-0.23 8.727c-0.456 17.274-4.574 33.912-11.945 48.952 17.949-6.073 34.236-17.083 46.99-32.151l6.342-7.493 9.405 2.813c26.393 7.894 57.104 12.241 86.477 12.241 154.37 0 224.68-93.473 224.68-180.32 0-46.776-19.524-90.384-54.976-122.79-40.713-37.216-99.397-56.888-169.71-56.888z"/>
</svg>;
var svg_keyboard = <svg viewBox="0 0 511.07 511.07">
<g>
<g>
<path d="M491.979,217.631H110.205c0.157-1.362,0.365-2.717,0.637-4.061c4.408-21.755,23.676-38.152,46.71-38.152h39.404h109.91
c39.306,0,71.282-32.246,71.282-71.552c0-11.28-9.145-20.56-20.426-20.56s-20.426,9.077-20.426,20.359
c0,3.419-0.575,6.941-1.619,10.01c-4.082,11.998-15.451,20.893-28.812,20.893h-17.777H157.553
c-46.995,0-85.535,36.766-88.331,83.064H20.021C8.739,217.631,0,226.296,0,237.578v170.345c0,11.28,8.739,20.773,20.021,20.773
H491.98c11.28,0,20.021-9.492,20.021-20.773V237.578C512,226.296,503.261,217.631,491.979,217.631z M471.149,387.844H40.851
V258.482h430.298V387.844z"/>
</g>
</g>
<g>
<g>
<path d="M113.021,273.461H89.872c-11.28,0-20.426,9.145-20.426,20.426s9.145,20.426,20.426,20.426h23.149
c11.28,0,20.426-9.145,20.426-20.426S124.302,273.461,113.021,273.461z"/>
</g>
</g>
<g>
<g>
<path d="M190.638,273.461h-23.149c-11.28,0-20.426,9.145-20.426,20.426s9.145,20.426,20.426,20.426h23.149
c11.28,0,20.426-9.145,20.426-20.426S201.919,273.461,190.638,273.461z"/>
</g>
</g>
<g>
<g>
<path d="M268.255,273.461h-23.149c-11.28,0-20.426,9.145-20.426,20.426s9.145,20.426,20.426,20.426h23.149
c11.28,0,20.426-9.145,20.426-20.426S279.536,273.461,268.255,273.461z"/>
</g>
</g>
<g>
<g>
<path d="M345.872,273.461h-23.149c-11.28,0-20.426,9.145-20.426,20.426s9.145,20.426,20.426,20.426h23.149
c11.28,0,20.426-9.145,20.426-20.426S357.153,273.461,345.872,273.461z"/>
</g>
</g>
<g>
<g>
<path d="M423.489,273.461H400.34c-11.28,0-20.426,9.145-20.426,20.426s9.145,20.426,20.426,20.426h23.149
c11.28,0,20.426-9.145,20.426-20.426S434.77,273.461,423.489,273.461z"/>
</g>
</g>
<g>
<g>
<path d="M113.021,325.206H89.872c-11.28,0-20.426,9.145-20.426,20.426s9.145,20.425,20.426,20.425h23.149
c11.28,0,20.426-9.145,20.426-20.425S124.302,325.206,113.021,325.206z"/>
</g>
</g>
<g>
<g>
<path d="M423.489,325.206H400.34c-11.28,0-20.426,9.145-20.426,20.426s9.145,20.425,20.426,20.425h23.149
c11.28,0,20.426-9.145,20.426-20.425S434.77,325.206,423.489,325.206z"/>
</g>
</g>
<g>
<g>
<path d="M345.872,329.291H167.489c-11.28,0-20.426,9.145-20.426,20.426s9.145,20.426,20.426,20.426h178.383
c11.28,0,20.426-9.145,20.426-20.426S357.153,329.291,345.872,329.291z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>;
function scrollToBottom(el) {
var y = el.box(#height, #content) - el.box(#height, #client);

View File

@ -139,11 +139,23 @@ class Header: Reactor.Component {
<span #chat>{svg_chat}</span>
<span #action>{svg_action}</span>
<span #display>{svg_display}</span>
<span #keyboard>{svg_keyboard}</span>
{this.renderKeyboardPop()}
{this.renderDisplayPop()}
{this.renderActionPop()}
</div>;
}
function renderKeyboardPop(){
return <popup>
<menu.context #keyboard-options>
<li #legacy><span>{svg_checkmark}</span>{translate('Legacy mode')}</li>
<li #map><span>{svg_checkmark}</span>{translate('Map mode')}</li>
<li #translate><span>{svg_checkmark}</span>{translate('Translate mode')}</li>
</menu>
</popup>;
}
function renderDisplayPop() {
var codecs = handler.supported_hwcodec();
var show_codec = handler.has_hwcodec() && (codecs[0] || codecs[1]);
@ -263,6 +275,11 @@ class Header: Reactor.Component {
me.popup(menu);
}
event click $(#keyboard) (_, me) {
var menu = $(menu#keyboard-options);
me.popup(menu);
}
event click $(#screen) (_, me) {
if (pi.current_display == me.index) return;
handler.switch_display(me.index);
@ -352,6 +369,17 @@ class Header: Reactor.Component {
toggleMenuState();
}
}
event click $(menu#keyboard-options>li) (_, me) {
if (me.id == "legacy") {
handler.save_keyboard_mode("legacy");
} else if (me.id == "map") {
handler.save_keyboard_mode("map");
} else if (me.id == "translate") {
handler.save_keyboard_mode("translate");
}
toggleMenuState()
}
}
function handle_custom_image_quality() {
@ -375,12 +403,17 @@ function toggleMenuState() {
var s = handler.get_view_style();
if (!s) s = "original";
values.push(s);
var k = handler.get_keyboard_mode();
values.push(k);
var c = handler.get_option("codec-preference");
if (!c) c = "auto";
values.push(c);
for (var el in $$(menu#display-options li)) {
el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0);
}
for (var el in $$(menu#keyboard-options>li)) {
el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0);
}
for (var id in ["show-remote-cursor", "show-quality-monitor", "disable-audio", "enable-file-transfer", "disable-clipboard", "lock-after-session-end"]) {
var el = self.select('#' + id);
if (el) {

View File

@ -1,5 +1,5 @@
use std::{
collections::HashMap,
collections::{HashMap, HashSet},
ops::Deref,
sync::{
atomic::{AtomicBool, AtomicUsize, Ordering},
@ -46,6 +46,7 @@ use hbb_common::{
},
get_version_number,
};
use rdev::{Event, EventType::*, Key as RdevKey, Keyboard as RdevKeyboard, KeyboardState};
#[cfg(windows)]
use crate::clipboard_file::*;
@ -59,6 +60,8 @@ type Video = AssetPtr<video_destination>;
lazy_static::lazy_static! {
static ref ENIGO: Arc<Mutex<Enigo>> = Arc::new(Mutex::new(Enigo::new()));
static ref VIDEO: Arc<Mutex<Option<Video>>> = Default::default();
static ref TO_RELEASE: Arc<Mutex<HashSet<RdevKey>>> = Arc::new(Mutex::new(HashSet::<RdevKey>::new()));
static ref KEYBOARD: Arc<Mutex<RdevKeyboard>> = Arc::new(Mutex::new(RdevKeyboard::new().unwrap()));
}
fn get_key_state(key: enigo::Key) -> bool {
@ -235,6 +238,8 @@ impl sciter::EventHandler for Handler {
fn get_remember();
fn peer_platform();
fn set_write_override(i32, i32, bool, bool, bool);
fn get_keyboard_mode();
fn save_keyboard_mode(String);
fn has_hwcodec();
fn supported_hwcodec();
fn change_prefer_codec();
@ -282,222 +287,53 @@ impl Handler {
}
log::info!("keyboard hooked");
let mut me = self.clone();
let peer = self.peer_platform();
let is_win = peer == "Windows";
#[cfg(windows)]
crate::platform::windows::enable_lowlevel_keyboard(std::ptr::null_mut() as _);
std::thread::spawn(move || {
// This will block.
std::env::set_var("KEYBOARD_ONLY", "y"); // pass to rdev
use rdev::{EventType::*, *};
std::env::set_var("KEYBOARD_ONLY", "y");
lazy_static::lazy_static! {
static ref MUTEX_SPECIAL_KEYS: Mutex<HashMap<RdevKey, bool>> = {
let mut m = HashMap::new();
m.insert(RdevKey::ShiftLeft, false);
m.insert(RdevKey::ShiftRight, false);
m.insert(RdevKey::ControlLeft, false);
m.insert(RdevKey::ControlRight, false);
m.insert(RdevKey::Alt, false);
m.insert(RdevKey::AltGr, false);
m.insert(RdevKey::MetaLeft, false);
m.insert(RdevKey::MetaRight, false);
Mutex::new(m)
};
}
let func = move |evt: Event| {
if !IS_IN.load(Ordering::SeqCst) || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst)
{
return;
}
let (key, down) = match evt.event_type {
KeyPress(k) => (k, 1),
KeyRelease(k) => (k, 0),
let (_key, down) = match evt.event_type {
KeyPress(k) => {
// keyboard long press
if MUTEX_SPECIAL_KEYS.lock().unwrap().contains_key(&k) {
if *MUTEX_SPECIAL_KEYS.lock().unwrap().get(&k).unwrap() {
return;
}
MUTEX_SPECIAL_KEYS.lock().unwrap().insert(k, true);
}
(k, true)
}
KeyRelease(k) => {
// keyboard long press
if MUTEX_SPECIAL_KEYS.lock().unwrap().contains_key(&k) {
MUTEX_SPECIAL_KEYS.lock().unwrap().insert(k, false);
}
(k, false)
}
_ => return,
};
let alt = get_key_state(enigo::Key::Alt);
#[cfg(windows)]
let ctrl = {
let mut tmp = get_key_state(enigo::Key::Control);
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);
let shift = get_key_state(enigo::Key::Shift);
#[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 evt.scan_code & 0x200 != 0 {
unsafe {
IS_ALT_GR = true;
}
return;
}
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 {
me.ctrl_alt_del();
return;
}
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;
}
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,
};
let mut key_event = KeyEvent::new();
if let Some(k) = control_key {
key_event.set_control_key(k);
} else {
let mut chr = match evt.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::BackSlash => '\\',
Key::Minus => '-',
Key::Equal => '=',
Key::BackQuote => '`',
_ => '\0',
}
}
if chr != '\0' {
if chr == 'l' && is_win && command {
me.lock_screen();
return;
}
key_event.set_chr(chr as _);
} else {
log::error!("Unknown key {:?}", evt);
return;
}
}
me.key_down_or_up(down, key_event, alt, ctrl, shift, command);
me.key_down_or_up(down, _key, evt);
};
if let Err(error) = rdev::listen(func) {
log::error!("rdev: {:?}", error);
@ -513,6 +349,16 @@ impl Handler {
return self.lc.read().unwrap().image_quality.clone();
}
fn get_keyboard_mode(&mut self) -> String {
return std::env::var("KEYBOARD_MODE")
.unwrap_or(String::from("legacy"))
.to_lowercase();
}
fn save_keyboard_mode(&mut self, value: String) {
std::env::set_var("KEYBOARD_MODE", value);
}
fn get_custom_image_quality(&mut self) -> Value {
let mut v = Value::array(0);
for x in self.lc.read().unwrap().custom_image_quality.iter() {
@ -946,6 +792,9 @@ impl Handler {
}
fn leave(&mut self) {
for key in TO_RELEASE.lock().unwrap().iter() {
self.map_keyboard_mode(false, *key, None)
}
#[cfg(windows)]
crate::platform::windows::stop_system_key_propagate(false);
IS_IN.store(false, Ordering::SeqCst);
@ -969,7 +818,7 @@ impl Handler {
command = true;
}
}
send_mouse(mask, x, y, alt, ctrl, shift, command, self);
// on macos, ctrl + left button down = right button down, up won't emit, so we need to
// emit up myself if peer is not macos
@ -1129,18 +978,25 @@ impl Handler {
if self.peer_platform() == "Windows" {
let mut key_event = KeyEvent::new();
key_event.set_control_key(ControlKey::CtrlAltDel);
self.key_down_or_up(1, key_event, false, false, false, false);
// todo
key_event.down = true;
self.send_key_event(key_event, KeyboardMode::Legacy);
} else {
let mut key_event = KeyEvent::new();
key_event.set_control_key(ControlKey::Delete);
self.key_down_or_up(3, key_event, true, true, false, false);
self.legacy_modifiers(&mut key_event, true, true, false, false);
// todo
key_event.press = true;
self.send_key_event(key_event, KeyboardMode::Legacy);
}
}
fn lock_screen(&mut self) {
let mut key_event = KeyEvent::new();
key_event.set_control_key(ControlKey::LockScreen);
self.key_down_or_up(1, key_event, false, false, false, false);
// todo
key_event.down = true;
self.send_key_event(key_event, KeyboardMode::Legacy);
}
fn transfer_file(&mut self) {
@ -1159,17 +1015,130 @@ impl Handler {
}
}
fn key_down_or_up(
&mut self,
down_or_up: i32,
evt: KeyEvent,
fn send_key_event(&mut self, mut evt: KeyEvent, keyboard_mode: KeyboardMode) {
// mode: legacy(0), map(1), translate(2), auto(3)
evt.mode = keyboard_mode.into();
let mut msg_out = Message::new();
msg_out.set_key_event(evt);
self.send(Data::Message(msg_out));
}
#[allow(dead_code)]
fn convert_numpad_keys(&mut self, key: RdevKey) -> RdevKey {
if get_key_state(enigo::Key::NumLock) {
return key;
}
match key {
RdevKey::Kp0 => RdevKey::Insert,
RdevKey::KpDecimal => RdevKey::Delete,
RdevKey::Kp1 => RdevKey::End,
RdevKey::Kp2 => RdevKey::DownArrow,
RdevKey::Kp3 => RdevKey::PageDown,
RdevKey::Kp4 => RdevKey::LeftArrow,
RdevKey::Kp5 => RdevKey::Clear,
RdevKey::Kp6 => RdevKey::RightArrow,
RdevKey::Kp7 => RdevKey::Home,
RdevKey::Kp8 => RdevKey::UpArrow,
RdevKey::Kp9 => RdevKey::PageUp,
_ => key,
}
}
fn map_keyboard_mode(&mut self, down_or_up: bool, key: RdevKey, _evt: Option<Event>) {
// map mode(1): Send keycode according to the peer platform.
#[cfg(target_os = "windows")]
let key = if let Some(e) = _evt {
rdev::get_win_key(e.code.into(), e.scan_code)
} else {
key
};
let peer = self.peer_platform();
let mut key_event = KeyEvent::new();
// According to peer platform.
let keycode: u32 = if peer == "Linux" {
rdev::linux_keycode_from_key(key).unwrap_or_default().into()
} else if peer == "Windows" {
#[cfg(not(windows))]
let key = self.convert_numpad_keys(key);
rdev::win_keycode_from_key(key).unwrap_or_default().into()
} else {
rdev::macos_keycode_from_key(key).unwrap_or_default().into()
};
key_event.set_chr(keycode);
key_event.down = down_or_up;
if get_key_state(enigo::Key::CapsLock) {
key_event.modifiers.push(ControlKey::CapsLock.into());
}
if get_key_state(enigo::Key::NumLock) {
key_event.modifiers.push(ControlKey::NumLock.into());
}
self.send_key_event(key_event, KeyboardMode::Map);
}
fn translate_keyboard_mode(&mut self, down_or_up: bool, key: RdevKey, evt: Event) {
// translate mode(2): locally generated characters are send to the peer.
// get char
let string = match KEYBOARD.lock() {
Ok(mut keyboard) => {
let string = keyboard.add(&evt.event_type).unwrap_or_default();
if keyboard.is_dead() && string == "" && down_or_up == true {
return;
}
string
}
Err(_) => "".to_owned(),
};
// maybe two string
let chars = if string == "" {
None
} else {
let chars: Vec<char> = string.chars().collect();
Some(chars)
};
if let Some(chars) = chars {
for chr in chars {
dbg!(chr);
let mut key_event = KeyEvent::new();
key_event.set_chr(chr as _);
key_event.down = true;
key_event.press = false;
self.send_key_event(key_event, KeyboardMode::Translate);
}
} else {
let success = if down_or_up == true {
TO_RELEASE.lock().unwrap().insert(key)
} else {
TO_RELEASE.lock().unwrap().remove(&key)
};
// AltGr && LeftControl(SpecialKey) without action
if key == RdevKey::AltGr || evt.scan_code == 541 {
return;
}
if success{
self.map_keyboard_mode(down_or_up, key, None);
}
}
}
fn legacy_modifiers(
&self,
key_event: &mut KeyEvent,
alt: bool,
ctrl: bool,
shift: bool,
command: bool,
) {
let mut key_event = evt;
if alt
&& !crate::is_control_key(&key_event, &ControlKey::Alt)
&& !crate::is_control_key(&key_event, &ControlKey::RAlt)
@ -1198,19 +1167,247 @@ impl Handler {
key_event.modifiers.push(ControlKey::CapsLock.into());
}
if self.peer_platform() != "Mac OS" {
if get_key_state(enigo::Key::NumLock) && common::valid_for_numlock(&key_event) {
if get_key_state(enigo::Key::NumLock) {
key_event.modifiers.push(ControlKey::NumLock.into());
}
}
if down_or_up == 1 {
key_event.down = true;
} else if down_or_up == 3 {
key_event.press = true;
}
fn legacy_keyboard_mode(&mut self, down_or_up: bool, key: RdevKey, evt: Event) {
// legacy mode(0): Generate characters locally, look for keycode on other side.
let peer = self.peer_platform();
let is_win = peer == "Windows";
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 == RdevKey::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 {
RdevKey::Alt => Some(ControlKey::Alt),
RdevKey::AltGr => Some(ControlKey::RAlt),
RdevKey::Backspace => Some(ControlKey::Backspace),
RdevKey::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 evt.scan_code & 0x200 != 0 {
unsafe {
IS_ALT_GR = true;
}
return;
}
Some(ControlKey::Control)
}
RdevKey::ControlRight => Some(ControlKey::RControl),
RdevKey::DownArrow => Some(ControlKey::DownArrow),
RdevKey::Escape => Some(ControlKey::Escape),
RdevKey::F1 => Some(ControlKey::F1),
RdevKey::F10 => Some(ControlKey::F10),
RdevKey::F11 => Some(ControlKey::F11),
RdevKey::F12 => Some(ControlKey::F12),
RdevKey::F2 => Some(ControlKey::F2),
RdevKey::F3 => Some(ControlKey::F3),
RdevKey::F4 => Some(ControlKey::F4),
RdevKey::F5 => Some(ControlKey::F5),
RdevKey::F6 => Some(ControlKey::F6),
RdevKey::F7 => Some(ControlKey::F7),
RdevKey::F8 => Some(ControlKey::F8),
RdevKey::F9 => Some(ControlKey::F9),
RdevKey::LeftArrow => Some(ControlKey::LeftArrow),
RdevKey::MetaLeft => Some(ControlKey::Meta),
RdevKey::MetaRight => Some(ControlKey::RWin),
RdevKey::Return => Some(ControlKey::Return),
RdevKey::RightArrow => Some(ControlKey::RightArrow),
RdevKey::ShiftLeft => Some(ControlKey::Shift),
RdevKey::ShiftRight => Some(ControlKey::RShift),
RdevKey::Space => Some(ControlKey::Space),
RdevKey::Tab => Some(ControlKey::Tab),
RdevKey::UpArrow => Some(ControlKey::UpArrow),
RdevKey::Delete => {
if is_win && ctrl && alt {
self.ctrl_alt_del();
return;
}
Some(ControlKey::Delete)
}
RdevKey::Apps => Some(ControlKey::Apps),
RdevKey::Cancel => Some(ControlKey::Cancel),
RdevKey::Clear => Some(ControlKey::Clear),
RdevKey::Kana => Some(ControlKey::Kana),
RdevKey::Hangul => Some(ControlKey::Hangul),
RdevKey::Junja => Some(ControlKey::Junja),
RdevKey::Final => Some(ControlKey::Final),
RdevKey::Hanja => Some(ControlKey::Hanja),
RdevKey::Hanji => Some(ControlKey::Hanja),
RdevKey::Convert => Some(ControlKey::Convert),
RdevKey::Print => Some(ControlKey::Print),
RdevKey::Select => Some(ControlKey::Select),
RdevKey::Execute => Some(ControlKey::Execute),
RdevKey::PrintScreen => Some(ControlKey::Snapshot),
RdevKey::Help => Some(ControlKey::Help),
RdevKey::Sleep => Some(ControlKey::Sleep),
RdevKey::Separator => Some(ControlKey::Separator),
RdevKey::KpReturn => Some(ControlKey::NumpadEnter),
RdevKey::Kp0 => Some(ControlKey::Numpad0),
RdevKey::Kp1 => Some(ControlKey::Numpad1),
RdevKey::Kp2 => Some(ControlKey::Numpad2),
RdevKey::Kp3 => Some(ControlKey::Numpad3),
RdevKey::Kp4 => Some(ControlKey::Numpad4),
RdevKey::Kp5 => Some(ControlKey::Numpad5),
RdevKey::Kp6 => Some(ControlKey::Numpad6),
RdevKey::Kp7 => Some(ControlKey::Numpad7),
RdevKey::Kp8 => Some(ControlKey::Numpad8),
RdevKey::Kp9 => Some(ControlKey::Numpad9),
RdevKey::KpDivide => Some(ControlKey::Divide),
RdevKey::KpMultiply => Some(ControlKey::Multiply),
RdevKey::KpDecimal => Some(ControlKey::Decimal),
RdevKey::KpMinus => Some(ControlKey::Subtract),
RdevKey::KpPlus => Some(ControlKey::Add),
RdevKey::CapsLock | RdevKey::NumLock | RdevKey::ScrollLock => {
return;
}
RdevKey::Home => Some(ControlKey::Home),
RdevKey::End => Some(ControlKey::End),
RdevKey::Insert => Some(ControlKey::Insert),
RdevKey::PageUp => Some(ControlKey::PageUp),
RdevKey::PageDown => Some(ControlKey::PageDown),
RdevKey::Pause => Some(ControlKey::Pause),
_ => None,
};
let mut key_event = KeyEvent::new();
if let Some(k) = control_key {
key_event.set_control_key(k);
} else {
let mut chr = match evt.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 {
RdevKey::Num1 => '1',
RdevKey::Num2 => '2',
RdevKey::Num3 => '3',
RdevKey::Num4 => '4',
RdevKey::Num5 => '5',
RdevKey::Num6 => '6',
RdevKey::Num7 => '7',
RdevKey::Num8 => '8',
RdevKey::Num9 => '9',
RdevKey::Num0 => '0',
RdevKey::KeyA => 'a',
RdevKey::KeyB => 'b',
RdevKey::KeyC => 'c',
RdevKey::KeyD => 'd',
RdevKey::KeyE => 'e',
RdevKey::KeyF => 'f',
RdevKey::KeyG => 'g',
RdevKey::KeyH => 'h',
RdevKey::KeyI => 'i',
RdevKey::KeyJ => 'j',
RdevKey::KeyK => 'k',
RdevKey::KeyL => 'l',
RdevKey::KeyM => 'm',
RdevKey::KeyN => 'n',
RdevKey::KeyO => 'o',
RdevKey::KeyP => 'p',
RdevKey::KeyQ => 'q',
RdevKey::KeyR => 'r',
RdevKey::KeyS => 's',
RdevKey::KeyT => 't',
RdevKey::KeyU => 'u',
RdevKey::KeyV => 'v',
RdevKey::KeyW => 'w',
RdevKey::KeyX => 'x',
RdevKey::KeyY => 'y',
RdevKey::KeyZ => 'z',
RdevKey::Comma => ',',
RdevKey::Dot => '.',
RdevKey::SemiColon => ';',
RdevKey::Quote => '\'',
RdevKey::LeftBracket => '[',
RdevKey::RightBracket => ']',
RdevKey::BackSlash => '\\',
RdevKey::Minus => '-',
RdevKey::Equal => '=',
RdevKey::BackQuote => '`',
_ => '\0',
}
}
if chr != '\0' {
if chr == 'l' && is_win && command {
self.lock_screen();
return;
}
key_event.set_chr(chr as _);
} else {
log::error!("Unknown key {:?}", evt);
return;
}
}
self.legacy_modifiers(&mut key_event, alt, ctrl, shift, command);
if down_or_up == true {
key_event.down = true;
}
self.send_key_event(key_event, KeyboardMode::Legacy)
}
fn key_down_or_up(&mut self, down_or_up: bool, key: RdevKey, evt: Event) {
// Call different functions according to keyboard mode.
let mode = match self.get_keyboard_mode().as_str() {
"map" => KeyboardMode::Map,
"legacy" => KeyboardMode::Legacy,
"translate" => KeyboardMode::Translate,
_ => KeyboardMode::Legacy,
};
match mode {
KeyboardMode::Map => {
if down_or_up == true {
TO_RELEASE.lock().unwrap().insert(key);
} else {
TO_RELEASE.lock().unwrap().remove(&key);
}
self.map_keyboard_mode(down_or_up, key, Some(evt));
}
KeyboardMode::Legacy => self.legacy_keyboard_mode(down_or_up, key, evt),
KeyboardMode::Translate => {
self.translate_keyboard_mode(down_or_up, key, evt);
}
_ => self.legacy_keyboard_mode(down_or_up, key, evt),
}
let mut msg_out = Message::new();
msg_out.set_key_event(key_event);
log::debug!("{:?}", msg_out);
self.send(Data::Message(msg_out));
}
#[inline]