Merge pull request #3888 from fufesou/feat/translate_mode_win2win_hotkey
Feat/translate mode win2win hotkey
This commit is contained in:
commit
707fde151c
@ -203,11 +203,13 @@ message KeyEvent {
|
|||||||
bool press = 2;
|
bool press = 2;
|
||||||
oneof union {
|
oneof union {
|
||||||
ControlKey control_key = 3;
|
ControlKey control_key = 3;
|
||||||
// high word, sym key code. win: virtual-key code, linux: keysym ?, macos:
|
// position key code. win: scancode, linux: key code, macos: key code
|
||||||
// low word, position key code. win: scancode, linux: key code, macos: key code
|
|
||||||
uint32 chr = 4;
|
uint32 chr = 4;
|
||||||
uint32 unicode = 5;
|
uint32 unicode = 5;
|
||||||
string seq = 6;
|
string seq = 6;
|
||||||
|
// high word. virtual keycode
|
||||||
|
// low word. unicode
|
||||||
|
uint32 win2win_hotkey = 7;
|
||||||
}
|
}
|
||||||
repeated ControlKey modifiers = 8;
|
repeated ControlKey modifiers = 8;
|
||||||
KeyboardMode mode = 9;
|
KeyboardMode mode = 9;
|
||||||
|
@ -4,7 +4,7 @@ use crate::common::GrabState;
|
|||||||
#[cfg(feature = "flutter")]
|
#[cfg(feature = "flutter")]
|
||||||
use crate::flutter::{CUR_SESSION_ID, SESSIONS};
|
use crate::flutter::{CUR_SESSION_ID, SESSIONS};
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
use crate::platform::windows::get_char_by_vk;
|
use crate::platform::windows::{get_char_from_vk, get_unicode_from_vk};
|
||||||
#[cfg(not(any(feature = "flutter", feature = "cli")))]
|
#[cfg(not(any(feature = "flutter", feature = "cli")))]
|
||||||
use crate::ui::CUR_SESSION;
|
use crate::ui::CUR_SESSION;
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
@ -865,6 +865,7 @@ fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &m
|
|||||||
if name.len() > 0 {
|
if name.len() > 0 {
|
||||||
let mut evt = key_event.clone();
|
let mut evt = key_event.clone();
|
||||||
evt.set_seq(name.to_string());
|
evt.set_seq(name.to_string());
|
||||||
|
evt.down = true;
|
||||||
events.push(evt);
|
events.push(evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -874,9 +875,10 @@ fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &m
|
|||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
if _peer == OS_LOWER_LINUX {
|
if _peer == OS_LOWER_LINUX {
|
||||||
if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } {
|
if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } {
|
||||||
if let Some(chr) = get_char_by_vk(event.platform_code as u32) {
|
if let Some(chr) = get_char_from_vk(event.platform_code as u32) {
|
||||||
let mut evt = key_event.clone();
|
let mut evt = key_event.clone();
|
||||||
evt.set_seq(chr.to_string());
|
evt.set_seq(chr.to_string());
|
||||||
|
evt.down = true;
|
||||||
events.push(evt);
|
events.push(evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -885,6 +887,36 @@ fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn try_file_win2win_hotkey(
|
||||||
|
peer: &str,
|
||||||
|
event: &Event,
|
||||||
|
key_event: &KeyEvent,
|
||||||
|
events: &mut Vec<KeyEvent>,
|
||||||
|
) {
|
||||||
|
if peer == OS_LOWER_WINDOWS && is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } {
|
||||||
|
let mut down = false;
|
||||||
|
let win2win_hotkey = match event.event_type {
|
||||||
|
EventType::KeyPress(..) => {
|
||||||
|
down = true;
|
||||||
|
if let Some(unicode) = get_unicode_from_vk(event.platform_code as u32) {
|
||||||
|
Some((unicode as u32 & 0x0000FFFF) | (event.platform_code << 16))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EventType::KeyRelease(..) => Some(event.platform_code << 16),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if let Some(code) = win2win_hotkey {
|
||||||
|
let mut evt = key_event.clone();
|
||||||
|
evt.set_win2win_hotkey(code);
|
||||||
|
evt.down = down;
|
||||||
|
events.push(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
fn is_hot_key_modifiers_down() -> bool {
|
fn is_hot_key_modifiers_down() -> bool {
|
||||||
if rdev::get_modifier(Key::ControlLeft) || rdev::get_modifier(Key::ControlRight) {
|
if rdev::get_modifier(Key::ControlLeft) || rdev::get_modifier(Key::ControlRight) {
|
||||||
@ -899,25 +931,6 @@ fn is_hot_key_modifiers_down() -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option<KeyEvent> {
|
|
||||||
let mut key_event = map_keyboard_mode(peer, event, key_event)?;
|
|
||||||
let chr = if peer == OS_LOWER_WINDOWS {
|
|
||||||
(key_event.chr() & 0x0000FFFF) | ((event.platform_code as u32) << 16)
|
|
||||||
} else {
|
|
||||||
key_event.chr()
|
|
||||||
};
|
|
||||||
key_event.set_chr(chr);
|
|
||||||
Some(key_event)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option<KeyEvent> {
|
|
||||||
map_keyboard_mode(peer, event, key_event)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||||
fn is_altgr(event: &Event) -> bool {
|
fn is_altgr(event: &Event) -> bool {
|
||||||
@ -945,6 +958,7 @@ fn is_press(event: &Event) -> bool {
|
|||||||
// https://github.com/fufesou/rustdesk/wiki/Keyboard-mode----Translate-Mode
|
// https://github.com/fufesou/rustdesk/wiki/Keyboard-mode----Translate-Mode
|
||||||
pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -> Vec<KeyEvent> {
|
pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -> Vec<KeyEvent> {
|
||||||
let mut events: Vec<KeyEvent> = Vec::new();
|
let mut events: Vec<KeyEvent> = Vec::new();
|
||||||
|
|
||||||
if let Some(unicode_info) = &event.unicode {
|
if let Some(unicode_info) = &event.unicode {
|
||||||
if unicode_info.is_dead {
|
if unicode_info.is_dead {
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
@ -961,7 +975,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -
|
|||||||
|
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
if is_numpad_key(&event) {
|
if is_numpad_key(&event) {
|
||||||
if let Some(evt) = translate_key_code(peer, event, key_event) {
|
if let Some(evt) = map_keyboard_mode(peer, event, key_event) {
|
||||||
events.push(evt);
|
events.push(evt);
|
||||||
}
|
}
|
||||||
return events;
|
return events;
|
||||||
@ -983,11 +997,15 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -
|
|||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
try_file_win2win_hotkey(peer, event, &key_event, &mut events);
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||||
if is_press(event) {
|
if events.is_empty() && is_press(event) {
|
||||||
try_fill_unicode(peer, event, &key_event, &mut events);
|
try_fill_unicode(peer, event, &key_event, &mut events);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If AltGr is down, no need to send events other than unicode.
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
unsafe {
|
unsafe {
|
||||||
if IS_0X021D_DOWN {
|
if IS_0X021D_DOWN {
|
||||||
@ -1001,7 +1019,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -
|
|||||||
}
|
}
|
||||||
|
|
||||||
if events.is_empty() {
|
if events.is_empty() {
|
||||||
if let Some(evt) = translate_key_code(peer, event, key_event) {
|
if let Some(evt) = map_keyboard_mode(peer, event, key_event) {
|
||||||
events.push(evt);
|
events.push(evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1229,7 +1229,7 @@ sc delete {app_name}
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_after_install() -> ResultType<()> {
|
pub fn run_after_install() -> ResultType<()> {
|
||||||
let (_, _, _, exe,_) = get_install_info();
|
let (_, _, _, exe, _) = get_install_info();
|
||||||
run_cmds(get_after_install(&exe), true, "after_install")
|
run_cmds(get_after_install(&exe), true, "after_install")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2130,7 +2130,25 @@ mod cert {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_char_by_vk(vk: u32) -> Option<char> {
|
#[inline]
|
||||||
|
pub fn get_char_from_vk(vk: u32) -> Option<char> {
|
||||||
|
get_char_from_unicode(get_unicode_from_vk(vk)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_char_from_unicode(unicode: u16) -> Option<char> {
|
||||||
|
let buff = [unicode];
|
||||||
|
if let Some(chr) = String::from_utf16(&buff[..1]).ok()?.chars().next() {
|
||||||
|
if chr.is_control() {
|
||||||
|
return None;
|
||||||
|
} else {
|
||||||
|
Some(chr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_unicode_from_vk(vk: u32) -> Option<u16> {
|
||||||
const BUF_LEN: i32 = 32;
|
const BUF_LEN: i32 = 32;
|
||||||
let mut buff = [0_u16; BUF_LEN as usize];
|
let mut buff = [0_u16; BUF_LEN as usize];
|
||||||
let buff_ptr = buff.as_mut_ptr();
|
let buff_ptr = buff.as_mut_ptr();
|
||||||
@ -2155,19 +2173,7 @@ pub fn get_char_by_vk(vk: u32) -> Option<char> {
|
|||||||
ToUnicodeEx(vk, 0x00, &state as _, buff_ptr, BUF_LEN, 0, layout)
|
ToUnicodeEx(vk, 0x00, &state as _, buff_ptr, BUF_LEN, 0, layout)
|
||||||
};
|
};
|
||||||
if len == 1 {
|
if len == 1 {
|
||||||
if let Some(chr) = String::from_utf16(&buff[..len as usize])
|
Some(buff[0])
|
||||||
.ok()?
|
|
||||||
.chars()
|
|
||||||
.next()
|
|
||||||
{
|
|
||||||
if chr.is_control() {
|
|
||||||
return None;
|
|
||||||
} else {
|
|
||||||
Some(chr)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -2190,10 +2196,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_char_by_vk() {
|
fn test_get_unicode_char_by_vk() {
|
||||||
let chr = get_char_by_vk(0x41); // VK_A
|
let chr = get_char_from_vk(0x41); // VK_A
|
||||||
assert_eq!(chr, Some('a'));
|
assert_eq!(chr, Some('a'));
|
||||||
let chr = get_char_by_vk(VK_ESCAPE as u32); // VK_ESC
|
let chr = get_char_from_vk(VK_ESCAPE as u32); // VK_ESC
|
||||||
assert_eq!(chr, None)
|
assert_eq!(chr, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1280,12 +1280,49 @@ fn translate_keyboard_mode(evt: &KeyEvent) {
|
|||||||
Some(key_event::Union::Unicode(..)) => {
|
Some(key_event::Union::Unicode(..)) => {
|
||||||
// Do not handle unicode for now.
|
// Do not handle unicode for now.
|
||||||
}
|
}
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
Some(key_event::Union::Win2winHotkey(code)) => {
|
||||||
|
simulate_win2win_hotkey(*code, evt.down);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
log::debug!("Unreachable. Unexpected key event {:?}", &evt);
|
log::debug!("Unreachable. Unexpected key event {:?}", &evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn simulate_win2win_hotkey(code: u32, down: bool) {
|
||||||
|
let unicode: u16 = (code & 0x0000FFFF) as u16;
|
||||||
|
if down {
|
||||||
|
// Try convert unicode to virtual keycode first.
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscanw
|
||||||
|
let res = unsafe { winapi::um::winuser::VkKeyScanW(unicode) };
|
||||||
|
if res as u16 != 0xFFFF {
|
||||||
|
let vk = res & 0x00FF;
|
||||||
|
let flag = res >> 8;
|
||||||
|
let modifiers = [rdev::Key::ShiftLeft, rdev::Key::ControlLeft, rdev::Key::Alt];
|
||||||
|
let mod_len = modifiers.len();
|
||||||
|
for pos in 0..mod_len {
|
||||||
|
if flag & (0x0001 << pos) != 0 {
|
||||||
|
allow_err!(rdev::simulate(&EventType::KeyPress(modifiers[pos])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allow_err!(rdev::simulate_code(Some(vk as _), None, true));
|
||||||
|
allow_err!(rdev::simulate_code(Some(vk as _), None, false));
|
||||||
|
for pos in 0..mod_len {
|
||||||
|
let rpos = mod_len - 1 - pos;
|
||||||
|
if flag & (0x0001 << rpos) != 0 {
|
||||||
|
allow_err!(rdev::simulate(&EventType::KeyRelease(modifiers[rpos])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let keycode: u16 = ((code >> 16) & 0x0000FFFF) as u16;
|
||||||
|
allow_err!(rdev::simulate_code(Some(keycode), None, down));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_key_(evt: &KeyEvent) {
|
pub fn handle_key_(evt: &KeyEvent) {
|
||||||
if EXITING.load(Ordering::SeqCst) {
|
if EXITING.load(Ordering::SeqCst) {
|
||||||
return;
|
return;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user