From f11104fcb59c5ae79aabd26975d90731d4a9f197 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 28 Nov 2023 23:57:48 +0800 Subject: [PATCH 01/12] tmp commit Signed-off-by: fufesou --- flutter/lib/common.dart | 3 +- flutter/lib/consts.dart | 3 + .../lib/desktop/pages/remote_tab_page.dart | 2 +- .../desktop/screen/desktop_remote_screen.dart | 5 +- .../lib/desktop/widgets/remote_toolbar.dart | 37 ++++- flutter/lib/models/input_model.dart | 10 +- flutter/lib/models/model.dart | 3 +- flutter/lib/models/native_model.dart | 4 +- flutter/lib/models/state_model.dart | 16 ++- src/flutter_ffi.rs | 57 +++++--- src/keyboard.rs | 126 ++++++++++++++++-- src/lang/ar.rs | 2 + src/lang/ca.rs | 2 + src/lang/cn.rs | 2 + src/lang/cs.rs | 2 + src/lang/da.rs | 2 + src/lang/de.rs | 2 + src/lang/el.rs | 2 + src/lang/en.rs | 2 + src/lang/eo.rs | 2 + src/lang/es.rs | 2 + src/lang/fa.rs | 2 + src/lang/fr.rs | 2 + src/lang/hu.rs | 2 + src/lang/id.rs | 2 + src/lang/it.rs | 2 + src/lang/ja.rs | 2 + src/lang/ko.rs | 2 + src/lang/kz.rs | 2 + src/lang/lt.rs | 2 + src/lang/lv.rs | 2 + src/lang/nb.rs | 2 + src/lang/nl.rs | 2 + src/lang/pl.rs | 2 + src/lang/pt_PT.rs | 2 + src/lang/ptbr.rs | 2 + src/lang/ro.rs | 2 + src/lang/ru.rs | 2 + src/lang/sk.rs | 2 + src/lang/sl.rs | 2 + src/lang/sq.rs | 2 + src/lang/sr.rs | 2 + src/lang/sv.rs | 2 + src/lang/template.rs | 2 + src/lang/th.rs | 2 + src/lang/tr.rs | 2 + src/lang/tw.rs | 2 + src/lang/ua.rs | 2 + src/lang/vn.rs | 2 + 49 files changed, 296 insertions(+), 46 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index bc5b19c97..53e304596 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -959,7 +959,6 @@ class CustomAlertDialog extends StatelessWidget { void msgBox(SessionID sessionId, String type, String title, String text, String link, OverlayDialogManager dialogManager, {bool? hasCancel, ReconnectHandle? reconnect, int? reconnectTimeout}) { - dialogManager.dismissAll(); List buttons = []; bool hasOk = false; @@ -2766,6 +2765,8 @@ parseParamScreenRect(Map params) { return screenRect; } +get isInputSourceFlutter => stateGlobal.getInputSource() == "Input source 2"; + class _ReconnectCountDownButton extends StatefulWidget { _ReconnectCountDownButton({ Key? key, diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index 4236e96d4..00d9c5bd1 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -45,6 +45,7 @@ const String kAppTypeDesktopPortForward = "port forward"; const String kWindowMainWindowOnTop = "main_window_on_top"; const String kWindowGetWindowInfo = "get_window_info"; const String kWindowGetScreenList = "get_screen_list"; +// This method is not used, maybe it can be removed. const String kWindowDisableGrabKeyboard = "disable_grab_keyboard"; const String kWindowActionRebuild = "rebuild"; const String kWindowEventHide = "hide"; @@ -68,6 +69,8 @@ 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/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index c97a0ef3f..dee2d2e29 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -156,7 +156,7 @@ class _ConnectionTabPageState extends State { ), )); } else if (call.method == kWindowDisableGrabKeyboard) { - stateGlobal.grabKeyboard = false; + // ??? } else if (call.method == "onDestroy") { tabController.clear(); } else if (call.method == kWindowActionRebuild) { diff --git a/flutter/lib/desktop/screen/desktop_remote_screen.dart b/flutter/lib/desktop/screen/desktop_remote_screen.dart index 4427cf8a3..255f1a5b2 100644 --- a/flutter/lib/desktop/screen/desktop_remote_screen.dart +++ b/flutter/lib/desktop/screen/desktop_remote_screen.dart @@ -12,9 +12,8 @@ class DesktopRemoteScreen extends StatelessWidget { final Map params; DesktopRemoteScreen({Key? key, required this.params}) : super(key: key) { - if (!bind.mainStartGrabKeyboard()) { - stateGlobal.grabKeyboard = true; - } + bind.mainInitInputSource(); + stateGlobal.getInputSource(force: true); } @override diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 271879428..36f11defc 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1585,7 +1585,7 @@ class _KeyboardMenu extends StatelessWidget { // If use flutter to grab keys, we can only use one mode. // Map mode and Legacy mode, at least one of them is supported. String? modeOnly; - if (stateGlobal.grabKeyboard) { + if (isInputSourceFlutter) { if (bind.sessionIsKeyboardModeSupported( sessionId: ffi.sessionId, mode: kKeyMapMode)) { modeOnly = kKeyMapMode; @@ -1604,6 +1604,8 @@ class _KeyboardMenu extends StatelessWidget { keyboardMode(modeOnly), localKeyboardType(), Divider(), + inputSource(), + Divider(), viewMode(), Divider(), reverseMouseWheel(), @@ -1678,6 +1680,39 @@ class _KeyboardMenu extends StatelessWidget { ); } + inputSource() { + final supportedInputSource = bind.mainSupportedInputSource(); + if (supportedInputSource.isEmpty) return Offstage(); + late final List supportedInputSourceList; + try { + supportedInputSourceList = jsonDecode(supportedInputSource); + } catch (e) { + debugPrint('Failed to decode $supportedInputSource, $e'); + return; + } + if (supportedInputSourceList.length < 2) return Offstage(); + 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(), + ); + } + viewMode() { final ffiModel = ffi.ffiModel; final enabled = versionCmp(pi.version, '1.2.0') >= 0 && ffiModel.keyboard; diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index 29c13f625..156a1201b 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -13,7 +13,6 @@ import '../../models/model.dart'; import '../../models/platform_model.dart'; import '../common.dart'; import '../consts.dart'; -import './state_model.dart'; /// Mouse button enum. enum MouseButtons { left, right, wheel } @@ -91,7 +90,7 @@ class InputModel { } KeyEventResult handleRawKeyEvent(FocusNode data, RawKeyEvent e) { - if (isDesktop && !stateGlobal.grabKeyboard) { + if (isDesktop && !isInputSourceFlutter) { return KeyEventResult.handled; } @@ -325,7 +324,9 @@ class InputModel { resetModifiers(); } _flingTimer?.cancel(); - bind.sessionEnterOrLeave(sessionId: sessionId, enter: enter); + if (!isInputSourceFlutter) { + bind.sessionEnterOrLeave(sessionId: sessionId, enter: enter); + } } /// Send mouse movement event with distance in [x] and [y]. @@ -396,7 +397,8 @@ class InputModel { } if (x != 0 || y != 0) { if (peerPlatform == kPeerPlatformAndroid) { - handlePointerEvent('touch', 'pan_update', Offset(x.toDouble(), y.toDouble())); + handlePointerEvent( + 'touch', 'pan_update', Offset(x.toDouble(), y.toDouble())); } else { bind.sessionSendMouse( sessionId: sessionId, diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 2de85aa11..78d1a273a 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -744,8 +744,7 @@ class FfiModel with ChangeNotifier { } // If current keyboard mode is not supported, change to another one. - - if (stateGlobal.grabKeyboard) { + if (isInputSourceFlutter) { for (final mode in [kKeyMapMode, kKeyLegacyMode]) { if (bind.sessionIsKeyboardModeSupported( sessionId: sessionId, mode: mode)) { diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index ef74a17a2..6778cdbb9 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -153,10 +153,10 @@ class PlatformFFI { AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; name = '${androidInfo.brand}-${androidInfo.model}'; id = androidInfo.id.hashCode.toString(); - androidVersion = androidInfo.version.sdkInt ?? 0; + androidVersion = androidInfo.version.sdkInt; } else if (Platform.isIOS) { IosDeviceInfo iosInfo = await deviceInfo.iosInfo; - name = iosInfo.utsname.machine ?? ''; + name = iosInfo.utsname.machine; id = iosInfo.identifierForVendor.hashCode.toString(); } else if (Platform.isLinux) { LinuxDeviceInfo linuxInfo = await deviceInfo.linuxInfo; diff --git a/flutter/lib/models/state_model.dart b/flutter/lib/models/state_model.dart index c80c3551e..4814bfcfe 100644 --- a/flutter/lib/models/state_model.dart +++ b/flutter/lib/models/state_model.dart @@ -5,12 +5,12 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../consts.dart'; +import './platform_model.dart'; enum SvcStatus { notReady, connecting, ready } class StateGlobal { int _windowId = -1; - bool grabKeyboard = false; final RxBool _fullscreen = false.obs; bool _isMinimized = false; final RxBool isMaximized = false.obs; @@ -22,6 +22,8 @@ class StateGlobal { // Only used for macOS bool? closeOnFullscreen; + String _inputSource = ''; + // Use for desktop -> remote toolbar -> resolution final Map> _lastResolutionGroupValues = {}; @@ -94,6 +96,18 @@ class StateGlobal { } } + String getInputSource({bool force = false}) { + if (force || _inputSource.isEmpty) { + _inputSource = bind.mainGetLocalOption(key: kOptionInputSource); + } + return _inputSource; + } + + void setInputSource(String v) async { + await bind.mainSetLocalOption(key: kOptionInputSource, value: v); + _inputSource = bind.mainGetLocalOption(key: kOptionInputSource); + } + StateGlobal._(); static final StateGlobal instance = StateGlobal._(); diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 106bc64e8..ea2894e57 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -469,13 +469,15 @@ pub fn session_handle_flutter_key_event( // 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 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 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); + } } } SyncReturn(()) @@ -846,7 +848,12 @@ pub fn main_post_request(url: String, body: String, header: String) { } pub fn main_get_local_option(key: String) -> SyncReturn { - SyncReturn(get_local_option(key)) + 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) } pub fn main_get_env(key: String) -> SyncReturn { @@ -854,7 +861,11 @@ pub fn main_get_env(key: String) -> SyncReturn { } pub fn main_set_local_option(key: String, value: String) { - set_local_option(key, value) + if key == crate::keyboard::input_source::CONFIG_OPTION_INPUT_SOURCE { + crate::keyboard::input_source::change_input_source(&value); + } else { + set_local_option(key, value); + } } pub fn main_get_my_id() -> String { @@ -1593,16 +1604,10 @@ pub fn main_is_installed() -> SyncReturn { SyncReturn(is_installed()) } -pub fn main_start_grab_keyboard() -> SyncReturn { - #[cfg(target_os = "linux")] - if !crate::platform::linux::is_x11() { - return SyncReturn(false); - } - crate::keyboard::client::start_grab_loop(); - if !is_can_input_monitoring(false) { - return SyncReturn(false); - } - SyncReturn(true) +pub fn main_init_input_source() -> SyncReturn<()> { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + crate::keyboard::input_source::init_input_source(); + SyncReturn(()) } pub fn main_is_installed_lower_version() -> SyncReturn { @@ -1979,6 +1984,20 @@ pub fn main_supported_privacy_mode_impls() -> SyncReturn { ) } +pub fn main_supported_input_source() -> SyncReturn { + #[cfg(any(target_os = "android", target_os = "ios"))] + { + SyncReturn("".to_owned()) + } + #[cfg(not(any(target_os = "android", target_os = "ios")))] + { + SyncReturn( + serde_json::to_string(&crate::keyboard::input_source::get_supported_input_source()) + .unwrap_or_default(), + ) + } +} + #[cfg(target_os = "android")] pub mod server_side { use hbb_common::{config, log}; diff --git a/src/keyboard.rs b/src/keyboard.rs index be2827c3d..3d4c1550d 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -52,18 +52,6 @@ lazy_static::lazy_static! { pub mod client { use super::*; - lazy_static::lazy_static! { - static ref IS_GRAB_STARTED: Arc> = Arc::new(Mutex::new(false)); - } - - pub fn start_grab_loop() { - let mut lock = IS_GRAB_STARTED.lock().unwrap(); - if *lock { - return; - } - super::start_grab_loop(); - *lock = true; - } #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn change_grab_status(state: GrabState, keyboard_mode: &str) { @@ -327,6 +315,14 @@ pub fn start_grab_loop() { }; } +pub fn stop_grab_loop() -> Result<(), rdev::GrabError> { + #[cfg(any(target_os = "windows", target_os = "macos"))] + rdev::exit_grab()?; + #[cfg(target_os = "linux")] + rdev::exit_grab_listen()?; + Ok(()) +} + pub fn is_long_press(event: &Event) -> bool { let keys = MODIFIERS_STATE.lock().unwrap(); match event.event_type { @@ -1076,3 +1072,109 @@ pub fn keycode_to_rdev_key(keycode: u32) -> Key { #[cfg(target_os = "macos")] return rdev::macos_key_from_code(keycode.try_into().unwrap_or_default()); } + +#[cfg(feature = "flutter")] +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub mod input_source { + 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 + pub const CONFIG_INPUT_SOURCE_1: &str = "Input source 1"; + pub const CONFIG_INPUT_SOURCE_1_TIP: &str = "input_source_1_tip"; + // flutter grab mode + 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(); + } + } + + pub fn change_input_source(input_source: &str) { + let cur_input_source = get_cur_session_input_source(); + if cur_input_source == input_source { + return; + } + if input_source == CONFIG_INPUT_SOURCE_1 { + #[cfg(target_os = "macos")] + if !crate::platform::macos::is_can_input_monitoring(false) { + log::error!("change_input_source, is_can_input_monitoring() false"); + return; + } + super::start_grab_loop(); + } 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; + } + } + set_local_option( + CONFIG_OPTION_INPUT_SOURCE.to_string(), + input_source.to_string(), + ); + } + + #[inline] + pub fn get_cur_session_input_source() -> String { + #[cfg(target_os = "linux")] + if !crate::platform::linux::is_x11() { + return CONFIG_INPUT_SOURCE_2.to_string(); + } + let input_source = get_local_option(CONFIG_OPTION_INPUT_SOURCE.to_string()); + if input_source.is_empty() { + CONFIG_INPUT_SOURCE_DEFAULT.to_string() + } else { + 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")] + if !crate::platform::linux::is_x11() { + return vec![( + CONFIG_INPUT_SOURCE_2.to_string(), + CONFIG_INPUT_SOURCE_2_TIP.to_string(), + )]; + } + vec![ + ( + CONFIG_INPUT_SOURCE_1.to_string(), + CONFIG_INPUT_SOURCE_1_TIP.to_string(), + ), + ( + CONFIG_INPUT_SOURCE_2.to_string(), + CONFIG_INPUT_SOURCE_2_TIP.to_string(), + ), + ] + } +} diff --git a/src/lang/ar.rs b/src/lang/ar.rs index bdab9e466..d281facee 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 36aba208c..eafc06864 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 86a01bd6b..66e65db72 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "进入隐私模式"), ("Exit privacy mode", "退出隐私模式"), ("idd_not_support_under_win10_2004_tip", "不支持 Indirect display driver 。需要 windows 10, version 2004 及更高的版本。"), + ("input_source_1_tip", "输入源 1"), + ("input_source_2_tip", "输入源 2"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 616071e88..6e185ce83 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Vstup do režimu soukromí"), ("Exit privacy mode", "Ukončit režim soukromí"), ("idd_not_support_under_win10_2004_tip", "Ovladač nepřímého zobrazení není podporován. Je vyžadován systém Windows 10, verze 2004 nebo novější."), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index ed926f0df..739e12690 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 1473119c4..4af92444d 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Datenschutzmodus aktivieren"), ("Exit privacy mode", "Datenschutzmodus beenden"), ("idd_not_support_under_win10_2004_tip", "Indirekter Grafiktreiber wird nicht unterstützt. Windows 10, Version 2004 oder neuer ist erforderlich."), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 8bb3a3d79..a18812b98 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 101bc63a3..365076018 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -206,5 +206,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("privacy_mode_impl_mag_tip", "Mode 1"), ("privacy_mode_impl_virtual_display_tip", "Mode 2"), ("idd_not_support_under_win10_2004_tip", "Indirect display driver is not supported. Windows 10, version 2004 or newer is required."), + ("input_source_1_tip", "Input source 1"), + ("input_source_2_tip", "Input source 2"), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 73eb77aa5..5efda9cf7 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 40a23f317..3b8b8ab46 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Entrar al modo privado"), ("Exit privacy mode", "Salir del modo privado"), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index af0b8e944..4d1ad99d5 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index ea90ff654..b0d4782e5 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index f179da1df..79f0505bf 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 780b24bbd..e3bc5d4ba 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Masuk mode privasi"), ("Exit privacy mode", "Keluar mode privasi"), ("idd_not_support_under_win10_2004_tip", "Driver grafis yang Anda gunakan tidak kompatibel dengan versi Windows Anda dan memerlukan Windows 10 versi 2004 atau yang lebih baru"), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index e148ece66..d049d79ef 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Entra in modalità privacy"), ("Exit privacy mode", "Esci dalla modalità privacy"), ("idd_not_support_under_win10_2004_tip", "Il driver video indiretto non è supportato. È richiesto Windows 10, versione 2004 o successiva."), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 0ff52484c..2b646c8ac 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 36d8338b9..2455b4099 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "개인정보 보호 모드 사용"), ("Exit privacy mode", "개인정보 보호 모드 종료"), ("idd_not_support_under_win10_2004_tip", "간접 디스플레이 드라이버는 지원되지 않습니다. Windows 10 버전 2004 이상이 필요합니다."), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 7a552e96a..a485d78bb 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index b4990d9c6..78716e3a2 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 53b714723..a13ad8520 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Ieiet privātuma režīmā"), ("Exit privacy mode", "Iziet no privātuma režīma"), ("idd_not_support_under_win10_2004_tip", "Netiešā displeja draiveris netiek atbalstīts. Nepieciešama operētājsistēma Windows 10, versija 2004 vai jaunāka."), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index 4f8945721..b3f1360cf 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 7b66fb3f3..9267d9be0 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 3aa80786e..763a7ed19 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Wejdź w tryb prywatności"), ("Exit privacy mode", "Wyjdź z trybu prywatności"), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 6b89eaa8c..79b3374b6 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index c5c4f2768..5f477f853 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 5e806e426..30434268b 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 98994795d..b4e12f93d 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Включить режим конфиденциальности"), ("Exit privacy mode", "Отключить режим конфиденциальности"), ("idd_not_support_under_win10_2004_tip", "Драйвер непрямого отображения не поддерживается. Требуется Windows 10 версии 2004 или новее."), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 46848da73..9173f7467 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Vstup do režimu súkromia"), ("Exit privacy mode", "Ukončiť režim súkromia"), ("idd_not_support_under_win10_2004_tip", "Ovládač nepriameho zobrazenia nie je podporovaný. Vyžaduje sa systém Windows 10, verzia 2004 alebo novšia."), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index be9774be8..9a2b39366 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index ff8851b94..499c2fd09 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index ae4c4e364..8ded31ae3 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index cfefe48a1..56e64e8fb 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index e3f8f6f38..2431d0130 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index ea3a72af2..971745e56 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index e14be47db..c5b2f4816 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 57b7d7b9f..c1072f373 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 6fa172311..ffe887436 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Увійти в режим конфіденційності"), ("Exit privacy mode", "Вийти з режиму конфіденційності"), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 84b0bf752..4ef8825fa 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -573,5 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), + ("input_source_1_tip", ""), + ("input_source_2_tip", ""), ].iter().cloned().collect(); } From 4cbbb5b64f62abae8e7df9c26efbcc1875e7cd44 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Nov 2023 11:24:03 +0800 Subject: [PATCH 02/12] 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); } From 7a2590d1f901cb0e50efffb82aac8b2461fbbf43 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Nov 2023 01:42:44 -0500 Subject: [PATCH 03/12] fix, compilation Signed-off-by: fufesou --- .../lib/desktop/widgets/remote_toolbar.dart | 37 +++++++++---------- src/keyboard.rs | 6 ++- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index bc28f637a..e38aa9546 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1603,7 +1603,6 @@ class _KeyboardMenu extends StatelessWidget { menuChildren: [ keyboardMode(modeOnly), localKeyboardType(), - Divider(), inputSource(), Divider(), viewMode(), @@ -1693,25 +1692,25 @@ class _KeyboardMenu extends StatelessWidget { if (supportedInputSourceList.length < 2) return Offstage(); 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 - ? (v) async { - if (v != null) { - await stateGlobal.setInputSource(ffi.sessionId, v); - await ffi.ffiModel.checkDesktopKeyboardMode(); - } + final children = [Divider()]; + children.addAll(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(), - ); + } + : null, + ffi: ffi, + ); + })); + return Column(children: children); } viewMode() { diff --git a/src/keyboard.rs b/src/keyboard.rs index b98afacc0..db5cc132d 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -12,7 +12,7 @@ use hbb_common::message_proto::*; #[cfg(any(target_os = "windows", target_os = "macos"))] use rdev::KeyCode; use rdev::{Event, EventType, Key}; -#[cfg(any(target_os = "windows", target_os = "macos"))] +#[cfg(not(any(target_os = "android", target_os = "ios")))] use std::sync::atomic::{AtomicBool, Ordering}; use std::{ collections::HashMap, @@ -324,7 +324,7 @@ pub fn stop_grab_loop() -> Result<(), rdev::GrabError> { #[cfg(any(target_os = "windows", target_os = "macos"))] rdev::exit_grab()?; #[cfg(target_os = "linux")] - rdev::exit_grab_listen()?; + rdev::exit_grab_listen(); Ok(()) } @@ -1082,6 +1082,8 @@ pub fn keycode_to_rdev_key(keycode: u32) -> Key { #[cfg(not(any(target_os = "android", target_os = "ios")))] pub mod input_source { use hbb_common::SessionID; + #[cfg(target_os = "macos")] + use hbb_common::log; use crate::ui_interface::{get_local_option, set_local_option}; From 6fc4253d466857ddbd8d9d87950f98fda7df24ce Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Nov 2023 02:44:21 -0500 Subject: [PATCH 04/12] debug, linux Signed-off-by: fufesou --- src/keyboard.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index db5cc132d..7bd64121e 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1098,7 +1098,6 @@ pub mod input_source { pub const CONFIG_INPUT_SOURCE_DEFAULT: &str = CONFIG_INPUT_SOURCE_1; pub fn init_input_source() { - let cur_input_source = get_cur_session_input_source(); #[cfg(target_os = "linux")] if !crate::platform::linux::is_x11() { // If switching from X11 to Wayland, the grab loop will not be started. @@ -1114,6 +1113,7 @@ pub mod input_source { ); return; } + let cur_input_source = get_cur_session_input_source(); if cur_input_source == CONFIG_INPUT_SOURCE_1 { super::IS_RDEV_ENABLED.store(true, super::Ordering::SeqCst); } From 6c21529594d6a5e3c777918f6a4ef197d77b245a Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Nov 2023 03:12:53 -0500 Subject: [PATCH 05/12] update rdev Signed-off-by: fufesou --- Cargo.lock | 30 +++--------------------------- Cargo.toml | 2 +- libs/enigo/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 888115066..4c896da8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1806,7 +1806,7 @@ dependencies = [ "log", "objc", "pkg-config", - "rdev 0.5.0-2 (git+https://github.com/fufesou/rdev)", + "rdev", "serde 1.0.190", "serde_derive", "tfc", @@ -4894,31 +4894,7 @@ dependencies = [ [[package]] name = "rdev" version = "0.5.0-2" -source = "git+https://github.com/fufesou/rdev?branch=master#339b2a334ba273afebb7e27fb76984e620fc76e5" -dependencies = [ - "cocoa", - "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation-sys 0.8.4", - "core-graphics 0.22.3", - "dispatch", - "enum-map", - "epoll", - "inotify", - "lazy_static", - "libc", - "log", - "mio", - "strum 0.24.1", - "strum_macros 0.24.3", - "widestring", - "winapi 0.3.9", - "x11 2.21.0", -] - -[[package]] -name = "rdev" -version = "0.5.0-2" -source = "git+https://github.com/fufesou/rdev#339b2a334ba273afebb7e27fb76984e620fc76e5" +source = "git+https://github.com/fufesou/rdev?branch=refact/keycodes#b3434caee84c92412b45a2f655a15ac5dad33488" dependencies = [ "cocoa", "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5261,7 +5237,7 @@ dependencies = [ "pam", "parity-tokio-ipc", "percent-encoding", - "rdev 0.5.0-2 (git+https://github.com/fufesou/rdev?branch=master)", + "rdev", "repng", "reqwest", "ringbuf", diff --git a/Cargo.toml b/Cargo.toml index c41d0744b..ba2ba3bc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,7 @@ default-net = "0.14" wol-rs = "1.0" flutter_rust_bridge = { version = "=1.80", features = ["uuid"], optional = true} errno = "0.3" -rdev = { git = "https://github.com/fufesou/rdev", branch = "master" } +rdev = { git = "https://github.com/fufesou/rdev", branch = "refact/keycodes" } url = { version = "2.3", features = ["serde"] } crossbeam-queue = "0.3" hex = "0.4" diff --git a/libs/enigo/Cargo.toml b/libs/enigo/Cargo.toml index f020411ca..1fcea3673 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 = { path = "../../../rdev" } +rdev = { git = "https://github.com/fufesou/rdev", branch = "refact/keycodes" } tfc = { git = "https://github.com/fufesou/The-Fat-Controller" } hbb_common = { path = "../hbb_common" } From c441d2f03f58823a09f58bdd548d0e069dd8cb06 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Nov 2023 16:46:40 +0800 Subject: [PATCH 06/12] fix build Signed-off-by: fufesou --- src/keyboard.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 7bd64121e..6f7001e93 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -56,6 +56,19 @@ lazy_static::lazy_static! { pub mod client { use super::*; + lazy_static::lazy_static! { + static ref IS_GRAB_STARTED: Arc> = Arc::new(Mutex::new(false)); + } + + pub fn start_grab_loop() { + let mut lock = IS_GRAB_STARTED.lock().unwrap(); + if *lock { + return; + } + super::start_grab_loop(); + *lock = true; + } + #[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) { @@ -234,7 +247,7 @@ fn get_keyboard_mode() -> String { "legacy".to_string() } -pub fn start_grab_loop() { +fn start_grab_loop() { std::env::set_var("KEYBOARD_ONLY", "y"); #[cfg(any(target_os = "windows", target_os = "macos"))] std::thread::spawn(move || { @@ -320,7 +333,7 @@ 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> { +fn stop_grab_loop() -> Result<(), rdev::GrabError> { #[cfg(any(target_os = "windows", target_os = "macos"))] rdev::exit_grab()?; #[cfg(target_os = "linux")] @@ -1081,9 +1094,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; #[cfg(target_os = "macos")] use hbb_common::log; + use hbb_common::SessionID; use crate::ui_interface::{get_local_option, set_local_option}; @@ -1117,7 +1130,7 @@ pub mod input_source { if cur_input_source == CONFIG_INPUT_SOURCE_1 { super::IS_RDEV_ENABLED.store(true, super::Ordering::SeqCst); } - super::start_grab_loop(); + super::client::start_grab_loop(); } pub fn change_input_source(session_id: SessionID, input_source: String) { @@ -1132,7 +1145,7 @@ pub mod input_source { return; } // It is ok to start grab loop multiple times. - super::start_grab_loop(); + super::client::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 { From 4246fe5b9294e050424683bd341a8c229783f5b6 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Nov 2023 17:00:11 +0800 Subject: [PATCH 07/12] update rdev Signed-off-by: fufesou --- Cargo.lock | 2 +- Cargo.toml | 2 +- libs/enigo/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c896da8b..8542738bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4894,7 +4894,7 @@ dependencies = [ [[package]] name = "rdev" version = "0.5.0-2" -source = "git+https://github.com/fufesou/rdev?branch=refact/keycodes#b3434caee84c92412b45a2f655a15ac5dad33488" +source = "git+https://github.com/fufesou/rdev#8e98f8aa395c8cb6c171f99f8b2cf5e9d30ddb9b" dependencies = [ "cocoa", "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index ba2ba3bc6..cb94b7da1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,7 @@ default-net = "0.14" wol-rs = "1.0" flutter_rust_bridge = { version = "=1.80", features = ["uuid"], optional = true} errno = "0.3" -rdev = { git = "https://github.com/fufesou/rdev", branch = "refact/keycodes" } +rdev = { git = "https://github.com/fufesou/rdev" } url = { version = "2.3", features = ["serde"] } crossbeam-queue = "0.3" hex = "0.4" diff --git a/libs/enigo/Cargo.toml b/libs/enigo/Cargo.toml index 1fcea3673..1f67ae36b 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", branch = "refact/keycodes" } +rdev = { git = "https://github.com/fufesou/rdev" } tfc = { git = "https://github.com/fufesou/The-Fat-Controller" } hbb_common = { path = "../hbb_common" } From 5c6017b0a911d57393fc0481d611f8605acc6171 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Nov 2023 17:03:44 +0800 Subject: [PATCH 08/12] fix build, mobile Signed-off-by: fufesou --- src/flutter_ffi.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 7f4f37ecf..293c24502 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1,14 +1,16 @@ -#[cfg(not(any(target_os = "android", target_os = "ios")))] -use crate::common::get_default_sound_input; use crate::{ client::file_trait::FileManager, common::is_keyboard_mode_supported, 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, *}, }; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use crate::{ + common::get_default_sound_input, + keyboard::input_source::{change_input_source, get_cur_session_input_source}, +}; use flutter_rust_bridge::{StreamSink, SyncReturn}; #[cfg(feature = "plugin_framework")] #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -858,10 +860,15 @@ pub fn main_set_local_option(key: String, value: String) { } pub fn main_get_input_source() -> SyncReturn { - SyncReturn(get_cur_session_input_source()) + #[cfg(not(any(target_os = "android", target_os = "ios")))] + let input_source = get_cur_session_input_source(); + #[cfg(any(target_os = "android", target_os = "ios"))] + let input_source = "".to_owned(); + SyncReturn(input_source) } pub fn main_set_input_source(session_id: SessionID, value: String) { + #[cfg(not(any(target_os = "android", target_os = "ios")))] change_input_source(session_id, value); } From 1589209567b651ef27bf9fd270c6afeb82e29aab Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Nov 2023 20:57:44 +0800 Subject: [PATCH 09/12] release keys for flutter input source Signed-off-by: fufesou --- flutter/lib/common/widgets/remote_input.dart | 3 +- flutter/lib/models/input_model.dart | 114 ++++++++++++++++++- 2 files changed, 115 insertions(+), 2 deletions(-) diff --git a/flutter/lib/common/widgets/remote_input.dart b/flutter/lib/common/widgets/remote_input.dart index a07bffa47..e073bffc9 100644 --- a/flutter/lib/common/widgets/remote_input.dart +++ b/flutter/lib/common/widgets/remote_input.dart @@ -34,7 +34,8 @@ class RawKeyFocusScope extends StatelessWidget { canRequestFocus: true, focusNode: focusNode, onFocusChange: onFocusChange, - onKey: inputModel.handleRawKeyEvent, + onKey: (FocusNode data, RawKeyEvent e) => + inputModel.handleRawKeyEvent(e), child: child)); } } diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index 156a1201b..322897145 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -52,6 +52,111 @@ class PointerEventToRust { } } +class ToReleaseKeys { + RawKeyEvent? lastLShiftKeyEvent; + RawKeyEvent? lastRShiftKeyEvent; + RawKeyEvent? lastLCtrlKeyEvent; + RawKeyEvent? lastRCtrlKeyEvent; + RawKeyEvent? lastLAltKeyEvent; + RawKeyEvent? lastRAltKeyEvent; + RawKeyEvent? lastLCommandKeyEvent; + RawKeyEvent? lastRCommandKeyEvent; + RawKeyEvent? lastSuperKeyEvent; + + reset() { + lastLShiftKeyEvent = null; + lastRShiftKeyEvent = null; + lastLCtrlKeyEvent = null; + lastRCtrlKeyEvent = null; + lastLAltKeyEvent = null; + lastRAltKeyEvent = null; + lastLCommandKeyEvent = null; + lastRCommandKeyEvent = null; + lastSuperKeyEvent = null; + } + + updateKeyDown(LogicalKeyboardKey logicKey, RawKeyDownEvent e) { + if (e.isAltPressed) { + if (logicKey == LogicalKeyboardKey.altLeft) { + lastLAltKeyEvent = e; + } else if (logicKey == LogicalKeyboardKey.altRight) { + lastRAltKeyEvent = e; + } + } else if (e.isControlPressed) { + if (logicKey == LogicalKeyboardKey.controlLeft) { + lastLCtrlKeyEvent = e; + } else if (logicKey == LogicalKeyboardKey.controlRight) { + lastRCtrlKeyEvent = e; + } + } else if (e.isShiftPressed) { + if (logicKey == LogicalKeyboardKey.shiftLeft) { + lastLShiftKeyEvent = e; + } else if (logicKey == LogicalKeyboardKey.shiftRight) { + lastRShiftKeyEvent = e; + } + } else if (e.isMetaPressed) { + if (logicKey == LogicalKeyboardKey.metaLeft) { + lastLCommandKeyEvent = e; + } else if (logicKey == LogicalKeyboardKey.metaRight) { + lastRCommandKeyEvent = e; + } else if (logicKey == LogicalKeyboardKey.superKey) { + lastSuperKeyEvent = e; + } + } + } + + updateKeyUp(LogicalKeyboardKey logicKey, RawKeyUpEvent e) { + if (e.isAltPressed) { + if (logicKey == LogicalKeyboardKey.altLeft) { + lastLAltKeyEvent = null; + } else if (logicKey == LogicalKeyboardKey.altRight) { + lastRAltKeyEvent = null; + } + } else if (e.isControlPressed) { + if (logicKey == LogicalKeyboardKey.controlLeft) { + lastLCtrlKeyEvent = null; + } else if (logicKey == LogicalKeyboardKey.controlRight) { + lastRCtrlKeyEvent = null; + } + } else if (e.isShiftPressed) { + if (logicKey == LogicalKeyboardKey.shiftLeft) { + lastLShiftKeyEvent = null; + } else if (logicKey == LogicalKeyboardKey.shiftRight) { + lastRShiftKeyEvent = null; + } + } else if (e.isMetaPressed) { + if (logicKey == LogicalKeyboardKey.metaLeft) { + lastLCommandKeyEvent = null; + } else if (logicKey == LogicalKeyboardKey.metaRight) { + lastRCommandKeyEvent = null; + } else if (logicKey == LogicalKeyboardKey.superKey) { + lastSuperKeyEvent = null; + } + } + } + + release(KeyEventResult Function(RawKeyEvent e) handleRawKeyEvent) { + for (final key in [ + lastLShiftKeyEvent, + lastRShiftKeyEvent, + lastLCtrlKeyEvent, + lastRCtrlKeyEvent, + lastLAltKeyEvent, + lastRAltKeyEvent, + lastLCommandKeyEvent, + lastRCommandKeyEvent, + lastSuperKeyEvent, + ]) { + if (key != null) { + handleRawKeyEvent(RawKeyUpEvent( + data: key.data, + character: key.character, + )); + } + } + } +} + class InputModel { final WeakReference parent; String keyboardMode = "legacy"; @@ -62,6 +167,8 @@ class InputModel { var alt = false; var command = false; + final ToReleaseKeys toReleaseKeys = ToReleaseKeys(); + // trackpad var _trackpadLastDelta = Offset.zero; var _stopFling = true; @@ -89,7 +196,7 @@ class InputModel { sessionId = parent.target!.sessionId; } - KeyEventResult handleRawKeyEvent(FocusNode data, RawKeyEvent e) { + KeyEventResult handleRawKeyEvent(RawKeyEvent e) { if (isDesktop && !isInputSourceFlutter) { return KeyEventResult.handled; } @@ -114,6 +221,7 @@ class InputModel { command = true; } } + toReleaseKeys.updateKeyDown(key, e); } if (e is RawKeyUpEvent) { if (key == LogicalKeyboardKey.altLeft || @@ -130,6 +238,8 @@ class InputModel { key == LogicalKeyboardKey.superKey) { command = false; } + + toReleaseKeys.updateKeyUp(key, e); } // * Currently mobile does not enable map mode @@ -319,6 +429,8 @@ class InputModel { } void enterOrLeave(bool enter) { + toReleaseKeys.release(handleRawKeyEvent); + // Fix status if (!enter) { resetModifiers(); From 4a9e7f29da0b43fe878372fbe7e51128c3291836 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Nov 2023 21:31:27 +0800 Subject: [PATCH 10/12] fix, flutter, keyboard mode Signed-off-by: fufesou --- .../lib/desktop/widgets/remote_toolbar.dart | 2 ++ flutter/lib/models/input_model.dart | 27 +++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index e38aa9546..1ae380761 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1628,6 +1628,7 @@ class _KeyboardMenu extends StatelessWidget { if (value == null) return; await bind.sessionSetKeyboardMode( sessionId: ffi.sessionId, value: value); + await ffi.inputModel.updateKeyboardMode(); } for (InputModeMenu mode in modes) { @@ -1704,6 +1705,7 @@ class _KeyboardMenu extends StatelessWidget { if (v != null) { await stateGlobal.setInputSource(ffi.sessionId, v); await ffi.ffiModel.checkDesktopKeyboardMode(); + await ffi.inputModel.updateKeyboardMode(); } } : null, diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index 322897145..458ccf3ad 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -159,7 +159,7 @@ class ToReleaseKeys { class InputModel { final WeakReference parent; - String keyboardMode = "legacy"; + String keyboardMode = ''; // keyboard var shift = false; @@ -194,6 +194,24 @@ class InputModel { InputModel(this.parent) { sessionId = parent.target!.sessionId; + + // It is ok to call updateKeyboardMode() directly. + // Because `bind` is initialized in `PlatformFFI.init()` which is called very early. + // But we still wrap it in a Future.delayed() to make it more clear. + Future.delayed(Duration(milliseconds: 100), () { + updateKeyboardMode(); + }); + } + + updateKeyboardMode() async { + // * Currently mobile does not enable map mode + if (isDesktop) { + if (keyboardMode.isEmpty) { + keyboardMode = + await bind.sessionGetKeyboardMode(sessionId: sessionId) ?? + kKeyLegacyMode; + } + } } KeyEventResult handleRawKeyEvent(RawKeyEvent e) { @@ -201,13 +219,6 @@ class InputModel { return KeyEventResult.handled; } - // * Currently mobile does not enable map mode - if (isDesktop) { - bind.sessionGetKeyboardMode(sessionId: sessionId).then((result) { - keyboardMode = result.toString(); - }); - } - final key = e.logicalKey; if (e is RawKeyDownEvent) { if (!e.repeat) { From 58f43da23eb2db44944999081cd0266083526f20 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Nov 2023 21:50:17 +0800 Subject: [PATCH 11/12] update rdev Signed-off-by: fufesou --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 8542738bc..642828d4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4894,7 +4894,7 @@ dependencies = [ [[package]] name = "rdev" version = "0.5.0-2" -source = "git+https://github.com/fufesou/rdev#8e98f8aa395c8cb6c171f99f8b2cf5e9d30ddb9b" +source = "git+https://github.com/fufesou/rdev#b3434caee84c92412b45a2f655a15ac5dad33488" dependencies = [ "cocoa", "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", From 0e05f5305cc44d17707433e618c12aa6eda6ced3 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Nov 2023 22:13:38 +0800 Subject: [PATCH 12/12] fix build, android Signed-off-by: fufesou --- src/flutter_ffi.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 293c24502..d25923de5 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -471,6 +471,7 @@ 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 let Some(session) = sessions::get_session_by_session_id(&_session_id) { let keyboard_mode = session.get_keyboard_mode(); if _enter {