Merge pull request #2673 from fufesou/fix/keyboard_macos_fn
fix macos sticky fn
This commit is contained in:
commit
3bd48cd087
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -4305,7 +4305,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rdev"
|
name = "rdev"
|
||||||
version = "0.5.0-2"
|
version = "0.5.0-2"
|
||||||
source = "git+https://github.com/fufesou/rdev#bc2db2f13dfdc95df8a02eb03f71325c173283dc"
|
source = "git+https://github.com/fufesou/rdev#196b589573f90703a601e6b105dd7c917fc388f9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cocoa",
|
"cocoa",
|
||||||
"core-foundation 0.9.3",
|
"core-foundation 0.9.3",
|
||||||
|
@ -40,6 +40,7 @@ const BUF_LEN: usize = 4;
|
|||||||
#[allow(improper_ctypes)]
|
#[allow(improper_ctypes)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[link(name = "ApplicationServices", kind = "framework")]
|
#[link(name = "ApplicationServices", kind = "framework")]
|
||||||
|
#[link(name = "Carbon", kind = "framework")]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn CFDataGetBytePtr(theData: CFDataRef) -> *const u8;
|
fn CFDataGetBytePtr(theData: CFDataRef) -> *const u8;
|
||||||
fn TISCopyCurrentKeyboardInputSource() -> TISInputSourceRef;
|
fn TISCopyCurrentKeyboardInputSource() -> TISInputSourceRef;
|
||||||
|
@ -523,6 +523,8 @@ impl Connection {
|
|||||||
rdev::set_dw_mouse_extra_info(enigo::ENIGO_INPUT_EXTRA_VALUE);
|
rdev::set_dw_mouse_extra_info(enigo::ENIGO_INPUT_EXTRA_VALUE);
|
||||||
rdev::set_dw_keyboard_extra_info(enigo::ENIGO_INPUT_EXTRA_VALUE);
|
rdev::set_dw_keyboard_extra_info(enigo::ENIGO_INPUT_EXTRA_VALUE);
|
||||||
}
|
}
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
reset_input_ondisconn();
|
||||||
loop {
|
loop {
|
||||||
match receiver.recv_timeout(std::time::Duration::from_millis(500)) {
|
match receiver.recv_timeout(std::time::Duration::from_millis(500)) {
|
||||||
Ok(v) => match v {
|
Ok(v) => match v {
|
||||||
|
@ -5,7 +5,9 @@ use crate::common::IS_X11;
|
|||||||
use dispatch::Queue;
|
use dispatch::Queue;
|
||||||
use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable};
|
use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable};
|
||||||
use hbb_common::{config::COMPRESS_LEVEL, get_time, protobuf::EnumOrUnknown};
|
use hbb_common::{config::COMPRESS_LEVEL, get_time, protobuf::EnumOrUnknown};
|
||||||
use rdev::{self, simulate, EventType, Key as RdevKey, RawKey};
|
use rdev::{self, EventType, Key as RdevKey, RawKey};
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use rdev::{CGEventSourceStateID, CGEventTapLocation, VirtualInput};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{
|
use std::{
|
||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
@ -221,6 +223,11 @@ lazy_static::lazy_static! {
|
|||||||
static ref IS_SERVER: bool = std::env::args().nth(1) == Some("--server".to_owned());
|
static ref IS_SERVER: bool = std::env::args().nth(1) == Some("--server".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
static mut VIRTUAL_INPUT_MTX: Mutex<()> = Mutex::new(());
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
static mut VIRTUAL_INPUT: Option<VirtualInput> = None;
|
||||||
|
|
||||||
// First call set_uinput() will create keyboard and mouse clients.
|
// First call set_uinput() will create keyboard and mouse clients.
|
||||||
// The clients are ipc connections that must live shorter than tokio runtime.
|
// The clients are ipc connections that must live shorter than tokio runtime.
|
||||||
// Thus this function must not be called in a temporary runtime.
|
// Thus this function must not be called in a temporary runtime.
|
||||||
@ -288,6 +295,7 @@ fn modifier_sleep() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
fn is_pressed(key: &Key, en: &mut Enigo) -> bool {
|
fn is_pressed(key: &Key, en: &mut Enigo) -> bool {
|
||||||
get_modifier_state(key.clone(), en)
|
get_modifier_state(key.clone(), en)
|
||||||
}
|
}
|
||||||
@ -391,7 +399,9 @@ fn record_key_to_key(record_key: u64) -> Option<Key> {
|
|||||||
fn release_record_key(record_key: u64) {
|
fn release_record_key(record_key: u64) {
|
||||||
let func = move || {
|
let func = move || {
|
||||||
if record_key_is_rdev_layout(record_key) {
|
if record_key_is_rdev_layout(record_key) {
|
||||||
rdev_key_down_or_up(RdevKey::Unknown((record_key - KEY_RDEV_START) as _), false);
|
simulate_(&EventType::KeyRelease(RdevKey::Unknown(
|
||||||
|
(record_key - KEY_RDEV_START) as _,
|
||||||
|
)));
|
||||||
} else if let Some(key) = record_key_to_key(record_key) {
|
} else if let Some(key) = record_key_to_key(record_key) {
|
||||||
ENIGO.lock().unwrap().key_up(key);
|
ENIGO.lock().unwrap().key_up(key);
|
||||||
log::debug!("Fixed {:?} timeout", key);
|
log::debug!("Fixed {:?} timeout", key);
|
||||||
@ -678,15 +688,40 @@ pub fn handle_key(evt: &KeyEvent) {
|
|||||||
// having GUI, run main GUI thread, otherwise crash
|
// having GUI, run main GUI thread, otherwise crash
|
||||||
let evt = evt.clone();
|
let evt = evt.clone();
|
||||||
QUEUE.exec_async(move || handle_key_(&evt));
|
QUEUE.exec_async(move || handle_key_(&evt));
|
||||||
|
std::thread::sleep(Duration::from_millis(20));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
crate::portable_service::client::handle_key(evt);
|
crate::portable_service::client::handle_key(evt);
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
handle_key_(evt);
|
handle_key_(evt);
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
std::thread::sleep(Duration::from_millis(20));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sim_rdev_rawkey(code: u32, down_or_up: bool) {
|
#[cfg(target_os = "macos")]
|
||||||
|
#[inline]
|
||||||
|
fn reset_input() {
|
||||||
|
unsafe {
|
||||||
|
let _lock = VIRTUAL_INPUT_MTX.lock();
|
||||||
|
VIRTUAL_INPUT = VirtualInput::new(
|
||||||
|
CGEventSourceStateID::Private,
|
||||||
|
CGEventTapLocation::AnnotatedSession,
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub fn reset_input_ondisconn() {
|
||||||
|
if !*IS_SERVER {
|
||||||
|
QUEUE.exec_async(reset_input);
|
||||||
|
} else {
|
||||||
|
reset_input();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sim_rdev_rawkey(code: u32, keydown: bool) {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
let rawkey = RawKey::ScanCode(code);
|
let rawkey = RawKey::ScanCode(code);
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
@ -697,22 +732,34 @@ fn sim_rdev_rawkey(code: u32, down_or_up: bool) {
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
let rawkey = RawKey::MacVirtualKeycode(code);
|
let rawkey = RawKey::MacVirtualKeycode(code);
|
||||||
|
|
||||||
rdev_key_down_or_up(RdevKey::RawKey(rawkey), down_or_up);
|
let event_type = if keydown {
|
||||||
|
EventType::KeyPress(RdevKey::RawKey(rawkey))
|
||||||
|
} else {
|
||||||
|
EventType::KeyRelease(RdevKey::RawKey(rawkey))
|
||||||
|
};
|
||||||
|
simulate_(&event_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rdev_key_down_or_up(key: RdevKey, down_or_up: bool) {
|
#[cfg(target_os = "macos")]
|
||||||
let event_type = match down_or_up {
|
#[inline]
|
||||||
true => EventType::KeyPress(key),
|
fn simulate_(event_type: &EventType) {
|
||||||
false => EventType::KeyRelease(key),
|
unsafe {
|
||||||
};
|
let _lock = VIRTUAL_INPUT_MTX.lock();
|
||||||
match simulate(&event_type) {
|
if let Some(virtual_input) = &VIRTUAL_INPUT {
|
||||||
|
let _ = virtual_input.simulate(&event_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
#[inline]
|
||||||
|
fn simulate_(event_type: &EventType) {
|
||||||
|
match rdev::simulate(&event_type) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(_simulate_error) => {
|
Err(_simulate_error) => {
|
||||||
log::error!("Could not send {:?}", &event_type);
|
log::error!("Could not send {:?}", &event_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
std::thread::sleep(Duration::from_millis(20));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_modifier_in_key_event(control_key: ControlKey, key_event: &KeyEvent) -> bool {
|
fn is_modifier_in_key_event(control_key: ControlKey, key_event: &KeyEvent) -> bool {
|
||||||
@ -780,13 +827,13 @@ fn click_capslock(en: &mut Enigo) {
|
|||||||
#[cfg(not(targe_os = "macos"))]
|
#[cfg(not(targe_os = "macos"))]
|
||||||
en.key_click(enigo::Key::CapsLock);
|
en.key_click(enigo::Key::CapsLock);
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
en.key_down(enigo::Key::CapsLock);
|
let _ = en.key_down(enigo::Key::CapsLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn click_numlock(en: &mut Enigo) {
|
fn click_numlock(_en: &mut Enigo) {
|
||||||
// without numlock in macos
|
// without numlock in macos
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
en.key_click(enigo::Key::NumLock);
|
_en.key_click(enigo::Key::NumLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sync_numlock_capslock_status(key_event: &KeyEvent) {
|
fn sync_numlock_capslock_status(key_event: &KeyEvent) {
|
||||||
@ -872,6 +919,7 @@ fn is_altgr_pressed() -> bool {
|
|||||||
.is_some()
|
.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
fn press_modifiers(en: &mut Enigo, key_event: &KeyEvent, to_release: &mut Vec<Key>) {
|
fn press_modifiers(en: &mut Enigo, key_event: &KeyEvent, to_release: &mut Vec<Key>) {
|
||||||
for ref ck in key_event.modifiers.iter() {
|
for ref ck in key_event.modifiers.iter() {
|
||||||
if let Some(key) = control_key_value_to_key(ck.value()) {
|
if let Some(key) = control_key_value_to_key(ck.value()) {
|
||||||
@ -889,14 +937,14 @@ fn press_modifiers(en: &mut Enigo, key_event: &KeyEvent, to_release: &mut Vec<Ke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sync_modifiers(en: &mut Enigo, key_event: &KeyEvent, to_release: &mut Vec<Key>) {
|
fn sync_modifiers(en: &mut Enigo, key_event: &KeyEvent, _to_release: &mut Vec<Key>) {
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
add_flags_to_enigo(en, key_event);
|
add_flags_to_enigo(en, key_event);
|
||||||
|
|
||||||
if key_event.down {
|
if key_event.down {
|
||||||
release_unpressed_modifiers(en, key_event);
|
release_unpressed_modifiers(en, key_event);
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
press_modifiers(en, key_event, to_release);
|
press_modifiers(en, key_event, _to_release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -944,6 +992,7 @@ fn process_seq(en: &mut Enigo, sequence: &str) {
|
|||||||
en.key_sequence(&sequence);
|
en.key_sequence(&sequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
fn release_keys(en: &mut Enigo, to_release: &Vec<Key>) {
|
fn release_keys(en: &mut Enigo, to_release: &Vec<Key>) {
|
||||||
for key in to_release {
|
for key in to_release {
|
||||||
en.key_up(key.clone());
|
en.key_up(key.clone());
|
||||||
|
12
src/tray.rs
12
src/tray.rs
@ -1,3 +1,4 @@
|
|||||||
|
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||||
use super::ui_interface::get_option_opt;
|
use super::ui_interface::get_option_opt;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use hbb_common::log::{debug, error, info};
|
use hbb_common::log::{debug, error, info};
|
||||||
@ -44,7 +45,7 @@ pub fn start_tray() {
|
|||||||
} else {
|
} else {
|
||||||
*control_flow = ControlFlow::Wait;
|
*control_flow = ControlFlow::Wait;
|
||||||
}
|
}
|
||||||
let stopped = is_service_stoped();
|
let stopped = is_service_stopped();
|
||||||
let state = if stopped { 2 } else { 1 };
|
let state = if stopped { 2 } else { 1 };
|
||||||
let old = *old_state.lock().unwrap();
|
let old = *old_state.lock().unwrap();
|
||||||
if state != old {
|
if state != old {
|
||||||
@ -101,7 +102,7 @@ pub fn start_tray() {
|
|||||||
}
|
}
|
||||||
if let Some(mut appindicator) = get_default_app_indicator() {
|
if let Some(mut appindicator) = get_default_app_indicator() {
|
||||||
let mut menu = gtk::Menu::new();
|
let mut menu = gtk::Menu::new();
|
||||||
let stoped = is_service_stoped();
|
let stoped = is_service_stopped();
|
||||||
// start/stop service
|
// start/stop service
|
||||||
let label = if stoped {
|
let label = if stoped {
|
||||||
crate::client::translate("Start Service".to_owned())
|
crate::client::translate("Start Service".to_owned())
|
||||||
@ -137,7 +138,7 @@ pub fn start_tray() {
|
|||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
fn change_service_state() {
|
fn change_service_state() {
|
||||||
if is_service_stoped() {
|
if is_service_stopped() {
|
||||||
debug!("Now try to start service");
|
debug!("Now try to start service");
|
||||||
crate::ipc::set_option("stop-service", "");
|
crate::ipc::set_option("stop-service", "");
|
||||||
} else {
|
} else {
|
||||||
@ -151,7 +152,7 @@ fn change_service_state() {
|
|||||||
fn update_tray_service_item(item: >k::MenuItem) {
|
fn update_tray_service_item(item: >k::MenuItem) {
|
||||||
use gtk::traits::GtkMenuItemExt;
|
use gtk::traits::GtkMenuItemExt;
|
||||||
|
|
||||||
if is_service_stoped() {
|
if is_service_stopped() {
|
||||||
item.set_label(&crate::client::translate("Start Service".to_owned()));
|
item.set_label(&crate::client::translate("Start Service".to_owned()));
|
||||||
} else {
|
} else {
|
||||||
item.set_label(&crate::client::translate("Stop service".to_owned()));
|
item.set_label(&crate::client::translate("Stop service".to_owned()));
|
||||||
@ -194,7 +195,8 @@ fn get_default_app_indicator() -> Option<AppIndicator> {
|
|||||||
/// Check if service is stoped.
|
/// Check if service is stoped.
|
||||||
/// Return [`true`] if service is stoped, [`false`] otherwise.
|
/// Return [`true`] if service is stoped, [`false`] otherwise.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_service_stoped() -> bool {
|
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||||
|
fn is_service_stopped() -> bool {
|
||||||
if let Some(v) = get_option_opt("stop-service") {
|
if let Some(v) = get_option_opt("stop-service") {
|
||||||
v == "Y"
|
v == "Y"
|
||||||
} else {
|
} else {
|
||||||
|
@ -158,6 +158,7 @@ pub fn get_license() -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||||
pub fn get_option_opt(key: &str) -> Option<String> {
|
pub fn get_option_opt(key: &str) -> Option<String> {
|
||||||
OPTIONS.lock().unwrap().get(key).map(|x| x.clone())
|
OPTIONS.lock().unwrap().get(key).map(|x| x.clone())
|
||||||
}
|
}
|
||||||
@ -199,11 +200,13 @@ pub fn set_local_flutter_config(key: String, value: String) {
|
|||||||
LocalConfig::set_flutter_config(key, value);
|
LocalConfig::set_flutter_config(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "flutter")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_kb_layout_type() -> String {
|
pub fn get_kb_layout_type() -> String {
|
||||||
LocalConfig::get_kb_layout_type()
|
LocalConfig::get_kb_layout_type()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "flutter")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_kb_layout_type(kb_layout_type: String) {
|
pub fn set_kb_layout_type(kb_layout_type: String) {
|
||||||
LocalConfig::set_kb_layout_type(kb_layout_type);
|
LocalConfig::set_kb_layout_type(kb_layout_type);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user