mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-02-17 14:07:28 +08:00
Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -59,7 +59,8 @@ class CanvasCoords {
|
|||||||
model.scale = json['scale'];
|
model.scale = json['scale'];
|
||||||
model.scrollX = json['scrollX'];
|
model.scrollX = json['scrollX'];
|
||||||
model.scrollY = json['scrollY'];
|
model.scrollY = json['scrollY'];
|
||||||
model.scrollStyle = ScrollStyle.fromJson(json['scrollStyle'], ScrollStyle.scrollauto);
|
model.scrollStyle =
|
||||||
|
ScrollStyle.fromJson(json['scrollStyle'], ScrollStyle.scrollauto);
|
||||||
model.size = Size(json['size']['w'], json['size']['h']);
|
model.size = Size(json['size']['w'], json['size']['h']);
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
@@ -418,6 +419,74 @@ class InputModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/flutter/flutter/issues/157241
|
||||||
|
// Infer CapsLock state from the character output.
|
||||||
|
// This is needed because Flutter's HardwareKeyboard.lockModesEnabled may report
|
||||||
|
// incorrect CapsLock state on iOS.
|
||||||
|
bool _getIosCapsFromCharacter(KeyEvent e) {
|
||||||
|
if (!isIOS) return false;
|
||||||
|
final ch = e.character;
|
||||||
|
return _getIosCapsFromCharacterImpl(
|
||||||
|
ch, HardwareKeyboard.instance.isShiftPressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawKeyEvent version of _getIosCapsFromCharacter.
|
||||||
|
bool _getIosCapsFromRawCharacter(RawKeyEvent e) {
|
||||||
|
if (!isIOS) return false;
|
||||||
|
final ch = e.character;
|
||||||
|
return _getIosCapsFromCharacterImpl(ch, e.isShiftPressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared implementation for inferring CapsLock state from character.
|
||||||
|
// Uses Unicode-aware case detection to support non-ASCII letters (e.g., ü/Ü, é/É).
|
||||||
|
//
|
||||||
|
// Limitations:
|
||||||
|
// 1. This inference assumes the client and server use the same keyboard layout.
|
||||||
|
// If layouts differ (e.g., client uses EN, server uses DE), the character output
|
||||||
|
// may not match expectations. For example, ';' on EN layout maps to 'ö' on DE
|
||||||
|
// layout, making it impossible to correctly infer CapsLock state from the
|
||||||
|
// character alone.
|
||||||
|
// 2. On iOS, CapsLock+Shift produces uppercase letters (unlike desktop where it
|
||||||
|
// produces lowercase). This method cannot handle that case correctly.
|
||||||
|
bool _getIosCapsFromCharacterImpl(String? ch, bool shiftPressed) {
|
||||||
|
if (ch == null || ch.length != 1) return false;
|
||||||
|
// Use Dart's built-in Unicode-aware case detection
|
||||||
|
final upper = ch.toUpperCase();
|
||||||
|
final lower = ch.toLowerCase();
|
||||||
|
final isUpper = upper == ch && lower != ch;
|
||||||
|
final isLower = lower == ch && upper != ch;
|
||||||
|
// Skip non-letter characters (e.g., numbers, symbols, CJK characters without case)
|
||||||
|
if (!isUpper && !isLower) return false;
|
||||||
|
return isUpper != shiftPressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _buildLockModes(bool iosCapsLock) {
|
||||||
|
const capslock = 1;
|
||||||
|
const numlock = 2;
|
||||||
|
const scrolllock = 3;
|
||||||
|
int lockModes = 0;
|
||||||
|
if (isIOS) {
|
||||||
|
if (iosCapsLock) {
|
||||||
|
lockModes |= (1 << capslock);
|
||||||
|
}
|
||||||
|
// Ignore "NumLock/ScrollLock" on iOS for now.
|
||||||
|
} else {
|
||||||
|
if (HardwareKeyboard.instance.lockModesEnabled
|
||||||
|
.contains(KeyboardLockMode.capsLock)) {
|
||||||
|
lockModes |= (1 << capslock);
|
||||||
|
}
|
||||||
|
if (HardwareKeyboard.instance.lockModesEnabled
|
||||||
|
.contains(KeyboardLockMode.numLock)) {
|
||||||
|
lockModes |= (1 << numlock);
|
||||||
|
}
|
||||||
|
if (HardwareKeyboard.instance.lockModesEnabled
|
||||||
|
.contains(KeyboardLockMode.scrollLock)) {
|
||||||
|
lockModes |= (1 << scrolllock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lockModes;
|
||||||
|
}
|
||||||
|
|
||||||
// This function must be called after the peer info is received.
|
// This function must be called after the peer info is received.
|
||||||
// Because `sessionGetKeyboardMode` relies on the peer version.
|
// Because `sessionGetKeyboardMode` relies on the peer version.
|
||||||
updateKeyboardMode() async {
|
updateKeyboardMode() async {
|
||||||
@@ -550,6 +619,11 @@ class InputModel {
|
|||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool iosCapsLock = false;
|
||||||
|
if (isIOS && e is RawKeyDownEvent) {
|
||||||
|
iosCapsLock = _getIosCapsFromRawCharacter(e);
|
||||||
|
}
|
||||||
|
|
||||||
final key = e.logicalKey;
|
final key = e.logicalKey;
|
||||||
if (e is RawKeyDownEvent) {
|
if (e is RawKeyDownEvent) {
|
||||||
if (!e.repeat) {
|
if (!e.repeat) {
|
||||||
@@ -586,7 +660,7 @@ class InputModel {
|
|||||||
|
|
||||||
// * Currently mobile does not enable map mode
|
// * Currently mobile does not enable map mode
|
||||||
if ((isDesktop || isWebDesktop) && keyboardMode == kKeyMapMode) {
|
if ((isDesktop || isWebDesktop) && keyboardMode == kKeyMapMode) {
|
||||||
mapKeyboardModeRaw(e);
|
mapKeyboardModeRaw(e, iosCapsLock);
|
||||||
} else {
|
} else {
|
||||||
legacyKeyboardModeRaw(e);
|
legacyKeyboardModeRaw(e);
|
||||||
}
|
}
|
||||||
@@ -622,6 +696,11 @@ class InputModel {
|
|||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool iosCapsLock = false;
|
||||||
|
if (isIOS && (e is KeyDownEvent || e is KeyRepeatEvent)) {
|
||||||
|
iosCapsLock = _getIosCapsFromCharacter(e);
|
||||||
|
}
|
||||||
|
|
||||||
if (e is KeyUpEvent) {
|
if (e is KeyUpEvent) {
|
||||||
handleKeyUpEventModifiers(e);
|
handleKeyUpEventModifiers(e);
|
||||||
} else if (e is KeyDownEvent) {
|
} else if (e is KeyDownEvent) {
|
||||||
@@ -667,7 +746,8 @@ class InputModel {
|
|||||||
e.character ?? '',
|
e.character ?? '',
|
||||||
e.physicalKey.usbHidUsage & 0xFFFF,
|
e.physicalKey.usbHidUsage & 0xFFFF,
|
||||||
// Show repeat event be converted to "release+press" events?
|
// Show repeat event be converted to "release+press" events?
|
||||||
e is KeyDownEvent || e is KeyRepeatEvent);
|
e is KeyDownEvent || e is KeyRepeatEvent,
|
||||||
|
iosCapsLock);
|
||||||
} else {
|
} else {
|
||||||
legacyKeyboardMode(e);
|
legacyKeyboardMode(e);
|
||||||
}
|
}
|
||||||
@@ -676,23 +756,9 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Send Key Event
|
/// Send Key Event
|
||||||
void newKeyboardMode(String character, int usbHid, bool down) {
|
void newKeyboardMode(
|
||||||
const capslock = 1;
|
String character, int usbHid, bool down, bool iosCapsLock) {
|
||||||
const numlock = 2;
|
final lockModes = _buildLockModes(iosCapsLock);
|
||||||
const scrolllock = 3;
|
|
||||||
int lockModes = 0;
|
|
||||||
if (HardwareKeyboard.instance.lockModesEnabled
|
|
||||||
.contains(KeyboardLockMode.capsLock)) {
|
|
||||||
lockModes |= (1 << capslock);
|
|
||||||
}
|
|
||||||
if (HardwareKeyboard.instance.lockModesEnabled
|
|
||||||
.contains(KeyboardLockMode.numLock)) {
|
|
||||||
lockModes |= (1 << numlock);
|
|
||||||
}
|
|
||||||
if (HardwareKeyboard.instance.lockModesEnabled
|
|
||||||
.contains(KeyboardLockMode.scrollLock)) {
|
|
||||||
lockModes |= (1 << scrolllock);
|
|
||||||
}
|
|
||||||
bind.sessionHandleFlutterKeyEvent(
|
bind.sessionHandleFlutterKeyEvent(
|
||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
character: character,
|
character: character,
|
||||||
@@ -701,7 +767,7 @@ class InputModel {
|
|||||||
downOrUp: down);
|
downOrUp: down);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mapKeyboardModeRaw(RawKeyEvent e) {
|
void mapKeyboardModeRaw(RawKeyEvent e, bool iosCapsLock) {
|
||||||
int positionCode = -1;
|
int positionCode = -1;
|
||||||
int platformCode = -1;
|
int platformCode = -1;
|
||||||
bool down;
|
bool down;
|
||||||
@@ -732,27 +798,14 @@ class InputModel {
|
|||||||
} else {
|
} else {
|
||||||
down = false;
|
down = false;
|
||||||
}
|
}
|
||||||
inputRawKey(e.character ?? '', platformCode, positionCode, down);
|
inputRawKey(
|
||||||
|
e.character ?? '', platformCode, positionCode, down, iosCapsLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send raw Key Event
|
/// Send raw Key Event
|
||||||
void inputRawKey(String name, int platformCode, int positionCode, bool down) {
|
void inputRawKey(String name, int platformCode, int positionCode, bool down,
|
||||||
const capslock = 1;
|
bool iosCapsLock) {
|
||||||
const numlock = 2;
|
final lockModes = _buildLockModes(iosCapsLock);
|
||||||
const scrolllock = 3;
|
|
||||||
int lockModes = 0;
|
|
||||||
if (HardwareKeyboard.instance.lockModesEnabled
|
|
||||||
.contains(KeyboardLockMode.capsLock)) {
|
|
||||||
lockModes |= (1 << capslock);
|
|
||||||
}
|
|
||||||
if (HardwareKeyboard.instance.lockModesEnabled
|
|
||||||
.contains(KeyboardLockMode.numLock)) {
|
|
||||||
lockModes |= (1 << numlock);
|
|
||||||
}
|
|
||||||
if (HardwareKeyboard.instance.lockModesEnabled
|
|
||||||
.contains(KeyboardLockMode.scrollLock)) {
|
|
||||||
lockModes |= (1 << scrolllock);
|
|
||||||
}
|
|
||||||
bind.sessionHandleFlutterRawKeyEvent(
|
bind.sessionHandleFlutterRawKeyEvent(
|
||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
name: name,
|
name: name,
|
||||||
@@ -1800,9 +1853,9 @@ class InputModel {
|
|||||||
// Simulate a key press event.
|
// Simulate a key press event.
|
||||||
// `usbHidUsage` is the USB HID usage code of the key.
|
// `usbHidUsage` is the USB HID usage code of the key.
|
||||||
Future<void> tapHidKey(int usbHidUsage) async {
|
Future<void> tapHidKey(int usbHidUsage) async {
|
||||||
newKeyboardMode(kKeyFlutterKey, usbHidUsage, true);
|
newKeyboardMode(kKeyFlutterKey, usbHidUsage, true, false);
|
||||||
await Future.delayed(Duration(milliseconds: 100));
|
await Future.delayed(Duration(milliseconds: 100));
|
||||||
newKeyboardMode(kKeyFlutterKey, usbHidUsage, false);
|
newKeyboardMode(kKeyFlutterKey, usbHidUsage, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onMobileVolumeUp() async =>
|
Future<void> onMobileVolumeUp() async =>
|
||||||
|
|||||||
Reference in New Issue
Block a user