[windows] Adds support for keyboard mapping. (#54227)
This commit is contained in:
parent
45bf838014
commit
a0f7f6ca6d
@ -109,6 +109,11 @@ class _HardwareKeyDemoState extends State<RawKeyboardDemo> {
|
||||
dataText.add(Text('scanCode: ${data.scanCode}'));
|
||||
dataText.add(Text('unicodeScalarValues: ${data.unicodeScalarValues}'));
|
||||
dataText.add(Text('modifiers: ${data.modifiers} (${_asHex(data.modifiers)})'));
|
||||
} else if (data is RawKeyEventDataWindows) {
|
||||
dataText.add(Text('keyCode: ${data.keyCode} (${_asHex(data.keyCode)})'));
|
||||
dataText.add(Text('scanCode: ${data.scanCode}'));
|
||||
dataText.add(Text('characterCodePoint: ${data.characterCodePoint}'));
|
||||
dataText.add(Text('modifiers: ${data.modifiers} (${_asHex(data.modifiers)})'));
|
||||
}
|
||||
dataText.add(Text('logical: ${_event.logicalKey}'));
|
||||
dataText.add(Text('physical: ${_event.physicalKey}'));
|
||||
|
@ -18,8 +18,8 @@ import 'package:gen_keycodes/utils.dart';
|
||||
/// Get contents of the file that contains the key code mapping in Chromium
|
||||
/// source.
|
||||
Future<String> getChromiumConversions() async {
|
||||
final Uri keyCodeMapUri = Uri.parse('https://cs.chromium.org/codesearch/f/chromium/src/ui/events/keycodes/dom/dom_code_data.inc');
|
||||
return await http.read(keyCodeMapUri);
|
||||
final Uri keyCodesUri = Uri.parse('https://chromium.googlesource.com/codesearch/chromium/src/+/refs/heads/master/ui/events/keycodes/dom/dom_code_data.inc?format=TEXT');
|
||||
return utf8.decode(base64.decode(await http.read(keyCodesUri)));
|
||||
}
|
||||
|
||||
/// Get contents of the file that contains the key codes in Android source.
|
||||
|
@ -191,29 +191,6 @@
|
||||
"windows": null
|
||||
}
|
||||
},
|
||||
"privacyScreenToggle": {
|
||||
"names": {
|
||||
"domkey": "PrivacyScreenToggle",
|
||||
"android": null,
|
||||
"english": "Privacy Screen Toggle",
|
||||
"chromium": "privacyScreenToggle",
|
||||
"glfw": null,
|
||||
"windows": null
|
||||
},
|
||||
"scanCodes": {
|
||||
"android": null,
|
||||
"usb": 23,
|
||||
"linux": 633,
|
||||
"xkb": 641,
|
||||
"windows": null,
|
||||
"macos": null
|
||||
},
|
||||
"keyCodes": {
|
||||
"android": null,
|
||||
"glfw": null,
|
||||
"windows": null
|
||||
}
|
||||
},
|
||||
"sleep": {
|
||||
"names": {
|
||||
"domkey": "Sleep",
|
||||
@ -3080,7 +3057,9 @@
|
||||
"glfw": [
|
||||
"RIGHT"
|
||||
],
|
||||
"windows": null
|
||||
"windows": [
|
||||
"RIGHT"
|
||||
]
|
||||
},
|
||||
"scanCodes": {
|
||||
"android": [
|
||||
@ -3099,7 +3078,9 @@
|
||||
"glfw": [
|
||||
262
|
||||
],
|
||||
"windows": null
|
||||
"windows": [
|
||||
39
|
||||
]
|
||||
}
|
||||
},
|
||||
"arrowLeft": {
|
||||
@ -3113,7 +3094,9 @@
|
||||
"glfw": [
|
||||
"LEFT"
|
||||
],
|
||||
"windows": null
|
||||
"windows": [
|
||||
"LEFT"
|
||||
]
|
||||
},
|
||||
"scanCodes": {
|
||||
"android": [
|
||||
@ -3132,7 +3115,9 @@
|
||||
"glfw": [
|
||||
263
|
||||
],
|
||||
"windows": null
|
||||
"windows": [
|
||||
37
|
||||
]
|
||||
}
|
||||
},
|
||||
"arrowDown": {
|
||||
@ -3146,7 +3131,9 @@
|
||||
"glfw": [
|
||||
"DOWN"
|
||||
],
|
||||
"windows": null
|
||||
"windows": [
|
||||
"DOWN"
|
||||
]
|
||||
},
|
||||
"scanCodes": {
|
||||
"android": [
|
||||
@ -3165,7 +3152,9 @@
|
||||
"glfw": [
|
||||
264
|
||||
],
|
||||
"windows": null
|
||||
"windows": [
|
||||
40
|
||||
]
|
||||
}
|
||||
},
|
||||
"arrowUp": {
|
||||
@ -3179,7 +3168,9 @@
|
||||
"glfw": [
|
||||
"UP"
|
||||
],
|
||||
"windows": null
|
||||
"windows": [
|
||||
"UP"
|
||||
]
|
||||
},
|
||||
"scanCodes": {
|
||||
"android": [
|
||||
@ -3198,7 +3189,9 @@
|
||||
"glfw": [
|
||||
265
|
||||
],
|
||||
"windows": null
|
||||
"windows": [
|
||||
38
|
||||
]
|
||||
}
|
||||
},
|
||||
"numLock": {
|
||||
@ -8276,4 +8269,4 @@
|
||||
"windows": null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -31,10 +31,10 @@
|
||||
"next": ["NEXT"],
|
||||
"end": ["END"],
|
||||
"home": ["HOME"],
|
||||
"leftArrow": ["LEFT"],
|
||||
"upArrow": ["UP"],
|
||||
"rightArrow": ["RIGHT"],
|
||||
"downArrow": ["DOWN"],
|
||||
"arrowLeft": ["LEFT"],
|
||||
"arrowUp": ["UP"],
|
||||
"arrowRight": ["RIGHT"],
|
||||
"arrowDown": ["DOWN"],
|
||||
"select": ["SELECT"],
|
||||
"print": ["PRINT"],
|
||||
"execute": ["EXECUTE"],
|
||||
|
@ -19,7 +19,7 @@ import 'package:flutter/foundation.dart';
|
||||
/// that are returned from [RawKeyEvent.physicalKey].
|
||||
/// * [LogicalKeyboardKey], a class with static values that describe the keys
|
||||
/// that are returned from [RawKeyEvent.logicalKey].
|
||||
abstract class KeyboardKey extends Diagnosticable {
|
||||
abstract class KeyboardKey with Diagnosticable {
|
||||
/// A const constructor so that subclasses may be const.
|
||||
const KeyboardKey();
|
||||
}
|
||||
|
@ -92,3 +92,20 @@ const Map<String, PhysicalKeyboardKey> kWebToPhysicalKey = <String, PhysicalKeyb
|
||||
const Map<String, LogicalKeyboardKey> kWebNumPadMap = <String, LogicalKeyboardKey>{
|
||||
@@@WEB_NUMPAD_MAP@@@
|
||||
};
|
||||
|
||||
/// Maps Windows KeyboardEvent codes to the matching [LogicalKeyboardKey].
|
||||
const Map<int, LogicalKeyboardKey> kWindowsToLogicalKey = <int, LogicalKeyboardKey>{
|
||||
@@@WINDOWS_LOGICAL_KEY_MAP@@@
|
||||
};
|
||||
|
||||
/// Maps Windows KeyboardEvent codes to the matching [PhysicalKeyboardKey].
|
||||
const Map<int, PhysicalKeyboardKey> kWindowsToPhysicalKey = <int, PhysicalKeyboardKey>{
|
||||
@@@WINDOWS_PHYSICAL_KEY_MAP@@@
|
||||
};
|
||||
|
||||
/// A map of Windows KeyboardEvent 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> kWindowsNumPadMap = <int, LogicalKeyboardKey>{
|
||||
@@@WINDOWS_NUMPAD_MAP@@@
|
||||
};
|
||||
|
@ -225,6 +225,43 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK
|
||||
return androidScanCodeMap.toString().trimRight();
|
||||
}
|
||||
|
||||
/// This generates the map of Windows scan codes to physical keys.
|
||||
String get windowsScanCodeMap {
|
||||
final StringBuffer windowsScanCodeMap = StringBuffer();
|
||||
for (final Key entry in keyData.data) {
|
||||
if (entry.windowsScanCode != null) {
|
||||
windowsScanCodeMap.writeln(' ${toHex(entry.windowsScanCode)}: PhysicalKeyboardKey.${entry.constantName},');
|
||||
}
|
||||
}
|
||||
return windowsScanCodeMap.toString().trimRight();
|
||||
}
|
||||
|
||||
/// This generates the map of Windows number pad key codes to logical keys.
|
||||
String get windowsNumpadMap {
|
||||
final StringBuffer windowsNumPadMap = StringBuffer();
|
||||
for (final Key entry in numpadKeyData) {
|
||||
if (entry.windowsKeyCodes != null){
|
||||
for (final int code in entry.windowsKeyCodes) {
|
||||
windowsNumPadMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},');
|
||||
}
|
||||
}
|
||||
}
|
||||
return windowsNumPadMap.toString().trimRight();
|
||||
}
|
||||
|
||||
/// This generates the map of Windows key codes to logical keys.
|
||||
String get windowsKeyCodeMap {
|
||||
final StringBuffer windowsKeyCodeMap = StringBuffer();
|
||||
for (final Key entry in keyData.data) {
|
||||
if (entry.windowsKeyCodes != null) {
|
||||
for (final int code in entry.windowsKeyCodes) {
|
||||
windowsKeyCodeMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},');
|
||||
}
|
||||
}
|
||||
}
|
||||
return windowsKeyCodeMap.toString().trimRight();
|
||||
}
|
||||
|
||||
/// This generates the map of macOS key codes to physical keys.
|
||||
String get macOsScanCodeMap {
|
||||
final StringBuffer macOsScanCodeMap = StringBuffer();
|
||||
@ -381,6 +418,9 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK
|
||||
'WEB_LOGICAL_KEY_MAP': webLogicalKeyMap,
|
||||
'WEB_PHYSICAL_KEY_MAP': webPhysicalKeyMap,
|
||||
'WEB_NUMPAD_MAP': webNumpadMap,
|
||||
'WINDOWS_LOGICAL_KEY_MAP': windowsKeyCodeMap,
|
||||
'WINDOWS_PHYSICAL_KEY_MAP': windowsScanCodeMap,
|
||||
'WINDOWS_NUMPAD_MAP': windowsNumpadMap,
|
||||
};
|
||||
|
||||
final String template = File(path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'keyboard_maps.tmpl')).readAsStringSync();
|
||||
|
@ -29,6 +29,7 @@ export 'src/services/raw_keyboard_fuchsia.dart';
|
||||
export 'src/services/raw_keyboard_linux.dart';
|
||||
export 'src/services/raw_keyboard_macos.dart';
|
||||
export 'src/services/raw_keyboard_web.dart';
|
||||
export 'src/services/raw_keyboard_windows.dart';
|
||||
export 'src/services/system_channels.dart';
|
||||
export 'src/services/system_chrome.dart';
|
||||
export 'src/services/system_navigator.dart';
|
||||
|
@ -2020,3 +2020,306 @@ const Map<String, LogicalKeyboardKey> kWebNumPadMap = <String, LogicalKeyboardKe
|
||||
'NumpadParenLeft': LogicalKeyboardKey.numpadParenLeft,
|
||||
'NumpadParenRight': LogicalKeyboardKey.numpadParenRight,
|
||||
};
|
||||
|
||||
/// Maps Windows KeyboardEvent codes to the matching [LogicalKeyboardKey].
|
||||
const Map<int, LogicalKeyboardKey> kWindowsToLogicalKey = <int, LogicalKeyboardKey>{
|
||||
95: LogicalKeyboardKey.sleep,
|
||||
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,
|
||||
13: LogicalKeyboardKey.enter,
|
||||
27: LogicalKeyboardKey.escape,
|
||||
8: LogicalKeyboardKey.backspace,
|
||||
9: LogicalKeyboardKey.tab,
|
||||
32: LogicalKeyboardKey.space,
|
||||
189: LogicalKeyboardKey.minus,
|
||||
187: LogicalKeyboardKey.equal,
|
||||
219: LogicalKeyboardKey.bracketLeft,
|
||||
221: LogicalKeyboardKey.bracketRight,
|
||||
220: LogicalKeyboardKey.backslash,
|
||||
186: LogicalKeyboardKey.semicolon,
|
||||
222: LogicalKeyboardKey.quote,
|
||||
188: LogicalKeyboardKey.comma,
|
||||
190: LogicalKeyboardKey.period,
|
||||
191: LogicalKeyboardKey.slash,
|
||||
20: LogicalKeyboardKey.capsLock,
|
||||
112: LogicalKeyboardKey.f1,
|
||||
113: LogicalKeyboardKey.f2,
|
||||
114: LogicalKeyboardKey.f3,
|
||||
115: LogicalKeyboardKey.f4,
|
||||
116: LogicalKeyboardKey.f5,
|
||||
117: LogicalKeyboardKey.f6,
|
||||
118: LogicalKeyboardKey.f7,
|
||||
119: LogicalKeyboardKey.f8,
|
||||
120: LogicalKeyboardKey.f9,
|
||||
121: LogicalKeyboardKey.f10,
|
||||
122: LogicalKeyboardKey.f11,
|
||||
123: LogicalKeyboardKey.f12,
|
||||
19: LogicalKeyboardKey.pause,
|
||||
45: LogicalKeyboardKey.insert,
|
||||
36: LogicalKeyboardKey.home,
|
||||
46: LogicalKeyboardKey.delete,
|
||||
35: LogicalKeyboardKey.end,
|
||||
39: LogicalKeyboardKey.arrowRight,
|
||||
37: LogicalKeyboardKey.arrowLeft,
|
||||
40: LogicalKeyboardKey.arrowDown,
|
||||
38: LogicalKeyboardKey.arrowUp,
|
||||
111: LogicalKeyboardKey.numpadDivide,
|
||||
106: LogicalKeyboardKey.numpadMultiply,
|
||||
109: LogicalKeyboardKey.numpadSubtract,
|
||||
107: LogicalKeyboardKey.numpadAdd,
|
||||
97: LogicalKeyboardKey.numpad1,
|
||||
98: LogicalKeyboardKey.numpad2,
|
||||
99: LogicalKeyboardKey.numpad3,
|
||||
100: LogicalKeyboardKey.numpad4,
|
||||
101: LogicalKeyboardKey.numpad5,
|
||||
102: LogicalKeyboardKey.numpad6,
|
||||
103: LogicalKeyboardKey.numpad7,
|
||||
104: LogicalKeyboardKey.numpad8,
|
||||
105: LogicalKeyboardKey.numpad9,
|
||||
96: LogicalKeyboardKey.numpad0,
|
||||
110: LogicalKeyboardKey.numpadDecimal,
|
||||
146: LogicalKeyboardKey.numpadEqual,
|
||||
124: LogicalKeyboardKey.f13,
|
||||
125: LogicalKeyboardKey.f14,
|
||||
126: LogicalKeyboardKey.f15,
|
||||
127: LogicalKeyboardKey.f16,
|
||||
128: LogicalKeyboardKey.f17,
|
||||
129: LogicalKeyboardKey.f18,
|
||||
130: LogicalKeyboardKey.f19,
|
||||
131: LogicalKeyboardKey.f20,
|
||||
132: LogicalKeyboardKey.f21,
|
||||
133: LogicalKeyboardKey.f22,
|
||||
134: LogicalKeyboardKey.f23,
|
||||
135: LogicalKeyboardKey.f24,
|
||||
47: LogicalKeyboardKey.help,
|
||||
41: LogicalKeyboardKey.select,
|
||||
28: LogicalKeyboardKey.convert,
|
||||
162: LogicalKeyboardKey.controlLeft,
|
||||
160: LogicalKeyboardKey.shiftLeft,
|
||||
164: LogicalKeyboardKey.altLeft,
|
||||
91: LogicalKeyboardKey.metaLeft,
|
||||
163: LogicalKeyboardKey.controlRight,
|
||||
161: LogicalKeyboardKey.shiftRight,
|
||||
165: LogicalKeyboardKey.altRight,
|
||||
92: LogicalKeyboardKey.metaRight,
|
||||
178: LogicalKeyboardKey.mediaStop,
|
||||
179: LogicalKeyboardKey.mediaPlayPause,
|
||||
180: LogicalKeyboardKey.launchMail,
|
||||
183: LogicalKeyboardKey.launchApp2,
|
||||
182: LogicalKeyboardKey.launchApp1,
|
||||
42: LogicalKeyboardKey.print,
|
||||
170: LogicalKeyboardKey.browserSearch,
|
||||
172: LogicalKeyboardKey.browserHome,
|
||||
166: LogicalKeyboardKey.browserBack,
|
||||
167: LogicalKeyboardKey.browserForward,
|
||||
169: LogicalKeyboardKey.browserStop,
|
||||
168: LogicalKeyboardKey.browserRefresh,
|
||||
171: LogicalKeyboardKey.browserFavorites,
|
||||
};
|
||||
|
||||
/// Maps Windows KeyboardEvent codes to the matching [PhysicalKeyboardKey].
|
||||
const Map<int, PhysicalKeyboardKey> kWindowsToPhysicalKey = <int, PhysicalKeyboardKey>{
|
||||
0x0000e05f: PhysicalKeyboardKey.sleep,
|
||||
0x0000e063: PhysicalKeyboardKey.wakeUp,
|
||||
0x000000ff: PhysicalKeyboardKey.usbErrorRollOver,
|
||||
0x000000fc: PhysicalKeyboardKey.usbPostFail,
|
||||
0x0000001e: PhysicalKeyboardKey.keyA,
|
||||
0x00000030: PhysicalKeyboardKey.keyB,
|
||||
0x0000002e: PhysicalKeyboardKey.keyC,
|
||||
0x00000020: PhysicalKeyboardKey.keyD,
|
||||
0x00000012: PhysicalKeyboardKey.keyE,
|
||||
0x00000021: PhysicalKeyboardKey.keyF,
|
||||
0x00000022: PhysicalKeyboardKey.keyG,
|
||||
0x00000023: PhysicalKeyboardKey.keyH,
|
||||
0x00000017: PhysicalKeyboardKey.keyI,
|
||||
0x00000024: PhysicalKeyboardKey.keyJ,
|
||||
0x00000025: PhysicalKeyboardKey.keyK,
|
||||
0x00000026: PhysicalKeyboardKey.keyL,
|
||||
0x00000032: PhysicalKeyboardKey.keyM,
|
||||
0x00000031: PhysicalKeyboardKey.keyN,
|
||||
0x00000018: PhysicalKeyboardKey.keyO,
|
||||
0x00000019: PhysicalKeyboardKey.keyP,
|
||||
0x00000010: PhysicalKeyboardKey.keyQ,
|
||||
0x00000013: PhysicalKeyboardKey.keyR,
|
||||
0x0000001f: PhysicalKeyboardKey.keyS,
|
||||
0x00000014: PhysicalKeyboardKey.keyT,
|
||||
0x00000016: PhysicalKeyboardKey.keyU,
|
||||
0x0000002f: PhysicalKeyboardKey.keyV,
|
||||
0x00000011: PhysicalKeyboardKey.keyW,
|
||||
0x0000002d: PhysicalKeyboardKey.keyX,
|
||||
0x00000015: PhysicalKeyboardKey.keyY,
|
||||
0x0000002c: PhysicalKeyboardKey.keyZ,
|
||||
0x00000002: PhysicalKeyboardKey.digit1,
|
||||
0x00000003: PhysicalKeyboardKey.digit2,
|
||||
0x00000004: PhysicalKeyboardKey.digit3,
|
||||
0x00000005: PhysicalKeyboardKey.digit4,
|
||||
0x00000006: PhysicalKeyboardKey.digit5,
|
||||
0x00000007: PhysicalKeyboardKey.digit6,
|
||||
0x00000008: PhysicalKeyboardKey.digit7,
|
||||
0x00000009: PhysicalKeyboardKey.digit8,
|
||||
0x0000000a: PhysicalKeyboardKey.digit9,
|
||||
0x0000000b: PhysicalKeyboardKey.digit0,
|
||||
0x0000001c: PhysicalKeyboardKey.enter,
|
||||
0x00000001: PhysicalKeyboardKey.escape,
|
||||
0x0000000e: PhysicalKeyboardKey.backspace,
|
||||
0x0000000f: PhysicalKeyboardKey.tab,
|
||||
0x00000039: PhysicalKeyboardKey.space,
|
||||
0x0000000c: PhysicalKeyboardKey.minus,
|
||||
0x0000000d: PhysicalKeyboardKey.equal,
|
||||
0x0000001a: PhysicalKeyboardKey.bracketLeft,
|
||||
0x0000001b: PhysicalKeyboardKey.bracketRight,
|
||||
0x0000002b: PhysicalKeyboardKey.backslash,
|
||||
0x00000027: PhysicalKeyboardKey.semicolon,
|
||||
0x00000028: PhysicalKeyboardKey.quote,
|
||||
0x00000029: PhysicalKeyboardKey.backquote,
|
||||
0x00000033: PhysicalKeyboardKey.comma,
|
||||
0x00000034: PhysicalKeyboardKey.period,
|
||||
0x00000035: PhysicalKeyboardKey.slash,
|
||||
0x0000003a: PhysicalKeyboardKey.capsLock,
|
||||
0x0000003b: PhysicalKeyboardKey.f1,
|
||||
0x0000003c: PhysicalKeyboardKey.f2,
|
||||
0x0000003d: PhysicalKeyboardKey.f3,
|
||||
0x0000003e: PhysicalKeyboardKey.f4,
|
||||
0x0000003f: PhysicalKeyboardKey.f5,
|
||||
0x00000040: PhysicalKeyboardKey.f6,
|
||||
0x00000041: PhysicalKeyboardKey.f7,
|
||||
0x00000042: PhysicalKeyboardKey.f8,
|
||||
0x00000043: PhysicalKeyboardKey.f9,
|
||||
0x00000044: PhysicalKeyboardKey.f10,
|
||||
0x00000057: PhysicalKeyboardKey.f11,
|
||||
0x00000058: PhysicalKeyboardKey.f12,
|
||||
0x0000e037: PhysicalKeyboardKey.printScreen,
|
||||
0x00000046: PhysicalKeyboardKey.scrollLock,
|
||||
0x00000045: PhysicalKeyboardKey.pause,
|
||||
0x0000e052: PhysicalKeyboardKey.insert,
|
||||
0x0000e047: PhysicalKeyboardKey.home,
|
||||
0x0000e049: PhysicalKeyboardKey.pageUp,
|
||||
0x0000e053: PhysicalKeyboardKey.delete,
|
||||
0x0000e04f: PhysicalKeyboardKey.end,
|
||||
0x0000e051: PhysicalKeyboardKey.pageDown,
|
||||
0x0000e04d: PhysicalKeyboardKey.arrowRight,
|
||||
0x0000e04b: PhysicalKeyboardKey.arrowLeft,
|
||||
0x0000e050: PhysicalKeyboardKey.arrowDown,
|
||||
0x0000e048: PhysicalKeyboardKey.arrowUp,
|
||||
0x0000e045: PhysicalKeyboardKey.numLock,
|
||||
0x0000e035: PhysicalKeyboardKey.numpadDivide,
|
||||
0x00000037: PhysicalKeyboardKey.numpadMultiply,
|
||||
0x0000004a: PhysicalKeyboardKey.numpadSubtract,
|
||||
0x0000004e: PhysicalKeyboardKey.numpadAdd,
|
||||
0x0000e01c: PhysicalKeyboardKey.numpadEnter,
|
||||
0x0000004f: PhysicalKeyboardKey.numpad1,
|
||||
0x00000050: PhysicalKeyboardKey.numpad2,
|
||||
0x00000051: PhysicalKeyboardKey.numpad3,
|
||||
0x0000004b: PhysicalKeyboardKey.numpad4,
|
||||
0x0000004c: PhysicalKeyboardKey.numpad5,
|
||||
0x0000004d: PhysicalKeyboardKey.numpad6,
|
||||
0x00000047: PhysicalKeyboardKey.numpad7,
|
||||
0x00000048: PhysicalKeyboardKey.numpad8,
|
||||
0x00000049: PhysicalKeyboardKey.numpad9,
|
||||
0x00000052: PhysicalKeyboardKey.numpad0,
|
||||
0x00000053: PhysicalKeyboardKey.numpadDecimal,
|
||||
0x00000056: PhysicalKeyboardKey.intlBackslash,
|
||||
0x0000e05d: PhysicalKeyboardKey.contextMenu,
|
||||
0x0000e05e: PhysicalKeyboardKey.power,
|
||||
0x00000059: PhysicalKeyboardKey.numpadEqual,
|
||||
0x00000064: PhysicalKeyboardKey.f13,
|
||||
0x00000065: PhysicalKeyboardKey.f14,
|
||||
0x00000066: PhysicalKeyboardKey.f15,
|
||||
0x00000067: PhysicalKeyboardKey.f16,
|
||||
0x00000068: PhysicalKeyboardKey.f17,
|
||||
0x00000069: PhysicalKeyboardKey.f18,
|
||||
0x0000006a: PhysicalKeyboardKey.f19,
|
||||
0x0000006b: PhysicalKeyboardKey.f20,
|
||||
0x0000006c: PhysicalKeyboardKey.f21,
|
||||
0x0000006d: PhysicalKeyboardKey.f22,
|
||||
0x0000006e: PhysicalKeyboardKey.f23,
|
||||
0x00000076: PhysicalKeyboardKey.f24,
|
||||
0x0000e03b: PhysicalKeyboardKey.help,
|
||||
0x0000e008: PhysicalKeyboardKey.undo,
|
||||
0x0000e017: PhysicalKeyboardKey.cut,
|
||||
0x0000e018: PhysicalKeyboardKey.copy,
|
||||
0x0000e00a: PhysicalKeyboardKey.paste,
|
||||
0x0000e020: PhysicalKeyboardKey.audioVolumeMute,
|
||||
0x0000e030: PhysicalKeyboardKey.audioVolumeUp,
|
||||
0x0000e02e: PhysicalKeyboardKey.audioVolumeDown,
|
||||
0x0000007e: PhysicalKeyboardKey.numpadComma,
|
||||
0x00000073: PhysicalKeyboardKey.intlRo,
|
||||
0x00000070: PhysicalKeyboardKey.kanaMode,
|
||||
0x0000007d: PhysicalKeyboardKey.intlYen,
|
||||
0x00000079: PhysicalKeyboardKey.convert,
|
||||
0x0000007b: PhysicalKeyboardKey.nonConvert,
|
||||
0x00000072: PhysicalKeyboardKey.lang1,
|
||||
0x00000071: PhysicalKeyboardKey.lang2,
|
||||
0x00000078: PhysicalKeyboardKey.lang3,
|
||||
0x00000077: PhysicalKeyboardKey.lang4,
|
||||
0x0000001d: PhysicalKeyboardKey.controlLeft,
|
||||
0x0000002a: PhysicalKeyboardKey.shiftLeft,
|
||||
0x00000038: PhysicalKeyboardKey.altLeft,
|
||||
0x0000e05b: PhysicalKeyboardKey.metaLeft,
|
||||
0x0000e01d: PhysicalKeyboardKey.controlRight,
|
||||
0x00000036: PhysicalKeyboardKey.shiftRight,
|
||||
0x0000e038: PhysicalKeyboardKey.altRight,
|
||||
0x0000e05c: PhysicalKeyboardKey.metaRight,
|
||||
0x0000e019: PhysicalKeyboardKey.mediaTrackNext,
|
||||
0x0000e010: PhysicalKeyboardKey.mediaTrackPrevious,
|
||||
0x0000e024: PhysicalKeyboardKey.mediaStop,
|
||||
0x0000e02c: PhysicalKeyboardKey.eject,
|
||||
0x0000e022: PhysicalKeyboardKey.mediaPlayPause,
|
||||
0x0000e06d: PhysicalKeyboardKey.mediaSelect,
|
||||
0x0000e06c: PhysicalKeyboardKey.launchMail,
|
||||
0x0000e021: PhysicalKeyboardKey.launchApp2,
|
||||
0x0000e06b: PhysicalKeyboardKey.launchApp1,
|
||||
0x0000e065: PhysicalKeyboardKey.browserSearch,
|
||||
0x0000e032: PhysicalKeyboardKey.browserHome,
|
||||
0x0000e06a: PhysicalKeyboardKey.browserBack,
|
||||
0x0000e069: PhysicalKeyboardKey.browserForward,
|
||||
0x0000e068: PhysicalKeyboardKey.browserStop,
|
||||
0x0000e067: PhysicalKeyboardKey.browserRefresh,
|
||||
0x0000e066: PhysicalKeyboardKey.browserFavorites,
|
||||
};
|
||||
|
||||
/// A map of Windows KeyboardEvent 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> kWindowsNumPadMap = <int, LogicalKeyboardKey>{
|
||||
111: LogicalKeyboardKey.numpadDivide,
|
||||
106: LogicalKeyboardKey.numpadMultiply,
|
||||
109: LogicalKeyboardKey.numpadSubtract,
|
||||
107: LogicalKeyboardKey.numpadAdd,
|
||||
97: LogicalKeyboardKey.numpad1,
|
||||
98: LogicalKeyboardKey.numpad2,
|
||||
99: LogicalKeyboardKey.numpad3,
|
||||
100: LogicalKeyboardKey.numpad4,
|
||||
101: LogicalKeyboardKey.numpad5,
|
||||
102: LogicalKeyboardKey.numpad6,
|
||||
103: LogicalKeyboardKey.numpad7,
|
||||
104: LogicalKeyboardKey.numpad8,
|
||||
105: LogicalKeyboardKey.numpad9,
|
||||
96: LogicalKeyboardKey.numpad0,
|
||||
110: LogicalKeyboardKey.numpadDecimal,
|
||||
146: LogicalKeyboardKey.numpadEqual,
|
||||
};
|
||||
|
@ -14,6 +14,7 @@ import 'raw_keyboard_fuchsia.dart';
|
||||
import 'raw_keyboard_linux.dart';
|
||||
import 'raw_keyboard_macos.dart';
|
||||
import 'raw_keyboard_web.dart';
|
||||
import 'raw_keyboard_windows.dart';
|
||||
import 'system_channels.dart';
|
||||
|
||||
/// An enum describing the side of the keyboard that a key is on, to allow
|
||||
@ -296,6 +297,14 @@ abstract class RawKeyEvent with Diagnosticable {
|
||||
metaState: message['metaState'] as int,
|
||||
);
|
||||
break;
|
||||
case 'windows':
|
||||
data = RawKeyEventDataWindows(
|
||||
keyCode: message['keyCode'] as int,
|
||||
scanCode: message['scanCode'] as int,
|
||||
characterCodePoint: message['characterCodePoint'] as int,
|
||||
modifiers: message['modifiers'] as int,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
// Raw key events are not yet implemented on iOS or other platforms,
|
||||
// but this exception isn't hit, because the engine never sends these
|
||||
|
291
packages/flutter/lib/src/services/raw_keyboard_windows.dart
Normal file
291
packages/flutter/lib/src/services/raw_keyboard_windows.dart
Normal file
@ -0,0 +1,291 @@
|
||||
// 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.
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'keyboard_key.dart';
|
||||
import 'keyboard_maps.dart';
|
||||
import 'raw_keyboard.dart';
|
||||
|
||||
/// Platform-specific key event data for Windows.
|
||||
///
|
||||
/// This object contains information about key events obtained from Windows's
|
||||
/// win32 API.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [RawKeyboard], which uses this interface to expose key data.
|
||||
class RawKeyEventDataWindows extends RawKeyEventData {
|
||||
/// Creates a key event data structure specific for Windows.
|
||||
///
|
||||
/// The [keyCode], [scanCode], [characterCodePoint], and [modifiers], arguments
|
||||
/// must not be null.
|
||||
const RawKeyEventDataWindows({
|
||||
this.keyCode = 0,
|
||||
this.scanCode = 0,
|
||||
this.characterCodePoint = 0,
|
||||
this.modifiers = 0,
|
||||
}) : assert(keyCode != null),
|
||||
assert(scanCode != null),
|
||||
assert(characterCodePoint != null),
|
||||
assert(modifiers != null);
|
||||
|
||||
/// The hardware key code corresponding to this key event.
|
||||
///
|
||||
/// This is the physical key that was pressed, not the Unicode character.
|
||||
/// See [characterCodePoint] for the Unicode character.
|
||||
final int keyCode;
|
||||
|
||||
/// 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.
|
||||
final int characterCodePoint;
|
||||
|
||||
/// A mask of the current modifiers. The modifier values must be in sync with
|
||||
/// the ones defined in https://github.com/flutter/engine/blob/master/shell/platform/windows/key_event_handler.cc
|
||||
final int modifiers;
|
||||
|
||||
@override
|
||||
String get keyLabel => characterCodePoint == 0 ? null : String.fromCharCode(characterCodePoint);
|
||||
|
||||
@override
|
||||
PhysicalKeyboardKey get physicalKey => kWindowsToPhysicalKey[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 = kWindowsNumPadMap[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 && keyLabel.isNotEmpty && !LogicalKeyboardKey.isControlCharacter(keyLabel)) {
|
||||
final int keyId = LogicalKeyboardKey.unicodePlane | (characterCodePoint & 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 = kWindowsToLogicalKey[keyCode];
|
||||
if (newKey != null) {
|
||||
return newKey;
|
||||
}
|
||||
|
||||
// This is a non-printable key that we don't know about, so we mint a new
|
||||
// code with the autogenerated bit set.
|
||||
const int windowsKeyIdPlane = 0x00700000000;
|
||||
newKey ??= LogicalKeyboardKey(
|
||||
windowsKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask,
|
||||
debugName: kReleaseMode ? null : 'Unknown Windows key code $keyCode',
|
||||
);
|
||||
return newKey;
|
||||
}
|
||||
|
||||
bool _isLeftRightModifierPressed(KeyboardSide side, int anyMask, int leftMask, int rightMask) {
|
||||
if (modifiers & anyMask == 0 &&
|
||||
modifiers & leftMask == 0 &&
|
||||
modifiers & rightMask == 0) {
|
||||
return false;
|
||||
}
|
||||
// If only the "anyMask" bit is set, then we respond true for requests of
|
||||
// whether either left or right is pressed.
|
||||
// Handles the case where Windows supplies just the "either" modifier flag,
|
||||
// but not the left/right flag. (e.g. modifierShift but not
|
||||
// modifierLeftShift).
|
||||
final bool anyOnly = modifiers & (leftMask | rightMask | anyMask) == anyMask;
|
||||
switch (side) {
|
||||
case KeyboardSide.any:
|
||||
return true;
|
||||
case KeyboardSide.all:
|
||||
return modifiers & leftMask != 0 && modifiers & rightMask != 0 || anyOnly;
|
||||
case KeyboardSide.left:
|
||||
return modifiers & leftMask != 0 || anyOnly;
|
||||
case KeyboardSide.right:
|
||||
return modifiers & rightMask != 0 || anyOnly;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
bool isModifierPressed(ModifierKey key, {KeyboardSide side = KeyboardSide.any}) {
|
||||
bool result;
|
||||
switch (key) {
|
||||
case ModifierKey.controlModifier:
|
||||
result = _isLeftRightModifierPressed(side, modifierControl, modifierLeftControl, modifierRightControl);
|
||||
break;
|
||||
case ModifierKey.shiftModifier:
|
||||
result = _isLeftRightModifierPressed(side, modifierShift, modifierLeftShift, modifierRightShift);
|
||||
break;
|
||||
case ModifierKey.altModifier:
|
||||
result = _isLeftRightModifierPressed(side, modifierAlt, modifierLeftAlt, modifierRightAlt);
|
||||
break;
|
||||
case ModifierKey.metaModifier:
|
||||
// Windows does not provide an "any" key for win key press.
|
||||
result = _isLeftRightModifierPressed(side, modifierLeftMeta | modifierRightMeta , modifierLeftMeta, modifierRightMeta);
|
||||
break;
|
||||
case ModifierKey.capsLockModifier:
|
||||
result = modifiers & modifierCaps != 0;
|
||||
break;
|
||||
case ModifierKey.scrollLockModifier:
|
||||
result = modifiers & modifierScrollLock != 0;
|
||||
break;
|
||||
case ModifierKey.numLockModifier:
|
||||
result = modifiers & modifierNumLock != 0;
|
||||
break;
|
||||
// The OS does not expose the Fn key to the drivers, it doesn't generate a key message.
|
||||
case ModifierKey.functionModifier:
|
||||
case ModifierKey.symbolModifier:
|
||||
// These modifier masks are not used in Windows keyboards.
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
assert(!result || getModifierSide(key) != null, "$runtimeType thinks that a modifier is pressed, but can't figure out what side it's on.");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
KeyboardSide getModifierSide(ModifierKey key) {
|
||||
KeyboardSide findSide(int leftMask, int rightMask, int anyMask) {
|
||||
final int combinedMask = leftMask | rightMask;
|
||||
final int combined = modifiers & combinedMask;
|
||||
if (combined == leftMask) {
|
||||
return KeyboardSide.left;
|
||||
} else if (combined == rightMask) {
|
||||
return KeyboardSide.right;
|
||||
} else if (combined == combinedMask) {
|
||||
return KeyboardSide.all;
|
||||
} else if (modifiers & (combinedMask | anyMask) == anyMask) {
|
||||
// Handles the case where Windows supplies just the "either" modifier
|
||||
// flag, but not the left/right flag. (e.g. modifierShift but not
|
||||
// modifierLeftShift).
|
||||
return KeyboardSide.any;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case ModifierKey.controlModifier:
|
||||
return findSide(modifierLeftControl, modifierRightControl, modifierControl);
|
||||
case ModifierKey.shiftModifier:
|
||||
return findSide(modifierLeftShift, modifierRightShift, modifierShift);
|
||||
case ModifierKey.altModifier:
|
||||
return findSide(modifierLeftAlt, modifierRightAlt, modifierAlt);
|
||||
case ModifierKey.metaModifier:
|
||||
return findSide(modifierLeftMeta, modifierRightMeta, 0);
|
||||
case ModifierKey.capsLockModifier:
|
||||
case ModifierKey.numLockModifier:
|
||||
case ModifierKey.scrollLockModifier:
|
||||
case ModifierKey.functionModifier:
|
||||
case ModifierKey.symbolModifier:
|
||||
return KeyboardSide.all;
|
||||
}
|
||||
|
||||
assert(false, 'Not handling $key type properly.');
|
||||
return null;
|
||||
}
|
||||
|
||||
// These are not the values defined by the Windows header for each modifier. Since they
|
||||
// can't be packaged into a single int, we are re-defining them here to reduce the size
|
||||
// of the message from the embedder. Embedders should map these values to the native key codes.
|
||||
// Keep this in sync with https://github.com/flutter/engine/blob/master/shell/platform/windows/key_event_handler.cc
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether one of the
|
||||
/// SHIFT modifier keys is pressed.
|
||||
///
|
||||
/// {@macro flutter.services.rawKeyEventDataWindows.modifiers}
|
||||
static const int modifierShift = 1 << 0;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether the left
|
||||
/// SHIFT modifier key is pressed.
|
||||
///
|
||||
/// {@macro flutter.services.rawKeyEventDataWindows.modifiers}
|
||||
static const int modifierLeftShift = 1 << 1;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether the right
|
||||
/// SHIFT modifier key is pressed.
|
||||
///
|
||||
/// {@macro flutter.services.rawKeyEventDataWindows.modifiers}
|
||||
static const int modifierRightShift = 1 << 2;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether one of the
|
||||
/// CTRL modifier keys is pressed.
|
||||
///
|
||||
/// {@macro flutter.services.rawKeyEventDataWindows.modifiers}
|
||||
static const int modifierControl = 1 << 3;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether the left
|
||||
/// CTRL modifier key is pressed.
|
||||
///
|
||||
/// {@macro flutter.services.rawKeyEventDataWindows.modifiers}
|
||||
static const int modifierLeftControl = 1 << 4;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether the right
|
||||
/// CTRL modifier key is pressed.
|
||||
///
|
||||
/// {@macro flutter.services.rawKeyEventDataWindows.modifiers}
|
||||
static const int modifierRightControl = 1 << 5;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether one of the
|
||||
/// ALT modifier keys is pressed.
|
||||
///
|
||||
/// {@macro flutter.services.rawKeyEventDataWindows.modifiers}
|
||||
static const int modifierAlt = 1 << 6;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether the left
|
||||
/// ALT modifier key is pressed.
|
||||
///
|
||||
/// {@macro flutter.services.rawKeyEventDataWindows.modifiers}
|
||||
static const int modifierLeftAlt = 1 << 7;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether the right
|
||||
/// ALT modifier key is pressed.
|
||||
///
|
||||
/// {@macro flutter.services.rawKeyEventDataWindows.modifiers}
|
||||
static const int modifierRightAlt = 1 << 8;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether the left
|
||||
/// WIN modifier keys is pressed.
|
||||
///
|
||||
/// {@macro flutter.services.rawKeyEventDataWindows.modifiers}
|
||||
static const int modifierLeftMeta = 1 << 9;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether the right
|
||||
/// WIN modifier keys is pressed.
|
||||
///
|
||||
/// {@macro flutter.services.rawKeyEventDataWindows.modifiers}
|
||||
static const int modifierRightMeta = 1 << 10;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether the CAPS LOCK key
|
||||
/// is pressed.
|
||||
///
|
||||
/// {@macro flutter.services.rawKeyEventDataWindows.modifiers}
|
||||
static const int modifierCaps = 1 << 11;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether the NUM LOCK key
|
||||
/// is pressed.
|
||||
///
|
||||
/// {@macro flutter.services.rawKeyEventDataWindows.modifiers}
|
||||
static const int modifierNumLock = 1 << 12;
|
||||
|
||||
/// This mask is used to check the [modifiers] field to test whether the SCROLL LOCK key
|
||||
/// is pressed.
|
||||
///
|
||||
/// {@macro flutter.services.rawKeyEventDataWindows.modifiers}
|
||||
static const int modifierScrollLock = 1 << 13;
|
||||
}
|
@ -15,7 +15,7 @@ class _ModifierCheck {
|
||||
void main() {
|
||||
group('RawKeyboard', () {
|
||||
testWidgets('keysPressed is maintained', (WidgetTester tester) async {
|
||||
for (final String platform in <String>['linux', 'android', 'macos', 'fuchsia']) {
|
||||
for (final String platform in <String>['linux', 'android', 'macos', 'fuchsia', 'windows']) {
|
||||
RawKeyboard.instance.clearKeysPressed();
|
||||
expect(RawKeyboard.instance.keysPressed, isEmpty, reason: 'on $platform');
|
||||
await simulateKeyDownEvent(LogicalKeyboardKey.shiftLeft, platform: platform);
|
||||
@ -70,8 +70,8 @@ void main() {
|
||||
);
|
||||
await simulateKeyUpEvent(LogicalKeyboardKey.shiftLeft, platform: platform);
|
||||
expect(RawKeyboard.instance.keysPressed, isEmpty, reason: 'on $platform');
|
||||
// The Fn key isn't mapped on linux.
|
||||
if (platform != 'linux') {
|
||||
// The Fn key isn't mapped on linux or Windows.
|
||||
if (platform != 'linux' && platform != 'windows') {
|
||||
await simulateKeyDownEvent(LogicalKeyboardKey.fn, platform: platform);
|
||||
expect(
|
||||
RawKeyboard.instance.keysPressed,
|
||||
@ -107,7 +107,7 @@ void main() {
|
||||
}, skip: kIsWeb);
|
||||
|
||||
testWidgets('keysPressed is correct when modifier is released before key', (WidgetTester tester) async {
|
||||
for (final String platform in <String>['linux', 'android', 'macos', 'fuchsia']) {
|
||||
for (final String platform in <String>['linux', 'android', 'macos', 'fuchsia', 'windows']) {
|
||||
RawKeyboard.instance.clearKeysPressed();
|
||||
expect(RawKeyboard.instance.keysPressed, isEmpty, reason: 'on $platform');
|
||||
await simulateKeyDownEvent(LogicalKeyboardKey.shiftLeft, platform: platform, physicalKey: PhysicalKeyboardKey.shiftLeft);
|
||||
@ -176,6 +176,31 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('keysPressed modifiers are synchronized with key events on Windows', (WidgetTester tester) async {
|
||||
expect(RawKeyboard.instance.keysPressed, isEmpty);
|
||||
// Generate the data for a regular key down event.
|
||||
final Map<String, dynamic> data = KeyEventSimulator.getKeyData(
|
||||
LogicalKeyboardKey.keyA,
|
||||
platform: 'windows',
|
||||
isDown: true,
|
||||
);
|
||||
// Change the modifiers so that they show the shift key as already down
|
||||
// when this event is received, but it's not in keysPressed yet.
|
||||
data['modifiers'] |= RawKeyEventDataWindows.modifierLeftShift | RawKeyEventDataWindows.modifierShift;
|
||||
// dispatch the modified data.
|
||||
await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
|
||||
SystemChannels.keyEvent.name,
|
||||
SystemChannels.keyEvent.codec.encodeMessage(data),
|
||||
(ByteData data) {},
|
||||
);
|
||||
expect(
|
||||
RawKeyboard.instance.keysPressed,
|
||||
equals(
|
||||
<LogicalKeyboardKey>{LogicalKeyboardKey.shiftLeft, LogicalKeyboardKey.keyA},
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('keysPressed modifiers are synchronized with key events on android', (WidgetTester tester) async {
|
||||
expect(RawKeyboard.instance.keysPressed, isEmpty);
|
||||
// Generate the data for a regular key down event.
|
||||
@ -746,6 +771,151 @@ void main() {
|
||||
expect(data.logicalKey.keyLabel, isNull);
|
||||
});
|
||||
}, skip: isBrowser);
|
||||
|
||||
group('RawKeyEventDataWindows', () {
|
||||
const Map<int, _ModifierCheck> modifierTests = <int, _ModifierCheck>{
|
||||
RawKeyEventDataWindows.modifierLeftAlt: _ModifierCheck(ModifierKey.altModifier, KeyboardSide.left),
|
||||
RawKeyEventDataWindows.modifierRightAlt: _ModifierCheck(ModifierKey.altModifier, KeyboardSide.right),
|
||||
RawKeyEventDataWindows.modifierLeftShift: _ModifierCheck(ModifierKey.shiftModifier, KeyboardSide.left),
|
||||
RawKeyEventDataWindows.modifierRightShift: _ModifierCheck(ModifierKey.shiftModifier, KeyboardSide.right),
|
||||
RawKeyEventDataWindows.modifierLeftControl: _ModifierCheck(ModifierKey.controlModifier, KeyboardSide.left),
|
||||
RawKeyEventDataWindows.modifierRightControl: _ModifierCheck(ModifierKey.controlModifier, KeyboardSide.right),
|
||||
RawKeyEventDataWindows.modifierLeftMeta: _ModifierCheck(ModifierKey.metaModifier, KeyboardSide.left),
|
||||
RawKeyEventDataWindows.modifierRightMeta: _ModifierCheck(ModifierKey.metaModifier, KeyboardSide.right),
|
||||
RawKeyEventDataWindows.modifierShift: _ModifierCheck(ModifierKey.shiftModifier, KeyboardSide.any),
|
||||
RawKeyEventDataWindows.modifierControl: _ModifierCheck(ModifierKey.controlModifier, KeyboardSide.any),
|
||||
RawKeyEventDataWindows.modifierAlt: _ModifierCheck(ModifierKey.altModifier, KeyboardSide.any),
|
||||
RawKeyEventDataWindows.modifierCaps: _ModifierCheck(ModifierKey.capsLockModifier, KeyboardSide.all),
|
||||
RawKeyEventDataWindows.modifierNumLock: _ModifierCheck(ModifierKey.numLockModifier, KeyboardSide.all),
|
||||
RawKeyEventDataWindows.modifierScrollLock: _ModifierCheck(ModifierKey.scrollLockModifier, KeyboardSide.all),
|
||||
};
|
||||
|
||||
test('modifier keys are recognized individually', () {
|
||||
for (final int modifier in modifierTests.keys) {
|
||||
final RawKeyEvent event = RawKeyEvent.fromMessage(<String, dynamic>{
|
||||
'type': 'keydown',
|
||||
'keymap': 'windows',
|
||||
'keyCode': 0x04,
|
||||
'characterCodePoint': 0,
|
||||
'scanCode': 0x04,
|
||||
'modifiers': modifier,
|
||||
});
|
||||
final RawKeyEventDataWindows data = event.data as RawKeyEventDataWindows;
|
||||
for (final 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 modifier $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 (final int modifier in modifierTests.keys) {
|
||||
if (modifier == RawKeyEventDataWindows.modifierCaps) {
|
||||
// No need to combine caps lock key with itself.
|
||||
continue;
|
||||
}
|
||||
final RawKeyEvent event = RawKeyEvent.fromMessage(<String, dynamic>{
|
||||
'type': 'keydown',
|
||||
'keymap': 'windows',
|
||||
'keyCode': 0x04,
|
||||
'characterCodePoint': 0,
|
||||
'scanCode': 0x04,
|
||||
'modifiers': modifier | RawKeyEventDataWindows.modifierCaps,
|
||||
});
|
||||
final RawKeyEventDataWindows data = event.data as RawKeyEventDataWindows;
|
||||
for (final ModifierKey key in ModifierKey.values) {
|
||||
if (modifierTests[modifier].key == key || key == ModifierKey.capsLockModifier) {
|
||||
expect(
|
||||
data.isModifierPressed(key, side: modifierTests[modifier].side),
|
||||
isTrue,
|
||||
reason: '$key should be pressed with metaState $modifier '
|
||||
"and additional key ${RawKeyEventDataWindows.modifierCaps}, but isn't.",
|
||||
);
|
||||
if (key != ModifierKey.capsLockModifier) {
|
||||
expect(data.getModifierSide(key), equals(modifierTests[modifier].side));
|
||||
} else {
|
||||
expect(data.getModifierSide(key), equals(KeyboardSide.all));
|
||||
}
|
||||
} else {
|
||||
expect(
|
||||
data.isModifierPressed(key, side: modifierTests[modifier].side),
|
||||
isFalse,
|
||||
reason: '$key should not be pressed with metaState $modifier '
|
||||
'and additional key ${RawKeyEventDataWindows.modifierCaps}.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
test('Printable keyboard keys are correctly translated', () {
|
||||
const int unmodifiedCharacter = 97; // ASCII value for 'a'.
|
||||
final RawKeyEvent keyAEvent = RawKeyEvent.fromMessage(const <String, dynamic>{
|
||||
'type': 'keydown',
|
||||
'keymap': 'windows',
|
||||
'keyCode': 0x00000000,
|
||||
'characterCodePoint': unmodifiedCharacter,
|
||||
'scanCode': 0x0000001e,
|
||||
'modifiers': 0x0,
|
||||
});
|
||||
final RawKeyEventDataWindows data = keyAEvent.data as RawKeyEventDataWindows;
|
||||
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': 'windows',
|
||||
'keyCode': 27, // keycode for escape key
|
||||
'scanCode': 0x00000001, // scanCode for escape key
|
||||
'characterCodePoint': 0,
|
||||
'modifiers': 0x0,
|
||||
});
|
||||
final RawKeyEventDataWindows data = escapeKeyEvent.data as RawKeyEventDataWindows;
|
||||
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': 'windows',
|
||||
'keyCode': 160, // keyCode for left shift.
|
||||
'scanCode': 0x0000002a, // scanCode for left shift.
|
||||
'characterCodePoint': 0,
|
||||
'modifiers': RawKeyEventDataWindows.modifierLeftShift,
|
||||
});
|
||||
final RawKeyEventDataWindows data = shiftLeftKeyEvent.data as RawKeyEventDataWindows;
|
||||
expect(data.physicalKey, equals(PhysicalKeyboardKey.shiftLeft));
|
||||
expect(data.logicalKey, equals(LogicalKeyboardKey.shiftLeft));
|
||||
expect(data.keyLabel, isNull);
|
||||
});
|
||||
test('Unprintable keyboard keys are correctly translated', () {
|
||||
final RawKeyEvent leftArrowKey = RawKeyEvent.fromMessage(const <String, dynamic>{
|
||||
'type': 'keydown',
|
||||
'keymap': 'windows',
|
||||
'keyCode': 37, // keyCode for left arrow.
|
||||
'scanCode': 0x0000e04b, // scanCode for left arrow.
|
||||
'characterCodePoint': 0,
|
||||
'modifiers': 0,
|
||||
});
|
||||
final RawKeyEventDataWindows data = leftArrowKey.data as RawKeyEventDataWindows;
|
||||
expect(data.physicalKey, equals(PhysicalKeyboardKey.arrowLeft));
|
||||
expect(data.logicalKey, equals(LogicalKeyboardKey.arrowLeft));
|
||||
expect(data.logicalKey.keyLabel, isNull);
|
||||
});
|
||||
}, skip: isBrowser);
|
||||
group('RawKeyEventDataLinux-GFLW', () {
|
||||
const Map<int, _ModifierCheck> modifierTests = <int, _ModifierCheck>{
|
||||
GLFWKeyHelper.modifierAlt: _ModifierCheck(ModifierKey.altModifier, KeyboardSide.any),
|
||||
|
@ -40,6 +40,7 @@ class KeyEventSimulator {
|
||||
case 'macos':
|
||||
case 'linux':
|
||||
case 'web':
|
||||
case 'windows':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -62,6 +63,9 @@ class KeyEventSimulator {
|
||||
case 'linux':
|
||||
map = kLinuxToPhysicalKey;
|
||||
break;
|
||||
case 'windows':
|
||||
map = kWindowsToPhysicalKey;
|
||||
break;
|
||||
case 'web':
|
||||
// web doesn't have int type code
|
||||
return null;
|
||||
@ -95,6 +99,9 @@ class KeyEventSimulator {
|
||||
case 'linux':
|
||||
map = kGlfwToLogicalKey;
|
||||
break;
|
||||
case 'windows':
|
||||
map = kWindowsToLogicalKey;
|
||||
break;
|
||||
}
|
||||
for (final int code in map.keys) {
|
||||
if (key.keyId == map[code].keyId) {
|
||||
@ -132,6 +139,9 @@ class KeyEventSimulator {
|
||||
case 'web':
|
||||
map = kWebToPhysicalKey;
|
||||
break;
|
||||
case 'windows':
|
||||
map = kWindowsToPhysicalKey;
|
||||
break;
|
||||
}
|
||||
for (final PhysicalKeyboardKey physicalKey in map.values) {
|
||||
if (key.debugName == physicalKey.debugName) {
|
||||
@ -195,6 +205,12 @@ class KeyEventSimulator {
|
||||
result['code'] = _getWebKeyCode(key);
|
||||
result['key'] = '';
|
||||
result['metaState'] = _getWebModifierFlags(key, isDown);
|
||||
break;
|
||||
case 'windows':
|
||||
result['keyCode'] = keyCode;
|
||||
result['scanCode'] = scanCode;
|
||||
result['characterCodePoint'] = key.keyLabel?.codeUnitAt(0) ?? 0;
|
||||
result['modifiers'] = _getWindowsModifierFlags(key, isDown);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -272,6 +288,59 @@ class KeyEventSimulator {
|
||||
return result;
|
||||
}
|
||||
|
||||
static int _getWindowsModifierFlags(LogicalKeyboardKey newKey, bool isDown) {
|
||||
int result = 0;
|
||||
final Set<LogicalKeyboardKey> pressed = RawKeyboard.instance.keysPressed;
|
||||
if (isDown) {
|
||||
pressed.add(newKey);
|
||||
} else {
|
||||
pressed.remove(newKey);
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.shift)) {
|
||||
result |= RawKeyEventDataWindows.modifierShift;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.shiftLeft)) {
|
||||
result |= RawKeyEventDataWindows.modifierLeftShift;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.shiftRight)) {
|
||||
result |= RawKeyEventDataWindows.modifierRightShift;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.metaLeft)) {
|
||||
result |= RawKeyEventDataWindows.modifierLeftMeta;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.metaRight)) {
|
||||
result |= RawKeyEventDataWindows.modifierRightMeta;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.control)) {
|
||||
result |= RawKeyEventDataWindows.modifierControl;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.controlLeft)) {
|
||||
result |= RawKeyEventDataWindows.modifierLeftControl;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.controlRight)) {
|
||||
result |= RawKeyEventDataWindows.modifierRightControl;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.alt)) {
|
||||
result |= RawKeyEventDataWindows.modifierAlt;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.altLeft)) {
|
||||
result |= RawKeyEventDataWindows.modifierLeftAlt;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.altRight)) {
|
||||
result |= RawKeyEventDataWindows.modifierRightAlt;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.capsLock)) {
|
||||
result |= RawKeyEventDataWindows.modifierCaps;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.numLock)) {
|
||||
result |= RawKeyEventDataWindows.modifierNumLock;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.scrollLock)) {
|
||||
result |= RawKeyEventDataWindows.modifierScrollLock;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int _getFuchsiaModifierFlags(LogicalKeyboardKey newKey, bool isDown) {
|
||||
int result = 0;
|
||||
final Set<LogicalKeyboardKey> pressed = RawKeyboard.instance.keysPressed;
|
||||
|
Loading…
x
Reference in New Issue
Block a user