Merge pull request #3719 from chiehw/win-linux
translate mode: support linux in server
This commit is contained in:
commit
8b229f304d
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -5939,7 +5939,7 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tfc"
|
name = "tfc"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
source = "git+https://github.com/fufesou/The-Fat-Controller#102f2ec2cb2bbbd64413d20d28323e5e77e0fe71"
|
source = "git+https://github.com/fufesou/The-Fat-Controller#1dba9a39c089ac9a7853b9dd5399c1d4aa3157d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"core-graphics 0.22.3",
|
"core-graphics 0.22.3",
|
||||||
|
@ -1685,7 +1685,7 @@ class _KeyboardMenu extends StatelessWidget {
|
|||||||
for (KeyboardModeMenu mode in modes) {
|
for (KeyboardModeMenu mode in modes) {
|
||||||
if (bind.sessionIsKeyboardModeSupported(id: id, mode: mode.key)) {
|
if (bind.sessionIsKeyboardModeSupported(id: id, mode: mode.key)) {
|
||||||
if (mode.key == _kKeyTranslateMode) {
|
if (mode.key == _kKeyTranslateMode) {
|
||||||
if (Platform.isLinux || pi.platform == kPeerPlatformLinux) {
|
if (Platform.isLinux) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::xdo::EnigoXdo;
|
use super::xdo::EnigoXdo;
|
||||||
use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};
|
use crate::{Key, KeyboardControllable, MouseButton, MouseControllable, ResultType};
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use tfc::{traits::*, Context as TFC_Context, Key as TFC_Key};
|
use tfc::{traits::*, Context as TFC_Context, Key as TFC_Key};
|
||||||
|
|
||||||
@ -42,6 +42,37 @@ impl Enigo {
|
|||||||
&mut self.custom_mouse
|
&mut self.custom_mouse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clear remapped keycodes
|
||||||
|
pub fn tfc_clear_remapped(&mut self) {
|
||||||
|
if let Some(tfc) = &mut self.tfc {
|
||||||
|
tfc.recover_remapped_keycodes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tfc_key_click(&mut self, key: Key) -> ResultType {
|
||||||
|
if let Some(tfc) = &mut self.tfc {
|
||||||
|
let res = match key {
|
||||||
|
Key::Layout(chr) => tfc.unicode_char(chr),
|
||||||
|
key => {
|
||||||
|
let tfc_key: TFC_Key = match convert_to_tfc_key(key) {
|
||||||
|
Some(key) => key,
|
||||||
|
None => {
|
||||||
|
return Err(format!("Failed to convert {:?} to TFC_Key", key).into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
tfc.key_click(tfc_key)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if res.is_err() {
|
||||||
|
Err(format!("Failed to click {:?} by tfc", key).into())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err("Not Found TFC".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn tfc_key_down_or_up(&mut self, key: Key, down: bool, up: bool) -> bool {
|
fn tfc_key_down_or_up(&mut self, key: Key, down: bool, up: bool) -> bool {
|
||||||
match &mut self.tfc {
|
match &mut self.tfc {
|
||||||
None => false,
|
None => false,
|
||||||
@ -223,6 +254,7 @@ impl KeyboardControllable for Enigo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Warning: Get 6^ in French.
|
||||||
fn key_sequence(&mut self, sequence: &str) {
|
fn key_sequence(&mut self, sequence: &str) {
|
||||||
if self.is_x11 {
|
if self.is_x11 {
|
||||||
self.xdo.key_sequence(sequence)
|
self.xdo.key_sequence(sequence)
|
||||||
@ -262,8 +294,10 @@ impl KeyboardControllable for Enigo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn key_click(&mut self, key: Key) {
|
fn key_click(&mut self, key: Key) {
|
||||||
self.key_down(key).ok();
|
if self.tfc_key_click(key).is_err() {
|
||||||
self.key_up(key);
|
self.key_down(key).ok();
|
||||||
|
self.key_up(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,3 +369,10 @@ fn convert_to_tfc_key(key: Key) -> Option<TFC_Key> {
|
|||||||
};
|
};
|
||||||
Some(key)
|
Some(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_key_seq() {
|
||||||
|
// Get 6^ in French.
|
||||||
|
let mut en = Enigo::new();
|
||||||
|
en.key_sequence("^^");
|
||||||
|
}
|
||||||
|
@ -7,6 +7,8 @@ pub mod macos;
|
|||||||
use crate::{config::Config, log};
|
use crate::{config::Config, log};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
|
static mut GLOBAL_CALLBACK: Option<Box<dyn Fn()>> = None;
|
||||||
|
|
||||||
extern "C" fn breakdown_signal_handler(sig: i32) {
|
extern "C" fn breakdown_signal_handler(sig: i32) {
|
||||||
let mut stack = vec![];
|
let mut stack = vec![];
|
||||||
backtrace::trace(|frame| {
|
backtrace::trace(|frame| {
|
||||||
@ -41,11 +43,20 @@ extern "C" fn breakdown_signal_handler(sig: i32) {
|
|||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
unsafe {
|
||||||
|
if let Some(callback) = &GLOBAL_CALLBACK {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_breakdown_handler() {
|
pub fn register_breakdown_handler<T>(callback: T)
|
||||||
|
where
|
||||||
|
T: Fn() + 'static,
|
||||||
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
|
GLOBAL_CALLBACK = Some(Box::new(callback));
|
||||||
libc::signal(libc::SIGSEGV, breakdown_signal_handler as _);
|
libc::signal(libc::SIGSEGV, breakdown_signal_handler as _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::platform::breakdown_callback;
|
||||||
use hbb_common::log;
|
use hbb_common::log;
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
use hbb_common::platform::register_breakdown_handler;
|
use hbb_common::platform::register_breakdown_handler;
|
||||||
@ -38,7 +39,7 @@ pub fn core_main() -> Option<Vec<String>> {
|
|||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
register_breakdown_handler();
|
register_breakdown_handler(breakdown_callback);
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
#[cfg(feature = "flutter")]
|
#[cfg(feature = "flutter")]
|
||||||
{
|
{
|
||||||
|
@ -3,11 +3,13 @@ use crate::client::get_key_state;
|
|||||||
use crate::common::GrabState;
|
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")]
|
||||||
|
use crate::platform::windows::get_char_by_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;
|
||||||
use hbb_common::message_proto::*;
|
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
use hbb_common::log;
|
use hbb_common::log;
|
||||||
|
use hbb_common::message_proto::*;
|
||||||
use rdev::{Event, EventType, Key};
|
use rdev::{Event, EventType, Key};
|
||||||
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
@ -789,7 +791,17 @@ fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec<KeyEve
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {}
|
None =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } {
|
||||||
|
if let Some(chr) = get_char_by_vk(event.code as u32) {
|
||||||
|
let mut evt = key_event.clone();
|
||||||
|
evt.set_seq(chr.to_string());
|
||||||
|
events.push(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -857,7 +869,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
if unsafe { IS_0X021D_DOWN } || !is_hot_key_modifiers_down() {
|
if matches!(event.event_type, EventType::KeyPress(_)) {
|
||||||
try_fill_unicode(event, &key_event, &mut events);
|
try_fill_unicode(event, &key_event, &mut events);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,11 @@ pub fn is_xfce() -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn breakdown_callback() {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
crate::input_service::clear_remapped_keycode()
|
||||||
|
}
|
||||||
|
|
||||||
// Android
|
// Android
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
pub fn get_active_username() -> String {
|
pub fn get_active_username() -> String {
|
||||||
|
@ -10,6 +10,7 @@ use hbb_common::{
|
|||||||
sleep, timeout, tokio,
|
sleep, timeout, tokio,
|
||||||
};
|
};
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
use std::ptr::null_mut;
|
||||||
use std::{
|
use std::{
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
fs, io, mem,
|
fs, io, mem,
|
||||||
@ -1961,7 +1962,8 @@ mod cert {
|
|||||||
RegKey,
|
RegKey,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ROOT_CERT_STORE_PATH: &str = "SOFTWARE\\Microsoft\\SystemCertificates\\ROOT\\Certificates\\";
|
const ROOT_CERT_STORE_PATH: &str =
|
||||||
|
"SOFTWARE\\Microsoft\\SystemCertificates\\ROOT\\Certificates\\";
|
||||||
const THUMBPRINT_ALG: ALG_ID = CALG_SHA1;
|
const THUMBPRINT_ALG: ALG_ID = CALG_SHA1;
|
||||||
const THUMBPRINT_LEN: DWORD = 20;
|
const THUMBPRINT_LEN: DWORD = 20;
|
||||||
|
|
||||||
@ -1979,7 +1981,10 @@ mod cert {
|
|||||||
&mut size,
|
&mut size,
|
||||||
) == TRUE
|
) == TRUE
|
||||||
{
|
{
|
||||||
(thumbprint.to_vec(), hex::encode(thumbprint).to_ascii_uppercase())
|
(
|
||||||
|
thumbprint.to_vec(),
|
||||||
|
hex::encode(thumbprint).to_ascii_uppercase(),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
(thumbprint.to_vec(), "".to_owned())
|
(thumbprint.to_vec(), "".to_owned())
|
||||||
}
|
}
|
||||||
@ -2086,16 +2091,70 @@ mod cert {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_char_by_vk(vk: u32) -> Option<char> {
|
||||||
|
const BUF_LEN: i32 = 32;
|
||||||
|
let mut buff = [0_u16; BUF_LEN as usize];
|
||||||
|
let buff_ptr = buff.as_mut_ptr();
|
||||||
|
let len = unsafe {
|
||||||
|
let current_window_thread_id = GetWindowThreadProcessId(GetForegroundWindow(), null_mut());
|
||||||
|
let layout = GetKeyboardLayout(current_window_thread_id);
|
||||||
|
|
||||||
|
// refs: https://github.com/fufesou/rdev/blob/25a99ce71ab42843ad253dd51e6a35e83e87a8a4/src/windows/keyboard.rs#L115
|
||||||
|
let press_state = 129;
|
||||||
|
let mut state: [BYTE; 256] = [0; 256];
|
||||||
|
let shift_left = rdev::get_modifier(rdev::Key::ShiftLeft);
|
||||||
|
let shift_right = rdev::get_modifier(rdev::Key::ShiftRight);
|
||||||
|
if shift_left {
|
||||||
|
state[VK_LSHIFT as usize] = press_state;
|
||||||
|
}
|
||||||
|
if shift_right {
|
||||||
|
state[VK_RSHIFT as usize] = press_state;
|
||||||
|
}
|
||||||
|
if shift_left || shift_right {
|
||||||
|
state[VK_SHIFT as usize] = press_state;
|
||||||
|
}
|
||||||
|
ToUnicodeEx(vk, 0x00, &state as _, buff_ptr, BUF_LEN, 0, layout)
|
||||||
|
};
|
||||||
|
if len == 1 {
|
||||||
|
if let Some(chr) = String::from_utf16(&buff[..len as usize])
|
||||||
|
.ok()?
|
||||||
|
.chars()
|
||||||
|
.next()
|
||||||
|
{
|
||||||
|
if chr.is_control() {
|
||||||
|
return None;
|
||||||
|
} else {
|
||||||
|
Some(chr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_install_cert() {
|
fn test_install_cert() {
|
||||||
println!("install driver cert: {:?}", cert::install_cert("RustDeskIddDriver.cer"));
|
println!(
|
||||||
|
"install driver cert: {:?}",
|
||||||
|
cert::install_cert("RustDeskIddDriver.cer")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_uninstall_cert() {
|
fn test_uninstall_cert() {
|
||||||
println!("uninstall driver certs: {:?}", cert::uninstall_certs());
|
println!("uninstall driver certs: {:?}", cert::uninstall_certs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_char_by_vk() {
|
||||||
|
let chr = get_char_by_vk(0x41); // VK_A
|
||||||
|
assert_eq!(chr, Some('a'));
|
||||||
|
let chr = get_char_by_vk(VK_ESCAPE as u32); // VK_ESC
|
||||||
|
assert_eq!(chr, None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -612,6 +612,8 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
clear_remapped_keycode();
|
||||||
log::info!("Input thread exited");
|
log::info!("Input thread exited");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,6 +413,11 @@ pub fn fix_key_down_timeout_at_exit() {
|
|||||||
log::info!("fix_key_down_timeout_at_exit");
|
log::info!("fix_key_down_timeout_at_exit");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn clear_remapped_keycode() {
|
||||||
|
ENIGO.lock().unwrap().tfc_clear_remapped();
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn record_key_is_control_key(record_key: u64) -> bool {
|
fn record_key_is_control_key(record_key: u64) -> bool {
|
||||||
record_key < KEY_CHAR_START
|
record_key < KEY_CHAR_START
|
||||||
@ -1130,7 +1135,24 @@ fn translate_process_code(code: u32, down: bool) {
|
|||||||
fn translate_keyboard_mode(evt: &KeyEvent) {
|
fn translate_keyboard_mode(evt: &KeyEvent) {
|
||||||
match &evt.union {
|
match &evt.union {
|
||||||
Some(key_event::Union::Seq(seq)) => {
|
Some(key_event::Union::Seq(seq)) => {
|
||||||
ENIGO.lock().unwrap().key_sequence(seq);
|
// Fr -> US
|
||||||
|
// client: Shift + & => 1(send to remote)
|
||||||
|
// remote: Shift + 1 => !
|
||||||
|
//
|
||||||
|
// Try to release shift first.
|
||||||
|
// remote: Shift + 1 => 1
|
||||||
|
let mut en = ENIGO.lock().unwrap();
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
simulate_(&EventType::KeyRelease(RdevKey::ShiftLeft));
|
||||||
|
simulate_(&EventType::KeyRelease(RdevKey::ShiftRight));
|
||||||
|
for chr in seq.chars() {
|
||||||
|
en.key_click(Key::Layout(chr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
en.key_sequence(seq);
|
||||||
}
|
}
|
||||||
Some(key_event::Union::Chr(..)) => {
|
Some(key_event::Union::Chr(..)) => {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
Loading…
Reference in New Issue
Block a user