Unverified Commit b70d260b authored by Francisco Magdaleno's avatar Francisco Magdaleno Committed by GitHub

Adds the keyboard mapping for Linux (#29993)

parent a1712dcf
......@@ -97,6 +97,11 @@ class _HardwareKeyDemoState extends State<RawKeyboardDemo> {
dataText.add(Text('characters: ${data.characters}'));
dataText.add(Text('charactersIgnoringModifiers: ${data.charactersIgnoringModifiers}'));
dataText.add(Text('modifiers: ${data.modifiers} (${_asHex(data.modifiers)})'));
} else if (data is RawKeyEventDataLinux) {
dataText.add(Text('keyCode: ${data.keyCode} (${_asHex(data.keyCode)})'));
dataText.add(Text('scanCode: ${data.scanCode}'));
dataText.add(Text('codePoint: ${data.codePoint}'));
dataText.add(Text('modifiers: ${data.modifiers} (${_asHex(data.modifiers)})'));
}
dataText.add(Text('logical: ${_event.logicalKey}'));
dataText.add(Text('physical: ${_event.physicalKey}'));
......
......@@ -38,6 +38,11 @@ Future<String> getAndroidScanCodes() async {
return utf8.decode(base64.decode(await http.read(scanCodesUri)));
}
Future<String> getGlfwKeyCodes() async {
final Uri keyCodesUri = Uri.parse('https://raw.githubusercontent.com/glfw/glfw/master/include/GLFW/glfw3.h');
return await http.read(keyCodesUri);
}
Future<void> main(List<String> rawArguments) async {
final ArgParser argParser = ArgParser();
argParser.addOption(
......@@ -66,6 +71,19 @@ Future<void> main(List<String> rawArguments) async {
defaultsTo: path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'key_name_to_android_name.json'),
help: 'The path to where the Android keycode to DomKey mapping is.',
);
argParser.addOption(
'glfw-keycodes',
defaultsTo: null,
help: 'The path to where the GLFW keycodes header file should be read. '
'If --glfw-keycodes is not specified, the input will be read from the '
'correct file in the GLFW github repository.',
);
argParser.addOption(
'glfw-domkey',
defaultsTo: path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'key_name_to_glfw_name.json'),
help: 'The path to where the GLFW keycode to DomKey mapping is.',
);
argParser.addOption(
'data',
defaultsTo: path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'key_data.json'),
......@@ -136,8 +154,17 @@ Future<void> main(List<String> rawArguments) async {
androidScanCodes = File(parsedArguments['android-scancodes']).readAsStringSync();
}
String glfwKeyCodes;
if (parsedArguments['glfw-keycodes'] == null) {
glfwKeyCodes = await getGlfwKeyCodes();
} else {
glfwKeyCodes = File(parsedArguments['glfw-keycodes']).readAsStringSync();
}
final String glfwToDomKey = File(parsedArguments['glfw-domkey']).readAsStringSync();
final String androidToDomKey = File(parsedArguments['android-domkey']).readAsStringSync();
data = KeyData(hidCodes, androidScanCodes, androidKeyCodes, androidToDomKey);
data = KeyData(hidCodes, androidScanCodes, androidKeyCodes, androidToDomKey, glfwKeyCodes, glfwToDomKey);
const JsonEncoder encoder = JsonEncoder.withIndent(' ');
File(parsedArguments['data']).writeAsStringSync(encoder.convert(data.toJson()));
......
This diff is collapsed.
{
"altLeft": ["LEFT_ALT"],
"altRight": ["RIGHT_ALT"],
"arrowDown": ["DOWN"],
"arrowLeft": ["LEFT"],
"arrowRight": ["RIGHT"],
"arrowUp": ["UP"],
"backquote": ["GRAVE_ACCENT"],
"backslash": ["BACKSLASH"],
"backspace": ["BACKSPACE"],
"bracketLeft": ["LEFT_BRACKET"],
"bracketRight": ["RIGHT_BRACKET"],
"capsLock": ["CAPS_LOCK"],
"controlLeft": ["LEFT_CONTROL"],
"controlRight": ["RIGHT_CONTROL"],
"contextMenu": ["MENU"],
"comma": ["COMMA"],
"delete": ["DELETE"],
"digit0": ["0"],
"digit1": ["1"],
"digit2": ["2"],
"digit3": ["3"],
"digit4": ["4"],
"digit5": ["5"],
"digit6": ["6"],
"digit7": ["7"],
"digit8": ["8"],
"digit9": ["9"],
"end": ["END"],
"enter": ["ENTER"],
"equal": ["EQUAL"],
"escape": ["ESCAPE"],
"f1": ["F1"],
"f2": ["F2"],
"f3": ["F3"],
"f4": ["F4"],
"f5": ["F5"],
"f6": ["F6"],
"f7": ["F7"],
"f8": ["F8"],
"f9": ["F9"],
"f10": ["F10"],
"f11": ["F11"],
"f12": ["F12"],
"f13": ["F13"],
"f14": ["F14"],
"f15": ["F15"],
"f16": ["F16"],
"f17": ["F17"],
"f18": ["F18"],
"f19": ["F19"],
"f20": ["F20"],
"f21": ["F21"],
"f22": ["F22"],
"f23": ["F23"],
"f25": ["F25"],
"home": ["HOME"],
"insert": ["INSERT"],
"keyA": ["A"],
"keyB": ["B"],
"keyC": ["C"],
"keyD": ["D"],
"keyE": ["E"],
"keyF": ["F"],
"keyG": ["G"],
"keyH": ["H"],
"keyI": ["I"],
"keyJ": ["J"],
"keyK": ["K"],
"keyL": ["L"],
"keyM": ["M"],
"keyN": ["N"],
"keyO": ["O"],
"keyP": ["P"],
"keyQ": ["Q"],
"keyR": ["R"],
"keyS": ["S"],
"keyT": ["T"],
"keyU": ["U"],
"keyV": ["V"],
"keyW": ["W"],
"keyX": ["X"],
"keyY": ["Y"],
"keyZ": ["Z"],
"minus": ["MINUS"],
"numLock": ["NUM_LOCK"],
"numpad0": ["KP_0"],
"numpad1": ["KP_1"],
"numpad2": ["KP_2"],
"numpad3": ["KP_3"],
"numpad4": ["KP_4"],
"numpad5": ["KP_5"],
"numpad6": ["KP_6"],
"numpad7": ["KP_7"],
"numpad8": ["KP_8"],
"numpad9": ["KP_9"],
"numpadDecimal": ["KP_DECIMAL"],
"numpadDivide": ["KP_DIVIDE"],
"numpadMultiply": ["KP_MULTIPLY"],
"numpadSubtract": ["NUMPAD_SUBTRACT"],
"numpadAdd": ["KP_ADD"],
"numpadEnter": ["KP_ENTER"],
"numpadEqual": ["KP_EQUAL"],
"pageDown": ["PAGE_DOWN"],
"pageUp": ["PAGE_UP"],
"pause": ["PAUSE"],
"printScreen": ["PRINT_SCREEN"],
"quote": ["APOSTROPHE"],
"period": ["PERIOD"],
"semicolon": ["SEMICOLON"],
"slash": ["SLASH"],
"space": ["SPACE"],
"shiftLeft": ["LEFT_SHIFT"],
"shiftRight": ["RIGHT_SHIFT"],
"tab": ["TAB"]
}
......@@ -50,3 +50,21 @@ const Map<int, PhysicalKeyboardKey> kMacOsToPhysicalKey = <int, PhysicalKeyboard
const Map<int, LogicalKeyboardKey> kMacOsNumPadMap = <int, LogicalKeyboardKey>{
@@@MACOS_NUMPAD_MAP@@@
};
/// Maps GLFW-specific key codes to the matching [LogicalKeyboardKey].
const Map<int, LogicalKeyboardKey> kGlfwToLogicalKey = <int, LogicalKeyboardKey>{
@@@GLFW_KEY_CODE_MAP@@@
};
/// A map of GLFW key codes which have printable representations, but appear
/// on the number pad. Used to provide different key objects for keys like
/// KEY_EQUALS and NUMPAD_EQUALS.
const Map<int, LogicalKeyboardKey> kGlfwNumpadMap = <int, LogicalKeyboardKey>{
@@@GLFW_NUMPAD_MAP@@@
};
/// Maps XKB specific key code values representing [PhysicalKeyboardKey].
const Map<int, PhysicalKeyboardKey> kLinuxToPhysicalKey = <int, PhysicalKeyboardKey>{
@@@XKB_SCAN_CODE_MAP@@@
};
......@@ -93,6 +93,46 @@ $otherComments static const LogicalKeyboardKey ${entry.constantName} = LogicalK
return keyCodeMap.toString().trimRight();
}
/// This generates the map of GLFW number pad key codes to logical keys.
String get glfwNumpadMap {
final StringBuffer glfwNumpadMap = StringBuffer();
final List<Key> onlyNumpads = keyData.data.where((Key entry) {
return entry.constantName.startsWith('numpad') && entry.keyLabel != null;
}).toList();
for (Key entry in onlyNumpads) {
if (entry.glfwKeyCodes != null) {
for (int code in entry.glfwKeyCodes.cast<int>()) {
glfwNumpadMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},');
}
}
}
return glfwNumpadMap.toString().trimRight();
}
/// This generates the map of GLFW key codes to logical keys.
String get glfwKeyCodeMap {
final StringBuffer glfwKeyCodeMap = StringBuffer();
for (Key entry in keyData.data) {
if (entry.glfwKeyCodes != null) {
for (int code in entry.glfwKeyCodes.cast<int>()) {
glfwKeyCodeMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},');
}
}
}
return glfwKeyCodeMap.toString().trimRight();
}
/// This generates the map of XKB USB HID codes to physical keys.
String get xkbScanCodeMap {
final StringBuffer xkbScanCodeMap = StringBuffer();
for (Key entry in keyData.data) {
if (entry.xKbScanCode != null) {
xkbScanCodeMap.writeln(' ${toHex(entry.xKbScanCode)}: PhysicalKeyboardKey.${entry.constantName},');
}
}
return xkbScanCodeMap.toString().trimRight();
}
/// This generates the map of Android key codes to logical keys.
String get androidKeyCodeMap {
final StringBuffer androidKeyCodeMap = StringBuffer();
......@@ -210,6 +250,9 @@ $otherComments static const LogicalKeyboardKey ${entry.constantName} = LogicalK
'FUCHSIA_KEY_CODE_MAP': fuchsiaKeyCodeMap,
'MACOS_SCAN_CODE_MAP': macOsScanCodeMap,
'MACOS_NUMPAD_MAP': macOsNumpadMap,
'GLFW_KEY_CODE_MAP': glfwKeyCodeMap,
'GLFW_NUMPAD_MAP': glfwNumpadMap,
'XKB_SCAN_CODE_MAP': xkbScanCodeMap,
};
final String template = File(path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'keyboard_maps.tmpl')).readAsStringSync();
......
......@@ -26,14 +26,25 @@ class KeyData {
String androidKeyboardLayout,
String androidKeyCodeHeader,
String androidNameMap,
String glfwKeyCodeHeader,
String glfwNameMap
) : assert(chromiumHidCodes != null),
assert(androidKeyboardLayout != null),
assert(androidKeyCodeHeader != null),
assert(androidNameMap != null) {
assert(androidNameMap != null),
assert(glfwKeyCodeHeader != null),
assert(glfwNameMap != null) {
_nameToAndroidScanCodes = _readAndroidScanCodes(androidKeyboardLayout);
_nameToAndroidKeyCode = _readAndroidKeyCodes(androidKeyCodeHeader);
final Map<String, List<dynamic>> dynamicNames = json.decode(androidNameMap).cast<String, List<dynamic>>();
_nameToAndroidName = dynamicNames.map<String, List<String>>((String key, List<dynamic> value) {
_nameToGlfwKeyCode = _readGlfwKeyCodes(glfwKeyCodeHeader);
// Cast Android dom map
final Map<String, List<dynamic>> dynamicAndroidNames = json.decode(androidNameMap).cast<String, List<dynamic>>();
_nameToAndroidName = dynamicAndroidNames.map<String, List<String>>((String key, List<dynamic> value) {
return MapEntry<String, List<String>>(key, value.cast<String>());
});
// Cast GLFW dom map
final Map<String, List<dynamic>> dynamicGlfwNames = json.decode(glfwNameMap).cast<String, List<dynamic>>();
_nameToGlfwName = dynamicGlfwNames.map<String, List<String>>((String key, List<dynamic> value) {
return MapEntry<String, List<String>>(key, value.cast<String>());
});
data = _readHidEntries(chromiumHidCodes);
......@@ -51,6 +62,7 @@ class KeyData {
/// [KeyData.fromJson].
Map<String, dynamic> toJson() {
for (Key entry in data) {
// Android Key names
entry.androidKeyNames = _nameToAndroidName[entry.constantName]?.cast<String>();
if (entry.androidKeyNames != null && entry.androidKeyNames.isNotEmpty) {
for (String androidKeyName in entry.androidKeyNames) {
......@@ -64,6 +76,17 @@ class KeyData {
}
}
}
// GLFW key names
entry.glfwKeyNames = _nameToGlfwName[entry.constantName]?.cast<String>();
if (entry.glfwKeyNames != null && entry.glfwKeyNames.isNotEmpty) {
for (String glfwKeyName in entry.glfwKeyNames) {
if (_nameToGlfwKeyCode[glfwKeyName] != null) {
entry.glfwKeyCodes ??= <int>[];
entry.glfwKeyCodes.add(_nameToGlfwKeyCode[glfwKeyName]);
}
}
}
}
final Map<String, dynamic> outputMap = <String, dynamic>{};
......@@ -83,6 +106,13 @@ class KeyData {
/// JSON.
Map<String, List<String>> _nameToAndroidName;
/// The mapping from the Flutter name (e.g. "eject") to the GLFW name (e.g.
/// "GLFW_MEDIA_EJECT").
///
/// Only populated if data is parsed from the source files, not if parsed from
/// JSON.
Map<String, List<String>> _nameToGlfwName;
/// The mapping from the Android name (e.g. "MEDIA_EJECT") to the integer scan
/// code (physical location) of the key.
///
......@@ -97,6 +127,13 @@ class KeyData {
/// JSON.
Map<String, int> _nameToAndroidKeyCode;
/// The mapping from GLFW name (e.g. "GLFW_KEY_COMMA") to the integer key code
/// (logical meaning) of the key.
///
/// Only populated if data is parsed from the source files, not if parsed from
/// JSON.
Map<String, int> _nameToGlfwKeyCode;
/// Parses entries from Androids Generic.kl scan code data file.
///
/// Lines in this file look like this (without the ///):
......@@ -147,6 +184,30 @@ class KeyData {
return result;
}
/// Parses entries from GLFW's keycodes.h key code data file.
///
/// Lines in this file look like this (without the ///):
/// /** Space key. */
/// #define GLFW_KEY_SPACE 32,
Map<String, int> _readGlfwKeyCodes(String headerFile) {
// Only get the KEY definitions, ignore the rest (mouse, joystick, etc).
final RegExp enumEntry = RegExp(r'''define GLFW_KEY_([A-Z0-9_]+)\s*([A-Z0-9_]+),?''');
final Map<String, dynamic> replaced = <String, dynamic>{};
headerFile.replaceAllMapped(enumEntry, (Match match) {
replaced[match.group(1)] = int.tryParse(match.group(2)) ?? match.group(2).replaceAll('GLFW_KEY_', '');
});
final Map<String, int> result = <String, int>{};
replaced.forEach((String key, dynamic value) {
// Some definition values point to other definitions (e.g #define GLFW_KEY_LAST GLFW_KEY_MENU).
if (value is String) {
result[key] = replaced[value];
} else {
result[key] = value;
}
});
return result;
}
/// Parses entries from Chromium's HID code mapping header file.
///
/// Lines in this file look like this (without the ///):
......@@ -211,6 +272,8 @@ class Key {
this.androidKeyNames,
this.androidScanCodes,
this.androidKeyCodes,
this.glfwKeyNames,
this.glfwKeyCodes,
}) : assert(usbHidCode != null),
assert(chromiumName != null),
_constantName = enumName;
......@@ -261,6 +324,15 @@ class Key {
/// code value.
List<int> androidScanCodes;
/// The list of names that GFLW gives to this key (symbol names minus the
/// prefix).
List<String> glfwKeyNames;
/// The list of GLFW key codes matching this key, created by looking up the
/// Linux name in the Chromium data, and substituting the GLFW key code
/// value.
List<int> glfwKeyCodes;
/// Creates a JSON map from the key data.
Map<String, dynamic> toJson() {
return <String, dynamic>{
......@@ -269,6 +341,7 @@ class Key {
'android': androidKeyNames,
'english': commentName,
'chromium': chromiumName,
'glfw': glfwKeyNames,
},
'scanCodes': <String, dynamic>{
'android': androidScanCodes,
......@@ -280,6 +353,7 @@ class Key {
},
'keyCodes': <String, List<int>>{
'android': androidKeyCodes,
'glfw': glfwKeyCodes,
},
};
}
......@@ -337,8 +411,8 @@ class Key {
@override
String toString() {
return """'$constantName': (name: "$name", usbHidCode: ${toHex(usbHidCode)}, """
'''linuxKeyCode: ${toHex(linuxScanCode)}, xKbKeyCode: ${toHex(xKbScanCode)}, '''
'''windowsKeyCode: ${toHex(windowsScanCode)}, macOsKeyCode: ${toHex(macOsScanCode)}, '''
'''linuxScanCode: ${toHex(linuxScanCode)}, xKbScanCode: ${toHex(xKbScanCode)}, '''
'''windowsKeyCode: ${toHex(windowsScanCode)}, macOsScanCode: ${toHex(macOsScanCode)}, '''
'''chromiumSymbolName: $chromiumName''';
}
......
......@@ -24,6 +24,7 @@ export 'src/services/platform_views.dart';
export 'src/services/raw_keyboard.dart';
export 'src/services/raw_keyboard_android.dart';
export 'src/services/raw_keyboard_fuchsia.dart';
export 'src/services/raw_keyboard_linux.dart';
export 'src/services/raw_keyboard_macos.dart';
export 'src/services/system_channels.dart';
export 'src/services/system_chrome.dart';
......
......@@ -9,6 +9,7 @@ import 'package:flutter/foundation.dart';
import 'keyboard_key.dart';
import 'raw_keyboard_android.dart';
import 'raw_keyboard_fuchsia.dart';
import 'raw_keyboard_linux.dart';
import 'raw_keyboard_macos.dart';
import 'system_channels.dart';
......@@ -272,6 +273,14 @@ abstract class RawKeyEvent {
keyCode: message['keyCode'] ?? 0,
modifiers: message['modifiers'] ?? 0);
break;
case 'linux':
data = RawKeyEventDataLinux(
keyHelper: KeyHelper(message['toolkit'] ?? ''),
codePoint: message['codePoint'] ?? 0,
keyCode: message['keyCode'] ?? 0,
scanCode: message['scanCode'] ?? 0,
modifiers: message['modifiers'] ?? 0);
break;
default:
// We don't yet implement raw key events on iOS or other platforms, but
// we don't hit this exception because the engine never sends us these
......
This diff is collapsed.
......@@ -92,7 +92,7 @@ void main() {
expect(
data.isModifierPressed(key, side: modifierTests[modifier].side),
isFalse,
reason: '$key should not be pressed with metaState $modifier with metaState $modifier '
reason: '$key should not be pressed with metaState $modifier '
'and additional key ${RawKeyEventDataAndroid.modifierFunction}.',
);
}
......@@ -264,7 +264,7 @@ void main() {
expect(data.keyLabel, isNull);
});
});
group('RawKeyEventDataMacOs', () {
group('RawKeyEventDataMacOs', () {
const Map<int, _ModifierCheck> modifierTests = <int, _ModifierCheck>{
RawKeyEventDataMacOs.modifierOption | RawKeyEventDataMacOs.modifierLeftOption: _ModifierCheck(ModifierKey.altModifier, KeyboardSide.left),
RawKeyEventDataMacOs.modifierOption | RawKeyEventDataMacOs.modifierRightOption: _ModifierCheck(ModifierKey.altModifier, KeyboardSide.right),
......@@ -340,7 +340,7 @@ void main() {
expect(
data.isModifierPressed(key, side: modifierTests[modifier].side),
isFalse,
reason: '$key should not be pressed with metaState $modifier with metaState $modifier '
reason: '$key should not be pressed with metaState $modifier '
'and additional key ${RawKeyEventDataMacOs.modifierFunction}.',
);
}
......@@ -393,4 +393,129 @@ void main() {
expect(data.keyLabel, isNull);
});
});
group('RawKeyEventDataLinux-GFLW', () {
const Map<int, _ModifierCheck> modifierTests = <int, _ModifierCheck>{
GLFWKeyHelper.modifierAlt: _ModifierCheck(ModifierKey.altModifier, KeyboardSide.any),
GLFWKeyHelper.modifierShift: _ModifierCheck(ModifierKey.shiftModifier, KeyboardSide.any),
GLFWKeyHelper.modifierControl: _ModifierCheck(ModifierKey.controlModifier, KeyboardSide.any),
GLFWKeyHelper.modifierMeta: _ModifierCheck(ModifierKey.metaModifier, KeyboardSide.any),
GLFWKeyHelper.modifierNumericPad: _ModifierCheck(ModifierKey.numLockModifier, KeyboardSide.all),
GLFWKeyHelper.modifierCapsLock: _ModifierCheck(ModifierKey.capsLockModifier, KeyboardSide.all),
};
test('modifier keys are recognized individually', () {
for (int modifier in modifierTests.keys) {
final RawKeyEvent event = RawKeyEvent.fromMessage(<String, dynamic>{
'type': 'keydown',
'keymap': 'linux',
'toolkit': 'glfw',
'keyCode': 0x04,
'scanCode': 0x01,
'codePoint': 0x10,
'modifiers': modifier,
});
final RawKeyEventDataLinux data = event.data;
for (ModifierKey key in ModifierKey.values) {
if (modifierTests[modifier].key == key) {
expect(
data.isModifierPressed(key, side: modifierTests[modifier].side),
isTrue,
reason: "$key should be pressed with metaState $modifier, but isn't.",
);
expect(data.getModifierSide(key), equals(modifierTests[modifier].side));
} else {
expect(
data.isModifierPressed(key, side: modifierTests[modifier].side),
isFalse,
reason: '$key should not be pressed with metaState $modifier.',
);
}
}
}
});
test('modifier keys are recognized when combined', () {
for (int modifier in modifierTests.keys) {
if (modifier == GLFWKeyHelper.modifierControl) {
// No need to combine CTRL key with itself.
continue;
}
final RawKeyEvent event = RawKeyEvent.fromMessage(<String, dynamic>{
'type': 'keydown',
'keymap': 'linux',
'toolkit': 'glfw',
'keyCode': 0x04,
'scanCode': 0x64,
'codePoint': 0x1,
'modifiers': modifier | GLFWKeyHelper.modifierControl,
});
final RawKeyEventDataLinux data = event.data;
for (ModifierKey key in ModifierKey.values) {
if (modifierTests[modifier].key == key || key == ModifierKey.controlModifier) {
expect(
data.isModifierPressed(key, side: modifierTests[modifier].side),
isTrue,
reason: '$key should be pressed with metaState $modifier '
"and additional key ${GLFWKeyHelper.modifierControl}, but isn't.",
);
if (key != ModifierKey.controlModifier) {
expect(data.getModifierSide(key), equals(modifierTests[modifier].side));
} else {
expect(data.getModifierSide(key), equals(KeyboardSide.any));
}
} else {
expect(
data.isModifierPressed(key, side: modifierTests[modifier].side),
isFalse,
reason: '$key should not be pressed with metaState $modifier '
'and additional key ${GLFWKeyHelper.modifierControl}.',
);
}
}
}
});
test('Printable keyboard keys are correctly translated', () {
final RawKeyEvent keyAEvent = RawKeyEvent.fromMessage(const <String, dynamic>{
'type': 'keydown',
'keymap': 'linux',
'toolkit': 'glfw',
'keyCode': 65,
'scanCode': 0x00000026,
'codePoint': 97,
'modifiers': 0x0,
});
final RawKeyEventDataLinux data = keyAEvent.data;
expect(data.physicalKey, equals(PhysicalKeyboardKey.keyA));
expect(data.logicalKey, equals(LogicalKeyboardKey.keyA));
expect(data.keyLabel, equals('a'));
});
test('Control keyboard keys are correctly translated', () {
final RawKeyEvent escapeKeyEvent = RawKeyEvent.fromMessage(const <String, dynamic>{
'type': 'keydown',
'keymap': 'linux',
'toolkit': 'glfw',
'keyCode': 256,
'scanCode': 0x00000009,
'codePoint': 0,
'modifiers': 0x0,
});
final RawKeyEventDataLinux data = escapeKeyEvent.data;
expect(data.physicalKey, equals(PhysicalKeyboardKey.escape));
expect(data.logicalKey, equals(LogicalKeyboardKey.escape));
expect(data.keyLabel, isNull);
});
test('Modifier keyboard keys are correctly translated', () {
final RawKeyEvent shiftLeftKeyEvent = RawKeyEvent.fromMessage(const <String, dynamic>{
'type': 'keydown',
'keymap': 'linux',
'toolkit': 'glfw',
'keyCode': 340,
'scanCode': 0x00000032,
'codePoint': 0,
});
final RawKeyEventDataLinux data = shiftLeftKeyEvent.data;
expect(data.physicalKey, equals(PhysicalKeyboardKey.shiftLeft));
expect(data.logicalKey, equals(LogicalKeyboardKey.shiftLeft));
expect(data.keyLabel, isNull);
});
});
}
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