Merge pull request #1694 from asur4s/master

Feat: Grab hot key
This commit is contained in:
RustDesk
2022-10-10 14:27:15 +08:00
committed by GitHub
3 changed files with 235 additions and 38 deletions

View File

@@ -48,7 +48,7 @@ pub use super::lang::*;
pub mod file_trait;
pub mod helper;
pub mod io_loop;
use crate::ui_session_interface::global_save_keyboard_mode;
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);
@@ -1294,6 +1294,9 @@ 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(),

View File

@@ -6,6 +6,7 @@ use crate::client::{
load_config, send_mouse, start_video_audio_threads, FileManager, Key, LoginConfigHandler,
QualityStatus, KEY_MAP, SERVER_KEYBOARD_ENABLED,
};
use crate::common::IS_X11;
use crate::{client::Data, client::Interface};
use async_trait::async_trait;
use hbb_common::config::{Config, LocalConfig, PeerConfig};
@@ -13,7 +14,7 @@ use hbb_common::rendezvous_proto::ConnType;
use hbb_common::tokio::{self, sync::mpsc};
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use rdev::Keyboard as RdevKeyboard;
use rdev::{Event, EventType::*, Key as RdevKey, KeyboardState};
use rdev::{Event, EventType, EventType::*, Key as RdevKey, KeyboardState};
use hbb_common::{allow_err, message_proto::*};
use hbb_common::{fs, get_version_number, log, Stream};
@@ -25,7 +26,9 @@ 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(true);
pub static HOTKEY_HOOK_ENABLED: AtomicBool = AtomicBool::new(false);
#[cfg(target_os = "linux")]
use rdev::IS_GRAB;
#[cfg(windows)]
static mut IS_ALT_GR: bool = false;
@@ -38,6 +41,21 @@ lazy_static::lazy_static! {
static ref KEYBOARD: Arc<Mutex<RdevKeyboard>> = Arc::new(Mutex::new(RdevKeyboard::new().unwrap()));
}
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> {
pub id: String,
@@ -63,13 +81,11 @@ impl<T: InvokeUiSession> Session<T> {
}
pub fn get_keyboard_mode(&self) -> String {
return std::env::var("KEYBOARD_MODE")
.unwrap_or(String::from("legacy"))
.to_lowercase();
global_get_keyboard_mode()
}
pub fn save_keyboard_mode(&self, value: String) {
std::env::set_var("KEYBOARD_MODE", value);
global_save_keyboard_mode(value);
}
pub fn save_view_style(&mut self, value: String) {
@@ -146,7 +162,12 @@ impl<T: InvokeUiSession> Session<T> {
let decoder = scrap::codec::Decoder::video_codec_state(&self.id);
let mut h264 = decoder.score_h264 > 0;
let mut h265 = decoder.score_h265 > 0;
let (encoding_264, encoding_265) = self.lc.read().unwrap().supported_encoding.unwrap_or_default();
let (encoding_264, encoding_265) = self
.lc
.read()
.unwrap()
.supported_encoding
.unwrap_or_default();
h264 = h264 && encoding_264;
h265 = h265 && encoding_265;
return (h264, h265);
@@ -622,6 +643,7 @@ impl<T: InvokeUiSession> Session<T> {
RdevKey::Quote => '\'',
RdevKey::LeftBracket => '[',
RdevKey::RightBracket => ']',
RdevKey::Slash => '/',
RdevKey::BackSlash => '\\',
RdevKey::Minus => '-',
RdevKey::Equal => '=',
@@ -746,12 +768,24 @@ impl<T: InvokeUiSession> Session<T> {
}
pub fn enter(&self) {
HOTKEY_HOOK_ENABLED.store(true, Ordering::SeqCst);
#[cfg(target_os = "linux")]
unsafe {
IS_GRAB.store(true, Ordering::SeqCst);
}
#[cfg(windows)]
crate::platform::windows::stop_system_key_propagate(true);
IS_IN.store(true, Ordering::SeqCst);
}
pub fn leave(&self) {
HOTKEY_HOOK_ENABLED.store(false, Ordering::SeqCst);
#[cfg(target_os = "linux")]
unsafe {
IS_GRAB.store(false, Ordering::SeqCst);
}
for key in TO_RELEASE.lock().unwrap().iter() {
self.map_keyboard_mode(false, *key, None)
}
@@ -865,27 +899,19 @@ impl<T: InvokeUiSession> Session<T> {
ControlKey::Numpad9 => ControlKey::PageUp,
_ => key,
}
}else{
} else {
key
};
key_event.set_control_key(key.clone());
}
Key::_Raw(raw) => {
if raw > 'z' as u32 || raw < 'a' as u32 {
key_event.set_unicode(raw);
// TODO
// if down_or_up == 0 {
// // ignore up, avoiding trigger twice
// return;
// }
// down_or_up = 1; // if press, turn into down for avoiding trigger twice on server side
} else {
// to make ctrl+c works on windows
key_event.set_chr(raw);
}
key_event.set_chr(raw);
}
}
#[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;
@@ -915,6 +941,9 @@ 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);
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
@@ -1164,8 +1193,11 @@ impl<T: InvokeUiSession> Interface for Session<T> {
crate::platform::windows::add_recent_document(&path);
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
self.start_keyboard_hook();
#[cfg(not(any(target_os = "android", target_os = "ios")))]
self.start_hotkey_grab();
}
async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream) {
@@ -1208,6 +1240,93 @@ impl<T: InvokeUiSession> Interface for Session<T> {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
impl<T: InvokeUiSession> Session<T> {
fn handle_hot_key_event(&self, event: Event) {
// keyboard long press
match event.event_type {
EventType::KeyPress(k) => {
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);
}
}
EventType::KeyRelease(k) => {
if MUTEX_SPECIAL_KEYS.lock().unwrap().contains_key(&k) {
MUTEX_SPECIAL_KEYS.lock().unwrap().insert(k, false);
}
}
_ => return,
};
// keyboard short press
match event.event_type {
EventType::KeyPress(key) => {
self.key_down_or_up(true, key, event);
}
EventType::KeyRelease(key) => {
self.key_down_or_up(false, key, event);
}
_ => {}
}
}
fn start_hotkey_grab(&self) {
#[cfg(target_os = "linux")]
if !*IS_X11.lock().unwrap() {
return;
}
if self.is_port_forward() || self.is_file_transfer() {
return;
}
let me = self.clone();
log::info!("hotkey grabing");
std::thread::spawn(move || {
std::env::set_var("KEYBOARD_ONLY", "y");
let func = move |event: Event| {
#[cfg(any(target_os = "windows", target_os = "macos"))]
if !HOTKEY_HOOK_ENABLED.load(Ordering::SeqCst) {
return Some(event);
};
match event.event_type {
EventType::KeyPress(_key) | EventType::KeyRelease(_key) => {
#[cfg(any(target_os = "windows", target_os = "macos"))]
if MUTEX_SPECIAL_KEYS.lock().unwrap().contains_key(&_key) {
me.handle_hot_key_event(event);
return None;
} else {
return Some(event);
}
#[cfg(target_os = "linux")]
me.handle_hot_key_event(event);
None
}
_ => Some(event),
}
};
#[cfg(target_os = "linux")]
{
use rdev::GRABED_KEYS;
GRABED_KEYS.lock().unwrap().insert(RdevKey::ShiftLeft);
GRABED_KEYS.lock().unwrap().insert(RdevKey::ShiftRight);
GRABED_KEYS.lock().unwrap().insert(RdevKey::ControlLeft);
GRABED_KEYS.lock().unwrap().insert(RdevKey::ControlRight);
GRABED_KEYS.lock().unwrap().insert(RdevKey::Alt);
GRABED_KEYS.lock().unwrap().insert(RdevKey::AltGr);
GRABED_KEYS.lock().unwrap().insert(RdevKey::MetaLeft);
GRABED_KEYS.lock().unwrap().insert(RdevKey::MetaRight);
}
if let Err(error) = rdev::grab(func) {
log::error!("Error: {:?}", error)
}
});
}
fn start_keyboard_hook(&self) {
if self.is_port_forward() || self.is_file_transfer() {
return;
@@ -1215,6 +1334,11 @@ impl<T: InvokeUiSession> Session<T> {
if !KEYBOARD_HOOKED.load(Ordering::SeqCst) {
return;
}
// rdev::grab and rdev::listen use the same api on macOS
#[cfg(target_os = "macos")]
if HOTKEY_HOOK_ENABLED.load(Ordering::SeqCst) {
return;
}
log::info!("keyboard hooked");
let me = self.clone();
#[cfg(windows)]
@@ -1222,20 +1346,6 @@ impl<T: InvokeUiSession> Session<T> {
std::thread::spawn(move || {
// This will block.
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)
@@ -1421,3 +1531,34 @@ 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 {
*MUTEX_SPECIAL_KEYS.lock().unwrap().get(&key).unwrap()
}
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)
}
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);
}