refactor: keyboard of client

This commit is contained in:
Asura 2022-11-15 23:09:29 -08:00 committed by Chieh Wang
parent 9e75019d13
commit 3b60304d14
18 changed files with 833 additions and 857 deletions

26
Cargo.lock generated
View File

@ -1533,7 +1533,7 @@ dependencies = [
"log",
"objc",
"pkg-config",
"rdev",
"rdev 0.5.0-2 (git+https://github.com/asur4s/rdev)",
"serde 1.0.147",
"serde_derive",
"tfc",
@ -4229,7 +4229,27 @@ dependencies = [
[[package]]
name = "rdev"
version = "0.5.0-2"
source = "git+https://github.com/asur4s/rdev#4051761e7ccf434a443b8e9592c23160c9cace56"
dependencies = [
"cocoa",
"core-foundation 0.9.3",
"core-foundation-sys 0.8.3",
"core-graphics 0.22.3",
"enum-map",
"epoll",
"inotify",
"lazy_static",
"libc",
"strum 0.24.1",
"strum_macros 0.24.3",
"widestring 1.0.2",
"winapi 0.3.9",
"x11 2.20.0",
]
[[package]]
name = "rdev"
version = "0.5.0-2"
source = "git+https://github.com/asur4s/rdev#fdcee04f10ea0ef00d36aa612eabb9605ae9f2fc"
dependencies = [
"cocoa",
"core-foundation 0.9.3",
@ -4518,7 +4538,7 @@ dependencies = [
"num_cpus",
"objc",
"parity-tokio-ipc",
"rdev",
"rdev 0.5.0-2",
"repng",
"reqwest",
"rpassword 7.1.0",

View File

@ -63,7 +63,7 @@ default-net = "0.11.0"
wol-rs = "0.9.1"
flutter_rust_bridge = { git = "https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge", optional = true }
errno = "0.2.8"
rdev = { git = "https://github.com/asur4s/rdev" }
rdev = { path = "../rdev" }
url = { version = "2.1", features = ["serde"] }
reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false }

View File

@ -1189,11 +1189,12 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
MenuEntryRadioOption(text: translate('Legacy mode'), value: 'legacy'),
MenuEntryRadioOption(text: translate('Map mode'), value: 'map'),
],
curOptionGetter: () async =>
await bind.sessionGetKeyboardName(id: widget.id),
curOptionGetter: () async {
return await bind.sessionGetKeyboardMode(id: widget.id) ?? "legacy";
},
optionSetter: (String oldValue, String newValue) async {
await bind.sessionSetKeyboardMode(
id: widget.id, keyboardMode: newValue);
await bind.sessionSetKeyboardMode(id: widget.id, value: newValue);
widget.ffi.canvasModel.updateViewStyle();
},
)
];

View File

@ -692,10 +692,11 @@ class _RemotePageState extends State<RemotePage> {
}
void changePhysicalKeyboardInputMode() async {
var current = await bind.sessionGetKeyboardName(id: widget.id);
var current = await bind.sessionGetKeyboardMode(id: widget.id) ?? "legacy";
gFFI.dialogManager.show((setState, close) {
void setMode(String? v) async {
await bind.sessionSetKeyboardMode(id: widget.id, keyboardMode: v ?? '');
await bind.sessionPeerOption(
id: widget.id, name: "keyboard-mode", value: v ?? "");
setState(() => current = v ?? '');
Future.delayed(Duration(milliseconds: 300), close);
}

View File

@ -54,7 +54,7 @@ class InputModel {
InputModel(this.parent);
KeyEventResult handleRawKeyEvent(FocusNode data, RawKeyEvent e) {
bind.sessionGetKeyboardName(id: id).then((result) {
bind.sessionGetKeyboardMode(id: id).then((result) {
keyboardMode = result.toString();
});

View File

@ -63,14 +63,7 @@ packages:
name: back_button_interceptor
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.2"
bot_toast:
dependency: "direct main"
description:
name: bot_toast
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.3"
version: "6.0.1"
build:
dependency: transitive
description:
@ -264,8 +257,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "82f9eab81cb2c7bfb938def7a1b399a6279bbc75"
resolved-ref: "82f9eab81cb2c7bfb938def7a1b399a6279bbc75"
ref: bf278fc8a8ff787e46fa3ab97674373bfaa20f23
resolved-ref: bf278fc8a8ff787e46fa3ab97674373bfaa20f23
url: "https://github.com/Kingtous/rustdesk_desktop_multi_window"
source: git
version: "0.1.0"
@ -390,8 +383,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "74b1b314142b6775c1243067a3503ac568ebc74b"
resolved-ref: "74b1b314142b6775c1243067a3503ac568ebc74b"
ref: dec2166e881c47d922e1edc484d10d2cd5c2103b
resolved-ref: dec2166e881c47d922e1edc484d10d2cd5c2103b
url: "https://github.com/Kingtous/rustdesk_flutter_custom_cursor"
source: git
version: "0.0.1"
@ -1015,7 +1008,7 @@ packages:
name: uni_links_desktop
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
version: "0.1.3"
uni_links_platform_interface:
dependency: transitive
description:

View File

@ -203,6 +203,8 @@ pub struct PeerConfig {
pub enable_file_transfer: bool,
#[serde(default)]
pub show_quality_monitor: bool,
#[serde(default)]
pub keyboard_mode: String,
// The other scalar value must before this
#[serde(default, deserialize_with = "PeerConfig::deserialize_options")]

View File

@ -49,10 +49,7 @@ pub use super::lang::*;
pub mod file_trait;
pub mod helper;
pub mod io_loop;
use crate::{
server::video_service::{SCRAP_X11_REF_URL, SCRAP_X11_REQUIRED},
ui_session_interface::global_save_keyboard_mode,
};
use crate::server::video_service::{SCRAP_X11_REF_URL, SCRAP_X11_REQUIRED};
pub static SERVER_KEYBOARD_ENABLED: AtomicBool = AtomicBool::new(true);
pub static SERVER_FILE_TRANSFER_ENABLED: AtomicBool = AtomicBool::new(true);
pub static SERVER_CLIPBOARD_ENABLED: AtomicBool = AtomicBool::new(true);
@ -989,6 +986,17 @@ impl LoginConfigHandler {
self.save_config(config);
}
/// Save keyboard mode to the current config.
///
/// # Arguments
///
/// * `value` - The view style to be saved.
pub fn save_keyboard_mode(&mut self, value: String) {
let mut config = self.load_config();
config.keyboard_mode = value;
self.save_config(config);
}
/// Save scroll style to the current config.
///
/// # Arguments
@ -1382,9 +1390,6 @@ impl LoginConfigHandler {
if !pi.version.is_empty() {
self.version = hbb_common::get_version_number(&pi.version);
}
if hbb_common::get_version_number(&pi.version) < hbb_common::get_version_number("1.2.0") {
global_save_keyboard_mode("legacy".to_owned());
}
self.features = pi.features.clone().into_option();
let serde = PeerInfoSerde {
username: pi.username.clone(),
@ -1407,6 +1412,14 @@ impl LoginConfigHandler {
log::debug!("remove password of {}", self.id);
}
}
if config.keyboard_mode == "" {
if hbb_common::get_version_number(&pi.version) < hbb_common::get_version_number("1.2.0")
{
config.keyboard_mode = "legacy".to_string();
} else {
config.keyboard_mode = "map".to_string();
}
}
self.conn_id = pi.conn_id;
// no matter if change, for update file time
self.save_config(config);
@ -2024,8 +2037,3 @@ fn decode_id_pk(signed: &[u8], key: &sign::PublicKey) -> ResultType<(String, [u8
bail!("Wrong public length");
}
}
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
pub fn disable_keyboard_listening() {
crate::ui_session_interface::KEYBOARD_HOOKED.store(true, Ordering::SeqCst);
}

View File

@ -3,6 +3,14 @@ use std::{
sync::{Arc, Mutex},
};
#[derive(Debug, Eq, PartialEq)]
pub enum GrabState {
Ready,
Run,
Wait,
Exit,
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub use arboard::Clipboard as ClipboardContext;
@ -11,7 +19,7 @@ use hbb_common::compress::decompress;
use hbb_common::{
allow_err,
anyhow::bail,
compress::{compress as compress_func},
compress::compress as compress_func,
config::{self, Config, COMPRESS_LEVEL, RENDEZVOUS_TIMEOUT},
get_version_number, log,
message_proto::*,

View File

@ -422,8 +422,6 @@ pub fn session_start_(id: &str, event_stream: StreamSink<EventToUI>) -> ResultTy
*session.event_stream.write().unwrap() = Some(event_stream);
let session = session.clone();
std::thread::spawn(move || {
// if flutter : disable keyboard listen
crate::client::disable_keyboard_listening();
io_loop(session);
});
Ok(())

View File

@ -18,7 +18,7 @@ use hbb_common::{
use crate::flutter::{self, SESSIONS};
use crate::ui_interface::{self, *};
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use crate::ui_session_interface::CUR_SESSION;
use crate::keyboard::CUR_SESSION;
use crate::{
client::file_trait::FileManager,
flutter::{make_fd_to_json, session_add, session_start_},
@ -225,6 +225,20 @@ pub fn session_set_image_quality(id: String, value: String) {
}
}
pub fn session_get_keyboard_mode(id: String) -> Option<String> {
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
Some(session.get_keyboard_mode())
} else {
None
}
}
pub fn session_set_keyboard_mode(id: String, value: String) {
if let Some(session) = SESSIONS.write().unwrap().get_mut(&id) {
session.save_keyboard_mode(value);
}
}
pub fn session_get_custom_image_quality(id: String) -> Option<Vec<i32>> {
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
Some(session.get_custom_image_quality())
@ -271,7 +285,7 @@ pub fn session_handle_flutter_key_event(
down_or_up: bool,
) {
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
session.handle_flutter_key_event(&name, keycode, scancode, down_or_up);
// session.handle_flutter_key_event(&name, keycode, scancode, down_or_up);
}
}
@ -282,7 +296,6 @@ pub fn session_enter_or_leave(id: String, enter: bool) {
*CUR_SESSION.lock().unwrap() = Some(session.clone());
session.enter();
} else {
*CUR_SESSION.lock().unwrap() = None;
session.leave();
}
}
@ -299,12 +312,14 @@ pub fn session_input_key(
command: bool,
) {
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
// #[cfg(any(target_os = "android", target_os = "ios"))]
session.input_key(&name, down, press, alt, ctrl, shift, command);
}
}
pub fn session_input_string(id: String, value: String) {
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
// #[cfg(any(target_os = "android", target_os = "ios"))]
session.input_string(&value);
}
}
@ -329,19 +344,6 @@ pub fn session_get_peer_option(id: String, name: String) -> String {
"".to_string()
}
pub fn session_get_keyboard_name(id: String) -> String {
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
return session.get_keyboard_mode();
}
"legacy".to_string()
}
pub fn session_set_keyboard_mode(id: String, keyboard_mode: String) {
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
session.save_keyboard_mode(keyboard_mode);
}
}
pub fn session_input_os_password(id: String, value: String) {
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
session.input_os_password(value, true);
@ -1083,8 +1085,7 @@ pub fn main_is_installed() -> SyncReturn<bool> {
}
pub fn main_start_grab_keyboard() {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
crate::ui_session_interface::global_grab_keyboard();
crate::keyboard::client::start_grab_loop();
}
pub fn main_is_installed_lower_version() -> SyncReturn<bool> {

678
src/keyboard.rs Normal file
View File

@ -0,0 +1,678 @@
use crate::client::get_key_state;
use crate::common::GrabState;
#[cfg(feature = "flutter")]
use crate::flutter::FlutterHandler;
use crate::platform;
use crate::ui_session_interface::Session;
use hbb_common::{allow_err, log, message_proto::*};
use rdev::{Event, EventType, Key};
use std::collections::{HashMap, HashSet};
use std::sync::{mpsc, Arc, Mutex, RwLock};
use std::time::SystemTime;
#[cfg(feature = "flutter")]
lazy_static::lazy_static! {
pub static ref CUR_SESSION: Arc<Mutex<Option<Session<FlutterHandler>>>> = Default::default();
pub static ref GRAB_SENDER: Arc<Mutex<Option<mpsc::Sender<GrabState>>>> = Default::default();
}
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::{client_keyboard_mode, components, *};
pub fn get_keyboard_mode() -> String {
return components::get_keyboard_mode();
}
pub fn save_keyboard_mode(value: String) {
components::save_keyboard_mode(value);
}
pub fn start_grab_loop() {
let (sender, receiver) = mpsc::channel::<GrabState>();
unsafe {
components::grab_loop(receiver);
*GRAB_SENDER.lock().unwrap() = Some(sender);
}
change_grab_status(GrabState::Ready);
}
pub fn change_grab_status(state: GrabState) {
if GrabState::Wait == state {
components::release_remote_keys();
}
unsafe {
if let Some(sender) = &*GRAB_SENDER.lock().unwrap() {
log::info!("grab state: {:?}", state);
sender.send(state);
}
}
}
pub fn process_event(event: Event) {
if components::is_long_press(&event) {
return;
}
let key_event = components::event_to_key_event(&event);
log::info!("key event: {:?}", key_event);
components::send_key_event(&key_event);
}
pub fn get_modifiers_state(
alt: bool,
ctrl: bool,
shift: bool,
command: bool,
) -> (bool, bool, bool, bool) {
components::get_modifiers_state(alt, ctrl, shift, command)
}
pub fn legacy_modifiers(
key_event: &mut KeyEvent,
alt: bool,
ctrl: bool,
shift: bool,
command: bool,
) {
components::legacy_modifiers(key_event, alt, ctrl, shift, command);
}
pub fn lock_screen() {
components::lock_screen();
}
pub fn ctrl_alt_del() {
components::ctrl_alt_del();
}
}
pub mod server {
pub fn simulate() {}
}
mod components {
use super::*;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::thread;
pub static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false);
pub fn grab_loop(recv: mpsc::Receiver<GrabState>) {
thread::spawn(move || loop {
if let Some(state) = recv.recv().ok() {
match state {
GrabState::Ready => {
#[cfg(any(target_os = "windows", target_os = "macos"))]
std::thread::spawn(move || {
let func = move |event: Event| match event.event_type {
EventType::KeyPress(key) | EventType::KeyRelease(key) => {
// todo!: CAPSLOCK don't work
if KEYBOARD_HOOKED.load(Ordering::SeqCst) {
keyboard::client::process_event(event);
return None;
} else {
return Some(event);
}
}
_ => Some(event),
};
if let Err(error) = rdev::grab(func) {
log::error!("rdev Error: {:?}", error)
}
});
#[cfg(target_os = "linux")]
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 {
crate::keyboard::client::process_event(event);
}
None
}
_ => Some(event),
});
}
GrabState::Run => {
#[cfg(any(target_os = "windows", target_os = "macos"))]
KEYBOARD_HOOKED.swap(true, Ordering::SeqCst);
#[cfg(target_os = "linux")]
rdev::enable_grab().ok();
}
GrabState::Wait => {
#[cfg(any(target_os = "windows", target_os = "macos"))]
KEYBOARD_HOOKED.swap(false, Ordering::SeqCst);
#[cfg(target_os = "linux")]
rdev::disable_grab().ok();
}
GrabState::Exit => {
#[cfg(target_os = "linux")]
rdev::exit_grab_listen().ok();
}
}
}
});
}
pub fn is_long_press(event: &Event) -> bool {
let mut 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;
}
pub fn release_remote_keys() {
// todo!: client quit suddenly, how to release keys?
let to_release = TO_RELEASE.lock().unwrap();
let keys = to_release.iter().map(|&key| key).collect::<Vec<Key>>();
drop(to_release);
for key in keys {
let event_type = EventType::KeyRelease(key);
let event = event_type_to_event(event_type);
log::info!("release key: {:?}", key);
client::process_event(event);
}
}
pub fn get_keyboard_mode() -> String {
if let Some(handler) = CUR_SESSION.lock().unwrap().as_ref() {
handler.get_keyboard_mode()
} else {
"legacy".to_string()
}
}
pub fn save_keyboard_mode(value: String) {
release_remote_keys();
if let Some(handler) = CUR_SESSION.lock().unwrap().as_mut() {
handler.save_keyboard_mode(value);
}
}
pub fn get_keyboard_mode_enum() -> KeyboardMode {
match get_keyboard_mode().as_str() {
"map" => KeyboardMode::Map,
"translate" => KeyboardMode::Translate,
_ => KeyboardMode::Legacy,
}
}
pub fn add_numlock_capslock_state(key_event: &mut KeyEvent) {
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());
}
}
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,
}
}
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)
}
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 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_to_key_event(event: &Event) -> KeyEvent {
let mut key_event = KeyEvent::new();
update_modifiers_state(event);
let mut to_release = TO_RELEASE.lock().unwrap();
match event.event_type {
EventType::KeyPress(key) => {
to_release.insert(key);
}
EventType::KeyRelease(key) => {
to_release.remove(&key);
}
_ => {}
}
drop(to_release);
let keyboard_mode = get_keyboard_mode_enum();
key_event.mode = keyboard_mode.into();
match keyboard_mode {
KeyboardMode::Map => {
client_keyboard_mode::map_keyboard_mode(event, &mut key_event);
}
KeyboardMode::Translate => {
client_keyboard_mode::translate_keyboard_mode(event, &mut key_event);
}
_ => {
client_keyboard_mode::legacy_keyboard_mode(event, &mut key_event);
}
};
#[cfg(not(any(target_os = "android", target_os = "ios")))]
add_numlock_capslock_state(&mut key_event);
return key_event;
}
pub fn event_type_to_event(event_type: EventType) -> Event {
Event {
event_type,
time: SystemTime::now(),
name: None,
code: 0,
scan_code: 0,
}
}
pub fn ctrl_alt_del() {
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();
send_key_event(&key_event);
}
pub fn lock_screen() {
let mut key_event = KeyEvent::new();
key_event.set_control_key(ControlKey::LockScreen);
key_event.down = true;
key_event.mode = KeyboardMode::Legacy.into();
send_key_event(&key_event);
}
#[cfg(feature = "flutter")]
pub fn send_key_event(key_event: &KeyEvent) {
if let Some(handler) = CUR_SESSION.lock().unwrap().as_ref() {
log::info!("Sending key even {:?}", key_event);
handler.send_key_event(key_event);
}
}
pub fn get_peer_platform() -> String {
if let Some(handler) = CUR_SESSION.lock().unwrap().as_ref() {
handler.peer_platform()
} else {
log::error!("get peer platform error");
"Windows".to_string()
}
}
}
mod client_keyboard_mode {
use super::*;
use components;
use rdev::EventType;
static mut IS_ALT_GR: bool = false;
pub fn legacy_keyboard_mode(event: &Event, key_event: &mut KeyEvent) {
// 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;
}
};
let peer = components::get_peer_platform();
let is_win = peer == "Windows";
if is_win {
key = components::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.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 {
components::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,
};
if let Some(k) = control_key {
key_event.set_control_key(k);
} else {
let mut chr = match event.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 {
components::lock_screen();
return;
}
key_event.set_chr(chr as _);
} else {
log::error!("Unknown key {:?}", &event);
return;
}
}
let (alt, ctrl, shift, command) =
components::get_modifiers_state(alt, ctrl, shift, command);
components::legacy_modifiers(key_event, alt, ctrl, shift, command);
if down_or_up == true {
key_event.down = true;
}
}
pub fn map_keyboard_mode(event: &Event, key_event: &mut KeyEvent) {
let peer = components::get_peer_platform();
let key = match event.event_type {
EventType::KeyPress(key) => {
key_event.down = true;
key
}
EventType::KeyRelease(key) => {
key_event.down = false;
key
}
_ => return,
};
let keycode: u32 = match peer.as_str() {
"Windows" => rdev::win_keycode_from_key(key).unwrap_or_default().into(),
"MacOS" => rdev::macos_keycode_from_key(key).unwrap_or_default().into(),
_ => rdev::linux_keycode_from_key(key).unwrap_or_default().into(),
};
key_event.set_chr(keycode);
}
pub fn translate_keyboard_mode(event: &Event, key_event: &mut KeyEvent) {}
}
pub mod server_keyboard_mode {
use super::*;
pub fn legacy_keyboard_mode(key_event: &KeyEvent) {}
pub fn map_keyboard_mode(key_event: &KeyEvent) {}
pub fn translate_keyboard_mode(key_event: &KeyEvent) {}
}

View File

@ -1,6 +1,7 @@
#[cfg(not(any(target_os = "ios")))]
/// cbindgen:ignore
pub mod platform;
mod keyboard;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub use platform::{get_cursor, get_cursor_data, get_cursor_pos, start_os_service};
#[cfg(not(any(target_os = "ios")))]

View File

@ -715,3 +715,23 @@ pub fn get_double_click_time() -> u32 {
double_click_time
}
}
pub mod keyboard {
use crate::common::GrabState;
use hbb_common::{allow_err, log, message_proto::*};
use rdev::{Event, EventType, Key};
use std::sync::mpsc;
use std::thread;
pub fn _legacy_keyboard_mode(event: &Event, key_event: &KeyEvent) {
log::info!("{:?}", event);
}
pub fn _client_map_keyboard_mode(event: &Event, key_event: &KeyEvent) {
}
pub fn _translate_keyboard_mode(event: &Event, key_event: &KeyEvent) {}
}

View File

@ -542,3 +542,7 @@ pub fn get_double_click_time() -> u32 {
// to-do: https://github.com/servo/core-foundation-rs/blob/786895643140fa0ee4f913d7b4aeb0c4626b2085/cocoa/src/appkit.rs#L2823
500 as _
}
pub mod keyboard{
}

View File

@ -75,3 +75,4 @@ mod tests {
}
}
}

View File

@ -1712,3 +1712,17 @@ pub fn send_message_to_hnwd(
}
return true;
}
pub mod keyboard {
use crate::common::GrabState;
use hbb_common::{allow_err, log, message_proto::*};
use rdev::{Event, EventType, Key};
use std::sync::mpsc;
use crate::keyboard;
}

View File

@ -4,8 +4,6 @@ use crate::client::{
load_config, send_mouse, start_video_audio_threads, FileManager, Key, LoginConfigHandler,
QualityStatus, KEY_MAP,
};
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use crate::client::{get_key_state, SERVER_KEYBOARD_ENABLED};
#[cfg(target_os = "linux")]
use crate::common::IS_X11;
use crate::{client::Data, client::Interface};
@ -15,53 +13,12 @@ use hbb_common::rendezvous_proto::ConnType;
use hbb_common::tokio::{self, sync::mpsc};
use hbb_common::{allow_err, message_proto::*};
use hbb_common::{fs, get_version_number, log, Stream};
use rdev::{Event, EventType, EventType::*, Key as RdevKey};
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use rdev::{Keyboard as RdevKeyboard, KeyboardState};
use std::collections::{HashMap, HashSet};
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, RwLock};
/// IS_IN KEYBOARD_HOOKED sciter only
pub static IS_IN: AtomicBool = AtomicBool::new(false);
pub static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false);
pub static HOTKEY_HOOKED: AtomicBool = AtomicBool::new(false);
#[cfg(windows)]
static mut IS_ALT_GR: bool = false;
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use crate::flutter::FlutterHandler;
lazy_static::lazy_static! {
static ref TO_RELEASE: Arc<Mutex<HashSet<RdevKey>>> = Arc::new(Mutex::new(HashSet::<RdevKey>::new()));
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
lazy_static::lazy_static! {
static ref KEYBOARD: Arc<Mutex<RdevKeyboard>> = Arc::new(Mutex::new(RdevKeyboard::new().unwrap()));
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
lazy_static::lazy_static! {
pub static ref CUR_SESSION: Arc<Mutex<Option<Session<FlutterHandler>>>> = Default::default();
}
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)
};
}
#[derive(Clone, Default)]
pub struct Session<T: InvokeUiSession> {
@ -92,11 +49,11 @@ impl<T: InvokeUiSession> Session<T> {
}
pub fn get_keyboard_mode(&self) -> String {
global_get_keyboard_mode()
self.lc.read().unwrap().keyboard_mode.clone()
}
pub fn save_keyboard_mode(&self, value: String) {
global_save_keyboard_mode(value);
pub fn save_keyboard_mode(&mut self, value: String) {
self.lc.write().unwrap().save_keyboard_mode(value);
}
pub fn save_view_style(&mut self, value: String) {
@ -307,439 +264,6 @@ impl<T: InvokeUiSession> Session<T> {
self.lc.read().unwrap().info.platform.clone()
}
pub fn ctrl_alt_del(&self) {
if self.peer_platform() == "Windows" {
let mut key_event = KeyEvent::new();
key_event.set_control_key(ControlKey::CtrlAltDel);
// 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.legacy_modifiers(&mut key_event, true, true, false, false);
// todo
key_event.press = true;
self.send_key_event(key_event, KeyboardMode::Legacy);
}
}
fn send_key_event(&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(&self, key: RdevKey) -> RdevKey {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
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(&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" {
rdev::win_keycode_from_key(key).unwrap_or_default().into()
} else {
// Without Clear Key on Mac OS
if key == rdev::Key::Clear {
return;
}
rdev::macos_keycode_from_key(key).unwrap_or_default().into()
};
key_event.set_chr(keycode);
key_event.down = down_or_up;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if get_key_state(enigo::Key::CapsLock) {
key_event.modifiers.push(ControlKey::CapsLock.into());
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if get_key_state(enigo::Key::NumLock) {
key_event.modifiers.push(ControlKey::NumLock.into());
}
self.send_key_event(key_event, KeyboardMode::Map);
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn translate_keyboard_mode(&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 {
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,
) {
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());
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if get_key_state(enigo::Key::CapsLock) {
key_event.modifiers.push(ControlKey::CapsLock.into());
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if self.peer_platform() != "Mac OS" {
if get_key_state(enigo::Key::NumLock) {
key_event.modifiers.push(ControlKey::NumLock.into());
}
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn legacy_keyboard_mode(&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::Slash => '/',
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;
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
let (alt, ctrl, shift, command) = get_all_hotkey_state(alt, ctrl, shift, command);
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(&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,
};
#[cfg(not(windows))]
let key = self.convert_numpad_keys(key);
let mut to_release = TO_RELEASE.lock().unwrap();
match mode {
KeyboardMode::Map => {
if down_or_up == true {
to_release.insert(key);
} else {
to_release.remove(&key);
}
self.map_keyboard_mode(down_or_up, key, Some(evt));
}
KeyboardMode::Legacy =>
{
#[cfg(not(any(target_os = "android", target_os = "ios")))]
self.legacy_keyboard_mode(down_or_up, key, evt)
}
KeyboardMode::Translate => {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
self.translate_keyboard_mode(down_or_up, key, evt);
}
_ =>
{
#[cfg(not(any(target_os = "android", target_os = "ios")))]
self.legacy_keyboard_mode(down_or_up, key, evt)
}
}
}
pub fn get_platform(&self, is_remote: bool) -> String {
if is_remote {
self.peer_platform()
@ -768,6 +292,13 @@ impl<T: InvokeUiSession> Session<T> {
return "".to_owned();
}
pub fn send_key_event(&self, mut evt: &KeyEvent) {
// mode: legacy(0), map(1), translate(2), auto(3)
let mut msg_out = Message::new();
msg_out.set_key_event(evt.clone());
self.send(Data::Message(msg_out));
}
pub fn send_chat(&self, text: String) {
let mut misc = Misc::new();
misc.set_chat_message(ChatMessage {
@ -790,77 +321,14 @@ impl<T: InvokeUiSession> Session<T> {
self.send(Data::Message(msg_out));
}
pub fn lock_screen(&self) {
let mut key_event = KeyEvent::new();
key_event.set_control_key(ControlKey::LockScreen);
// todo
key_event.down = true;
self.send_key_event(key_event, KeyboardMode::Legacy);
}
pub fn enter(&self) {
IS_IN.store(true, Ordering::SeqCst);
#[cfg(target_os = "linux")]
self.grab_hotkeys(true);
#[cfg(windows)]
crate::platform::windows::stop_system_key_propagate(true);
keyboard::client::change_grab_status(GrabState::Run);
}
pub fn leave(&self) {
IS_IN.store(false, Ordering::SeqCst);
#[cfg(target_os = "linux")]
self.grab_hotkeys(false);
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);
}
#[cfg(target_os = "linux")]
pub fn grab_hotkeys(&self, _grab: bool) {
if _grab {
rdev::enable_grab().ok();
} else {
rdev::disable_grab().ok();
}
}
pub fn handle_flutter_key_event(
&self,
name: &str,
keycode: i32,
scancode: i32,
down_or_up: bool,
) {
if scancode < 0 || keycode < 0 {
return;
}
let keycode: u32 = keycode as u32;
let scancode: u32 = scancode as u32;
#[cfg(not(target_os = "windows"))]
let key = rdev::key_from_scancode(scancode) as RdevKey;
// Windows requires special handling
#[cfg(target_os = "windows")]
let key = rdev::get_win_key(keycode, scancode);
let event_type = if down_or_up {
KeyPress(key)
} else {
KeyRelease(key)
};
let evt = Event {
time: std::time::SystemTime::now(),
name: Option::Some(name.to_owned()),
code: keycode as _,
scan_code: scancode as _,
event_type: event_type,
};
self.key_down_or_up(down_or_up, key, evt)
keyboard::client::change_grab_status(GrabState::Wait);
}
// flutter only TODO new input
@ -874,9 +342,6 @@ impl<T: InvokeUiSession> Session<T> {
shift: bool,
command: bool,
) {
if HOTKEY_HOOKED.load(Ordering::SeqCst) {
return;
}
let chars: Vec<char> = name.chars().collect();
if chars.len() == 1 {
let key = Key::_Raw(chars[0] as _);
@ -921,25 +386,6 @@ impl<T: InvokeUiSession> Session<T> {
key_event.set_chr(chr);
}
Key::ControlKey(key) => {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
let key = if !get_key_state(enigo::Key::NumLock) {
match key {
ControlKey::Numpad0 => ControlKey::Insert,
ControlKey::Decimal => ControlKey::Delete,
ControlKey::Numpad1 => ControlKey::End,
ControlKey::Numpad2 => ControlKey::DownArrow,
ControlKey::Numpad3 => ControlKey::PageDown,
ControlKey::Numpad4 => ControlKey::LeftArrow,
ControlKey::Numpad5 => ControlKey::Clear,
ControlKey::Numpad6 => ControlKey::RightArrow,
ControlKey::Numpad7 => ControlKey::Home,
ControlKey::Numpad8 => ControlKey::UpArrow,
ControlKey::Numpad9 => ControlKey::PageUp,
_ => key,
}
} else {
key
};
key_event.set_control_key(key.clone());
}
Key::_Raw(raw) => {
@ -947,17 +393,15 @@ impl<T: InvokeUiSession> Session<T> {
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
let (alt, ctrl, shift, command) = get_all_hotkey_state(alt, ctrl, shift, command);
self.legacy_modifiers(&mut key_event, alt, ctrl, shift, command);
if v == 1 {
key_event.down = true;
} else if v == 3 {
key_event.press = true;
}
keyboard::client::legacy_modifiers(&mut key_event, alt, ctrl, shift, command);
key_event.mode = KeyboardMode::Legacy.into();
self.send_key_event(key_event, KeyboardMode::Legacy);
self.send_key_event(&key_event);
}
pub fn send_mouse(
@ -979,8 +423,10 @@ impl<T: InvokeUiSession> Session<T> {
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
let (alt, ctrl, shift, command) = get_all_hotkey_state(alt, ctrl, shift, command);
// todo! chieh
// #[cfg(not(any(target_os = "android", target_os = "ios")))]
let (alt, ctrl, shift, command) =
keyboard::client::get_modifiers_state(alt, ctrl, shift, command);
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
@ -1243,23 +689,6 @@ impl<T: InvokeUiSession> Interface for Session<T> {
crate::platform::windows::add_recent_document(&path);
}
}
// only run in sciter
#[cfg(not(feature = "flutter"))]
{
// rdev::grab and rdev::listen use the same api in macOS & Windows
/* todo! Unused */
#[cfg(not(any(
target_os = "android",
target_os = "ios",
target_os = "macos",
target_os = "windows",
target_os = "linux",
)))]
self.start_keyboard_hook();
/* todo! (sciter) Only one device can be connected at the same time in linux */
#[cfg(not(any(target_os = "android", target_os = "ios")))]
self.start_grab_hotkey();
}
}
async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream) {
@ -1300,113 +729,14 @@ impl<T: InvokeUiSession> Interface for Session<T> {
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
impl<T: InvokeUiSession> Session<T> {
fn handle_hotkey_event(&self, event: Event) {
// if is long press, don't do anything.
if is_long_press(&event) {
return;
}
let (key, down) = match event.event_type {
EventType::KeyPress(key) => (key, true),
EventType::KeyRelease(key) => (key, false),
_ => return,
};
self.key_down_or_up(down, key, event);
pub fn lock_screen(&self) {
log::info!("Sending key even");
crate::keyboard::client::lock_screen();
}
#[allow(dead_code)]
fn start_grab_hotkey(&self) {
if self.is_port_forward() || self.is_file_transfer() {
return;
}
#[cfg(target_os = "linux")]
if !*IS_X11.lock().unwrap() {
return;
}
if HOTKEY_HOOKED.swap(true, Ordering::SeqCst) {
return;
}
log::info!("starting grab hotkeys");
let me = self.clone();
#[cfg(target_os = "linux")]
{
let func = move |event: Event| match event.event_type {
EventType::KeyPress(_key) | EventType::KeyRelease(_key) => {
me.handle_hotkey_event(event);
None
}
_ => Some(event),
};
rdev::start_grab_listen(func)
}
#[cfg(any(target_os = "windows", target_os = "macos"))]
std::thread::spawn(move || {
let func = move |event: Event| match event.event_type {
EventType::KeyPress(..) | EventType::KeyRelease(..) => {
// grab all keys
if !IS_IN.load(Ordering::SeqCst)
|| !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst)
{
return Some(event);
} else {
me.handle_hotkey_event(event);
return None;
}
}
_ => Some(event),
};
if let Err(error) = rdev::grab(func) {
log::error!("Error: {:?}", error)
}
});
}
#[allow(dead_code)]
fn start_keyboard_hook(&self) {
// only run in sciter
if self.is_port_forward() || self.is_file_transfer() {
return;
}
if KEYBOARD_HOOKED.swap(true, Ordering::SeqCst) {
return;
}
log::info!("keyboard hooked");
let me = self.clone();
#[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");
let func = move |evt: Event| {
/* todo! IS_IN can't determine if the user is focused on remote page */
if !IS_IN.load(Ordering::SeqCst) || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst)
{
return;
}
if is_long_press(&evt) {
return;
}
let (key, down) = match evt.event_type {
EventType::KeyPress(key) => (key, true),
EventType::KeyRelease(key) => (key, false),
_ => return,
};
me.key_down_or_up(down, key, evt);
};
/* todo!: Shift + a -> AA in sciter
* rdev::listen and rdev::grab both send a
*/
if let Err(error) = rdev::listen(func) {
log::error!("rdev: {:?}", error);
}
});
pub fn ctrl_alt_del(&self) {
log::info!("Sending key even");
crate::keyboard::client::lock_screen();
}
}
@ -1560,107 +890,3 @@ async fn send_note(url: String, id: String, conn_id: i32, note: String) {
let body = serde_json::json!({ "id": id, "Id": conn_id, "note": note });
allow_err!(crate::post_request(url, body.to_string(), "").await);
}
fn get_hotkey_state(key: RdevKey) -> bool {
if let Some(&state) = MUTEX_SPECIAL_KEYS.lock().unwrap().get(&key) {
return state;
} else {
return false;
}
}
fn get_all_hotkey_state(
alt: bool,
ctrl: bool,
shift: bool,
command: bool,
) -> (bool, bool, bool, bool) {
let ctrl =
get_hotkey_state(RdevKey::ControlLeft) || get_hotkey_state(RdevKey::ControlRight) || ctrl;
let shift =
get_hotkey_state(RdevKey::ShiftLeft) || get_hotkey_state(RdevKey::ShiftRight) || shift;
let command =
get_hotkey_state(RdevKey::MetaLeft) || get_hotkey_state(RdevKey::MetaRight) || command;
let alt = get_hotkey_state(RdevKey::Alt) || get_hotkey_state(RdevKey::AltGr) || alt;
(alt, ctrl, shift, command)
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn send_key_event_to_session(event: rdev::Event) {
if let Some(handler) = CUR_SESSION.lock().unwrap().as_ref() {
handler.handle_hotkey_event(event);
}
}
#[cfg(feature = "flutter")]
pub fn global_grab_keyboard() {
if HOTKEY_HOOKED.swap(true, Ordering::SeqCst) {
return;
}
log::info!("starting global grab keyboard");
#[cfg(target_os = "linux")]
{
let func = move |event: Event| match event.event_type {
EventType::KeyPress(_key) | EventType::KeyRelease(_key) => {
send_key_event_to_session(event);
None
}
_ => Some(event),
};
rdev::start_grab_listen(func)
}
#[cfg(any(target_os = "windows", target_os = "macos"))]
std::thread::spawn(move || {
let func = move |event: Event| match event.event_type {
EventType::KeyPress(..) | EventType::KeyRelease(..) => {
// grab all keys
if !IS_IN.load(Ordering::SeqCst) {
return Some(event);
} else {
send_key_event_to_session(event);
return None;
}
}
_ => Some(event),
};
if let Err(error) = rdev::grab(func) {
log::error!("Error: {:?}", error)
}
});
}
pub fn global_get_keyboard_mode() -> String {
return std::env::var("KEYBOARD_MODE")
.unwrap_or(String::from("map"))
.to_lowercase();
}
pub fn global_save_keyboard_mode(value: String) {
std::env::set_var("KEYBOARD_MODE", value);
}
fn is_long_press(event: &Event) -> bool {
let mut keys = MUTEX_SPECIAL_KEYS.lock().unwrap();
match event.event_type {
EventType::KeyPress(k) => {
if let Some(&state) = keys.get(&k) {
if state == true {
return true;
} else {
keys.insert(k, true);
}
}
}
EventType::KeyRelease(k) => {
if keys.contains_key(&k) {
keys.insert(k, false);
}
}
_ => {}
};
return false;
}