From 4cbbb5b64f62abae8e7df9c26efbcc1875e7cd44 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Nov 2023 11:24:03 +0800 Subject: [PATCH] feat, input source, win->win Signed-off-by: fufesou --- flutter/lib/consts.dart | 2 - .../lib/desktop/widgets/remote_toolbar.dart | 33 ++++---- flutter/lib/models/model.dart | 20 ++--- flutter/lib/models/state_model.dart | 9 ++- libs/enigo/Cargo.toml | 2 +- src/flutter_ffi.rs | 39 +++++----- src/keyboard.rs | 76 +++++++++---------- src/ui_session_interface.rs | 7 -- 8 files changed, 89 insertions(+), 99 deletions(-) diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index 00d9c5bd1..fe4052d99 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -69,8 +69,6 @@ const String kOptionOpenInTabs = "allow-open-in-tabs"; const String kOptionOpenInWindows = "allow-open-in-windows"; const String kOptionForceAlwaysRelay = "force-always-relay"; -const String kOptionInputSource = "input-source"; - const String kUniLinksPrefix = "rustdesk://"; const String kUrlActionClose = "close"; diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 36f11defc..bc28f637a 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1694,23 +1694,24 @@ class _KeyboardMenu extends StatelessWidget { final inputSource = stateGlobal.getInputSource(); final enabled = !ffi.ffiModel.viewOnly; return Column( - children: supportedInputSourceList.map((e) { - final d = e as List; - return RdoMenuButton( - child: Text(translate(d[1] as String)), - value: d[0] as String, - groupValue: inputSource, - onChanged: enabled - ? (e) { - if (e != null) { - stateGlobal.setInputSource(e); - } - } - : null, - ffi: ffi, - ); - }).toList(), + children: supportedInputSourceList.map((e) { + final d = e as List; + return RdoMenuButton( + child: Text(translate(d[1] as String)), + value: d[0] as String, + groupValue: inputSource, + onChanged: enabled + ? (v) async { + if (v != null) { + await stateGlobal.setInputSource(ffi.sessionId, v); + await ffi.ffiModel.checkDesktopKeyboardMode(); + } + } + : null, + ffi: ffi, ); + }).toList(), + ); } viewMode() { diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 78d1a273a..f01a97820 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -735,16 +735,9 @@ class FfiModel with ChangeNotifier { } checkDesktopKeyboardMode() async { - final curMode = await bind.sessionGetKeyboardMode(sessionId: sessionId); - if (curMode != null) { - if (bind.sessionIsKeyboardModeSupported( - sessionId: sessionId, mode: curMode)) { - return; - } - } - - // If current keyboard mode is not supported, change to another one. if (isInputSourceFlutter) { + // Local side, flutter keyboard input source + // Currently only map mode is supported, legacy mode is used for compatibility. for (final mode in [kKeyMapMode, kKeyLegacyMode]) { if (bind.sessionIsKeyboardModeSupported( sessionId: sessionId, mode: mode)) { @@ -753,6 +746,15 @@ class FfiModel with ChangeNotifier { } } } else { + final curMode = await bind.sessionGetKeyboardMode(sessionId: sessionId); + if (curMode != null) { + if (bind.sessionIsKeyboardModeSupported( + sessionId: sessionId, mode: curMode)) { + return; + } + } + + // If current keyboard mode is not supported, change to another one. for (final mode in [kKeyMapMode, kKeyTranslateMode, kKeyLegacyMode]) { if (bind.sessionIsKeyboardModeSupported( sessionId: sessionId, mode: mode)) { diff --git a/flutter/lib/models/state_model.dart b/flutter/lib/models/state_model.dart index 4814bfcfe..0d95b560b 100644 --- a/flutter/lib/models/state_model.dart +++ b/flutter/lib/models/state_model.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_hbb/common.dart'; import 'package:get/get.dart'; import '../consts.dart'; @@ -98,14 +99,14 @@ class StateGlobal { String getInputSource({bool force = false}) { if (force || _inputSource.isEmpty) { - _inputSource = bind.mainGetLocalOption(key: kOptionInputSource); + _inputSource = bind.mainGetInputSource(); } return _inputSource; } - void setInputSource(String v) async { - await bind.mainSetLocalOption(key: kOptionInputSource, value: v); - _inputSource = bind.mainGetLocalOption(key: kOptionInputSource); + setInputSource(SessionID sessionId, String v) async { + await bind.mainSetInputSource(sessionId: sessionId, value: v); + _inputSource = bind.mainGetInputSource(); } StateGlobal._(); diff --git a/libs/enigo/Cargo.toml b/libs/enigo/Cargo.toml index 1f67ae36b..f020411ca 100644 --- a/libs/enigo/Cargo.toml +++ b/libs/enigo/Cargo.toml @@ -22,7 +22,7 @@ appveyor = { repository = "pythoneer/enigo-85xiy" } serde = { version = "1.0", optional = true } serde_derive = { version = "1.0", optional = true } log = "0.4" -rdev = { git = "https://github.com/fufesou/rdev" } +rdev = { path = "../../../rdev" } tfc = { git = "https://github.com/fufesou/The-Fat-Controller" } hbb_common = { path = "../hbb_common" } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index ea2894e57..7f4f37ecf 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -6,6 +6,7 @@ use crate::{ common::make_fd_to_json, flutter::{self, session_add, session_add_existed, session_start_, sessions}, input::*, + keyboard::input_source::{change_input_source, get_cur_session_input_source}, ui_interface::{self, *}, }; use flutter_rust_bridge::{StreamSink, SyncReturn}; @@ -468,16 +469,13 @@ pub fn session_handle_flutter_key_event( // As rust is multi-thread, it is possible that enter() is called before leave(). // This will cause the keyboard input to take no effect. pub fn session_enter_or_leave(_session_id: SessionID, _enter: bool) -> SyncReturn<()> { - #[cfg(not(any(target_os = "android", target_os = "ios")))] - if crate::keyboard::input_source::is_cur_input_source_rdev() { - if let Some(session) = sessions::get_session_by_session_id(&_session_id) { - let keyboard_mode = session.get_keyboard_mode(); - if _enter { - set_cur_session_id_(_session_id, &keyboard_mode); - session.enter(keyboard_mode); - } else { - session.leave(keyboard_mode); - } + if let Some(session) = sessions::get_session_by_session_id(&_session_id) { + let keyboard_mode = session.get_keyboard_mode(); + if _enter { + set_cur_session_id_(_session_id, &keyboard_mode); + session.enter(keyboard_mode); + } else { + session.leave(keyboard_mode); } } SyncReturn(()) @@ -848,12 +846,7 @@ pub fn main_post_request(url: String, body: String, header: String) { } pub fn main_get_local_option(key: String) -> SyncReturn { - let v = if key == crate::keyboard::input_source::CONFIG_OPTION_INPUT_SOURCE { - crate::keyboard::input_source::get_cur_session_input_source() - } else { - get_local_option(key) - }; - SyncReturn(v) + SyncReturn(get_local_option(key)) } pub fn main_get_env(key: String) -> SyncReturn { @@ -861,11 +854,15 @@ pub fn main_get_env(key: String) -> SyncReturn { } pub fn main_set_local_option(key: String, value: String) { - if key == crate::keyboard::input_source::CONFIG_OPTION_INPUT_SOURCE { - crate::keyboard::input_source::change_input_source(&value); - } else { - set_local_option(key, value); - } + set_local_option(key, value) +} + +pub fn main_get_input_source() -> SyncReturn { + SyncReturn(get_cur_session_input_source()) +} + +pub fn main_set_input_source(session_id: SessionID, value: String) { + change_input_source(session_id, value); } pub fn main_get_my_id() -> String { diff --git a/src/keyboard.rs b/src/keyboard.rs index 3d4c1550d..b98afacc0 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -34,6 +34,9 @@ const OS_LOWER_ANDROID: &str = "android"; #[cfg(any(target_os = "windows", target_os = "macos"))] static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false); +#[cfg(not(any(target_os = "android", target_os = "ios")))] +static IS_RDEV_ENABLED: AtomicBool = AtomicBool::new(false); + lazy_static::lazy_static! { static ref TO_RELEASE: Arc>> = Arc::new(Mutex::new(HashMap::new())); static ref MODIFIERS_STATE: Mutex> = { @@ -55,6 +58,9 @@ pub mod client { #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn change_grab_status(state: GrabState, keyboard_mode: &str) { + if !IS_RDEV_ENABLED.load(Ordering::SeqCst) { + return; + } match state { GrabState::Ready => {} GrabState::Run => { @@ -78,10 +84,7 @@ pub mod client { #[cfg(target_os = "linux")] rdev::disable_grab(); } - GrabState::Exit => { - #[cfg(target_os = "linux")] - rdev::exit_grab_listen(); - } + GrabState::Exit => {} } } @@ -315,6 +318,8 @@ pub fn start_grab_loop() { }; } +// #[allow(dead_code)] is ok here. No need to stop grabbing loop. +#[allow(dead_code)] pub fn stop_grab_loop() -> Result<(), rdev::GrabError> { #[cfg(any(target_os = "windows", target_os = "macos"))] rdev::exit_grab()?; @@ -1076,8 +1081,9 @@ pub fn keycode_to_rdev_key(keycode: u32) -> Key { #[cfg(feature = "flutter")] #[cfg(not(any(target_os = "android", target_os = "ios")))] pub mod input_source { + use hbb_common::SessionID; + use crate::ui_interface::{get_local_option, set_local_option}; - use hbb_common::log; pub const CONFIG_OPTION_INPUT_SOURCE: &str = "input-source"; // rdev grab mode @@ -1087,34 +1093,32 @@ pub mod input_source { pub const CONFIG_INPUT_SOURCE_2: &str = "Input source 2"; pub const CONFIG_INPUT_SOURCE_2_TIP: &str = "input_source_2_tip"; - #[cfg(not(target_os = "macos"))] pub const CONFIG_INPUT_SOURCE_DEFAULT: &str = CONFIG_INPUT_SOURCE_1; - #[cfg(target_os = "macos")] - pub const CONFIG_INPUT_SOURCE_DEFAULT: &str = CONFIG_INPUT_SOURCE_2; pub fn init_input_source() { let cur_input_source = get_cur_session_input_source(); - if cur_input_source == CONFIG_INPUT_SOURCE_1 { - #[cfg(target_os = "macos")] - if !crate::platform::macos::is_can_input_monitoring(false) { - log::error!("init_input_source, is_can_input_monitoring() false"); - set_local_option( - CONFIG_OPTION_INPUT_SOURCE.to_string(), - CONFIG_INPUT_SOURCE_2.to_string(), - ); - return; - } - #[cfg(target_os = "linux")] - if !crate::platform::linux::is_x11() { - // If switching from X11 to Wayland, the grab loop will not be started. - // Do not change the config here. - return; - } - super::start_grab_loop(); + #[cfg(target_os = "linux")] + if !crate::platform::linux::is_x11() { + // If switching from X11 to Wayland, the grab loop will not be started. + // Do not change the config here. + return; } + #[cfg(target_os = "macos")] + if !crate::platform::macos::is_can_input_monitoring(false) { + log::error!("init_input_source, is_can_input_monitoring() false"); + set_local_option( + CONFIG_OPTION_INPUT_SOURCE.to_string(), + CONFIG_INPUT_SOURCE_2.to_string(), + ); + return; + } + if cur_input_source == CONFIG_INPUT_SOURCE_1 { + super::IS_RDEV_ENABLED.store(true, super::Ordering::SeqCst); + } + super::start_grab_loop(); } - pub fn change_input_source(input_source: &str) { + pub fn change_input_source(session_id: SessionID, input_source: String) { let cur_input_source = get_cur_session_input_source(); if cur_input_source == input_source { return; @@ -1125,17 +1129,16 @@ pub mod input_source { log::error!("change_input_source, is_can_input_monitoring() false"); return; } + // It is ok to start grab loop multiple times. super::start_grab_loop(); + super::IS_RDEV_ENABLED.store(true, super::Ordering::SeqCst); + crate::flutter_ffi::session_enter_or_leave(session_id, true); } else if input_source == CONFIG_INPUT_SOURCE_2 { - if let Err(e) = super::stop_grab_loop() { - log::error!("change_input_source, stop_grab_loop error: {:?}", e); - return; - } + // No need to stop grab loop. + crate::flutter_ffi::session_enter_or_leave(session_id, false); + super::IS_RDEV_ENABLED.store(false, super::Ordering::SeqCst); } - set_local_option( - CONFIG_OPTION_INPUT_SOURCE.to_string(), - input_source.to_string(), - ); + set_local_option(CONFIG_OPTION_INPUT_SOURCE.to_string(), input_source); } #[inline] @@ -1152,11 +1155,6 @@ pub mod input_source { } } - #[inline] - pub fn is_cur_input_source_rdev() -> bool { - get_cur_session_input_source() == CONFIG_INPUT_SOURCE_1 - } - #[inline] pub fn get_supported_input_source() -> Vec<(String, String)> { #[cfg(target_os = "linux")] diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index bc44161ab..fd9802986 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -5,8 +5,6 @@ use crate::{ use async_trait::async_trait; use bytes::Bytes; use rdev::{Event, EventType::*, KeyCode}; -#[cfg(not(any(target_os = "android", target_os = "ios")))] -use std::sync::atomic::{AtomicBool, Ordering}; use std::{ collections::HashMap, ops::{Deref, DerefMut}, @@ -43,9 +41,6 @@ use crate::common::GrabState; use crate::keyboard; use crate::{client::Data, client::Interface}; -#[cfg(not(any(target_os = "android", target_os = "ios")))] -pub static IS_IN: AtomicBool = AtomicBool::new(false); - const CHANGE_RESOLUTION_VALID_TIMEOUT_SECS: u64 = 15; #[derive(Clone, Default)] @@ -725,13 +720,11 @@ impl Session { #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn enter(&self, keyboard_mode: String) { - IS_IN.store(true, Ordering::SeqCst); keyboard::client::change_grab_status(GrabState::Run, &keyboard_mode); } #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn leave(&self, keyboard_mode: String) { - IS_IN.store(false, Ordering::SeqCst); keyboard::client::change_grab_status(GrabState::Wait, &keyboard_mode); }