Unverified Commit 65ea7671 authored by Tong Mu's avatar Tong Mu Committed by GitHub

[gen_keycode, RawKeyboard] Apply derived keyboard layout from Linux (#102709)

parent 5288ff8f
...@@ -206,6 +206,8 @@ Future<void> main(List<String> rawArguments) async { ...@@ -206,6 +206,8 @@ Future<void> main(List<String> rawArguments) async {
logicalData = LogicalKeyData.fromJson(json.decode(await File(parsedArguments['logical-data'] as String).readAsString()) as Map<String, dynamic>); logicalData = LogicalKeyData.fromJson(json.decode(await File(parsedArguments['logical-data'] as String).readAsString()) as Map<String, dynamic>);
} }
final Map<String, bool> layoutGoals = parseMapOfBool(readDataFile('layout_goals.json'));
final File codeFile = File(parsedArguments['code'] as String); final File codeFile = File(parsedArguments['code'] as String);
if (!codeFile.existsSync()) { if (!codeFile.existsSync()) {
codeFile.createSync(recursive: true); codeFile.createSync(recursive: true);
...@@ -236,6 +238,7 @@ Future<void> main(List<String> rawArguments) async { ...@@ -236,6 +238,7 @@ Future<void> main(List<String> rawArguments) async {
'macos': MacOSCodeGenerator( 'macos': MacOSCodeGenerator(
physicalData, physicalData,
logicalData, logicalData,
layoutGoals,
), ),
'ios': IOSCodeGenerator( 'ios': IOSCodeGenerator(
physicalData, physicalData,
...@@ -251,6 +254,7 @@ Future<void> main(List<String> rawArguments) async { ...@@ -251,6 +254,7 @@ Future<void> main(List<String> rawArguments) async {
logicalData, logicalData,
readDataFile('gtk_modifier_bit_mapping.json'), readDataFile('gtk_modifier_bit_mapping.json'),
readDataFile('gtk_lock_bit_mapping.json'), readDataFile('gtk_lock_bit_mapping.json'),
layoutGoals,
), ),
'web': WebCodeGenerator( 'web': WebCodeGenerator(
physicalData, physicalData,
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
| [`chromium_modifiers.json`](chromium_modifiers.json) | Maps the web's `key` for modifier keys to the names of the logical keys for these keys' left and right variations.This is used when generating logical keys to provide independent values for sided logical keys. Web uses the same `key` for modifier keys of different sides, but Flutter's logical key model treats them as different keys.| | [`chromium_modifiers.json`](chromium_modifiers.json) | Maps the web's `key` for modifier keys to the names of the logical keys for these keys' left and right variations.This is used when generating logical keys to provide independent values for sided logical keys. Web uses the same `key` for modifier keys of different sides, but Flutter's logical key model treats them as different keys.|
| [`printable.json`](printable.json) | Maps Flutter key name to its printable character. This character is used as the key label.| | [`printable.json`](printable.json) | Maps Flutter key name to its printable character. This character is used as the key label.|
| [`synonyms.json`](synonyms.json) | Maps pseudo-keys that represent other keys to the sets of keys they represent. For example, this contains the "shift" key that represents either a "shiftLeft" or "shiftRight" key.| | [`synonyms.json`](synonyms.json) | Maps pseudo-keys that represent other keys to the sets of keys they represent. For example, this contains the "shift" key that represents either a "shiftLeft" or "shiftRight" key.|
| [`layout_goals.json`](layout_goals.json) | A list of layout goals, keys that the platform keyboard manager should find mappings for. Each key in this file is the key name of the goal, both logical and physical simultaneously, while its value represents whether the goal is mandatory. A mandatory goal must be fulfilled, and the manager will use the default value from this file if a mapping can not be found. A non-mandatory goal is suggestive, only used if the key mapping information is malformed (e.g. contains no ASCII characters.) |
### Framework ### Framework
......
...@@ -35,4 +35,8 @@ void initialize_lock_bit_to_checked_keys(GHashTable* table) { ...@@ -35,4 +35,8 @@ void initialize_lock_bit_to_checked_keys(GHashTable* table) {
@@@GTK_MODE_BIT_MAP@@@ @@@GTK_MODE_BIT_MAP@@@
} }
const std::vector<LayoutGoal> layout_goals = {
@@@LAYOUT_GOALS@@@
};
@@@MASK_CONSTANTS@@@ @@@MASK_CONSTANTS@@@
{
"KeyA": true,
"KeyB": true,
"KeyC": true,
"KeyD": true,
"KeyE": true,
"KeyF": true,
"KeyG": true,
"KeyH": true,
"KeyI": true,
"KeyJ": true,
"KeyK": true,
"KeyL": true,
"KeyM": true,
"KeyN": true,
"KeyO": true,
"KeyP": true,
"KeyQ": true,
"KeyR": true,
"KeyS": true,
"KeyT": true,
"KeyU": true,
"KeyV": true,
"KeyW": true,
"KeyX": true,
"KeyY": true,
"KeyZ": true,
"Digit1": true,
"Digit2": true,
"Digit3": true,
"Digit4": true,
"Digit5": true,
"Digit6": true,
"Digit7": true,
"Digit8": true,
"Digit9": true,
"Digit0": true,
"Quote": false,
"Comma": false,
"Minus": false,
"Period": false,
"Slash": false,
"Semicolon": false,
"Equal": false,
"BracketLeft": false,
"Backslash": false,
"BracketRight": false,
"Backquote": false,
"IntlBackslash": false
}
...@@ -19,6 +19,7 @@ class GtkCodeGenerator extends PlatformCodeGenerator { ...@@ -19,6 +19,7 @@ class GtkCodeGenerator extends PlatformCodeGenerator {
super.logicalData, super.logicalData,
String modifierBitMapping, String modifierBitMapping,
String lockBitMapping, String lockBitMapping,
this._layoutGoals,
) : _modifierBitMapping = parseMapOfListOfString(modifierBitMapping), ) : _modifierBitMapping = parseMapOfListOfString(modifierBitMapping),
_lockBitMapping = parseMapOfListOfString(lockBitMapping); _lockBitMapping = parseMapOfListOfString(lockBitMapping);
...@@ -91,6 +92,24 @@ class GtkCodeGenerator extends PlatformCodeGenerator { ...@@ -91,6 +92,24 @@ class GtkCodeGenerator extends PlatformCodeGenerator {
} }
final Map<String, List<String>> _lockBitMapping; final Map<String, List<String>> _lockBitMapping;
final Map<String, bool> _layoutGoals;
String get _layoutGoalsString {
final OutputLines<int> lines = OutputLines<int>('GTK layout goals');
_layoutGoals.forEach((String name, bool mandatory) {
final PhysicalKeyEntry physicalEntry = keyData.entryByName(name);
final LogicalKeyEntry logicalEntry = logicalData.entryByName(name);
final String line = 'LayoutGoal{'
'${toHex(physicalEntry.xKbScanCode, digits: 2)}, '
'${toHex(logicalEntry.value, digits: 2)}, '
'${mandatory ? 'true' : 'false'}'
'},';
lines.add(logicalEntry.value,
' ${line.padRight(39)}'
'// ${logicalEntry.name}');
});
return lines.sortedJoin().trimRight();
}
/// This generates the mask values for the part of a key code that defines its plane. /// This generates the mask values for the part of a key code that defines its plane.
String get _maskConstants { String get _maskConstants {
final StringBuffer buffer = StringBuffer(); final StringBuffer buffer = StringBuffer();
...@@ -120,6 +139,7 @@ class GtkCodeGenerator extends PlatformCodeGenerator { ...@@ -120,6 +139,7 @@ class GtkCodeGenerator extends PlatformCodeGenerator {
'GTK_MODIFIER_BIT_MAP': _gtkModifierBitMap, 'GTK_MODIFIER_BIT_MAP': _gtkModifierBitMap,
'GTK_MODE_BIT_MAP': _gtkModeBitMap, 'GTK_MODE_BIT_MAP': _gtkModeBitMap,
'MASK_CONSTANTS': _maskConstants, 'MASK_CONSTANTS': _maskConstants,
'LAYOUT_GOALS': _layoutGoalsString,
}; };
} }
} }
...@@ -89,7 +89,7 @@ class IOSCodeGenerator extends PlatformCodeGenerator { ...@@ -89,7 +89,7 @@ class IOSCodeGenerator extends PlatformCodeGenerator {
String get _keyToModifierFlagMap { String get _keyToModifierFlagMap {
final StringBuffer modifierKeyMap = StringBuffer(); final StringBuffer modifierKeyMap = StringBuffer();
for (final String name in kModifiersOfInterest) { for (final String name in kModifiersOfInterest) {
final String line =' {${toHex(logicalData.entryByName(name).iOSKeyCodeValues[0])}, kModifierFlag${lowerCamelToUpperCamel(name)}},'; final String line = '{${toHex(logicalData.entryByName(name).iOSKeyCodeValues[0])}, kModifierFlag${lowerCamelToUpperCamel(name)}},';
modifierKeyMap.writeln(' ${line.padRight(42)}// $name'); modifierKeyMap.writeln(' ${line.padRight(42)}// $name');
} }
return modifierKeyMap.toString().trimRight(); return modifierKeyMap.toString().trimRight();
...@@ -99,7 +99,7 @@ class IOSCodeGenerator extends PlatformCodeGenerator { ...@@ -99,7 +99,7 @@ class IOSCodeGenerator extends PlatformCodeGenerator {
String get _modifierFlagToKeyMap { String get _modifierFlagToKeyMap {
final StringBuffer modifierKeyMap = StringBuffer(); final StringBuffer modifierKeyMap = StringBuffer();
for (final String name in kModifiersOfInterest) { for (final String name in kModifiersOfInterest) {
final String line =' {kModifierFlag${lowerCamelToUpperCamel(name)}, ${toHex(logicalData.entryByName(name).iOSKeyCodeValues[0])}},'; final String line = '{kModifierFlag${lowerCamelToUpperCamel(name)}, ${toHex(logicalData.entryByName(name).iOSKeyCodeValues[0])}},';
modifierKeyMap.writeln(' ${line.padRight(42)}// $name'); modifierKeyMap.writeln(' ${line.padRight(42)}// $name');
} }
return modifierKeyMap.toString().trimRight(); return modifierKeyMap.toString().trimRight();
......
...@@ -28,7 +28,7 @@ const List<String> kSpecialLogicalKeys = <String>['CapsLock']; ...@@ -28,7 +28,7 @@ const List<String> kSpecialLogicalKeys = <String>['CapsLock'];
/// Generates the key mapping for macOS, based on the information in the key /// Generates the key mapping for macOS, based on the information in the key
/// data structure given to it. /// data structure given to it.
class MacOSCodeGenerator extends PlatformCodeGenerator { class MacOSCodeGenerator extends PlatformCodeGenerator {
MacOSCodeGenerator(super.keyData, super.logicalData); MacOSCodeGenerator(super.keyData, super.logicalData, this._layoutGoals);
/// This generates the map of macOS key codes to physical keys. /// This generates the map of macOS key codes to physical keys.
String get _scanCodeMap { String get _scanCodeMap {
...@@ -96,24 +96,21 @@ class MacOSCodeGenerator extends PlatformCodeGenerator { ...@@ -96,24 +96,21 @@ class MacOSCodeGenerator extends PlatformCodeGenerator {
return specialKeyConstants.toString().trimRight(); return specialKeyConstants.toString().trimRight();
} }
String get _layoutGoals { final Map<String, bool> _layoutGoals;
String get _layoutGoalsString {
final OutputLines<int> lines = OutputLines<int>('macOS layout goals'); final OutputLines<int> lines = OutputLines<int>('macOS layout goals');
final Iterable<LogicalKeyEntry> asciiEntries = logicalData.entries.where( _layoutGoals.forEach((String name, bool mandatory) {
(LogicalKeyEntry entry) => entry.value <= 128); final PhysicalKeyEntry physicalEntry = keyData.entryByName(name);
for (final LogicalKeyEntry logicalEntry in asciiEntries) { final LogicalKeyEntry logicalEntry = logicalData.entryByName(name);
final int value = logicalEntry.value; final String line = 'LayoutGoal{'
final PhysicalKeyEntry? physicalEntry = keyData.tryEntryByName(logicalEntry.name); '${toHex(physicalEntry.macOSScanCode, digits: 2)}, '
if (physicalEntry == null) { '${toHex(logicalEntry.value, digits: 2)}, '
continue; '${mandatory ? 'true' : 'false'}'
} '},';
final bool mandatory = (value >= '0'.codeUnitAt(0) && value <= '9'.codeUnitAt(0)) lines.add(logicalEntry.value,
|| (value >= 'a'.codeUnitAt(0) && value <= 'z'.codeUnitAt(0)); ' ${line.padRight(39)}'
lines.add(value, '// ${logicalEntry.name}');
' LayoutGoal{${toHex(physicalEntry.macOSScanCode, digits: 2)}, ' });
'${toHex(value, digits: 2)}, '
'${mandatory ? 'true}, ' : 'false},'}'
' // ${logicalEntry.name}');
}
return lines.sortedJoin().trimRight(); return lines.sortedJoin().trimRight();
} }
...@@ -136,7 +133,7 @@ class MacOSCodeGenerator extends PlatformCodeGenerator { ...@@ -136,7 +133,7 @@ class MacOSCodeGenerator extends PlatformCodeGenerator {
'KEYCODE_TO_MODIFIER_FLAG_MAP': _keyToModifierFlagMap, 'KEYCODE_TO_MODIFIER_FLAG_MAP': _keyToModifierFlagMap,
'MODIFIER_FLAG_TO_KEYCODE_MAP': _modifierFlagToKeyMap, 'MODIFIER_FLAG_TO_KEYCODE_MAP': _modifierFlagToKeyMap,
'SPECIAL_KEY_CONSTANTS': _specialKeyConstants, 'SPECIAL_KEY_CONSTANTS': _specialKeyConstants,
'LAYOUT_GOALS': _layoutGoals, 'LAYOUT_GOALS': _layoutGoalsString,
}; };
} }
} }
...@@ -158,7 +158,7 @@ class PhysicalKeyData { ...@@ -158,7 +158,7 @@ class PhysicalKeyData {
input = input.replaceAll(commentRegExp, ''); input = input.replaceAll(commentRegExp, '');
for (final RegExpMatch match in usbMapRegExp.allMatches(input)) { for (final RegExpMatch match in usbMapRegExp.allMatches(input)) {
final int usbHidCode = getHex(match.namedGroup('usb')!); final int usbHidCode = getHex(match.namedGroup('usb')!);
final int linuxScanCode = getHex(match.namedGroup('evdev')!); final int evdevCode = getHex(match.namedGroup('evdev')!);
final int xKbScanCode = getHex(match.namedGroup('xkb')!); final int xKbScanCode = getHex(match.namedGroup('xkb')!);
final int windowsScanCode = getHex(match.namedGroup('win')!); final int windowsScanCode = getHex(match.namedGroup('win')!);
final int macScanCode = getHex(match.namedGroup('mac')!); final int macScanCode = getHex(match.namedGroup('mac')!);
...@@ -174,7 +174,7 @@ class PhysicalKeyData { ...@@ -174,7 +174,7 @@ class PhysicalKeyData {
final PhysicalKeyEntry newEntry = PhysicalKeyEntry( final PhysicalKeyEntry newEntry = PhysicalKeyEntry(
usbHidCode: usbHidCode, usbHidCode: usbHidCode,
androidScanCodes: nameToAndroidScanCodes[name] ?? <int>[], androidScanCodes: nameToAndroidScanCodes[name] ?? <int>[],
linuxScanCode: linuxScanCode == 0 ? null : linuxScanCode, evdevCode: evdevCode == 0 ? null : evdevCode,
xKbScanCode: xKbScanCode == 0 ? null : xKbScanCode, xKbScanCode: xKbScanCode == 0 ? null : xKbScanCode,
windowsScanCode: windowsScanCode == 0 ? null : windowsScanCode, windowsScanCode: windowsScanCode == 0 ? null : windowsScanCode,
macOSScanCode: macScanCode == 0xffff ? null : macScanCode, macOSScanCode: macScanCode == 0xffff ? null : macScanCode,
...@@ -210,7 +210,7 @@ class PhysicalKeyEntry { ...@@ -210,7 +210,7 @@ class PhysicalKeyEntry {
required this.usbHidCode, required this.usbHidCode,
required this.name, required this.name,
required this.androidScanCodes, required this.androidScanCodes,
required this.linuxScanCode, required this.evdevCode,
required this.xKbScanCode, required this.xKbScanCode,
required this.windowsScanCode, required this.windowsScanCode,
required this.macOSScanCode, required this.macOSScanCode,
...@@ -227,7 +227,7 @@ class PhysicalKeyEntry { ...@@ -227,7 +227,7 @@ class PhysicalKeyEntry {
chromiumCode: names['chromium'] as String?, chromiumCode: names['chromium'] as String?,
usbHidCode: scanCodes['usb'] as int, usbHidCode: scanCodes['usb'] as int,
androidScanCodes: (scanCodes['android'] as List<dynamic>?)?.cast<int>() ?? <int>[], androidScanCodes: (scanCodes['android'] as List<dynamic>?)?.cast<int>() ?? <int>[],
linuxScanCode: scanCodes['linux'] as int?, evdevCode: scanCodes['linux'] as int?,
xKbScanCode: scanCodes['xkb'] as int?, xKbScanCode: scanCodes['xkb'] as int?,
windowsScanCode: scanCodes['windows'] as int?, windowsScanCode: scanCodes['windows'] as int?,
macOSScanCode: scanCodes['macos'] as int?, macOSScanCode: scanCodes['macos'] as int?,
...@@ -238,8 +238,8 @@ class PhysicalKeyEntry { ...@@ -238,8 +238,8 @@ class PhysicalKeyEntry {
/// The USB HID code of the key /// The USB HID code of the key
final int usbHidCode; final int usbHidCode;
/// The Linux scan code of the key, from Chromium's header file. /// The Evdev scan code of the key, from Chromium's header file.
final int? linuxScanCode; final int? evdevCode;
/// The XKb scan code of the key from Chromium's header file. /// The XKb scan code of the key from Chromium's header file.
final int? xKbScanCode; final int? xKbScanCode;
/// The Windows scan code of the key from Chromium's header file. /// The Windows scan code of the key from Chromium's header file.
...@@ -269,7 +269,7 @@ class PhysicalKeyEntry { ...@@ -269,7 +269,7 @@ class PhysicalKeyEntry {
'scanCodes': <String, dynamic>{ 'scanCodes': <String, dynamic>{
'android': androidScanCodes, 'android': androidScanCodes,
'usb': usbHidCode, 'usb': usbHidCode,
'linux': linuxScanCode, 'linux': evdevCode,
'xkb': xKbScanCode, 'xkb': xKbScanCode,
'windows': windowsScanCode, 'windows': windowsScanCode,
'macos': macOSScanCode, 'macos': macOSScanCode,
...@@ -318,7 +318,7 @@ class PhysicalKeyEntry { ...@@ -318,7 +318,7 @@ class PhysicalKeyEntry {
@override @override
String toString() { String toString() {
return """'$constantName': (name: "$name", usbHidCode: ${toHex(usbHidCode)}, """ return """'$constantName': (name: "$name", usbHidCode: ${toHex(usbHidCode)}, """
'linuxScanCode: ${toHex(linuxScanCode)}, xKbScanCode: ${toHex(xKbScanCode)}, ' 'linuxScanCode: ${toHex(evdevCode)}, xKbScanCode: ${toHex(xKbScanCode)}, '
'windowsKeyCode: ${toHex(windowsScanCode)}, macOSScanCode: ${toHex(macOSScanCode)}, ' 'windowsKeyCode: ${toHex(windowsScanCode)}, macOSScanCode: ${toHex(macOSScanCode)}, '
'windowsScanCode: ${toHex(windowsScanCode)}, chromiumSymbolName: $chromiumCode ' 'windowsScanCode: ${toHex(windowsScanCode)}, chromiumSymbolName: $chromiumCode '
'iOSScanCode: ${toHex(iOSScanCode)})'; 'iOSScanCode: ${toHex(iOSScanCode)})';
......
...@@ -184,6 +184,10 @@ Map<String, List<String?>> parseMapOfListOfNullableString(String jsonString) { ...@@ -184,6 +184,10 @@ Map<String, List<String?>> parseMapOfListOfNullableString(String jsonString) {
}); });
} }
Map<String, bool> parseMapOfBool(String jsonString) {
return (json.decode(jsonString) as Map<String, dynamic>).cast<String, bool>();
}
/// Reverse the map of { fromValue -> list of toValue } to { toValue -> fromValue } and return. /// Reverse the map of { fromValue -> list of toValue } to { toValue -> fromValue } and return.
Map<String, String> reverseMapOfListOfString(Map<String, List<String>> inMap, void Function(String fromValue, String newToValue) onDuplicate) { Map<String, String> reverseMapOfListOfString(Map<String, List<String>> inMap, void Function(String fromValue, String newToValue) onDuplicate) {
final Map<String, String> result = <String, String>{}; final Map<String, String> result = <String, String>{};
......
...@@ -2,7 +2,7 @@ name: gen_keycodes ...@@ -2,7 +2,7 @@ name: gen_keycodes
description: Generates keycode source files from various resources. description: Generates keycode source files from various resources.
environment: environment:
sdk: ">=2.17.0-0 <3.0.0" sdk: ">=2.18.0-0 <3.0.0"
dependencies: dependencies:
args: 2.3.0 args: 2.3.0
......
...@@ -26,6 +26,8 @@ final PhysicalKeyData physicalData = PhysicalKeyData.fromJson( ...@@ -26,6 +26,8 @@ final PhysicalKeyData physicalData = PhysicalKeyData.fromJson(
json.decode(readDataFile('physical_key_data.json')) as Map<String, dynamic>); json.decode(readDataFile('physical_key_data.json')) as Map<String, dynamic>);
final LogicalKeyData logicalData = LogicalKeyData.fromJson( final LogicalKeyData logicalData = LogicalKeyData.fromJson(
json.decode(readDataFile('logical_key_data.json')) as Map<String, dynamic>); json.decode(readDataFile('logical_key_data.json')) as Map<String, dynamic>);
final Map<String, bool> keyGoals = parseMapOfBool(
readDataFile('layout_goals.json'));
void main() { void main() {
setUp(() { setUp(() {
...@@ -65,6 +67,7 @@ void main() { ...@@ -65,6 +67,7 @@ void main() {
final PlatformCodeGenerator codeGenerator = MacOSCodeGenerator( final PlatformCodeGenerator codeGenerator = MacOSCodeGenerator(
physicalData, physicalData,
logicalData, logicalData,
keyGoals,
); );
final String output = codeGenerator.generate(); final String output = codeGenerator.generate();
...@@ -119,6 +122,7 @@ void main() { ...@@ -119,6 +122,7 @@ void main() {
logicalData, logicalData,
readDataFile(path.join(dataRoot, 'gtk_modifier_bit_mapping.json')), readDataFile(path.join(dataRoot, 'gtk_modifier_bit_mapping.json')),
readDataFile(path.join(dataRoot, 'gtk_lock_bit_mapping.json')), readDataFile(path.join(dataRoot, 'gtk_lock_bit_mapping.json')),
keyGoals,
); );
final String output = codeGenerator.generate(); final String output = codeGenerator.generate();
......
...@@ -364,6 +364,7 @@ abstract class RawKeyEvent with Diagnosticable { ...@@ -364,6 +364,7 @@ abstract class RawKeyEvent with Diagnosticable {
scanCode: message['scanCode'] as int? ?? 0, scanCode: message['scanCode'] as int? ?? 0,
modifiers: message['modifiers'] as int? ?? 0, modifiers: message['modifiers'] as int? ?? 0,
isDown: message['type'] == 'keydown', isDown: message['type'] == 'keydown',
specifiedLogicalKey: message['specifiedLogicalKey'] as int?,
); );
if (unicodeScalarValues != 0) { if (unicodeScalarValues != 0) {
character = String.fromCharCode(unicodeScalarValues); character = String.fromCharCode(unicodeScalarValues);
......
...@@ -28,6 +28,7 @@ class RawKeyEventDataLinux extends RawKeyEventData { ...@@ -28,6 +28,7 @@ class RawKeyEventDataLinux extends RawKeyEventData {
this.keyCode = 0, this.keyCode = 0,
this.modifiers = 0, this.modifiers = 0,
required this.isDown, required this.isDown,
this.specifiedLogicalKey,
}) : assert(scanCode != null), }) : assert(scanCode != null),
assert(unicodeScalarValues != null), assert(unicodeScalarValues != null),
assert((unicodeScalarValues & ~LogicalKeyboardKey.valueMask) == 0), assert((unicodeScalarValues & ~LogicalKeyboardKey.valueMask) == 0),
...@@ -68,6 +69,15 @@ class RawKeyEventDataLinux extends RawKeyEventData { ...@@ -68,6 +69,15 @@ class RawKeyEventDataLinux extends RawKeyEventData {
/// Whether or not this key event is a key down (true) or key up (false). /// Whether or not this key event is a key down (true) or key up (false).
final bool isDown; final bool isDown;
/// A logical key specified by the embedding that should be used instead of
/// deriving from raw data.
///
/// The GTK embedding detects the keyboard layout and maps some keys to
/// logical keys in a way that can not be derived from per-key information.
///
/// This is not part of the native GTK key event.
final int? specifiedLogicalKey;
@override @override
String get keyLabel => unicodeScalarValues == 0 ? '' : String.fromCharCode(unicodeScalarValues); String get keyLabel => unicodeScalarValues == 0 ? '' : String.fromCharCode(unicodeScalarValues);
...@@ -76,6 +86,10 @@ class RawKeyEventDataLinux extends RawKeyEventData { ...@@ -76,6 +86,10 @@ class RawKeyEventDataLinux extends RawKeyEventData {
@override @override
LogicalKeyboardKey get logicalKey { LogicalKeyboardKey get logicalKey {
if (specifiedLogicalKey != null) {
final int key = specifiedLogicalKey!;
return LogicalKeyboardKey.findKeyByKeyId(key) ?? LogicalKeyboardKey(key);
}
// Look to see if the keyCode is a printable number pad key, so that a // Look to see if the keyCode is a printable number pad key, so that a
// difference between regular keys (e.g. "=") and the number pad version // difference between regular keys (e.g. "=") and the number pad version
// (e.g. the "=" on the number pad) can be determined. // (e.g. the "=" on the number pad) can be determined.
...@@ -124,6 +138,7 @@ class RawKeyEventDataLinux extends RawKeyEventData { ...@@ -124,6 +138,7 @@ class RawKeyEventDataLinux extends RawKeyEventData {
properties.add(DiagnosticsProperty<int>('keyCode', keyCode)); properties.add(DiagnosticsProperty<int>('keyCode', keyCode));
properties.add(DiagnosticsProperty<int>('modifiers', modifiers)); properties.add(DiagnosticsProperty<int>('modifiers', modifiers));
properties.add(DiagnosticsProperty<bool>('isDown', isDown)); properties.add(DiagnosticsProperty<bool>('isDown', isDown));
properties.add(DiagnosticsProperty<int?>('specifiedLogicalKey', specifiedLogicalKey, defaultValue: null));
} }
@override @override
......
...@@ -246,6 +246,7 @@ class RawKeyEventDataMacOs extends RawKeyEventData { ...@@ -246,6 +246,7 @@ class RawKeyEventDataMacOs extends RawKeyEventData {
properties.add(DiagnosticsProperty<String>('charactersIgnoringModifiers', charactersIgnoringModifiers)); properties.add(DiagnosticsProperty<String>('charactersIgnoringModifiers', charactersIgnoringModifiers));
properties.add(DiagnosticsProperty<int>('keyCode', keyCode)); properties.add(DiagnosticsProperty<int>('keyCode', keyCode));
properties.add(DiagnosticsProperty<int>('modifiers', modifiers)); properties.add(DiagnosticsProperty<int>('modifiers', modifiers));
properties.add(DiagnosticsProperty<int?>('specifiedLogicalKey', specifiedLogicalKey, defaultValue: null));
} }
@override @override
......
...@@ -2421,6 +2421,21 @@ void main() { ...@@ -2421,6 +2421,21 @@ void main() {
expect(data.keyLabel, isEmpty); expect(data.keyLabel, isEmpty);
}); });
test('Prioritize logical key from specifiedLogicalKey', () {
final RawKeyEvent digit1FromFrench = RawKeyEvent.fromMessage(const <String, dynamic>{
'type': 'keydown',
'keymap': 'linux',
'toolkit': 'gtk',
'keyCode': 0x6c6,
'scanCode': 0x26,
'unicodeScalarValues': 0x424,
'specifiedLogicalKey': 0x61,
});
final RawKeyEventDataLinux data = digit1FromFrench.data as RawKeyEventDataLinux;
expect(data.physicalKey, equals(PhysicalKeyboardKey.keyA));
expect(data.logicalKey, equals(LogicalKeyboardKey.keyA));
});
test('data.toString', () { test('data.toString', () {
expect(RawKeyEvent.fromMessage(const <String, Object?>{ expect(RawKeyEvent.fromMessage(const <String, Object?>{
'type': 'keydown', 'type': 'keydown',
......
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