diff --git a/Cargo.lock b/Cargo.lock index ef2cab92c807..1b1e66826c63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5187,7 +5187,7 @@ dependencies = [ [[package]] name = "rdev" version = "0.5.0-2" -source = "git+https://github.com/rustdesk-org/rdev#b3434caee84c92412b45a2f655a15ac5dad33488" +source = "git+https://github.com/rustdesk-org/rdev#d4c1759926d693ba269e2cb8cf9f87b13e424e4e" dependencies = [ "cocoa 0.24.1", "core-foundation 0.9.4", diff --git a/flutter/lib/common/widgets/remote_input.dart b/flutter/lib/common/widgets/remote_input.dart index 61bd4dd31bc5..73e5d8f39a83 100644 --- a/flutter/lib/common/widgets/remote_input.dart +++ b/flutter/lib/common/widgets/remote_input.dart @@ -34,8 +34,7 @@ class RawKeyFocusScope extends StatelessWidget { canRequestFocus: true, focusNode: focusNode, onFocusChange: onFocusChange, - onKey: (FocusNode data, RawKeyEvent e) => - inputModel.handleRawKeyEvent(e), + onKeyEvent: (node, event) => inputModel.handleKeyEvent(event), child: child)); } } diff --git a/flutter/lib/models/desktop_render_texture.dart b/flutter/lib/models/desktop_render_texture.dart index ab8df3c4539e..c6cf55256de8 100644 --- a/flutter/lib/models/desktop_render_texture.dart +++ b/flutter/lib/models/desktop_render_texture.dart @@ -181,6 +181,7 @@ class TextureModel { } updateCurrentDisplay(int curDisplay) { + if (isWeb) return; final ffi = parent.target; if (ffi == null) return; tryCreateTexture(int idx) { diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index dde815789ae2..35f00c3e1c37 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -178,15 +178,15 @@ class PointerEventToRust { } class ToReleaseKeys { - RawKeyEvent? lastLShiftKeyEvent; - RawKeyEvent? lastRShiftKeyEvent; - RawKeyEvent? lastLCtrlKeyEvent; - RawKeyEvent? lastRCtrlKeyEvent; - RawKeyEvent? lastLAltKeyEvent; - RawKeyEvent? lastRAltKeyEvent; - RawKeyEvent? lastLCommandKeyEvent; - RawKeyEvent? lastRCommandKeyEvent; - RawKeyEvent? lastSuperKeyEvent; + KeyEvent? lastLShiftKeyEvent; + KeyEvent? lastRShiftKeyEvent; + KeyEvent? lastLCtrlKeyEvent; + KeyEvent? lastRCtrlKeyEvent; + KeyEvent? lastLAltKeyEvent; + KeyEvent? lastRAltKeyEvent; + KeyEvent? lastLCommandKeyEvent; + KeyEvent? lastRCommandKeyEvent; + KeyEvent? lastSuperKeyEvent; reset() { lastLShiftKeyEvent = null; @@ -200,67 +200,7 @@ class ToReleaseKeys { 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) { + release(KeyEventResult Function(KeyEvent e) handleKeyEvent) { for (final key in [ lastLShiftKeyEvent, lastRShiftKeyEvent, @@ -273,10 +213,7 @@ class ToReleaseKeys { lastSuperKeyEvent, ]) { if (key != null) { - handleRawKeyEvent(RawKeyUpEvent( - data: key.data, - character: key.character, - )); + handleKeyEvent(key); } } } @@ -339,49 +276,116 @@ class InputModel { } } - KeyEventResult handleRawKeyEvent(RawKeyEvent e) { + void handleKeyDownEventModifiers(KeyEvent e) { + KeyUpEvent upEvent(e) => KeyUpEvent( + physicalKey: e.physicalKey, + logicalKey: e.logicalKey, + timeStamp: e.timeStamp, + ); + if (e.logicalKey == LogicalKeyboardKey.altLeft) { + if (!alt) { + alt = true; + } + toReleaseKeys.lastLAltKeyEvent = upEvent(e); + } else if (e.logicalKey == LogicalKeyboardKey.altRight) { + if (!alt) { + alt = true; + } + toReleaseKeys.lastLAltKeyEvent = upEvent(e); + } else if (e.logicalKey == LogicalKeyboardKey.controlLeft) { + if (!ctrl) { + ctrl = true; + } + toReleaseKeys.lastLCtrlKeyEvent = upEvent(e); + } else if (e.logicalKey == LogicalKeyboardKey.controlRight) { + if (!ctrl) { + ctrl = true; + } + toReleaseKeys.lastRCtrlKeyEvent = upEvent(e); + } else if (e.logicalKey == LogicalKeyboardKey.shiftLeft) { + if (!shift) { + shift = true; + } + toReleaseKeys.lastLShiftKeyEvent = upEvent(e); + } else if (e.logicalKey == LogicalKeyboardKey.shiftRight) { + if (!shift) { + shift = true; + } + toReleaseKeys.lastRShiftKeyEvent = upEvent(e); + } else if (e.logicalKey == LogicalKeyboardKey.metaLeft) { + if (!command) { + command = true; + } + toReleaseKeys.lastLCommandKeyEvent = upEvent(e); + } else if (e.logicalKey == LogicalKeyboardKey.metaRight) { + if (!command) { + command = true; + } + toReleaseKeys.lastRCommandKeyEvent = upEvent(e); + } else if (e.logicalKey == LogicalKeyboardKey.superKey) { + if (!command) { + command = true; + } + toReleaseKeys.lastSuperKeyEvent = upEvent(e); + } + } + + void handleKeyUpEventModifiers(KeyEvent e) { + if (e.logicalKey == LogicalKeyboardKey.altLeft) { + alt = false; + toReleaseKeys.lastLAltKeyEvent = null; + } else if (e.logicalKey == LogicalKeyboardKey.altRight) { + alt = false; + toReleaseKeys.lastRAltKeyEvent = null; + } else if (e.logicalKey == LogicalKeyboardKey.controlLeft) { + ctrl = false; + toReleaseKeys.lastLCtrlKeyEvent = null; + } else if (e.logicalKey == LogicalKeyboardKey.controlRight) { + ctrl = false; + toReleaseKeys.lastRCtrlKeyEvent = null; + } else if (e.logicalKey == LogicalKeyboardKey.shiftLeft) { + shift = false; + toReleaseKeys.lastLShiftKeyEvent = null; + } else if (e.logicalKey == LogicalKeyboardKey.shiftRight) { + shift = false; + toReleaseKeys.lastRShiftKeyEvent = null; + } else if (e.logicalKey == LogicalKeyboardKey.metaLeft) { + command = false; + toReleaseKeys.lastLCommandKeyEvent = null; + } else if (e.logicalKey == LogicalKeyboardKey.metaRight) { + command = false; + toReleaseKeys.lastRCommandKeyEvent = null; + } else if (e.logicalKey == LogicalKeyboardKey.superKey) { + command = false; + toReleaseKeys.lastSuperKeyEvent = null; + } + } + + KeyEventResult handleKeyEvent(KeyEvent e) { if (isViewOnly) return KeyEventResult.handled; if ((isDesktop || isWebDesktop) && !isInputSourceFlutter) { return KeyEventResult.handled; } - - final key = e.logicalKey; - if (e is RawKeyDownEvent) { - if (!e.repeat) { - if (e.isAltPressed && !alt) { - alt = true; - } else if (e.isControlPressed && !ctrl) { - ctrl = true; - } else if (e.isShiftPressed && !shift) { - shift = true; - } else if (e.isMetaPressed && !command) { - command = true; - } + if (isWindows || isLinux) { + // Ignore meta keys. Because flutter window will loose focus if meta key is pressed. + if (e.physicalKey == PhysicalKeyboardKey.metaLeft || + e.physicalKey == PhysicalKeyboardKey.metaRight) { + return KeyEventResult.handled; } - toReleaseKeys.updateKeyDown(key, e); } - if (e is RawKeyUpEvent) { - if (key == LogicalKeyboardKey.altLeft || - key == LogicalKeyboardKey.altRight) { - alt = false; - } else if (key == LogicalKeyboardKey.controlLeft || - key == LogicalKeyboardKey.controlRight) { - ctrl = false; - } else if (key == LogicalKeyboardKey.shiftRight || - key == LogicalKeyboardKey.shiftLeft) { - shift = false; - } else if (key == LogicalKeyboardKey.metaLeft || - key == LogicalKeyboardKey.metaRight || - key == LogicalKeyboardKey.superKey) { - command = false; - } - toReleaseKeys.updateKeyUp(key, e); + if (e is KeyUpEvent) { + handleKeyUpEventModifiers(e); + } else if (e is KeyDownEvent) { + handleKeyDownEventModifiers(e); } // * Currently mobile does not enable map mode - if ((isDesktop || isWebDesktop) && keyboardMode == 'map') { - mapKeyboardMode(e); + if ((isDesktop || isWebDesktop)) { + // FIXME: e.character is wrong for dead keys, eg: ^ in de + newKeyboardMode(e.character ?? '', e.physicalKey.usbHidUsage & 0xFFFF, + // Show repeat event be converted to "release+press" events? + e is KeyDownEvent || e is KeyRepeatEvent); } else { legacyKeyboardMode(e); } @@ -389,42 +393,8 @@ class InputModel { return KeyEventResult.handled; } - void mapKeyboardMode(RawKeyEvent e) { - int positionCode = -1; - int platformCode = -1; - bool down; - - if (e.data is RawKeyEventDataMacOs) { - RawKeyEventDataMacOs newData = e.data as RawKeyEventDataMacOs; - positionCode = newData.keyCode; - platformCode = newData.keyCode; - } else if (e.data is RawKeyEventDataWindows) { - RawKeyEventDataWindows newData = e.data as RawKeyEventDataWindows; - positionCode = newData.scanCode; - platformCode = newData.keyCode; - } else if (e.data is RawKeyEventDataLinux) { - RawKeyEventDataLinux newData = e.data as RawKeyEventDataLinux; - // scanCode and keyCode of RawKeyEventDataLinux are incorrect. - // 1. scanCode means keycode - // 2. keyCode means keysym - positionCode = newData.scanCode; - platformCode = newData.keyCode; - } else if (e.data is RawKeyEventDataAndroid) { - RawKeyEventDataAndroid newData = e.data as RawKeyEventDataAndroid; - positionCode = newData.scanCode + 8; - platformCode = newData.keyCode; - } else {} - - if (e is RawKeyDownEvent) { - down = true; - } else { - down = false; - } - inputRawKey(e.character ?? '', platformCode, positionCode, down); - } - - /// Send raw Key Event - void inputRawKey(String name, int platformCode, int positionCode, bool down) { + /// Send Key Event + void newKeyboardMode(String character, int usbHid, bool down) { const capslock = 1; const numlock = 2; const scrolllock = 3; @@ -443,27 +413,23 @@ class InputModel { } bind.sessionHandleFlutterKeyEvent( sessionId: sessionId, - name: name, - platformCode: platformCode, - positionCode: positionCode, + character: character, + usbHid: usbHid, lockModes: lockModes, downOrUp: down); } - void legacyKeyboardMode(RawKeyEvent e) { - if (e is RawKeyDownEvent) { - if (e.repeat) { - sendRawKey(e, press: true); - } else { - sendRawKey(e, down: true); - } - } - if (e is RawKeyUpEvent) { - sendRawKey(e); + void legacyKeyboardMode(KeyEvent e) { + if (e is KeyDownEvent) { + sendKey(e, down: true); + } else if (e is KeyRepeatEvent) { + sendKey(e, press: true); + } else if (e is KeyUpEvent) { + sendKey(e); } } - void sendRawKey(RawKeyEvent e, {bool? down, bool? press}) { + void sendKey(KeyEvent e, {bool? down, bool? press}) { // for maximum compatibility final label = physicalKeyMap[e.physicalKey.usbHidUsage] ?? logicalKeyMap[e.logicalKey.keyId] ?? @@ -566,7 +532,7 @@ class InputModel { } void enterOrLeave(bool enter) { - toReleaseKeys.release(handleRawKeyEvent); + toReleaseKeys.release(handleKeyEvent); _pointerMovedAfterEnter = false; // Fix status @@ -1164,15 +1130,15 @@ class InputModel { // Simulate a key press event. // `usbHidUsage` is the USB HID usage code of the key. Future tapHidKey(int usbHidUsage) async { - inputRawKey(kKeyFlutterKey, usbHidUsage, 0, true); + newKeyboardMode(kKeyFlutterKey, usbHidUsage, true); await Future.delayed(Duration(milliseconds: 100)); - inputRawKey(kKeyFlutterKey, usbHidUsage, 0, false); + newKeyboardMode(kKeyFlutterKey, usbHidUsage, false); } Future onMobileVolumeUp() async => - await tapHidKey(PhysicalKeyboardKey.audioVolumeUp.usbHidUsage); + await tapHidKey(PhysicalKeyboardKey.audioVolumeUp.usbHidUsage & 0xFFFF); Future onMobileVolumeDown() async => - await tapHidKey(PhysicalKeyboardKey.audioVolumeDown.usbHidUsage); + await tapHidKey(PhysicalKeyboardKey.audioVolumeDown.usbHidUsage & 0xFFFF); Future onMobilePower() async => - await tapHidKey(PhysicalKeyboardKey.power.usbHidUsage); + await tapHidKey(PhysicalKeyboardKey.power.usbHidUsage & 0xFFFF); } diff --git a/flutter/lib/web/bridge.dart b/flutter/lib/web/bridge.dart index 457911458ce1..5f6f5adef876 100644 --- a/flutter/lib/web/bridge.dart +++ b/flutter/lib/web/bridge.dart @@ -23,6 +23,7 @@ sealed class EventToUI { ) = EventToUI_Rgba; const factory EventToUI.texture( int field0, + bool field1, ) = EventToUI_Texture; } @@ -33,15 +34,19 @@ class EventToUI_Event implements EventToUI { } class EventToUI_Rgba implements EventToUI { - const EventToUI_Rgba(final int field0) : this.field = field0; + const EventToUI_Rgba(final int field0) : field = field0; final int field; int get field0 => field; } class EventToUI_Texture implements EventToUI { - const EventToUI_Texture(final int field0) : this.field = field0; - final int field; - int get field0 => field; + const EventToUI_Texture(final int field0, final bool field1) + : f0 = field0, + f1 = field1; + final int f0; + final bool f1; + int get field0 => f0; + bool get field1 => f1; } class RustdeskImpl { @@ -394,14 +399,20 @@ class RustdeskImpl { Future sessionHandleFlutterKeyEvent( {required UuidValue sessionId, - required String name, - required int platformCode, - required int positionCode, + required String character, + required int usbHid, required int lockModes, required bool downOrUp, dynamic hint}) { - // TODO: map mode - throw UnimplementedError(); + return Future(() => js.context.callMethod('setByName', [ + 'flutter_key_event', + jsonEncode({ + 'name': character, + 'usb_hid': usbHid, + 'lock_modes': lockModes, + if (downOrUp) 'down': 'true', + }) + ])); } void sessionEnterOrLeave( @@ -702,11 +713,11 @@ class RustdeskImpl { } Future mainGetAppName({dynamic hint}) { - throw UnimplementedError(); + return Future.value(mainGetAppNameSync(hint: hint)); } String mainGetAppNameSync({dynamic hint}) { - throw UnimplementedError(); + return 'RustDesk'; } String mainUriPrefixSync({dynamic hint}) { @@ -758,8 +769,9 @@ class RustdeskImpl { } Future mainIsUsingPublicServer({dynamic hint}) { - return Future( - () => js.context.callMethod('setByName', ["is_using_public_server"])); + return Future(() => + js.context.callMethod('getByName', ["is_using_public_server"]) == + 'true'); } Future mainDiscover({dynamic hint}) { @@ -1610,7 +1622,7 @@ class RustdeskImpl { } bool mainIsOptionFixed({required String key, dynamic hint}) { - throw UnimplementedError(); + return false; } bool mainGetUseTextureRender({dynamic hint}) { @@ -1650,5 +1662,36 @@ class RustdeskImpl { throw UnimplementedError(); } + Future getVoiceCallInputDevice({required bool isCm, dynamic hint}) { + throw UnimplementedError(); + } + + Future setVoiceCallInputDevice( + {required bool isCm, required String device, dynamic hint}) { + throw UnimplementedError(); + } + + bool isPresetPasswordMobileOnly({dynamic hint}) { + throw UnimplementedError(); + } + + String mainGetBuildinOption({required String key, dynamic hint}) { + return ''; + } + + String installInstallOptions({dynamic hint}) { + throw UnimplementedError(); + } + + sessionRenameFile( + {required UuidValue sessionId, + required int actId, + required String path, + required String newName, + required bool isRemote, + dynamic hint}) { + throw UnimplementedError(); + } + void dispose() {} } diff --git a/flutter/lib/web/texture_rgba_renderer.dart b/flutter/lib/web/texture_rgba_renderer.dart index 83407773583a..9a4a1879b12d 100644 --- a/flutter/lib/web/texture_rgba_renderer.dart +++ b/flutter/lib/web/texture_rgba_renderer.dart @@ -6,7 +6,7 @@ class TextureRgbaRenderer { } Future closeTexture(int key) { - throw UnimplementedError(); + return Future(() => true); } Future onRgba( diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 9b9914cfd0fb..e310febb150e 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -491,9 +491,8 @@ pub fn session_switch_display(is_desktop: bool, session_id: SessionID, value: Ve pub fn session_handle_flutter_key_event( session_id: SessionID, - name: String, - platform_code: i32, - position_code: i32, + character: String, + usb_hid: i32, lock_modes: i32, down_or_up: bool, ) { @@ -501,9 +500,8 @@ pub fn session_handle_flutter_key_event( let keyboard_mode = session.get_keyboard_mode(); session.handle_flutter_key_event( &keyboard_mode, - &name, - platform_code, - position_code, + &character, + usb_hid, lock_modes, down_or_up, ); diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 0e030aa8ba47..423794be58ee 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -803,19 +803,18 @@ impl Session { pub fn handle_flutter_key_event( &self, keyboard_mode: &str, - name: &str, - platform_code: i32, - position_code: i32, + character: &str, + usb_hid: i32, lock_modes: i32, down_or_up: bool, ) { - if name == "flutter_key" { - self._handle_key_flutter_simulation(keyboard_mode, platform_code, down_or_up); + if character == "flutter_key" { + self._handle_key_flutter_simulation(keyboard_mode, usb_hid, down_or_up); } else { self._handle_key_non_flutter_simulation( keyboard_mode, - platform_code, - position_code, + character, + usb_hid, lock_modes, down_or_up, ); @@ -831,10 +830,10 @@ impl Session { ) { // https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/services/keyboard_key.g.dart#L4356 let ctrl_key = match platform_code { - 0x0007007f => Some(ControlKey::VolumeMute), - 0x00070080 => Some(ControlKey::VolumeUp), - 0x00070081 => Some(ControlKey::VolumeDown), - 0x00070066 => Some(ControlKey::Power), + 0x007f => Some(ControlKey::VolumeMute), + 0x0080 => Some(ControlKey::VolumeUp), + 0x0081 => Some(ControlKey::VolumeDown), + 0x0066 => Some(ControlKey::Power), _ => None, }; let Some(ctrl_key) = ctrl_key else { return }; @@ -851,22 +850,28 @@ impl Session { fn _handle_key_non_flutter_simulation( &self, keyboard_mode: &str, - platform_code: i32, - position_code: i32, + character: &str, + usb_hid: i32, lock_modes: i32, down_or_up: bool, ) { - if position_code < 0 || platform_code < 0 { - return; - } - let platform_code: u32 = platform_code as _; - let position_code: KeyCode = position_code as _; + let key = rdev::usb_hid_key_from_code(usb_hid as _); - #[cfg(not(target_os = "windows"))] - let key = rdev::key_from_code(position_code) as rdev::Key; - // Windows requires special handling #[cfg(target_os = "windows")] - let key = rdev::get_win_key(platform_code, position_code); + let platform_code: u32 = rdev::win_code_from_key(key).unwrap_or(0); + #[cfg(target_os = "windows")] + let position_code: KeyCode = rdev::win_scancode_from_key(key).unwrap_or(0) as _; + + #[cfg(not(target_os = "windows"))] + let position_code: KeyCode = rdev::code_from_key(key).unwrap_or(0) as _; + #[cfg(not(any(target_os = "windows", target_os = "linux")))] + let platform_code: u32 = position_code as _; + // For translate mode. + // We need to set the platform code (keysym) if is AltGr. + // https://github.com/rustdesk/rustdesk/blob/07cf1b4db5ef2f925efd3b16b87c33ce03c94809/src/keyboard.rs#L1029 + // https://github.com/flutter/flutter/issues/153811 + #[cfg(target_os = "linux")] + let platform_code: u32 = position_code as _; let event_type = if down_or_up { KeyPress(key) @@ -875,7 +880,16 @@ impl Session { }; let event = Event { time: SystemTime::now(), - unicode: None, + unicode: if character.is_empty() { + None + } else { + Some(rdev::UnicodeInfo { + name: Some(character.to_string()), + unicode: character.encode_utf16().collect(), + // is_dead: is not correct here, because flutter cannot detect deadcode for now. + is_dead: false, + }) + }, platform_code, position_code: position_code as _, event_type,