Unverified Commit 073cefad authored by Bruno Leroux's avatar Bruno Leroux Committed by GitHub

[RawKeyboard] Fix Linux remapped CapsLock throws (#115009)

Co-authored-by: 's avatarBruno Leroux <bruno.leroux@gmail.com>
parent 567d0045
......@@ -824,9 +824,18 @@ class RawKeyboard {
modifierKeys[physicalModifier] = _allModifiers[physicalModifier]!;
}
}
_allModifiersExceptFn.keys
.where((PhysicalKeyboardKey key) => !anySideKeys.contains(key))
.forEach(_keysPressed.remove);
// On Linux, CapsLock key can be mapped to a non-modifier logical key:
// https://github.com/flutter/flutter/issues/114591.
// This is also affecting Flutter Web on Linux.
final bool nonModifierCapsLock = (event.data is RawKeyEventDataLinux || event.data is RawKeyEventDataWeb)
&& _keysPressed[PhysicalKeyboardKey.capsLock] != null
&& _keysPressed[PhysicalKeyboardKey.capsLock] != LogicalKeyboardKey.capsLock;
for (final PhysicalKeyboardKey physicalKey in _allModifiersExceptFn.keys) {
final bool skipReleasingKey = nonModifierCapsLock && physicalKey == PhysicalKeyboardKey.capsLock;
if (!anySideKeys.contains(physicalKey) && !skipReleasingKey) {
_keysPressed.remove(physicalKey);
}
}
if (event.data is! RawKeyEventDataFuchsia && event.data is! RawKeyEventDataMacOs) {
// On Fuchsia and macOS, the Fn key is not considered a modifier key.
_keysPressed.remove(PhysicalKeyboardKey.fn);
......
......@@ -106,8 +106,8 @@ class RawKeyEventDataWeb extends RawKeyEventData {
return maybeLocationKey;
}
// Look to see if the [code] is one we know about and have a mapping for.
final LogicalKeyboardKey? newKey = kWebToLogicalKey[code];
// Look to see if the [key] is one we know about and have a mapping for.
final LogicalKeyboardKey? newKey = kWebToLogicalKey[key];
if (newKey != null) {
return newKey;
}
......
......@@ -350,6 +350,33 @@ void main() {
);
}, skip: isBrowser); // [intended] This is a GLFW-specific test.
Future<void> simulateGTKKeyEvent(bool keyDown, int scancode, int keycode, int modifiers) async {
final Map<String, dynamic> data = <String, dynamic>{
'type': keyDown ? 'keydown' : 'keyup',
'keymap': 'linux',
'toolkit': 'gtk',
'scanCode': scancode,
'keyCode': keycode,
'modifiers': modifiers,
};
// Dispatch an empty key data to disable HardwareKeyboard sanity check,
// since we're only testing if the raw keyboard can handle the message.
// In a real application, the embedder responder will send the correct key data
// (which is tested in the engine).
TestDefaultBinaryMessengerBinding.instance!.keyEventManager.handleKeyData(const ui.KeyData(
type: ui.KeyEventType.down,
timeStamp: Duration.zero,
logical: 0,
physical: 0,
character: null,
synthesized: false,
));
await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.handlePlatformMessage(
SystemChannels.keyEvent.name,
SystemChannels.keyEvent.codec.encodeMessage(data),
(ByteData? data) {},
);
}
// Regression test for https://github.com/flutter/flutter/issues/93278 .
//
......@@ -357,38 +384,11 @@ void main() {
// result in a AltRight down event without Alt bitmask.
testWidgets('keysPressed modifiers are synchronized with key events on Linux GTK (down events)', (WidgetTester tester) async {
expect(RawKeyboard.instance.keysPressed, isEmpty);
Future<void> simulate(bool keyDown, int scancode, int keycode, int modifiers) async {
final Map<String, dynamic> data = <String, dynamic>{
'type': keyDown ? 'keydown' : 'keyup',
'keymap': 'linux',
'toolkit': 'gtk',
'scanCode': scancode,
'keyCode': keycode,
'modifiers': modifiers,
};
// Dispatch an empty key data to disable HardwareKeyboard sanity check,
// since we're only testing if the raw keyboard can handle the message.
// In real application the embedder responder will send correct key data
// (which is tested in the engine.)
TestDefaultBinaryMessengerBinding.instance!.keyEventManager.handleKeyData(const ui.KeyData(
type: ui.KeyEventType.down,
timeStamp: Duration.zero,
logical: 0,
physical: 0,
character: null,
synthesized: false,
));
await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.handlePlatformMessage(
SystemChannels.keyEvent.name,
SystemChannels.keyEvent.codec.encodeMessage(data),
(ByteData? data) {},
);
}
await simulate(true, 0x6c/*AltRight*/, 0xffea/*AltRight*/, 0x2000000);
await simulate(true, 0x32/*ShiftLeft*/, 0xfe08/*NextGroup*/, 0x2000008/*MOD3*/);
await simulate(false, 0x6c/*AltRight*/, 0xfe03/*AltRight*/, 0x2002008/*MOD3|Reserve14*/);
await simulate(true, 0x6c/*AltRight*/, 0xfe03/*AltRight*/, 0x2002000/*Reserve14*/);
await simulateGTKKeyEvent(true, 0x6c/*AltRight*/, 0xffea/*AltRight*/, 0x2000000);
await simulateGTKKeyEvent(true, 0x32/*ShiftLeft*/, 0xfe08/*NextGroup*/, 0x2000008/*MOD3*/);
await simulateGTKKeyEvent(false, 0x6c/*AltRight*/, 0xfe03/*AltRight*/, 0x2002008/*MOD3|Reserve14*/);
await simulateGTKKeyEvent(true, 0x6c/*AltRight*/, 0xfe03/*AltRight*/, 0x2002000/*Reserve14*/);
expect(
RawKeyboard.instance.keysPressed,
equals(
......@@ -399,6 +399,56 @@ void main() {
);
}, skip: isBrowser); // [intended] This is a GTK-specific test.
// Regression test for https://github.com/flutter/flutter/issues/114591 .
//
// On Linux, CapsLock can be remapped to a non-modifier key.
testWidgets('CapsLock should not be release when remapped on Linux', (WidgetTester tester) async {
expect(RawKeyboard.instance.keysPressed, isEmpty);
await simulateGTKKeyEvent(true, 0x42/*CapsLock*/, 0xff08/*Backspace*/, 0x2000000);
expect(
RawKeyboard.instance.keysPressed,
equals(
<LogicalKeyboardKey>{
LogicalKeyboardKey.backspace,
},
),
);
}, skip: isBrowser); // [intended] This is a GTK-specific test.
// Regression test for https://github.com/flutter/flutter/issues/114591 .
//
// On Web, CapsLock can be remapped to a non-modifier key.
testWidgets('CapsLock should not be release when remapped on Web', (WidgetTester _) async {
final List<RawKeyEvent> events = <RawKeyEvent>[];
RawKeyboard.instance.addListener(events.add);
addTearDown(() {
RawKeyboard.instance.removeListener(events.add);
});
await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.handlePlatformMessage(
SystemChannels.keyEvent.name,
SystemChannels.keyEvent.codec.encodeMessage(const <String, dynamic>{
'type': 'keydown',
'keymap': 'web',
'code': 'CapsLock',
'key': 'Backspace',
'location': 0,
'metaState': 0,
'keyCode': 8,
}),
(ByteData? data) { },
);
expect(
RawKeyboard.instance.keysPressed,
equals(
<LogicalKeyboardKey>{
LogicalKeyboardKey.backspace,
},
),
);
}, skip: !isBrowser); // [intended] This is a Browser-specific test.
testWidgets('keysPressed modifiers are synchronized with key events on web', (WidgetTester tester) async {
expect(RawKeyboard.instance.keysPressed, isEmpty);
// Generate the data for a regular key down event. Change the modifiers so
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment