mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Feat: Grab hot key
This commit is contained in:
@@ -13,7 +13,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 +25,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 +40,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,
|
||||
@@ -146,7 +163,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 +644,7 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
RdevKey::Quote => '\'',
|
||||
RdevKey::LeftBracket => '[',
|
||||
RdevKey::RightBracket => ']',
|
||||
RdevKey::Slash => '/',
|
||||
RdevKey::BackSlash => '\\',
|
||||
RdevKey::Minus => '-',
|
||||
RdevKey::Equal => '=',
|
||||
@@ -746,12 +769,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,7 +900,7 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
ControlKey::Numpad9 => ControlKey::PageUp,
|
||||
_ => key,
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
key
|
||||
};
|
||||
key_event.set_control_key(key.clone());
|
||||
@@ -886,6 +921,16 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let ctrl =
|
||||
get_hotkey_state(RdevKey::ControlLeft) || get_hotkey_state(RdevKey::ControlRight);
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let shift = get_hotkey_state(RdevKey::ShiftLeft) || get_hotkey_state(RdevKey::ShiftRight);
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let command = get_hotkey_state(RdevKey::MetaLeft) || get_hotkey_state(RdevKey::MetaRight);
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let alt = get_hotkey_state(RdevKey::Alt) || get_hotkey_state(RdevKey::AltGr);
|
||||
|
||||
self.legacy_modifiers(&mut key_event, alt, ctrl, shift, command);
|
||||
if v == 1 {
|
||||
key_event.down = true;
|
||||
@@ -915,6 +960,16 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let ctrl =
|
||||
get_hotkey_state(RdevKey::ControlLeft) || get_hotkey_state(RdevKey::ControlRight);
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let shift = get_hotkey_state(RdevKey::ShiftLeft) || get_hotkey_state(RdevKey::ShiftRight);
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let command = get_hotkey_state(RdevKey::MetaLeft) || get_hotkey_state(RdevKey::MetaRight);
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let alt = get_hotkey_state(RdevKey::Alt) || get_hotkey_state(RdevKey::AltGr);
|
||||
|
||||
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 +1219,11 @@ impl<T: InvokeUiSession> Interface for Session<T> {
|
||||
crate::platform::windows::add_recent_document(&path);
|
||||
}
|
||||
}
|
||||
// rdev::grab and rdev::listen use the same api on macOS
|
||||
#[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 +1266,93 @@ impl<T: InvokeUiSession> Interface for Session<T> {
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
impl<T: InvokeUiSession> Session<T> {
|
||||
fn send_hotkey(&self, key: RdevKey, is_press: bool) {
|
||||
log::info!("{:?} {:?}", key, is_press);
|
||||
}
|
||||
|
||||
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.send_hotkey(key, true);
|
||||
self.key_down_or_up(true, key, event);
|
||||
}
|
||||
EventType::KeyRelease(key) => {
|
||||
self.send_hotkey(key, false);
|
||||
self.key_down_or_up(false, key, event);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn start_hotkey_grab(&self) {
|
||||
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;
|
||||
}
|
||||
|
||||
#[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 +1360,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 +1372,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 +1557,7 @@ 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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user