Adds the keyboard mapping for Linux (#29993)

This commit is contained in:
Francisco Magdaleno 2019-03-28 10:04:23 -07:00 committed by GitHub
parent a1712dcfbe
commit b70d260b3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 2308 additions and 372 deletions

View File

@ -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}'));

View File

@ -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()));

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,116 @@
{
"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"]
}

View File

@ -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@@@
};

View File

@ -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();

View File

@ -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''';
}

View File

@ -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';

View File

@ -843,9 +843,7 @@ const Map<int, PhysicalKeyboardKey> kFuchsiaToPhysicalKey = <int, PhysicalKeyboa
0x000c028c: PhysicalKeyboardKey.mailSend,
};
/// Maps macOS-specific key code values representing [PhysicalKeyboardKey].
/// MacOS doesn't provide a scan code, but a virtual keycode to represent a physical key.
const Map<int, PhysicalKeyboardKey> kMacOsToPhysicalKey = <int, PhysicalKeyboardKey>{
0x00000000: PhysicalKeyboardKey.keyA,
0x0000000b: PhysicalKeyboardKey.keyB,
@ -989,3 +987,353 @@ const Map<int, LogicalKeyboardKey> kMacOsNumPadMap = <int, LogicalKeyboardKey>{
0x00000051: LogicalKeyboardKey.numpadEqual,
0x0000005f: LogicalKeyboardKey.numpadComma,
};
/// Maps GLFW-specific key codes to the matching [LogicalKeyboardKey].
const Map<int, LogicalKeyboardKey> kGlfwToLogicalKey = <int, LogicalKeyboardKey>{
65: LogicalKeyboardKey.keyA,
66: LogicalKeyboardKey.keyB,
67: LogicalKeyboardKey.keyC,
68: LogicalKeyboardKey.keyD,
69: LogicalKeyboardKey.keyE,
70: LogicalKeyboardKey.keyF,
71: LogicalKeyboardKey.keyG,
72: LogicalKeyboardKey.keyH,
73: LogicalKeyboardKey.keyI,
74: LogicalKeyboardKey.keyJ,
75: LogicalKeyboardKey.keyK,
76: LogicalKeyboardKey.keyL,
77: LogicalKeyboardKey.keyM,
78: LogicalKeyboardKey.keyN,
79: LogicalKeyboardKey.keyO,
80: LogicalKeyboardKey.keyP,
81: LogicalKeyboardKey.keyQ,
82: LogicalKeyboardKey.keyR,
83: LogicalKeyboardKey.keyS,
84: LogicalKeyboardKey.keyT,
85: LogicalKeyboardKey.keyU,
86: LogicalKeyboardKey.keyV,
87: LogicalKeyboardKey.keyW,
88: LogicalKeyboardKey.keyX,
89: LogicalKeyboardKey.keyY,
90: LogicalKeyboardKey.keyZ,
49: LogicalKeyboardKey.digit1,
50: LogicalKeyboardKey.digit2,
51: LogicalKeyboardKey.digit3,
52: LogicalKeyboardKey.digit4,
53: LogicalKeyboardKey.digit5,
54: LogicalKeyboardKey.digit6,
55: LogicalKeyboardKey.digit7,
56: LogicalKeyboardKey.digit8,
57: LogicalKeyboardKey.digit9,
48: LogicalKeyboardKey.digit0,
257: LogicalKeyboardKey.enter,
256: LogicalKeyboardKey.escape,
259: LogicalKeyboardKey.backspace,
258: LogicalKeyboardKey.tab,
32: LogicalKeyboardKey.space,
45: LogicalKeyboardKey.minus,
61: LogicalKeyboardKey.equal,
91: LogicalKeyboardKey.bracketLeft,
93: LogicalKeyboardKey.bracketRight,
92: LogicalKeyboardKey.backslash,
59: LogicalKeyboardKey.semicolon,
39: LogicalKeyboardKey.quote,
96: LogicalKeyboardKey.backquote,
44: LogicalKeyboardKey.comma,
46: LogicalKeyboardKey.period,
47: LogicalKeyboardKey.slash,
280: LogicalKeyboardKey.capsLock,
290: LogicalKeyboardKey.f1,
291: LogicalKeyboardKey.f2,
292: LogicalKeyboardKey.f3,
293: LogicalKeyboardKey.f4,
294: LogicalKeyboardKey.f5,
295: LogicalKeyboardKey.f6,
296: LogicalKeyboardKey.f7,
297: LogicalKeyboardKey.f8,
298: LogicalKeyboardKey.f9,
299: LogicalKeyboardKey.f10,
300: LogicalKeyboardKey.f11,
301: LogicalKeyboardKey.f12,
283: LogicalKeyboardKey.printScreen,
284: LogicalKeyboardKey.pause,
260: LogicalKeyboardKey.insert,
268: LogicalKeyboardKey.home,
266: LogicalKeyboardKey.pageUp,
261: LogicalKeyboardKey.delete,
269: LogicalKeyboardKey.end,
267: LogicalKeyboardKey.pageDown,
262: LogicalKeyboardKey.arrowRight,
263: LogicalKeyboardKey.arrowLeft,
264: LogicalKeyboardKey.arrowDown,
265: LogicalKeyboardKey.arrowUp,
282: LogicalKeyboardKey.numLock,
331: LogicalKeyboardKey.numpadDivide,
332: LogicalKeyboardKey.numpadMultiply,
334: LogicalKeyboardKey.numpadAdd,
335: LogicalKeyboardKey.numpadEnter,
321: LogicalKeyboardKey.numpad1,
322: LogicalKeyboardKey.numpad2,
323: LogicalKeyboardKey.numpad3,
324: LogicalKeyboardKey.numpad4,
325: LogicalKeyboardKey.numpad5,
326: LogicalKeyboardKey.numpad6,
327: LogicalKeyboardKey.numpad7,
328: LogicalKeyboardKey.numpad8,
329: LogicalKeyboardKey.numpad9,
320: LogicalKeyboardKey.numpad0,
330: LogicalKeyboardKey.numpadDecimal,
348: LogicalKeyboardKey.contextMenu,
336: LogicalKeyboardKey.numpadEqual,
302: LogicalKeyboardKey.f13,
303: LogicalKeyboardKey.f14,
304: LogicalKeyboardKey.f15,
305: LogicalKeyboardKey.f16,
306: LogicalKeyboardKey.f17,
307: LogicalKeyboardKey.f18,
308: LogicalKeyboardKey.f19,
309: LogicalKeyboardKey.f20,
310: LogicalKeyboardKey.f21,
311: LogicalKeyboardKey.f22,
312: LogicalKeyboardKey.f23,
341: LogicalKeyboardKey.controlLeft,
340: LogicalKeyboardKey.shiftLeft,
342: LogicalKeyboardKey.altLeft,
345: LogicalKeyboardKey.controlRight,
344: LogicalKeyboardKey.shiftRight,
346: LogicalKeyboardKey.altRight,
};
/// 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>{
331: LogicalKeyboardKey.numpadDivide,
332: LogicalKeyboardKey.numpadMultiply,
334: LogicalKeyboardKey.numpadAdd,
321: LogicalKeyboardKey.numpad1,
322: LogicalKeyboardKey.numpad2,
323: LogicalKeyboardKey.numpad3,
324: LogicalKeyboardKey.numpad4,
325: LogicalKeyboardKey.numpad5,
326: LogicalKeyboardKey.numpad6,
327: LogicalKeyboardKey.numpad7,
328: LogicalKeyboardKey.numpad8,
329: LogicalKeyboardKey.numpad9,
320: LogicalKeyboardKey.numpad0,
330: LogicalKeyboardKey.numpadDecimal,
336: LogicalKeyboardKey.numpadEqual,
};
/// Maps XKB specific key code values representing [PhysicalKeyboardKey].
const Map<int, PhysicalKeyboardKey> kLinuxToPhysicalKey = <int, PhysicalKeyboardKey>{
0x0000024f: PhysicalKeyboardKey.launchAssistant,
0x00000096: PhysicalKeyboardKey.sleep,
0x00000097: PhysicalKeyboardKey.wakeUp,
0x00000026: PhysicalKeyboardKey.keyA,
0x00000038: PhysicalKeyboardKey.keyB,
0x00000036: PhysicalKeyboardKey.keyC,
0x00000028: PhysicalKeyboardKey.keyD,
0x0000001a: PhysicalKeyboardKey.keyE,
0x00000029: PhysicalKeyboardKey.keyF,
0x0000002a: PhysicalKeyboardKey.keyG,
0x0000002b: PhysicalKeyboardKey.keyH,
0x0000001f: PhysicalKeyboardKey.keyI,
0x0000002c: PhysicalKeyboardKey.keyJ,
0x0000002d: PhysicalKeyboardKey.keyK,
0x0000002e: PhysicalKeyboardKey.keyL,
0x0000003a: PhysicalKeyboardKey.keyM,
0x00000039: PhysicalKeyboardKey.keyN,
0x00000020: PhysicalKeyboardKey.keyO,
0x00000021: PhysicalKeyboardKey.keyP,
0x00000018: PhysicalKeyboardKey.keyQ,
0x0000001b: PhysicalKeyboardKey.keyR,
0x00000027: PhysicalKeyboardKey.keyS,
0x0000001c: PhysicalKeyboardKey.keyT,
0x0000001e: PhysicalKeyboardKey.keyU,
0x00000037: PhysicalKeyboardKey.keyV,
0x00000019: PhysicalKeyboardKey.keyW,
0x00000035: PhysicalKeyboardKey.keyX,
0x0000001d: PhysicalKeyboardKey.keyY,
0x00000034: PhysicalKeyboardKey.keyZ,
0x0000000a: PhysicalKeyboardKey.digit1,
0x0000000b: PhysicalKeyboardKey.digit2,
0x0000000c: PhysicalKeyboardKey.digit3,
0x0000000d: PhysicalKeyboardKey.digit4,
0x0000000e: PhysicalKeyboardKey.digit5,
0x0000000f: PhysicalKeyboardKey.digit6,
0x00000010: PhysicalKeyboardKey.digit7,
0x00000011: PhysicalKeyboardKey.digit8,
0x00000012: PhysicalKeyboardKey.digit9,
0x00000013: PhysicalKeyboardKey.digit0,
0x00000024: PhysicalKeyboardKey.enter,
0x00000009: PhysicalKeyboardKey.escape,
0x00000016: PhysicalKeyboardKey.backspace,
0x00000017: PhysicalKeyboardKey.tab,
0x00000041: PhysicalKeyboardKey.space,
0x00000014: PhysicalKeyboardKey.minus,
0x00000015: PhysicalKeyboardKey.equal,
0x00000022: PhysicalKeyboardKey.bracketLeft,
0x00000023: PhysicalKeyboardKey.bracketRight,
0x00000033: PhysicalKeyboardKey.backslash,
0x0000002f: PhysicalKeyboardKey.semicolon,
0x00000030: PhysicalKeyboardKey.quote,
0x00000031: PhysicalKeyboardKey.backquote,
0x0000003b: PhysicalKeyboardKey.comma,
0x0000003c: PhysicalKeyboardKey.period,
0x0000003d: PhysicalKeyboardKey.slash,
0x00000042: PhysicalKeyboardKey.capsLock,
0x00000043: PhysicalKeyboardKey.f1,
0x00000044: PhysicalKeyboardKey.f2,
0x00000045: PhysicalKeyboardKey.f3,
0x00000046: PhysicalKeyboardKey.f4,
0x00000047: PhysicalKeyboardKey.f5,
0x00000048: PhysicalKeyboardKey.f6,
0x00000049: PhysicalKeyboardKey.f7,
0x0000004a: PhysicalKeyboardKey.f8,
0x0000004b: PhysicalKeyboardKey.f9,
0x0000004c: PhysicalKeyboardKey.f10,
0x0000005f: PhysicalKeyboardKey.f11,
0x00000060: PhysicalKeyboardKey.f12,
0x0000006b: PhysicalKeyboardKey.printScreen,
0x0000004e: PhysicalKeyboardKey.scrollLock,
0x0000007f: PhysicalKeyboardKey.pause,
0x00000076: PhysicalKeyboardKey.insert,
0x0000006e: PhysicalKeyboardKey.home,
0x00000070: PhysicalKeyboardKey.pageUp,
0x00000077: PhysicalKeyboardKey.delete,
0x00000073: PhysicalKeyboardKey.end,
0x00000075: PhysicalKeyboardKey.pageDown,
0x00000072: PhysicalKeyboardKey.arrowRight,
0x00000071: PhysicalKeyboardKey.arrowLeft,
0x00000074: PhysicalKeyboardKey.arrowDown,
0x0000006f: PhysicalKeyboardKey.arrowUp,
0x0000004d: PhysicalKeyboardKey.numLock,
0x0000006a: PhysicalKeyboardKey.numpadDivide,
0x0000003f: PhysicalKeyboardKey.numpadMultiply,
0x00000052: PhysicalKeyboardKey.numpadSubtract,
0x00000056: PhysicalKeyboardKey.numpadAdd,
0x00000068: PhysicalKeyboardKey.numpadEnter,
0x00000057: PhysicalKeyboardKey.numpad1,
0x00000058: PhysicalKeyboardKey.numpad2,
0x00000059: PhysicalKeyboardKey.numpad3,
0x00000053: PhysicalKeyboardKey.numpad4,
0x00000054: PhysicalKeyboardKey.numpad5,
0x00000055: PhysicalKeyboardKey.numpad6,
0x0000004f: PhysicalKeyboardKey.numpad7,
0x00000050: PhysicalKeyboardKey.numpad8,
0x00000051: PhysicalKeyboardKey.numpad9,
0x0000005a: PhysicalKeyboardKey.numpad0,
0x0000005b: PhysicalKeyboardKey.numpadDecimal,
0x0000005e: PhysicalKeyboardKey.intlBackslash,
0x00000087: PhysicalKeyboardKey.contextMenu,
0x0000007c: PhysicalKeyboardKey.power,
0x0000007d: PhysicalKeyboardKey.numpadEqual,
0x000000bf: PhysicalKeyboardKey.f13,
0x000000c0: PhysicalKeyboardKey.f14,
0x000000c1: PhysicalKeyboardKey.f15,
0x000000c2: PhysicalKeyboardKey.f16,
0x000000c3: PhysicalKeyboardKey.f17,
0x000000c4: PhysicalKeyboardKey.f18,
0x000000c5: PhysicalKeyboardKey.f19,
0x000000c6: PhysicalKeyboardKey.f20,
0x000000c7: PhysicalKeyboardKey.f21,
0x000000c8: PhysicalKeyboardKey.f22,
0x000000c9: PhysicalKeyboardKey.f23,
0x000000ca: PhysicalKeyboardKey.f24,
0x0000008e: PhysicalKeyboardKey.open,
0x00000092: PhysicalKeyboardKey.help,
0x0000008c: PhysicalKeyboardKey.select,
0x00000089: PhysicalKeyboardKey.again,
0x0000008b: PhysicalKeyboardKey.undo,
0x00000091: PhysicalKeyboardKey.cut,
0x0000008d: PhysicalKeyboardKey.copy,
0x0000008f: PhysicalKeyboardKey.paste,
0x00000090: PhysicalKeyboardKey.find,
0x00000079: PhysicalKeyboardKey.audioVolumeMute,
0x0000007b: PhysicalKeyboardKey.audioVolumeUp,
0x0000007a: PhysicalKeyboardKey.audioVolumeDown,
0x00000081: PhysicalKeyboardKey.numpadComma,
0x00000061: PhysicalKeyboardKey.intlRo,
0x00000065: PhysicalKeyboardKey.kanaMode,
0x00000084: PhysicalKeyboardKey.intlYen,
0x00000064: PhysicalKeyboardKey.convert,
0x00000066: PhysicalKeyboardKey.nonConvert,
0x00000082: PhysicalKeyboardKey.lang1,
0x00000083: PhysicalKeyboardKey.lang2,
0x00000062: PhysicalKeyboardKey.lang3,
0x00000063: PhysicalKeyboardKey.lang4,
0x0000005d: PhysicalKeyboardKey.lang5,
0x000000bb: PhysicalKeyboardKey.numpadParenLeft,
0x000000bc: PhysicalKeyboardKey.numpadParenRight,
0x0000007e: PhysicalKeyboardKey.numpadSignChange,
0x00000025: PhysicalKeyboardKey.controlLeft,
0x00000032: PhysicalKeyboardKey.shiftLeft,
0x00000040: PhysicalKeyboardKey.altLeft,
0x00000085: PhysicalKeyboardKey.metaLeft,
0x00000069: PhysicalKeyboardKey.controlRight,
0x0000003e: PhysicalKeyboardKey.shiftRight,
0x0000006c: PhysicalKeyboardKey.altRight,
0x00000086: PhysicalKeyboardKey.metaRight,
0x0000016e: PhysicalKeyboardKey.info,
0x0000017a: PhysicalKeyboardKey.closedCaptionToggle,
0x000000e9: PhysicalKeyboardKey.brightnessUp,
0x000000e8: PhysicalKeyboardKey.brightnessDown,
0x000001b7: PhysicalKeyboardKey.brightnessToggle,
0x00000258: PhysicalKeyboardKey.brightnessMinimum,
0x00000259: PhysicalKeyboardKey.brightnessMaximum,
0x000000fc: PhysicalKeyboardKey.brightnessAuto,
0x0000019d: PhysicalKeyboardKey.mediaLast,
0x000000b1: PhysicalKeyboardKey.launchPhone,
0x00000172: PhysicalKeyboardKey.programGuide,
0x000000b6: PhysicalKeyboardKey.exit,
0x000001a2: PhysicalKeyboardKey.channelUp,
0x000001a3: PhysicalKeyboardKey.channelDown,
0x000000d7: PhysicalKeyboardKey.mediaPlay,
0x000000af: PhysicalKeyboardKey.mediaRecord,
0x000000d8: PhysicalKeyboardKey.mediaFastForward,
0x000000b0: PhysicalKeyboardKey.mediaRewind,
0x000000ab: PhysicalKeyboardKey.mediaTrackNext,
0x000000ad: PhysicalKeyboardKey.mediaTrackPrevious,
0x000000ae: PhysicalKeyboardKey.mediaStop,
0x000000a9: PhysicalKeyboardKey.eject,
0x000000ac: PhysicalKeyboardKey.mediaPlayPause,
0x0000024e: PhysicalKeyboardKey.speechInputToggle,
0x000000d9: PhysicalKeyboardKey.bassBoost,
0x000000b3: PhysicalKeyboardKey.mediaSelect,
0x000001ad: PhysicalKeyboardKey.launchWordProcessor,
0x000001af: PhysicalKeyboardKey.launchSpreadsheet,
0x000000a3: PhysicalKeyboardKey.launchMail,
0x000001b5: PhysicalKeyboardKey.launchContacts,
0x00000195: PhysicalKeyboardKey.launchCalendar,
0x00000094: PhysicalKeyboardKey.launchApp2,
0x00000098: PhysicalKeyboardKey.launchApp1,
0x0000009e: PhysicalKeyboardKey.launchInternetBrowser,
0x000001b9: PhysicalKeyboardKey.logOff,
0x000000a0: PhysicalKeyboardKey.lockScreen,
0x0000024b: PhysicalKeyboardKey.launchControlPanel,
0x0000024c: PhysicalKeyboardKey.selectTask,
0x000000f3: PhysicalKeyboardKey.launchDocuments,
0x000001b8: PhysicalKeyboardKey.spellCheck,
0x0000017e: PhysicalKeyboardKey.launchKeyboardLayout,
0x0000024d: PhysicalKeyboardKey.launchScreenSaver,
0x00000190: PhysicalKeyboardKey.launchAudioBrowser,
0x000000bd: PhysicalKeyboardKey.newKey,
0x000000d6: PhysicalKeyboardKey.close,
0x000000f2: PhysicalKeyboardKey.save,
0x000000da: PhysicalKeyboardKey.print,
0x000000e1: PhysicalKeyboardKey.browserSearch,
0x000000b4: PhysicalKeyboardKey.browserHome,
0x000000a6: PhysicalKeyboardKey.browserBack,
0x000000a7: PhysicalKeyboardKey.browserForward,
0x00000088: PhysicalKeyboardKey.browserStop,
0x000000b5: PhysicalKeyboardKey.browserRefresh,
0x000000a4: PhysicalKeyboardKey.browserFavorites,
0x000001aa: PhysicalKeyboardKey.zoomIn,
0x000001ab: PhysicalKeyboardKey.zoomOut,
0x000000be: PhysicalKeyboardKey.redo,
0x000000f0: PhysicalKeyboardKey.mailReply,
0x000000f1: PhysicalKeyboardKey.mailForward,
0x000000ef: PhysicalKeyboardKey.mailSend,
};

View File

@ -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

View File

@ -0,0 +1,258 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'keyboard_key.dart';
import 'keyboard_maps.dart';
import 'raw_keyboard.dart';
/// Platform-specific key event data for Linux.
///
/// Different window toolkit implementations can map to different key codes. This class
/// will use the correct mapping depending on the [toolkit] provided.
///
/// See also:
///
/// * [RawKeyboard], which uses this interface to expose key data.
class RawKeyEventDataLinux extends RawKeyEventData {
/// Creates a key event data structure specific for macOS.
///
/// The [toolkit], [scanCode], [codePoint], [keyCode], and [modifiers], arguments
/// must not be null.
const RawKeyEventDataLinux({
@required this.keyHelper,
this.scanCode = 0,
this.codePoint = 0,
this.keyCode = 0,
this.modifiers = 0,
}) : assert(scanCode != null),
assert(codePoint != null),
assert(keyCode != null),
assert(modifiers != null),
assert(keyHelper != null);
/// A helper class that abstracts the fetching of the toolkit-specific mappings.
///
/// There is no real concept of a "native" window toolkit on Linux, and each implementation
/// (GLFW, GTK, QT, etc) may have a different key code mapping.
final KeyHelper keyHelper;
/// The hardware scan code id corresponding to this key event.
///
/// These values are not reliable and vary from device to device, so this
/// information is mainly useful for debugging.
final int scanCode;
/// The Unicode code point represented by the key event, if any.
///
/// If there is no Unicode code point, this value is zero.
///
/// Dead keys are represented as Unicode combining characters.
final int codePoint;
/// The hardware key code corresponding to this key event.
///
/// This is the physical key that was pressed, not the Unicode character.
/// See [codePoint] for the Unicode character. This value may be different depending
/// on the window toolkit used (See [toolkit]).
final int keyCode;
/// A mask of the current modifiers using the values in Modifier Flags.
/// This value may be different depending on the window toolkit used (See [toolkit]).
final int modifiers;
@override
String get keyLabel => codePoint == 0 ? null : String.fromCharCode(codePoint);
@override
PhysicalKeyboardKey get physicalKey => kLinuxToPhysicalKey[scanCode] ?? PhysicalKeyboardKey.none;
@override
LogicalKeyboardKey get logicalKey {
// 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
// (e.g. the "=" on the number pad) can be determined.
final LogicalKeyboardKey numPadKey = keyHelper.numpadKey(keyCode);
if (numPadKey != null) {
return numPadKey;
}
// If it has a non-control-character label, then either return the existing
// constant, or construct a new Unicode-based key from it. Don't mark it as
// autogenerated, since the label uniquely identifies an ID from the Unicode
// plane.
if (keyLabel != null &&
!LogicalKeyboardKey.isControlCharacter(keyLabel)) {
final int keyId = LogicalKeyboardKey.unicodePlane | (codePoint & LogicalKeyboardKey.valueMask);
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(
keyId,
keyLabel: keyLabel,
debugName: kReleaseMode ? null : 'Key ${keyLabel.toUpperCase()}',
);
}
// Look to see if the keyCode is one we know about and have a mapping for.
LogicalKeyboardKey newKey = keyHelper.logicalKey(keyCode);
if (newKey != null) {
return newKey;
}
const int linuxKeyIdPlane = 0x00600000000;
// This is a non-printable key that we don't know about, so we mint a new
// code with the autogenerated bit set.
newKey ??= LogicalKeyboardKey(
linuxKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask,
debugName: kReleaseMode ? null : 'Unknown key code $keyCode',
);
return newKey;
}
@override
bool isModifierPressed(ModifierKey key, {KeyboardSide side = KeyboardSide.any}) {
return keyHelper.isModifierPressed(key, modifiers, side: side);
}
@override
KeyboardSide getModifierSide(ModifierKey key) {
return keyHelper.getModifierSide(key);
}
@override
String toString() {
return '$runtimeType(keyLabel: $keyLabel, keyCode: $keyCode, scanCode: $scanCode,'
' codePoint: $codePoint, modifiers: $modifiers, '
'modifiers down: $modifiersPressed)';
}
}
/// Abstract class for window-specific key mappings.
///
/// Given that there might be multiple window toolkit implementations (GLFW,
/// GTK, QT, etc), this creates a common interface for each of the
/// different toolkits.
abstract class KeyHelper {
/// Create a KeyHelper implementation depending on the given toolkit.
factory KeyHelper(String toolkit) {
if (toolkit == 'glfw') {
return GLFWKeyHelper();
} else {
throw FlutterError('Window toolkit not recognized: $toolkit');
}
}
/// Returns a [KeyboardSide] enum value that describes which side or sides of
/// the given keyboard modifier key were pressed at the time of this event.
KeyboardSide getModifierSide(ModifierKey key);
/// Returns true if the given [ModifierKey] was pressed at the time of this
/// event.
bool isModifierPressed(ModifierKey key, int modifiers, {KeyboardSide side = KeyboardSide.any});
/// The numpad key from the specific key code mapping.
LogicalKeyboardKey numpadKey(int keyCode);
/// The logical key key from the specific key code mapping.
LogicalKeyboardKey logicalKey(int keyCode);
}
/// Helper class that uses GLFW-specific key mappings.
class GLFWKeyHelper with KeyHelper {
/// This mask is used to check the [modifiers] field to test whether the CAPS
/// LOCK modifier key is on.
///
/// {@template flutter.services.logicalKeyboardKey.modifiers}
/// Use this value if you need to decode the [modifiers] field yourself, but
/// it's much easier to use [isModifierPressed] if you just want to know if
/// a modifier is pressed.
/// {@endtemplate}
static const int modifierCapsLock = 0x0010;
/// This mask is used to check the [modifiers] field to test whether one of the
/// SHIFT modifier keys is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierShift = 0x0001;
/// This mask is used to check the [modifiers] field to test whether one of the
/// CTRL modifier keys is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierControl = 0x0002;
/// This mask is used to check the [modifiers] field to test whether one of the
/// ALT modifier keys is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierAlt = 0x0004;
/// This mask is used to check the [modifiers] field to test whether one of the
/// Meta(SUPER) modifier keys is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierMeta = 0x0008;
/// This mask is used to check the [modifiers] field to test whether any key in
/// the numeric keypad is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierNumericPad = 0x0020;
@override
bool isModifierPressed(ModifierKey key, int modifiers, {KeyboardSide side = KeyboardSide.any}) {
switch (key) {
case ModifierKey.controlModifier:
return modifiers & modifierControl != 0;
case ModifierKey.shiftModifier:
return modifiers & modifierShift != 0;
case ModifierKey.altModifier:
return modifiers & modifierAlt != 0;
case ModifierKey.metaModifier:
return modifiers & modifierMeta != 0;
case ModifierKey.capsLockModifier:
return modifiers & modifierCapsLock != 0;
case ModifierKey.numLockModifier:
return modifiers & modifierNumericPad != 0;
case ModifierKey.functionModifier:
case ModifierKey.symbolModifier:
case ModifierKey.scrollLockModifier:
// These are not used in GLFW keyboards.
return false;
}
return false;
}
@override
KeyboardSide getModifierSide(ModifierKey key) {
switch (key) {
case ModifierKey.controlModifier:
case ModifierKey.shiftModifier:
case ModifierKey.altModifier:
case ModifierKey.metaModifier:
// Neither GLFW or X11 provide a distiction between left and right modifiers, so defaults to KeyboardSide.any.
// https://code.woboq.org/qt5/include/X11/X.h.html#_M/ShiftMask
return KeyboardSide.any;
case ModifierKey.capsLockModifier:
case ModifierKey.numLockModifier:
case ModifierKey.functionModifier:
case ModifierKey.symbolModifier:
case ModifierKey.scrollLockModifier:
return KeyboardSide.all;
}
assert(false, 'Not handling $key type properly.');
return null;
}
@override
LogicalKeyboardKey numpadKey(int keyCode) {
return kGlfwNumpadMap[keyCode];
}
@override
LogicalKeyboardKey logicalKey(int keyCode) {
return kGlfwToLogicalKey[keyCode];
}
}

View File

@ -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);
});
});
}