Support GTK keycodes (#59961)
This commit is contained in:
@@ -49,6 +49,11 @@ Future<String> getGlfwKeyCodes() async {
|
||||
return await http.read(keyCodesUri);
|
||||
}
|
||||
|
||||
Future<String> getGtkKeyCodes() async {
|
||||
final Uri keyCodesUri = Uri.parse('https://gitlab.gnome.org/GNOME/gtk/-/raw/master/gdk/gdkkeysyms.h');
|
||||
return await http.read(keyCodesUri);
|
||||
}
|
||||
|
||||
Future<void> main(List<String> rawArguments) async {
|
||||
final ArgParser argParser = ArgParser();
|
||||
argParser.addOption(
|
||||
@@ -90,6 +95,13 @@ Future<void> main(List<String> rawArguments) async {
|
||||
'If --glfw-keycodes is not specified, the input will be read from the '
|
||||
'correct file in the GLFW github repository.',
|
||||
);
|
||||
argParser.addOption(
|
||||
'gtk-keycodes',
|
||||
defaultsTo: null,
|
||||
help: 'The path to where the GTK keycodes header file should be read. '
|
||||
'If --gtk-keycodes is not specified, the input will be read from the '
|
||||
'correct file in the GTK repository.',
|
||||
);
|
||||
argParser.addOption(
|
||||
'windows-keycodes',
|
||||
defaultsTo: null,
|
||||
@@ -107,6 +119,11 @@ Future<void> main(List<String> rawArguments) async {
|
||||
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(
|
||||
'gtk-domkey',
|
||||
defaultsTo: path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'key_name_to_gtk_name.json'),
|
||||
help: 'The path to where the GTK keycode to DomKey mapping is.',
|
||||
);
|
||||
argParser.addOption(
|
||||
'data',
|
||||
defaultsTo: path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'key_data.json'),
|
||||
@@ -187,6 +204,13 @@ Future<void> main(List<String> rawArguments) async {
|
||||
glfwKeyCodes = File(parsedArguments['glfw-keycodes'] as String).readAsStringSync();
|
||||
}
|
||||
|
||||
String gtkKeyCodes;
|
||||
if (parsedArguments['gtk-keycodes'] == null) {
|
||||
gtkKeyCodes = await getGtkKeyCodes();
|
||||
} else {
|
||||
gtkKeyCodes = File(parsedArguments['gtk-keycodes'] as String).readAsStringSync();
|
||||
}
|
||||
|
||||
String windowsKeyCodes;
|
||||
if (parsedArguments['windows-keycodes'] == null) {
|
||||
windowsKeyCodes = await getWindowsKeyCodes();
|
||||
@@ -196,9 +220,10 @@ Future<void> main(List<String> rawArguments) async {
|
||||
|
||||
final String windowsToDomKey = File(parsedArguments['windows-domkey'] as String).readAsStringSync();
|
||||
final String glfwToDomKey = File(parsedArguments['glfw-domkey'] as String).readAsStringSync();
|
||||
final String gtkToDomKey = File(parsedArguments['gtk-domkey'] as String).readAsStringSync();
|
||||
final String androidToDomKey = File(parsedArguments['android-domkey'] as String).readAsStringSync();
|
||||
|
||||
data = KeyData(hidCodes, androidScanCodes, androidKeyCodes, androidToDomKey, glfwKeyCodes, glfwToDomKey, windowsKeyCodes, windowsToDomKey);
|
||||
data = KeyData(hidCodes, androidScanCodes, androidKeyCodes, androidToDomKey, glfwKeyCodes, glfwToDomKey, gtkKeyCodes, gtkToDomKey, windowsKeyCodes, windowsToDomKey);
|
||||
|
||||
const JsonEncoder encoder = JsonEncoder.withIndent(' ');
|
||||
File(parsedArguments['data'] as String).writeAsStringSync(encoder.convert(data.toJson()));
|
||||
@@ -221,7 +246,7 @@ Future<void> main(List<String> rawArguments) async {
|
||||
await mapsFile.writeAsString(generator.generateKeyboardMaps());
|
||||
|
||||
final CcCodeGenerator ccCodeGenerator = CcCodeGenerator(data);
|
||||
for (final String platform in <String>['android', 'darwin', 'glfw', 'fuchsia', 'linux', 'windows']) {
|
||||
for (final String platform in <String>['android', 'darwin', 'glfw', 'gtk', 'fuchsia', 'linux', 'windows']) {
|
||||
final File platformFile = File(path.join(flutterRoot.path, '..', path.join('engine', 'src', 'flutter', 'shell', 'platform', platform, 'keycodes', 'keyboard_map_$platform.h')));
|
||||
if (!platformFile.existsSync()) {
|
||||
platformFile.createSync(recursive: true);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
179
dev/tools/gen_keycodes/data/key_name_to_gtk_name.json
Normal file
179
dev/tools/gen_keycodes/data/key_name_to_gtk_name.json
Normal file
@@ -0,0 +1,179 @@
|
||||
{
|
||||
"altLeft": ["Alt_L"],
|
||||
"altRight": ["Alt_R"],
|
||||
"arrowDown": ["Down", "KP_Down"],
|
||||
"arrowLeft": ["Left", "KP_Left"],
|
||||
"arrowRight": ["Right", "KP_Right"],
|
||||
"arrowUp": ["Up", "KP_Up"],
|
||||
"audioVolumeDown": ["AudioLowerVolume"],
|
||||
"audioVolumeMute": ["AudioMute"],
|
||||
"audioVolumeUp": ["AudioRaiseVolume"],
|
||||
"backquote": ["quoteleft"],
|
||||
"backslash": ["backslash"],
|
||||
"backspace": ["BackSpace"],
|
||||
"bracketLeft": ["bracketleft"],
|
||||
"bracketRight": ["bracketright"],
|
||||
"brightnessDown": ["MonBrightnessDown"],
|
||||
"brightnessUp": ["MonBrightnessUp"],
|
||||
"browserBack": ["Back"],
|
||||
"browserFavorites": ["Favorites"],
|
||||
"browserFavourites": ["Favourites"],
|
||||
"browserForward": ["Forward"],
|
||||
"browserHome": ["HomePage"],
|
||||
"browserRefresh": ["Refresh"],
|
||||
"browserSearch": ["Search"],
|
||||
"browserStop": ["Stop"],
|
||||
"capsLock": ["Caps_Lock"],
|
||||
"close": ["Close"],
|
||||
"comma": ["comma"],
|
||||
"contextMenu": ["Menu"],
|
||||
"controlLeft": ["Control_L"],
|
||||
"controlRight": ["Control_R"],
|
||||
"copy": ["Copy", "3270_Copy"],
|
||||
"delete": ["Delete", "KP_Delete"],
|
||||
"digit0": ["0"],
|
||||
"digit1": ["1"],
|
||||
"digit2": ["2"],
|
||||
"digit3": ["3"],
|
||||
"digit4": ["4"],
|
||||
"digit5": ["5"],
|
||||
"digit6": ["6"],
|
||||
"digit7": ["7"],
|
||||
"digit8": ["8"],
|
||||
"digit9": ["9"],
|
||||
"eject": ["Eject"],
|
||||
"end": ["End", "KP_End"],
|
||||
"enter": ["Return", "Enter"],
|
||||
"equal": ["equal"],
|
||||
"escape": ["Escape"],
|
||||
"f1": ["F1", "KP_F1"],
|
||||
"f2": ["F2", "KP_F2"],
|
||||
"f3": ["F3", "KP_F3"],
|
||||
"f4": ["F4", "KP_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"],
|
||||
"f24": ["F24"],
|
||||
"f25": ["F25"],
|
||||
"find": ["Find"],
|
||||
"help": ["Help"],
|
||||
"home": ["Home", "KP_Home"],
|
||||
"hyper": ["Hyper_L", "Hyper_R"],
|
||||
"insert": ["Insert", "KP_Insert"],
|
||||
"intlYen": ["yen"],
|
||||
"kanaMode": ["kana_switch"],
|
||||
"kbdIllumDown": ["KbdBrightnessDown"],
|
||||
"kbdIllumUp": ["KbdBrightnessUp"],
|
||||
"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"],
|
||||
"launchAudioBrowser": ["Music"],
|
||||
"launchCalendar": ["Calendar"],
|
||||
"launchDocuments": ["Document"],
|
||||
"launchInternetBrowser": ["WWW"],
|
||||
"launchMail": ["Mail"],
|
||||
"launchPhone": ["Phone"],
|
||||
"launchScreenSaver": ["ScreenSaver"],
|
||||
"logOff": ["LogOff"],
|
||||
"mailForward": ["MailForward"],
|
||||
"mailReply": ["Reply"],
|
||||
"mailSend": ["Send"],
|
||||
"mediaFastForward": ["AudioForward"],
|
||||
"mediaPause": ["AudioPause"],
|
||||
"mediaPlay": ["AudioPlay", "3270_Play"],
|
||||
"mediaRecord": ["AudioRecord"],
|
||||
"mediaRewind": ["AudioRewind"],
|
||||
"mediaStop": ["AudioStop"],
|
||||
"mediaTrackNext": ["AudioNext"],
|
||||
"mediaTrackPrevious": ["AudioPrev"],
|
||||
"metaLeft": ["Meta_L"],
|
||||
"metaRight": ["Meta_R"],
|
||||
"minus": ["minus"],
|
||||
"newKey": ["New"],
|
||||
"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"],
|
||||
"numpadAdd": ["KP_Add"],
|
||||
"numpadDecimal": ["KP_Decimal"],
|
||||
"numpadDivide": ["KP_Divide"],
|
||||
"numpadEnter": ["KP_Enter"],
|
||||
"numpadEqual": ["KP_Equal"],
|
||||
"numpadMultiply": ["KP_Multiply"],
|
||||
"numpadSubtract": ["KP_Subtract"],
|
||||
"open": ["Open"],
|
||||
"pageDown": ["Page_Down", "KP_Page_Down"],
|
||||
"pageUp": ["Page_Up", "KP_Page_Up"],
|
||||
"paste": ["Paste"],
|
||||
"pause": ["Pause"],
|
||||
"period": ["period"],
|
||||
"power": ["PowerOff"],
|
||||
"print": ["Print"],
|
||||
"printScreen": ["3270_PrintScreen"],
|
||||
"quote": ["apostrophe"],
|
||||
"redo": ["Redo"],
|
||||
"resume": ["Resume"],
|
||||
"save": ["Save"],
|
||||
"scrollLock": ["Scroll_Lock"],
|
||||
"select": ["Select"],
|
||||
"semicolon": ["semicolon"],
|
||||
"shiftLeft": ["Shift_L"],
|
||||
"shiftRight": ["Shift_R"],
|
||||
"slash": ["slash"],
|
||||
"sleep": ["Sleep"],
|
||||
"space": ["space", "KP_Space"],
|
||||
"spellCheck": ["Spell"],
|
||||
"superKey": ["Super_L", "Super_R"],
|
||||
"suspend": ["Suspend"],
|
||||
"tab": ["Tab", "KP_Tab"],
|
||||
"undo": ["Undo"],
|
||||
"wakeUp": ["WakeUp"],
|
||||
"zoomIn": ["ZoomIn"],
|
||||
"zoomOut": ["ZoomOut"]
|
||||
}
|
||||
24
dev/tools/gen_keycodes/data/keyboard_map_gtk_cc.tmpl
Normal file
24
dev/tools/gen_keycodes/data/keyboard_map_gtk_cc.tmpl
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <map>
|
||||
|
||||
// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT
|
||||
// This file is generated by flutter/flutter@dev/tools/gen_keycodes/bin/gen_keycodes.dart and
|
||||
// should not be edited directly.
|
||||
//
|
||||
// Edit the template dev/tools/gen_keycodes/data/keyboard_maps_gtk_cxx.tmpl instead.
|
||||
// See dev/tools/gen_keycodes/README.md for more information.
|
||||
|
||||
/// Maps GTK-specific key codes to the matching [LogicalKeyboardKey].
|
||||
const std::map<int, int> g_gtk_to_logical_key = {
|
||||
@@@GTK_KEY_CODE_MAP@@@
|
||||
};
|
||||
|
||||
/// A map of GTK 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 std::map<int, int> g_gtk_numpad_map = {
|
||||
@@@GTK_NUMPAD_MAP@@@
|
||||
};
|
||||
@@ -71,6 +71,18 @@ const Map<int, LogicalKeyboardKey> kGlfwNumpadMap = <int, LogicalKeyboardKey>{
|
||||
@@@GLFW_NUMPAD_MAP@@@
|
||||
};
|
||||
|
||||
/// Maps GTK-specific key codes to the matching [LogicalKeyboardKey].
|
||||
const Map<int, LogicalKeyboardKey> kGtkToLogicalKey = <int, LogicalKeyboardKey>{
|
||||
@@@GTK_KEY_CODE_MAP@@@
|
||||
};
|
||||
|
||||
/// A map of GTK 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> kGtkNumpadMap = <int, LogicalKeyboardKey>{
|
||||
@@@GTK_NUMPAD_MAP@@@
|
||||
};
|
||||
|
||||
/// Maps XKB specific key code values representing [PhysicalKeyboardKey].
|
||||
const Map<int, PhysicalKeyboardKey> kLinuxToPhysicalKey = <int, PhysicalKeyboardKey>{
|
||||
@@@XKB_SCAN_CODE_MAP@@@
|
||||
|
||||
@@ -175,6 +175,32 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK
|
||||
return glfwKeyCodeMap.toString().trimRight();
|
||||
}
|
||||
|
||||
/// This generates the map of GTK number pad key codes to logical keys.
|
||||
String get gtkNumpadMap {
|
||||
final StringBuffer gtkNumpadMap = StringBuffer();
|
||||
for (final Key entry in numpadKeyData) {
|
||||
if (entry.gtkKeyCodes != null) {
|
||||
for (final int code in entry.gtkKeyCodes.cast<int>()) {
|
||||
gtkNumpadMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},');
|
||||
}
|
||||
}
|
||||
}
|
||||
return gtkNumpadMap.toString().trimRight();
|
||||
}
|
||||
|
||||
/// This generates the map of GTK key codes to logical keys.
|
||||
String get gtkKeyCodeMap {
|
||||
final StringBuffer gtkKeyCodeMap = StringBuffer();
|
||||
for (final Key entry in keyData.data) {
|
||||
if (entry.gtkKeyCodes != null) {
|
||||
for (final int code in entry.gtkKeyCodes.cast<int>()) {
|
||||
gtkKeyCodeMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},');
|
||||
}
|
||||
}
|
||||
}
|
||||
return gtkKeyCodeMap.toString().trimRight();
|
||||
}
|
||||
|
||||
/// This generates the map of XKB USB HID codes to physical keys.
|
||||
String get xkbScanCodeMap {
|
||||
final StringBuffer xkbScanCodeMap = StringBuffer();
|
||||
@@ -414,6 +440,8 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK
|
||||
'MACOS_FUNCTION_KEY_MAP': macOsFunctionKeyMap,
|
||||
'GLFW_KEY_CODE_MAP': glfwKeyCodeMap,
|
||||
'GLFW_NUMPAD_MAP': glfwNumpadMap,
|
||||
'GTK_KEY_CODE_MAP': gtkKeyCodeMap,
|
||||
'GTK_NUMPAD_MAP': gtkNumpadMap,
|
||||
'XKB_SCAN_CODE_MAP': xkbScanCodeMap,
|
||||
'WEB_LOGICAL_KEY_MAP': webLogicalKeyMap,
|
||||
'WEB_PHYSICAL_KEY_MAP': webPhysicalKeyMap,
|
||||
|
||||
@@ -28,6 +28,8 @@ class KeyData {
|
||||
String androidNameMap,
|
||||
String glfwKeyCodeHeader,
|
||||
String glfwNameMap,
|
||||
String gtkKeyCodeHeader,
|
||||
String gtkNameMap,
|
||||
String windowsKeyCodeHeader,
|
||||
String windowsNameMap,
|
||||
) : assert(chromiumHidCodes != null),
|
||||
@@ -36,11 +38,14 @@ class KeyData {
|
||||
assert(androidNameMap != null),
|
||||
assert(glfwKeyCodeHeader != null),
|
||||
assert(glfwNameMap != null),
|
||||
assert(gtkKeyCodeHeader != null),
|
||||
assert(gtkNameMap != null),
|
||||
assert(windowsKeyCodeHeader != null),
|
||||
assert(windowsNameMap != null) {
|
||||
_nameToAndroidScanCodes = _readAndroidScanCodes(androidKeyboardLayout);
|
||||
_nameToAndroidKeyCode = _readAndroidKeyCodes(androidKeyCodeHeader);
|
||||
_nameToGlfwKeyCode = _readGlfwKeyCodes(glfwKeyCodeHeader);
|
||||
_nameToGtkKeyCode = _readGtkKeyCodes(gtkKeyCodeHeader);
|
||||
_nameToWindowsKeyCode = _readWindowsKeyCodes(windowsKeyCodeHeader);
|
||||
// Cast Android dom map
|
||||
final Map<String, List<dynamic>> dynamicAndroidNames = (json.decode(androidNameMap) as Map<String, dynamic>).cast<String, List<dynamic>>();
|
||||
@@ -52,6 +57,11 @@ class KeyData {
|
||||
_nameToGlfwName = dynamicGlfwNames.map<String, List<String>>((String key, List<dynamic> value) {
|
||||
return MapEntry<String, List<String>>(key, value.cast<String>());
|
||||
});
|
||||
// Cast GTK dom map
|
||||
final Map<String, List<dynamic>> dynamicGtkNames = (json.decode(gtkNameMap) as Map<String, dynamic>).cast<String, List<dynamic>>();
|
||||
_nameToGtkName = dynamicGtkNames.map<String, List<String>>((String key, List<dynamic> value) {
|
||||
return MapEntry<String, List<String>>(key, value.cast<String>());
|
||||
});
|
||||
// Cast Windows dom map
|
||||
final Map<String, List<dynamic>> dynamicWindowsNames = (json.decode(windowsNameMap) as Map<String, dynamic>).cast<String, List<dynamic>>();
|
||||
_nameToWindowsName = dynamicWindowsNames.map<String, List<String>>((String key, List<dynamic> value) {
|
||||
@@ -97,6 +107,17 @@ class KeyData {
|
||||
}
|
||||
}
|
||||
|
||||
// GTK key names
|
||||
entry.gtkKeyNames = _nameToGtkName[entry.constantName]?.cast<String>();
|
||||
if (entry.gtkKeyNames != null && entry.gtkKeyNames.isNotEmpty) {
|
||||
for (final String gtkKeyName in entry.gtkKeyNames) {
|
||||
if (_nameToGtkKeyCode[gtkKeyName] != null) {
|
||||
entry.gtkKeyCodes ??= <int>[];
|
||||
entry.gtkKeyCodes.add(_nameToGtkKeyCode[gtkKeyName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Windows key names
|
||||
entry.windowsKeyNames = _nameToWindowsName[entry.constantName]?.cast<String>();
|
||||
if (entry.windowsKeyNames != null && entry.windowsKeyNames.isNotEmpty) {
|
||||
@@ -133,6 +154,13 @@ class KeyData {
|
||||
/// JSON.
|
||||
Map<String, List<String>> _nameToGlfwName;
|
||||
|
||||
/// The mapping from the Flutter name (e.g. "eject") to the GTK name (e.g.
|
||||
/// "GDK_KEY_Eject").
|
||||
///
|
||||
/// Only populated if data is parsed from the source files, not if parsed from
|
||||
/// JSON.
|
||||
Map<String, List<String>> _nameToGtkName;
|
||||
|
||||
/// The mapping from the Android name (e.g. "MEDIA_EJECT") to the integer scan
|
||||
/// code (physical location) of the key.
|
||||
///
|
||||
@@ -154,6 +182,13 @@ class KeyData {
|
||||
/// JSON.
|
||||
Map<String, int> _nameToGlfwKeyCode;
|
||||
|
||||
/// The mapping from GTK name (e.g. "GTK_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> _nameToGtkKeyCode;
|
||||
|
||||
/// The mapping from Widows name (e.g. "RETURN") to the integer key code
|
||||
/// (logical meaning) of the key.
|
||||
///
|
||||
@@ -244,6 +279,20 @@ class KeyData {
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Parses entries from GTK's gdkkeysyms.h key code data file.
|
||||
///
|
||||
/// Lines in this file look like this (without the ///):
|
||||
/// /** Space key. */
|
||||
/// #define GDK_KEY_space 0x020
|
||||
Map<String, int> _readGtkKeyCodes(String headerFile) {
|
||||
final RegExp definedCodes = RegExp(r'#define GDK_KEY_([a-zA-Z0-9_]+)\s*0x([0-9a-f]+),?');
|
||||
final Map<String, int> replaced = <String, int>{};
|
||||
for (final Match match in definedCodes.allMatches(headerFile)) {
|
||||
replaced[match.group(1)] = int.parse(match.group(2), radix: 16);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
|
||||
Map<String, int> _readWindowsKeyCodes(String headerFile) {
|
||||
final RegExp definedCodes = RegExp(r'define VK_([A-Z0-9_]+)\s*([A-Z0-9_x]+),?');
|
||||
final Map<String, int> replaced = <String, int>{};
|
||||
@@ -332,6 +381,8 @@ class Key {
|
||||
this.androidKeyCodes,
|
||||
this.glfwKeyNames,
|
||||
this.glfwKeyCodes,
|
||||
this.gtkKeyNames,
|
||||
this.gtkKeyCodes,
|
||||
}) : assert(usbHidCode != null),
|
||||
assert(chromiumName != null),
|
||||
_constantName = enumName;
|
||||
@@ -354,6 +405,8 @@ class Key {
|
||||
macOsScanCode: map['scanCodes']['macos'] as int,
|
||||
glfwKeyNames: (map['names']['glfw'] as List<dynamic>)?.cast<String>(),
|
||||
glfwKeyCodes: (map['keyCodes']['glfw'] as List<dynamic>)?.cast<int>(),
|
||||
gtkKeyNames: (map['names']['gtk'] as List<dynamic>)?.cast<String>(),
|
||||
gtkKeyCodes: (map['keyCodes']['gtk'] as List<dynamic>)?.cast<int>(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -402,6 +455,15 @@ class Key {
|
||||
/// value.
|
||||
List<int> glfwKeyCodes;
|
||||
|
||||
/// The list of names that GTK gives to this key (symbol names minus the
|
||||
/// prefix).
|
||||
List<String> gtkKeyNames;
|
||||
|
||||
/// The list of GTK key codes matching this key, created by looking up the
|
||||
/// Linux name in the GTK data, and substituting the GTK key code
|
||||
/// value.
|
||||
List<int> gtkKeyCodes;
|
||||
|
||||
/// Creates a JSON map from the key data.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
@@ -411,6 +473,7 @@ class Key {
|
||||
'english': commentName,
|
||||
'chromium': chromiumName,
|
||||
'glfw': glfwKeyNames,
|
||||
'gtk': gtkKeyNames,
|
||||
'windows': windowsKeyNames,
|
||||
},
|
||||
'scanCodes': <String, dynamic>{
|
||||
@@ -424,6 +487,7 @@ class Key {
|
||||
'keyCodes': <String, List<int>>{
|
||||
'android': androidKeyCodes,
|
||||
'glfw': glfwKeyCodes,
|
||||
'gtk': gtkKeyCodes,
|
||||
'windows': windowsKeyCodes,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1312,6 +1312,223 @@ const Map<int, LogicalKeyboardKey> kGlfwNumpadMap = <int, LogicalKeyboardKey>{
|
||||
336: LogicalKeyboardKey.numpadEqual,
|
||||
};
|
||||
|
||||
/// Maps GTK-specific key codes to the matching [LogicalKeyboardKey].
|
||||
const Map<int, LogicalKeyboardKey> kGtkToLogicalKey = <int, LogicalKeyboardKey>{
|
||||
65517: LogicalKeyboardKey.hyper,
|
||||
65518: LogicalKeyboardKey.hyper,
|
||||
65515: LogicalKeyboardKey.superKey,
|
||||
65516: LogicalKeyboardKey.superKey,
|
||||
269025191: LogicalKeyboardKey.suspend,
|
||||
269025071: LogicalKeyboardKey.sleep,
|
||||
269025067: LogicalKeyboardKey.wakeUp,
|
||||
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,
|
||||
65293: LogicalKeyboardKey.enter,
|
||||
65307: LogicalKeyboardKey.escape,
|
||||
65288: LogicalKeyboardKey.backspace,
|
||||
65289: LogicalKeyboardKey.tab,
|
||||
65417: LogicalKeyboardKey.tab,
|
||||
32: LogicalKeyboardKey.space,
|
||||
65408: 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,
|
||||
65509: LogicalKeyboardKey.capsLock,
|
||||
65470: LogicalKeyboardKey.f1,
|
||||
65425: LogicalKeyboardKey.f1,
|
||||
65471: LogicalKeyboardKey.f2,
|
||||
65426: LogicalKeyboardKey.f2,
|
||||
65472: LogicalKeyboardKey.f3,
|
||||
65427: LogicalKeyboardKey.f3,
|
||||
65473: LogicalKeyboardKey.f4,
|
||||
65428: LogicalKeyboardKey.f4,
|
||||
65474: LogicalKeyboardKey.f5,
|
||||
65475: LogicalKeyboardKey.f6,
|
||||
65476: LogicalKeyboardKey.f7,
|
||||
65477: LogicalKeyboardKey.f8,
|
||||
65478: LogicalKeyboardKey.f9,
|
||||
65479: LogicalKeyboardKey.f10,
|
||||
65480: LogicalKeyboardKey.f11,
|
||||
65481: LogicalKeyboardKey.f12,
|
||||
64797: LogicalKeyboardKey.printScreen,
|
||||
65300: LogicalKeyboardKey.scrollLock,
|
||||
65299: LogicalKeyboardKey.pause,
|
||||
65379: LogicalKeyboardKey.insert,
|
||||
65438: LogicalKeyboardKey.insert,
|
||||
65360: LogicalKeyboardKey.home,
|
||||
65429: LogicalKeyboardKey.home,
|
||||
65365: LogicalKeyboardKey.pageUp,
|
||||
65434: LogicalKeyboardKey.pageUp,
|
||||
65535: LogicalKeyboardKey.delete,
|
||||
65439: LogicalKeyboardKey.delete,
|
||||
65367: LogicalKeyboardKey.end,
|
||||
65436: LogicalKeyboardKey.end,
|
||||
65366: LogicalKeyboardKey.pageDown,
|
||||
65435: LogicalKeyboardKey.pageDown,
|
||||
65363: LogicalKeyboardKey.arrowRight,
|
||||
65432: LogicalKeyboardKey.arrowRight,
|
||||
65361: LogicalKeyboardKey.arrowLeft,
|
||||
65430: LogicalKeyboardKey.arrowLeft,
|
||||
65364: LogicalKeyboardKey.arrowDown,
|
||||
65433: LogicalKeyboardKey.arrowDown,
|
||||
65362: LogicalKeyboardKey.arrowUp,
|
||||
65431: LogicalKeyboardKey.arrowUp,
|
||||
65407: LogicalKeyboardKey.numLock,
|
||||
65455: LogicalKeyboardKey.numpadDivide,
|
||||
65450: LogicalKeyboardKey.numpadMultiply,
|
||||
65453: LogicalKeyboardKey.numpadSubtract,
|
||||
65451: LogicalKeyboardKey.numpadAdd,
|
||||
65421: LogicalKeyboardKey.numpadEnter,
|
||||
65457: LogicalKeyboardKey.numpad1,
|
||||
65458: LogicalKeyboardKey.numpad2,
|
||||
65459: LogicalKeyboardKey.numpad3,
|
||||
65460: LogicalKeyboardKey.numpad4,
|
||||
65461: LogicalKeyboardKey.numpad5,
|
||||
65462: LogicalKeyboardKey.numpad6,
|
||||
65463: LogicalKeyboardKey.numpad7,
|
||||
65464: LogicalKeyboardKey.numpad8,
|
||||
65465: LogicalKeyboardKey.numpad9,
|
||||
65456: LogicalKeyboardKey.numpad0,
|
||||
65454: LogicalKeyboardKey.numpadDecimal,
|
||||
65383: LogicalKeyboardKey.contextMenu,
|
||||
269025066: LogicalKeyboardKey.power,
|
||||
65469: LogicalKeyboardKey.numpadEqual,
|
||||
65482: LogicalKeyboardKey.f13,
|
||||
65483: LogicalKeyboardKey.f14,
|
||||
65484: LogicalKeyboardKey.f15,
|
||||
65485: LogicalKeyboardKey.f16,
|
||||
65486: LogicalKeyboardKey.f17,
|
||||
65487: LogicalKeyboardKey.f18,
|
||||
65488: LogicalKeyboardKey.f19,
|
||||
65489: LogicalKeyboardKey.f20,
|
||||
65490: LogicalKeyboardKey.f21,
|
||||
65491: LogicalKeyboardKey.f22,
|
||||
65492: LogicalKeyboardKey.f23,
|
||||
65493: LogicalKeyboardKey.f24,
|
||||
269025131: LogicalKeyboardKey.open,
|
||||
65386: LogicalKeyboardKey.help,
|
||||
65376: LogicalKeyboardKey.select,
|
||||
65381: LogicalKeyboardKey.undo,
|
||||
269025111: LogicalKeyboardKey.copy,
|
||||
64789: LogicalKeyboardKey.copy,
|
||||
269025133: LogicalKeyboardKey.paste,
|
||||
65384: LogicalKeyboardKey.find,
|
||||
269025042: LogicalKeyboardKey.audioVolumeMute,
|
||||
269025043: LogicalKeyboardKey.audioVolumeUp,
|
||||
269025041: LogicalKeyboardKey.audioVolumeDown,
|
||||
65406: LogicalKeyboardKey.kanaMode,
|
||||
165: LogicalKeyboardKey.intlYen,
|
||||
65507: LogicalKeyboardKey.controlLeft,
|
||||
65505: LogicalKeyboardKey.shiftLeft,
|
||||
65513: LogicalKeyboardKey.altLeft,
|
||||
65511: LogicalKeyboardKey.metaLeft,
|
||||
65508: LogicalKeyboardKey.controlRight,
|
||||
65506: LogicalKeyboardKey.shiftRight,
|
||||
65514: LogicalKeyboardKey.altRight,
|
||||
65512: LogicalKeyboardKey.metaRight,
|
||||
269025026: LogicalKeyboardKey.brightnessUp,
|
||||
269025027: LogicalKeyboardKey.brightnessDown,
|
||||
269025134: LogicalKeyboardKey.launchPhone,
|
||||
269025044: LogicalKeyboardKey.mediaPlay,
|
||||
64790: LogicalKeyboardKey.mediaPlay,
|
||||
269025073: LogicalKeyboardKey.mediaPause,
|
||||
269025052: LogicalKeyboardKey.mediaRecord,
|
||||
269025175: LogicalKeyboardKey.mediaFastForward,
|
||||
269025086: LogicalKeyboardKey.mediaRewind,
|
||||
269025047: LogicalKeyboardKey.mediaTrackNext,
|
||||
269025046: LogicalKeyboardKey.mediaTrackPrevious,
|
||||
269025045: LogicalKeyboardKey.mediaStop,
|
||||
269025068: LogicalKeyboardKey.eject,
|
||||
269025049: LogicalKeyboardKey.launchMail,
|
||||
269025056: LogicalKeyboardKey.launchCalendar,
|
||||
269025070: LogicalKeyboardKey.launchInternetBrowser,
|
||||
269025121: LogicalKeyboardKey.logOff,
|
||||
269025148: LogicalKeyboardKey.spellCheck,
|
||||
269025069: LogicalKeyboardKey.launchScreenSaver,
|
||||
269025170: LogicalKeyboardKey.launchAudioBrowser,
|
||||
269025128: LogicalKeyboardKey.newKey,
|
||||
269025110: LogicalKeyboardKey.close,
|
||||
269025143: LogicalKeyboardKey.save,
|
||||
65377: LogicalKeyboardKey.print,
|
||||
269025051: LogicalKeyboardKey.browserSearch,
|
||||
269025048: LogicalKeyboardKey.browserHome,
|
||||
269025062: LogicalKeyboardKey.browserBack,
|
||||
269025063: LogicalKeyboardKey.browserForward,
|
||||
269025064: LogicalKeyboardKey.browserStop,
|
||||
269025065: LogicalKeyboardKey.browserRefresh,
|
||||
269025072: LogicalKeyboardKey.browserFavorites,
|
||||
269025163: LogicalKeyboardKey.zoomIn,
|
||||
269025164: LogicalKeyboardKey.zoomOut,
|
||||
65382: LogicalKeyboardKey.redo,
|
||||
269025138: LogicalKeyboardKey.mailReply,
|
||||
269025168: LogicalKeyboardKey.mailForward,
|
||||
269025147: LogicalKeyboardKey.mailSend,
|
||||
};
|
||||
|
||||
/// A map of GTK 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> kGtkNumpadMap = <int, LogicalKeyboardKey>{
|
||||
65455: LogicalKeyboardKey.numpadDivide,
|
||||
65450: LogicalKeyboardKey.numpadMultiply,
|
||||
65453: LogicalKeyboardKey.numpadSubtract,
|
||||
65451: LogicalKeyboardKey.numpadAdd,
|
||||
65457: LogicalKeyboardKey.numpad1,
|
||||
65458: LogicalKeyboardKey.numpad2,
|
||||
65459: LogicalKeyboardKey.numpad3,
|
||||
65460: LogicalKeyboardKey.numpad4,
|
||||
65461: LogicalKeyboardKey.numpad5,
|
||||
65462: LogicalKeyboardKey.numpad6,
|
||||
65463: LogicalKeyboardKey.numpad7,
|
||||
65464: LogicalKeyboardKey.numpad8,
|
||||
65465: LogicalKeyboardKey.numpad9,
|
||||
65456: LogicalKeyboardKey.numpad0,
|
||||
65454: LogicalKeyboardKey.numpadDecimal,
|
||||
65469: LogicalKeyboardKey.numpadEqual,
|
||||
};
|
||||
|
||||
/// Maps XKB specific key code values representing [PhysicalKeyboardKey].
|
||||
const Map<int, PhysicalKeyboardKey> kLinuxToPhysicalKey = <int, PhysicalKeyboardKey>{
|
||||
0x00000281: PhysicalKeyboardKey.privacyScreenToggle,
|
||||
|
||||
@@ -19,7 +19,7 @@ import 'raw_keyboard.dart';
|
||||
///
|
||||
/// * [RawKeyboard], which uses this interface to expose key data.
|
||||
class RawKeyEventDataLinux extends RawKeyEventData {
|
||||
/// Creates a key event data structure specific for macOS.
|
||||
/// Creates a key event data structure specific for Linux.
|
||||
///
|
||||
/// The [toolkit], [scanCode], [unicodeScalarValues], [keyCode], and [modifiers],
|
||||
/// arguments must not be null.
|
||||
@@ -145,6 +145,8 @@ abstract class KeyHelper {
|
||||
factory KeyHelper(String toolkit) {
|
||||
if (toolkit == 'glfw') {
|
||||
return GLFWKeyHelper();
|
||||
} else if (toolkit == 'gtk') {
|
||||
return GtkKeyHelper();
|
||||
} else {
|
||||
throw FlutterError('Window toolkit not recognized: $toolkit');
|
||||
}
|
||||
@@ -314,3 +316,149 @@ class GLFWKeyHelper with KeyHelper {
|
||||
return kGlfwToLogicalKey[keyCode];
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper class that uses GTK-specific key mappings.
|
||||
class GtkKeyHelper with KeyHelper {
|
||||
/// This mask is used to check the [modifiers] field to test whether one of the
|
||||
/// SHIFT modifier keys is pressed.
|
||||
///
|
||||
/// {@template flutter.services.gtkKeyHelper.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. This is especially true on GTK, since its modifiers
|
||||
/// don't include the effects of the current key event.
|
||||
/// {@endtemplate}
|
||||
static const int modifierShift = 1 << 0;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether the CAPS
|
||||
/// LOCK modifier key is on.
|
||||
/// {@macro flutter.services.gtkKeyHelper.modifiers}
|
||||
static const int modifierCapsLock = 1 << 1;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether one of the
|
||||
/// CTRL modifier keys is pressed.
|
||||
/// {@macro flutter.services.gtkKeyHelper.modifiers}
|
||||
static const int modifierControl = 1 << 2;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether the first
|
||||
/// modifier key is pressed (usually mapped to alt).
|
||||
/// {@macro flutter.services.gtkKeyHelper.modifiers}
|
||||
static const int modifierMod1 = 1 << 3;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether the second
|
||||
/// modifier key is pressed (assumed to be mapped to num lock).
|
||||
/// {@macro flutter.services.gtkKeyHelper.modifiers}
|
||||
static const int modifierMod2 = 1 << 4;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether one of the
|
||||
/// Meta(SUPER) modifier keys is pressed.
|
||||
/// {@macro flutter.services.gtkKeyHelper.modifiers}
|
||||
static const int modifierMeta = 1 << 28;
|
||||
|
||||
int _mergeModifiers({int modifiers, int keyCode, bool isDown}) {
|
||||
// GTK Key codes for modifier keys.
|
||||
const int shiftLeftKeyCode = 0xffe1;
|
||||
const int shiftRightKeyCode = 0xffe2;
|
||||
const int controlLeftKeyCode = 0xffe3;
|
||||
const int controlRightKeyCode = 0xffe4;
|
||||
const int capsLockKeyCode = 0xffe5;
|
||||
const int shiftLockKeyCode = 0xffe6;
|
||||
const int metaLeftKeyCode = 0xffe7;
|
||||
const int metaRightKeyCode = 0xffe8;
|
||||
const int altLeftKeyCode = 0xffe9;
|
||||
const int altRightKeyCode = 0xffea;
|
||||
const int numLockKeyCode = 0xff7f;
|
||||
|
||||
// On GTK, the "modifiers" bitfield is the state as it is BEFORE this event
|
||||
// happened, not AFTER, like every other platform. Consequently, if this is
|
||||
// a key down, then we need to add the correct modifier bits, and if it's a
|
||||
// key up, we need to remove them.
|
||||
|
||||
int modifierChange = 0;
|
||||
switch (keyCode) {
|
||||
case shiftLeftKeyCode:
|
||||
case shiftRightKeyCode:
|
||||
modifierChange = modifierShift;
|
||||
break;
|
||||
case controlLeftKeyCode:
|
||||
case controlRightKeyCode:
|
||||
modifierChange = modifierControl;
|
||||
break;
|
||||
case altLeftKeyCode:
|
||||
case altRightKeyCode:
|
||||
modifierChange = modifierMod1;
|
||||
break;
|
||||
case metaLeftKeyCode:
|
||||
case metaRightKeyCode:
|
||||
modifierChange = modifierMeta;
|
||||
break;
|
||||
case capsLockKeyCode:
|
||||
case shiftLockKeyCode:
|
||||
modifierChange = modifierCapsLock;
|
||||
break;
|
||||
case numLockKeyCode:
|
||||
modifierChange = modifierMod2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return isDown ? modifiers | modifierChange : modifiers & ~modifierChange;
|
||||
}
|
||||
|
||||
@override
|
||||
bool isModifierPressed(ModifierKey key, int modifiers, {KeyboardSide side = KeyboardSide.any, int keyCode, bool isDown}) {
|
||||
modifiers = _mergeModifiers(modifiers: modifiers, keyCode: keyCode, isDown: isDown);
|
||||
switch (key) {
|
||||
case ModifierKey.controlModifier:
|
||||
return modifiers & modifierControl != 0;
|
||||
case ModifierKey.shiftModifier:
|
||||
return modifiers & modifierShift != 0;
|
||||
case ModifierKey.altModifier:
|
||||
return modifiers & modifierMod1 != 0;
|
||||
case ModifierKey.metaModifier:
|
||||
return modifiers & modifierMeta != 0;
|
||||
case ModifierKey.capsLockModifier:
|
||||
return modifiers & modifierCapsLock != 0;
|
||||
case ModifierKey.numLockModifier:
|
||||
return modifiers & modifierMod2 != 0;
|
||||
case ModifierKey.functionModifier:
|
||||
case ModifierKey.symbolModifier:
|
||||
case ModifierKey.scrollLockModifier:
|
||||
// These are not used in GTK 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 GTK or X11 provide a distinction 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 kGtkNumpadMap[keyCode];
|
||||
}
|
||||
|
||||
@override
|
||||
LogicalKeyboardKey logicalKey(int keyCode) {
|
||||
return kGtkToLogicalKey[keyCode];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1101,6 +1101,189 @@ void main() {
|
||||
});
|
||||
}, skip: isBrowser);
|
||||
|
||||
group('RawKeyEventDataLinux-GTK', () {
|
||||
const Map<int, _ModifierCheck> modifierTests = <int, _ModifierCheck>{
|
||||
GtkKeyHelper.modifierMod1: _ModifierCheck(ModifierKey.altModifier, KeyboardSide.any),
|
||||
GtkKeyHelper.modifierShift: _ModifierCheck(ModifierKey.shiftModifier, KeyboardSide.any),
|
||||
GtkKeyHelper.modifierControl: _ModifierCheck(ModifierKey.controlModifier, KeyboardSide.any),
|
||||
GtkKeyHelper.modifierMeta: _ModifierCheck(ModifierKey.metaModifier, KeyboardSide.any),
|
||||
GtkKeyHelper.modifierMod2: _ModifierCheck(ModifierKey.numLockModifier, KeyboardSide.all),
|
||||
GtkKeyHelper.modifierCapsLock: _ModifierCheck(ModifierKey.capsLockModifier, KeyboardSide.all),
|
||||
};
|
||||
|
||||
// How modifiers are interpreted depends upon the keyCode for GTK.
|
||||
int keyCodeForModifier(int modifier, {bool isLeft}) {
|
||||
switch (modifier) {
|
||||
case GtkKeyHelper.modifierMod1:
|
||||
return isLeft ? 65513 : 65513;
|
||||
case GtkKeyHelper.modifierShift:
|
||||
return isLeft ? 65505 : 65506;
|
||||
case GtkKeyHelper.modifierControl:
|
||||
return isLeft ? 65507 : 65508;
|
||||
case GtkKeyHelper.modifierMeta:
|
||||
return isLeft ? 65511 : 65512;
|
||||
case GtkKeyHelper.modifierMod2:
|
||||
return 65407;
|
||||
case GtkKeyHelper.modifierCapsLock:
|
||||
return 65509;
|
||||
default:
|
||||
return 65; // keyA
|
||||
}
|
||||
}
|
||||
|
||||
test('modifier keys are recognized individually', () {
|
||||
for (final int modifier in modifierTests.keys) {
|
||||
for (final bool isDown in <bool>[true, false]) {
|
||||
for (final bool isLeft in <bool>[true, false]) {
|
||||
final RawKeyEvent event = RawKeyEvent.fromMessage(<String, dynamic>{
|
||||
'type': isDown ? 'keydown' : 'keyup',
|
||||
'keymap': 'linux',
|
||||
'toolkit': 'gtk',
|
||||
'keyCode': keyCodeForModifier(modifier, isLeft: isLeft),
|
||||
'scanCode': 0x00000026,
|
||||
'unicodeScalarValues': 97,
|
||||
// GTK modifiers don't include the current key event.
|
||||
'modifiers': isDown ? 0 : modifier,
|
||||
});
|
||||
final RawKeyEventDataLinux data = event.data as RawKeyEventDataLinux;
|
||||
for (final ModifierKey key in ModifierKey.values) {
|
||||
if (modifierTests[modifier].key == key) {
|
||||
expect(
|
||||
data.isModifierPressed(key, side: modifierTests[modifier].side),
|
||||
isDown ? isTrue : isFalse,
|
||||
reason: "${isLeft ? 'left' : 'right'} $key ${isDown ? 'should' : 'should not'} be pressed with metaState $modifier, when key is ${isDown ? 'down' : 'up'}, but isn't.",
|
||||
);
|
||||
expect(data.getModifierSide(key), equals(modifierTests[modifier].side));
|
||||
} else {
|
||||
expect(
|
||||
data.isModifierPressed(key, side: modifierTests[modifier].side),
|
||||
isFalse,
|
||||
reason: "${isLeft ? 'left' : 'right'} $key should not be pressed with metaState $modifier, wwhen key is ${isDown ? 'down' : 'up'}, but is.",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
test('modifier keys are recognized when combined', () {
|
||||
for (final int modifier in modifierTests.keys) {
|
||||
if (modifier == GtkKeyHelper.modifierControl) {
|
||||
// No need to combine CTRL key with itself.
|
||||
continue;
|
||||
}
|
||||
final RawKeyEvent event = RawKeyEvent.fromMessage(<String, dynamic>{
|
||||
'type': 'keydown',
|
||||
'keymap': 'linux',
|
||||
'toolkit': 'gtk',
|
||||
'keyCode': 65,
|
||||
'scanCode': 0x00000026,
|
||||
'unicodeScalarValues': 97,
|
||||
'modifiers': modifier | GtkKeyHelper.modifierControl,
|
||||
});
|
||||
final RawKeyEventDataLinux data = event.data as RawKeyEventDataLinux;
|
||||
for (final 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 ${GtkKeyHelper.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 ${GtkKeyHelper.modifierControl}.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
test('Printable keyboard keys are correctly translated', () {
|
||||
final RawKeyEvent keyAEvent = RawKeyEvent.fromMessage(const <String, dynamic>{
|
||||
'type': 'keydown',
|
||||
'keymap': 'linux',
|
||||
'toolkit': 'gtk',
|
||||
'keyCode': 65,
|
||||
'scanCode': 0x00000026,
|
||||
'unicodeScalarValues': 113,
|
||||
'modifiers': 0x0,
|
||||
});
|
||||
final RawKeyEventDataLinux data = keyAEvent.data as RawKeyEventDataLinux;
|
||||
expect(data.physicalKey, equals(PhysicalKeyboardKey.keyA));
|
||||
expect(data.logicalKey, equals(LogicalKeyboardKey.keyQ));
|
||||
expect(data.keyLabel, equals('q'));
|
||||
});
|
||||
test('Code points with two Unicode scalar values are allowed', () {
|
||||
final RawKeyEvent keyAEvent = RawKeyEvent.fromMessage(const <String, dynamic>{
|
||||
'type': 'keydown',
|
||||
'keymap': 'linux',
|
||||
'toolkit': 'gtk',
|
||||
'keyCode': 65,
|
||||
'scanCode': 0x00000026,
|
||||
'unicodeScalarValues': 0x10FFFF,
|
||||
'modifiers': 0x0,
|
||||
});
|
||||
final RawKeyEventDataLinux data = keyAEvent.data as RawKeyEventDataLinux;
|
||||
expect(data.physicalKey, equals(PhysicalKeyboardKey.keyA));
|
||||
expect(data.logicalKey.keyId, equals(0x10FFFF));
|
||||
expect(data.keyLabel, equals(''));
|
||||
});
|
||||
|
||||
test('Code points with more than three Unicode scalar values are not allowed', () {
|
||||
// |keyCode| and |scanCode| are arbitrary values. This test should fail due to an invalid |unicodeScalarValues|.
|
||||
void _createFailingKey() {
|
||||
RawKeyEvent.fromMessage(const <String, dynamic>{
|
||||
'type': 'keydown',
|
||||
'keymap': 'linux',
|
||||
'toolkit': 'gtk',
|
||||
'keyCode': 65,
|
||||
'scanCode': 0x00000026,
|
||||
'unicodeScalarValues': 0x1F00000000,
|
||||
'modifiers': 0x0,
|
||||
});
|
||||
}
|
||||
|
||||
expect(() => _createFailingKey(), throwsAssertionError);
|
||||
});
|
||||
test('Control keyboard keys are correctly translated', () {
|
||||
final RawKeyEvent escapeKeyEvent = RawKeyEvent.fromMessage(const <String, dynamic>{
|
||||
'type': 'keydown',
|
||||
'keymap': 'linux',
|
||||
'toolkit': 'gtk',
|
||||
'keyCode': 65307,
|
||||
'scanCode': 0x00000009,
|
||||
'unicodeScalarValues': 0,
|
||||
'modifiers': 0x0,
|
||||
});
|
||||
final RawKeyEventDataLinux data = escapeKeyEvent.data as RawKeyEventDataLinux;
|
||||
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': 'gtk',
|
||||
'keyCode': 65505,
|
||||
'scanCode': 0x00000032,
|
||||
'unicodeScalarValues': 0,
|
||||
});
|
||||
final RawKeyEventDataLinux data = shiftLeftKeyEvent.data as RawKeyEventDataLinux;
|
||||
expect(data.physicalKey, equals(PhysicalKeyboardKey.shiftLeft));
|
||||
expect(data.logicalKey, equals(LogicalKeyboardKey.shiftLeft));
|
||||
expect(data.keyLabel, isNull);
|
||||
});
|
||||
}, skip: isBrowser);
|
||||
|
||||
group('RawKeyEventDataWeb', () {
|
||||
const Map<int, ModifierKey> modifierTests = <int, ModifierKey>{
|
||||
RawKeyEventDataWeb.modifierAlt: ModifierKey.altModifier,
|
||||
|
||||
Reference in New Issue
Block a user