From a7899c1961cb51f6bd34728c97683d0977b8ed5b Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 29 Jul 2021 16:44:06 -0700 Subject: [PATCH] [gen_keycodes] Remove nonexistent Web keys and improve their emulation (#87098) --- .../gen_keycodes/data/logical_key_data.json | 527 +----------------- .../gen_keycodes/lib/logical_key_data.dart | 58 +- .../gen_keycodes/test/gen_keycodes_test.dart | 75 +-- .../lib/src/services/keyboard_maps.dart | 144 ----- .../lib/src/services/raw_keyboard.dart | 6 +- .../lib/src/services/raw_keyboard_web.dart | 18 +- .../test/services/raw_keyboard_test.dart | 26 + .../lib/src/event_simulation.dart | 59 +- 8 files changed, 175 insertions(+), 738 deletions(-) diff --git a/dev/tools/gen_keycodes/data/logical_key_data.json b/dev/tools/gen_keycodes/data/logical_key_data.json index fab6a3c864..1143b52e92 100644 --- a/dev/tools/gen_keycodes/data/logical_key_data.json +++ b/dev/tools/gen_keycodes/data/logical_key_data.json @@ -4,9 +4,6 @@ "value": 32, "keyLabel": " ", "names": { - "web": [ - "Space" - ], "gtk": [ "KP_Space" ], @@ -41,21 +38,13 @@ "Exclamation": { "name": "Exclamation", "value": 33, - "keyLabel": "!", - "names": { - "web": [ - "Exclamation" - ] - } + "keyLabel": "!" }, "Quote": { "name": "Quote", "value": 34, "keyLabel": "\"", "names": { - "web": [ - "Quote" - ], "windows": [ "OEM_7" ], @@ -86,9 +75,6 @@ "value": 35, "keyLabel": "#", "names": { - "web": [ - "NumberSign" - ], "android": [ "POUND" ] @@ -102,71 +88,38 @@ "Dollar": { "name": "Dollar", "value": 36, - "keyLabel": "$", - "names": { - "web": [ - "Dollar" - ] - } + "keyLabel": "$" }, "Percent": { "name": "Percent", "value": 37, - "keyLabel": "%", - "names": { - "web": [ - "Percent" - ] - } + "keyLabel": "%" }, "Ampersand": { "name": "Ampersand", "value": 38, - "keyLabel": "&", - "names": { - "web": [ - "Ampersand" - ] - } + "keyLabel": "&" }, "QuoteSingle": { "name": "QuoteSingle", "value": 39, - "keyLabel": "'", - "names": { - "web": [ - "QuoteSingle" - ] - } + "keyLabel": "'" }, "ParenthesisLeft": { "name": "ParenthesisLeft", "value": 40, - "keyLabel": "(", - "names": { - "web": [ - "ParenthesisLeft" - ] - } + "keyLabel": "(" }, "ParenthesisRight": { "name": "ParenthesisRight", "value": 41, - "keyLabel": ")", - "names": { - "web": [ - "ParenthesisRight" - ] - } + "keyLabel": ")" }, "Asterisk": { "name": "Asterisk", "value": 42, "keyLabel": "*", "names": { - "web": [ - "Asterisk" - ], "android": [ "STAR" ] @@ -182,9 +135,6 @@ "value": 43, "keyLabel": "+", "names": { - "web": [ - "Add" - ], "android": [ "PLUS" ] @@ -200,9 +150,6 @@ "value": 44, "keyLabel": ",", "names": { - "web": [ - "Comma" - ], "windows": [ "OEM_COMMA" ], @@ -233,9 +180,6 @@ "value": 45, "keyLabel": "-", "names": { - "web": [ - "Minus" - ], "windows": [ "OEM_MINUS" ], @@ -266,9 +210,6 @@ "value": 46, "keyLabel": ".", "names": { - "web": [ - "Period" - ], "gtk": [ "KP_Decimal" ], @@ -305,9 +246,6 @@ "value": 47, "keyLabel": "/", "names": { - "web": [ - "Slash" - ], "windows": [ "OEM_2" ], @@ -338,9 +276,6 @@ "value": 48, "keyLabel": "0", "names": { - "web": [ - "Digit0" - ], "android": [ "0" ], @@ -365,9 +300,6 @@ "value": 49, "keyLabel": "1", "names": { - "web": [ - "Digit1" - ], "android": [ "1" ], @@ -392,9 +324,6 @@ "value": 50, "keyLabel": "2", "names": { - "web": [ - "Digit2" - ], "android": [ "2" ], @@ -419,9 +348,6 @@ "value": 51, "keyLabel": "3", "names": { - "web": [ - "Digit3" - ], "android": [ "3" ], @@ -446,9 +372,6 @@ "value": 52, "keyLabel": "4", "names": { - "web": [ - "Digit4" - ], "android": [ "4" ], @@ -473,9 +396,6 @@ "value": 53, "keyLabel": "5", "names": { - "web": [ - "Digit5" - ], "android": [ "5" ], @@ -500,9 +420,6 @@ "value": 54, "keyLabel": "6", "names": { - "web": [ - "Digit6" - ], "android": [ "6" ], @@ -527,9 +444,6 @@ "value": 55, "keyLabel": "7", "names": { - "web": [ - "Digit7" - ], "android": [ "7" ], @@ -554,9 +468,6 @@ "value": 56, "keyLabel": "8", "names": { - "web": [ - "Digit8" - ], "android": [ "8" ], @@ -581,9 +492,6 @@ "value": 57, "keyLabel": "9", "names": { - "web": [ - "Digit9" - ], "android": [ "9" ], @@ -606,21 +514,13 @@ "Colon": { "name": "Colon", "value": 58, - "keyLabel": ":", - "names": { - "web": [ - "Colon" - ] - } + "keyLabel": ":" }, "Semicolon": { "name": "Semicolon", "value": 59, "keyLabel": ";", "names": { - "web": [ - "Semicolon" - ], "windows": [ "OEM_1" ], @@ -649,21 +549,13 @@ "Less": { "name": "Less", "value": 60, - "keyLabel": "<", - "names": { - "web": [ - "Less" - ] - } + "keyLabel": "<" }, "Equal": { "name": "Equal", "value": 61, "keyLabel": "=", "names": { - "web": [ - "Equal" - ], "windows": [ "OEM_PLUS" ], @@ -692,31 +584,18 @@ "Greater": { "name": "Greater", "value": 62, - "keyLabel": ">", - "names": { - "web": [ - "Greater" - ] - } + "keyLabel": ">" }, "Question": { "name": "Question", "value": 63, - "keyLabel": "?", - "names": { - "web": [ - "Question" - ] - } + "keyLabel": "?" }, "At": { "name": "At", "value": 64, "keyLabel": "@", "names": { - "web": [ - "At" - ], "android": [ "AT" ] @@ -732,9 +611,6 @@ "value": 91, "keyLabel": "[", "names": { - "web": [ - "BracketLeft" - ], "windows": [ "OEM_4" ], @@ -765,9 +641,6 @@ "value": 92, "keyLabel": "\\", "names": { - "web": [ - "Backslash" - ], "windows": [ "OEM_5" ], @@ -798,9 +671,6 @@ "value": 93, "keyLabel": "]", "names": { - "web": [ - "BracketRight" - ], "windows": [ "OEM_6" ], @@ -829,31 +699,18 @@ "Caret": { "name": "Caret", "value": 94, - "keyLabel": "^", - "names": { - "web": [ - "Caret" - ] - } + "keyLabel": "^" }, "Underscore": { "name": "Underscore", "value": 95, - "keyLabel": "_", - "names": { - "web": [ - "Underscore" - ] - } + "keyLabel": "_" }, "Backquote": { "name": "Backquote", "value": 96, "keyLabel": "`", "names": { - "web": [ - "Backquote" - ], "windows": [ "OEM_3" ], @@ -884,9 +741,6 @@ "value": 97, "keyLabel": "a", "names": { - "web": [ - "KeyA" - ], "android": [ "A" ], @@ -911,9 +765,6 @@ "value": 98, "keyLabel": "b", "names": { - "web": [ - "KeyB" - ], "android": [ "B" ], @@ -938,9 +789,6 @@ "value": 99, "keyLabel": "c", "names": { - "web": [ - "KeyC" - ], "android": [ "C" ], @@ -965,9 +813,6 @@ "value": 100, "keyLabel": "d", "names": { - "web": [ - "KeyD" - ], "android": [ "D" ], @@ -992,9 +837,6 @@ "value": 101, "keyLabel": "e", "names": { - "web": [ - "KeyE" - ], "android": [ "E" ], @@ -1019,9 +861,6 @@ "value": 102, "keyLabel": "f", "names": { - "web": [ - "KeyF" - ], "android": [ "F" ], @@ -1046,9 +885,6 @@ "value": 103, "keyLabel": "g", "names": { - "web": [ - "KeyG" - ], "android": [ "G" ], @@ -1073,9 +909,6 @@ "value": 104, "keyLabel": "h", "names": { - "web": [ - "KeyH" - ], "android": [ "H" ], @@ -1100,9 +933,6 @@ "value": 105, "keyLabel": "i", "names": { - "web": [ - "KeyI" - ], "android": [ "I" ], @@ -1127,9 +957,6 @@ "value": 106, "keyLabel": "j", "names": { - "web": [ - "KeyJ" - ], "android": [ "J" ], @@ -1154,9 +981,6 @@ "value": 107, "keyLabel": "k", "names": { - "web": [ - "KeyK" - ], "android": [ "K" ], @@ -1181,9 +1005,6 @@ "value": 108, "keyLabel": "l", "names": { - "web": [ - "KeyL" - ], "android": [ "L" ], @@ -1208,9 +1029,6 @@ "value": 109, "keyLabel": "m", "names": { - "web": [ - "KeyM" - ], "android": [ "M" ], @@ -1235,9 +1053,6 @@ "value": 110, "keyLabel": "n", "names": { - "web": [ - "KeyN" - ], "android": [ "N" ], @@ -1262,9 +1077,6 @@ "value": 111, "keyLabel": "o", "names": { - "web": [ - "KeyO" - ], "android": [ "O" ], @@ -1289,9 +1101,6 @@ "value": 112, "keyLabel": "p", "names": { - "web": [ - "KeyP" - ], "android": [ "P" ], @@ -1316,9 +1125,6 @@ "value": 113, "keyLabel": "q", "names": { - "web": [ - "KeyQ" - ], "android": [ "Q" ], @@ -1343,9 +1149,6 @@ "value": 114, "keyLabel": "r", "names": { - "web": [ - "KeyR" - ], "android": [ "R" ], @@ -1370,9 +1173,6 @@ "value": 115, "keyLabel": "s", "names": { - "web": [ - "KeyS" - ], "android": [ "S" ], @@ -1397,9 +1197,6 @@ "value": 116, "keyLabel": "t", "names": { - "web": [ - "KeyT" - ], "android": [ "T" ], @@ -1424,9 +1221,6 @@ "value": 117, "keyLabel": "u", "names": { - "web": [ - "KeyU" - ], "android": [ "U" ], @@ -1451,9 +1245,6 @@ "value": 118, "keyLabel": "v", "names": { - "web": [ - "KeyV" - ], "android": [ "V" ], @@ -1478,9 +1269,6 @@ "value": 119, "keyLabel": "w", "names": { - "web": [ - "KeyW" - ], "android": [ "W" ], @@ -1505,9 +1293,6 @@ "value": 120, "keyLabel": "x", "names": { - "web": [ - "KeyX" - ], "android": [ "X" ], @@ -1532,9 +1317,6 @@ "value": 121, "keyLabel": "y", "names": { - "web": [ - "KeyY" - ], "android": [ "Y" ], @@ -1559,9 +1341,6 @@ "value": 122, "keyLabel": "z", "names": { - "web": [ - "KeyZ" - ], "android": [ "Z" ], @@ -1584,42 +1363,22 @@ "BraceLeft": { "name": "BraceLeft", "value": 123, - "keyLabel": "{", - "names": { - "web": [ - "BraceLeft" - ] - } + "keyLabel": "{" }, "Bar": { "name": "Bar", "value": 124, - "keyLabel": "|", - "names": { - "web": [ - "Bar" - ] - } + "keyLabel": "|" }, "BraceRight": { "name": "BraceRight", "value": 125, - "keyLabel": "}", - "names": { - "web": [ - "BraceRight" - ] - } + "keyLabel": "}" }, "Tilde": { "name": "Tilde", "value": 126, - "keyLabel": "~", - "names": { - "web": [ - "Tilde" - ] - } + "keyLabel": "~" }, "Unidentified": { "name": "Unidentified", @@ -1633,7 +1392,6 @@ "Backspace": { "name": "Backspace", "value": 4294967304, - "keyLabel": "\b", "names": { "web": [ "Backspace" @@ -1684,7 +1442,6 @@ "Tab": { "name": "Tab", "value": 4294967305, - "keyLabel": "\t", "names": { "web": [ "Tab" @@ -1739,7 +1496,6 @@ "Enter": { "name": "Enter", "value": 4294967309, - "keyLabel": "\r", "names": { "web": [ "Enter" @@ -1794,7 +1550,6 @@ "Escape": { "name": "Escape", "value": 4294967323, - "keyLabel": "\u001b", "names": { "web": [ "Escape" @@ -1845,7 +1600,6 @@ "Delete": { "name": "Delete", "value": 4294967423, - "keyLabel": "", "names": { "web": [ "Delete" @@ -7866,9 +7620,6 @@ "name": "Suspend", "value": 8589934592, "names": { - "web": [ - "Suspend" - ], "gtk": [ "Suspend" ] @@ -7885,11 +7636,6 @@ "Resume": { "name": "Resume", "value": 8589934593, - "names": { - "web": [ - "Resume" - ] - }, "values": { "fuchsia": [ 77309411349 @@ -7900,9 +7646,6 @@ "name": "Sleep", "value": 8589934594, "names": { - "web": [ - "Sleep" - ], "gtk": [ "Sleep" ], @@ -7931,11 +7674,6 @@ "Abort": { "name": "Abort", "value": 8589934595, - "names": { - "web": [ - "Abort" - ] - }, "values": { "fuchsia": [ 77309870235 @@ -7946,9 +7684,6 @@ "name": "Lang1", "value": 8589934608, "names": { - "web": [ - "Lang1" - ], "macos": [ "Lang1" ], @@ -7978,9 +7713,6 @@ "name": "Lang2", "value": 8589934609, "names": { - "web": [ - "Lang2" - ], "macos": [ "Lang2" ], @@ -8004,9 +7736,6 @@ "name": "Lang3", "value": 8589934610, "names": { - "web": [ - "Lang3" - ], "ios": [ "Lang3" ] @@ -8024,9 +7753,6 @@ "name": "Lang4", "value": 8589934611, "names": { - "web": [ - "Lang4" - ], "ios": [ "Lang4" ] @@ -8044,9 +7770,6 @@ "name": "Lang5", "value": 8589934612, "names": { - "web": [ - "Lang5" - ], "ios": [ "Lang5" ] @@ -8063,11 +7786,6 @@ "IntlBackslash": { "name": "IntlBackslash", "value": 8589934624, - "names": { - "web": [ - "IntlBackslash" - ] - }, "values": { "fuchsia": [ 77309870180 @@ -8078,9 +7796,6 @@ "name": "IntlRo", "value": 8589934625, "names": { - "web": [ - "IntlRo" - ], "macos": [ "IntlRo" ], @@ -8110,9 +7825,6 @@ "name": "IntlYen", "value": 8589934626, "names": { - "web": [ - "IntlYen" - ], "macos": [ "IntlYen" ], @@ -8148,9 +7860,6 @@ "name": "ControlLeft", "value": 8589934848, "names": { - "web": [ - "ControlLeft" - ], "macos": [ "ControlLeft" ], @@ -8200,9 +7909,6 @@ "name": "ControlRight", "value": 8589934849, "names": { - "web": [ - "ControlRight" - ], "macos": [ "ControlRight" ], @@ -8250,9 +7956,6 @@ "name": "ShiftLeft", "value": 8589934850, "names": { - "web": [ - "ShiftLeft" - ], "macos": [ "ShiftLeft" ], @@ -8302,9 +8005,6 @@ "name": "ShiftRight", "value": 8589934851, "names": { - "web": [ - "ShiftRight" - ], "macos": [ "ShiftRight" ], @@ -8352,9 +8052,6 @@ "name": "AltLeft", "value": 8589934852, "names": { - "web": [ - "AltLeft" - ], "macos": [ "AltLeft" ], @@ -8402,9 +8099,6 @@ "name": "AltRight", "value": 8589934853, "names": { - "web": [ - "AltRight" - ], "macos": [ "AltRight" ], @@ -8454,9 +8148,6 @@ "name": "MetaLeft", "value": 8589934854, "names": { - "web": [ - "MetaLeft" - ], "macos": [ "MetaLeft" ], @@ -8504,9 +8195,6 @@ "name": "MetaRight", "value": 8589934855, "names": { - "web": [ - "MetaRight" - ], "macos": [ "MetaRight" ], @@ -8552,47 +8240,24 @@ }, "Control": { "name": "Control", - "value": 8589935088, - "names": { - "web": [ - "Control" - ] - } + "value": 8589935088 }, "Shift": { "name": "Shift", - "value": 8589935090, - "names": { - "web": [ - "Shift" - ] - } + "value": 8589935090 }, "Alt": { "name": "Alt", - "value": 8589935092, - "names": { - "web": [ - "Alt" - ] - } + "value": 8589935092 }, "Meta": { "name": "Meta", - "value": 8589935094, - "names": { - "web": [ - "Meta" - ] - } + "value": 8589935094 }, "NumpadEnter": { "name": "NumpadEnter", "value": 8589935117, "names": { - "web": [ - "NumpadEnter" - ], "macos": [ "NumpadEnter" ], @@ -8634,9 +8299,6 @@ "name": "NumpadParenLeft", "value": 8589935144, "names": { - "web": [ - "NumpadParenLeft" - ], "android": [ "NUMPAD_LEFT_PAREN" ] @@ -8654,9 +8316,6 @@ "name": "NumpadParenRight", "value": 8589935145, "names": { - "web": [ - "NumpadParenRight" - ], "android": [ "NUMPAD_RIGHT_PAREN" ] @@ -8674,9 +8333,6 @@ "name": "NumpadMultiply", "value": 8589935146, "names": { - "web": [ - "NumpadMultiply" - ], "macos": [ "NumpadMultiply" ], @@ -8724,9 +8380,6 @@ "name": "NumpadAdd", "value": 8589935147, "names": { - "web": [ - "NumpadAdd" - ], "macos": [ "NumpadAdd" ], @@ -8774,9 +8427,6 @@ "name": "NumpadComma", "value": 8589935148, "names": { - "web": [ - "NumpadComma" - ], "macos": [ "NumpadComma" ], @@ -8812,9 +8462,6 @@ "name": "NumpadSubtract", "value": 8589935149, "names": { - "web": [ - "NumpadSubtract" - ], "macos": [ "NumpadSubtract" ], @@ -8856,9 +8503,6 @@ "name": "NumpadDecimal", "value": 8589935150, "names": { - "web": [ - "NumpadDecimal" - ], "macos": [ "NumpadDecimal" ], @@ -8906,9 +8550,6 @@ "name": "NumpadDivide", "value": 8589935151, "names": { - "web": [ - "NumpadDivide" - ], "macos": [ "NumpadDivide" ], @@ -8956,9 +8597,6 @@ "name": "Numpad0", "value": 8589935152, "names": { - "web": [ - "Numpad0" - ], "macos": [ "Numpad0" ], @@ -9008,9 +8646,6 @@ "name": "Numpad1", "value": 8589935153, "names": { - "web": [ - "Numpad1" - ], "macos": [ "Numpad1" ], @@ -9060,9 +8695,6 @@ "name": "Numpad2", "value": 8589935154, "names": { - "web": [ - "Numpad2" - ], "macos": [ "Numpad2" ], @@ -9112,9 +8744,6 @@ "name": "Numpad3", "value": 8589935155, "names": { - "web": [ - "Numpad3" - ], "macos": [ "Numpad3" ], @@ -9164,9 +8793,6 @@ "name": "Numpad4", "value": 8589935156, "names": { - "web": [ - "Numpad4" - ], "macos": [ "Numpad4" ], @@ -9216,9 +8842,6 @@ "name": "Numpad5", "value": 8589935157, "names": { - "web": [ - "Numpad5" - ], "macos": [ "Numpad5" ], @@ -9266,9 +8889,6 @@ "name": "Numpad6", "value": 8589935158, "names": { - "web": [ - "Numpad6" - ], "macos": [ "Numpad6" ], @@ -9318,9 +8938,6 @@ "name": "Numpad7", "value": 8589935159, "names": { - "web": [ - "Numpad7" - ], "macos": [ "Numpad7" ], @@ -9370,9 +8987,6 @@ "name": "Numpad8", "value": 8589935160, "names": { - "web": [ - "Numpad8" - ], "macos": [ "Numpad8" ], @@ -9422,9 +9036,6 @@ "name": "Numpad9", "value": 8589935161, "names": { - "web": [ - "Numpad9" - ], "macos": [ "Numpad9" ], @@ -9474,9 +9085,6 @@ "name": "NumpadEqual", "value": 8589935165, "names": { - "web": [ - "NumpadEqual" - ], "macos": [ "NumpadEqual" ], @@ -9524,9 +9132,6 @@ "name": "GameButton1", "value": 8589935361, "names": { - "web": [ - "GameButton1" - ], "android": [ "BUTTON_1" ] @@ -9544,9 +9149,6 @@ "name": "GameButton2", "value": 8589935362, "names": { - "web": [ - "GameButton2" - ], "android": [ "BUTTON_2" ] @@ -9564,9 +9166,6 @@ "name": "GameButton3", "value": 8589935363, "names": { - "web": [ - "GameButton3" - ], "android": [ "BUTTON_3" ] @@ -9584,9 +9183,6 @@ "name": "GameButton4", "value": 8589935364, "names": { - "web": [ - "GameButton4" - ], "android": [ "BUTTON_4" ] @@ -9604,9 +9200,6 @@ "name": "GameButton5", "value": 8589935365, "names": { - "web": [ - "GameButton5" - ], "android": [ "BUTTON_5" ] @@ -9624,9 +9217,6 @@ "name": "GameButton6", "value": 8589935366, "names": { - "web": [ - "GameButton6" - ], "android": [ "BUTTON_6" ] @@ -9644,9 +9234,6 @@ "name": "GameButton7", "value": 8589935367, "names": { - "web": [ - "GameButton7" - ], "android": [ "BUTTON_7" ] @@ -9664,9 +9251,6 @@ "name": "GameButton8", "value": 8589935368, "names": { - "web": [ - "GameButton8" - ], "windows": [ "GAMEPAD_A" ], @@ -9690,9 +9274,6 @@ "name": "GameButton9", "value": 8589935369, "names": { - "web": [ - "GameButton9" - ], "windows": [ "GAMEPAD_B" ], @@ -9716,9 +9297,6 @@ "name": "GameButton10", "value": 8589935370, "names": { - "web": [ - "GameButton10" - ], "windows": [ "GAMEPAD_X" ], @@ -9742,9 +9320,6 @@ "name": "GameButton11", "value": 8589935371, "names": { - "web": [ - "GameButton11" - ], "windows": [ "GAMEPAD_Y" ], @@ -9768,9 +9343,6 @@ "name": "GameButton12", "value": 8589935372, "names": { - "web": [ - "GameButton12" - ], "windows": [ "GAMEPAD_RIGHT_SHOULDER" ], @@ -9794,9 +9366,6 @@ "name": "GameButton13", "value": 8589935373, "names": { - "web": [ - "GameButton13" - ], "windows": [ "GAMEPAD_LEFT_SHOULDER" ], @@ -9820,9 +9389,6 @@ "name": "GameButton14", "value": 8589935374, "names": { - "web": [ - "GameButton14" - ], "windows": [ "GAMEPAD_LEFT_TRIGGER" ], @@ -9846,9 +9412,6 @@ "name": "GameButton15", "value": 8589935375, "names": { - "web": [ - "GameButton15" - ], "windows": [ "GAMEPAD_RIGHT_TRIGGER" ], @@ -9872,9 +9435,6 @@ "name": "GameButton16", "value": 8589935376, "names": { - "web": [ - "GameButton16" - ], "windows": [ "GAMEPAD_DPAD_UP" ], @@ -9898,9 +9458,6 @@ "name": "GameButtonA", "value": 8589935377, "names": { - "web": [ - "GameButtonA" - ], "android": [ "BUTTON_A" ] @@ -9918,9 +9475,6 @@ "name": "GameButtonB", "value": 8589935378, "names": { - "web": [ - "GameButtonB" - ], "android": [ "BUTTON_B" ] @@ -9938,9 +9492,6 @@ "name": "GameButtonC", "value": 8589935379, "names": { - "web": [ - "GameButtonC" - ], "android": [ "BUTTON_C" ] @@ -9958,9 +9509,6 @@ "name": "GameButtonLeft1", "value": 8589935380, "names": { - "web": [ - "GameButtonLeft1" - ], "android": [ "BUTTON_L1" ] @@ -9978,9 +9526,6 @@ "name": "GameButtonLeft2", "value": 8589935381, "names": { - "web": [ - "GameButtonLeft2" - ], "android": [ "BUTTON_L2" ] @@ -9998,9 +9543,6 @@ "name": "GameButtonMode", "value": 8589935382, "names": { - "web": [ - "GameButtonMode" - ], "android": [ "BUTTON_MODE" ] @@ -10018,9 +9560,6 @@ "name": "GameButtonRight1", "value": 8589935383, "names": { - "web": [ - "GameButtonRight1" - ], "android": [ "BUTTON_R1" ] @@ -10038,9 +9577,6 @@ "name": "GameButtonRight2", "value": 8589935384, "names": { - "web": [ - "GameButtonRight2" - ], "android": [ "BUTTON_R2" ] @@ -10058,9 +9594,6 @@ "name": "GameButtonSelect", "value": 8589935385, "names": { - "web": [ - "GameButtonSelect" - ], "android": [ "BUTTON_SELECT" ] @@ -10078,9 +9611,6 @@ "name": "GameButtonStart", "value": 8589935386, "names": { - "web": [ - "GameButtonStart" - ], "android": [ "BUTTON_START" ] @@ -10098,9 +9628,6 @@ "name": "GameButtonThumbLeft", "value": 8589935387, "names": { - "web": [ - "GameButtonThumbLeft" - ], "android": [ "BUTTON_THUMBL" ] @@ -10118,9 +9645,6 @@ "name": "GameButtonThumbRight", "value": 8589935388, "names": { - "web": [ - "GameButtonThumbRight" - ], "android": [ "BUTTON_THUMBR" ] @@ -10138,9 +9662,6 @@ "name": "GameButtonX", "value": 8589935389, "names": { - "web": [ - "GameButtonX" - ], "android": [ "BUTTON_X" ] @@ -10158,9 +9679,6 @@ "name": "GameButtonY", "value": 8589935390, "names": { - "web": [ - "GameButtonY" - ], "android": [ "BUTTON_Y" ] @@ -10178,9 +9696,6 @@ "name": "GameButtonZ", "value": 8589935391, "names": { - "web": [ - "GameButtonZ" - ], "android": [ "BUTTON_Z" ] diff --git a/dev/tools/gen_keycodes/lib/logical_key_data.dart b/dev/tools/gen_keycodes/lib/logical_key_data.dart index a26d4b5341..9941902397 100644 --- a/dev/tools/gen_keycodes/lib/logical_key_data.dart +++ b/dev/tools/gen_keycodes/lib/logical_key_data.dart @@ -11,11 +11,7 @@ import 'package:path/path.dart' as path; import 'constants.dart'; import 'physical_key_data.dart'; -bool _isControlCharacter(String label) { - if (label.length != 1) { - return false; - } - final int codeUnit = label.codeUnitAt(0); +bool _isControlCharacter(int codeUnit) { return (codeUnit <= 0x1f && codeUnit >= 0x00) || (codeUnit >= 0x7f && codeUnit <= 0x9f); } @@ -27,8 +23,11 @@ class _ModifierPair { final String right; } -List _toNonEmptyArray(dynamic source) { - final List? dynamicNullableList = source as List?; +// Return map[key1][key2] as a non-nullable List, where both map[key1] or +// map[key1][key2] might be null. +List _getGrandchildList(Map map, String key1, String key2) { + final dynamic value = (map[key1] as Map?)?[key2]; + final List? dynamicNullableList = value as List?; final List dynamicList = dynamicNullableList ?? []; return dynamicList.cast(); } @@ -155,20 +154,23 @@ class LogicalKeyData { final int value = match.namedGroup('unicode') != null ? getHex(match.namedGroup('unicode')!) : match.namedGroup('char')!.codeUnitAt(0); - final String? keyLabel = match.namedGroup('kind')! == 'UNI' ? String.fromCharCode(value) : null; + final String? keyLabel = (match.namedGroup('kind')! == 'UNI' && !_isControlCharacter(value)) ? + String.fromCharCode(value) : null; // Skip modifier keys from DOM. They will be added with supplemental data. if (_chromeModifiers.containsKey(name) && source == 'DOM') { continue; } - final bool isPrintable = (keyLabel != null && !_isControlCharacter(keyLabel)) - || printable.containsKey(name); + final bool isPrintable = keyLabel != null; data.putIfAbsent(name, () { - return LogicalKeyEntry.fromName( + final LogicalKeyEntry entry = LogicalKeyEntry.fromName( value: toPlane(value, _sourceToPlane(source, isPrintable)), name: name, keyLabel: keyLabel, - )..webNames.add(webName); + ); + if (source == 'DOM' && !isPrintable) + entry.webNames.add(webName); + return entry; }); } } @@ -418,9 +420,11 @@ class LogicalKeyData { })(); static int _sourceToPlane(String source, bool isPrintable) { + if (isPrintable) + return kUnicodePlane.value; switch (source) { case 'DOM': - return isPrintable ? kUnicodePlane.value : kUnprintablePlane.value; + return kUnprintablePlane.value; case 'FLUTTER': return kFlutterPlane.value; default: @@ -470,20 +474,20 @@ class LogicalKeyEntry { LogicalKeyEntry.fromJsonMapEntry(Map map) : value = map['value'] as int, name = map['name'] as String, - webNames = _toNonEmptyArray((map['names'] as Map)['web']), - macOSKeyCodeNames = _toNonEmptyArray((map['names'] as Map)['macos']), - macOSKeyCodeValues = _toNonEmptyArray((map['values'] as Map?)?['macos']), - iOSKeyCodeNames = _toNonEmptyArray((map['names'] as Map)['ios']), - iOSKeyCodeValues = _toNonEmptyArray((map['values'] as Map?)?['ios']), - gtkNames = _toNonEmptyArray((map['names'] as Map)['gtk']), - gtkValues = _toNonEmptyArray((map['values'] as Map?)?['gtk']), - windowsNames = _toNonEmptyArray((map['names'] as Map)['windows']), - windowsValues = _toNonEmptyArray((map['values'] as Map?)?['windows']), - androidNames = _toNonEmptyArray((map['names'] as Map)['android']), - androidValues = _toNonEmptyArray((map['values'] as Map?)?['android']), - fuchsiaValues = _toNonEmptyArray((map['values'] as Map?)?['fuchsia']), - glfwNames = _toNonEmptyArray((map['names'] as Map)['glfw']), - glfwValues = _toNonEmptyArray((map['values'] as Map?)?['glfw']), + webNames = _getGrandchildList(map, 'names', 'web'), + macOSKeyCodeNames = _getGrandchildList(map, 'names', 'macos'), + macOSKeyCodeValues = _getGrandchildList(map, 'values', 'macos'), + iOSKeyCodeNames = _getGrandchildList(map, 'names', 'ios'), + iOSKeyCodeValues = _getGrandchildList(map, 'values', 'ios'), + gtkNames = _getGrandchildList(map, 'names', 'gtk'), + gtkValues = _getGrandchildList(map, 'values', 'gtk'), + windowsNames = _getGrandchildList(map, 'names', 'windows'), + windowsValues = _getGrandchildList(map, 'values', 'windows'), + androidNames = _getGrandchildList(map, 'names', 'android'), + androidValues = _getGrandchildList(map, 'values', 'android'), + fuchsiaValues = _getGrandchildList(map, 'values', 'fuchsia'), + glfwNames = _getGrandchildList(map, 'names', 'glfw'), + glfwValues = _getGrandchildList(map, 'values', 'glfw'), keyLabel = map['keyLabel'] as String?; final int value; diff --git a/dev/tools/gen_keycodes/test/gen_keycodes_test.dart b/dev/tools/gen_keycodes/test/gen_keycodes_test.dart index 447ee4d4dd..a963eb57af 100644 --- a/dev/tools/gen_keycodes/test/gen_keycodes_test.dart +++ b/dev/tools/gen_keycodes/test/gen_keycodes_test.dart @@ -22,8 +22,10 @@ String readDataFile(String fileName) { return File(path.join(dataRoot, fileName)).readAsStringSync(); } -final String testPhysicalData = path.join(dataRoot, 'physical_key_data.json'); -final String testLogicalData = path.join(dataRoot,'logical_key_data.json'); +final PhysicalKeyData physicalData = PhysicalKeyData.fromJson( + json.decode(readDataFile('physical_key_data.json')) as Map); +final LogicalKeyData logicalData = LogicalKeyData.fromJson( + json.decode(readDataFile('logical_key_data.json')) as Map); void main() { setUp(() { @@ -45,14 +47,6 @@ void main() { } test('Generate Keycodes for Android', () { - PhysicalKeyData physicalData; - LogicalKeyData logicalData; - - physicalData = PhysicalKeyData.fromJson( - json.decode(File(testPhysicalData).readAsStringSync()) as Map); - logicalData = LogicalKeyData.fromJson( - json.decode(File(testLogicalData).readAsStringSync()) as Map); - const String platform = 'android'; final PlatformCodeGenerator codeGenerator = AndroidCodeGenerator( physicalData, @@ -67,14 +61,6 @@ void main() { checkCommonOutput(output); }); test('Generate Keycodes for macOS', () { - PhysicalKeyData physicalData; - LogicalKeyData logicalData; - - physicalData = PhysicalKeyData.fromJson( - json.decode(File(testPhysicalData).readAsStringSync()) as Map); - logicalData = LogicalKeyData.fromJson( - json.decode(File(testLogicalData).readAsStringSync()) as Map); - const String platform = 'macos'; final PlatformCodeGenerator codeGenerator = MacOSCodeGenerator( physicalData, @@ -93,14 +79,6 @@ void main() { checkCommonOutput(output); }); test('Generate Keycodes for iOS', () { - PhysicalKeyData physicalData; - LogicalKeyData logicalData; - - physicalData = PhysicalKeyData.fromJson( - json.decode(File(testPhysicalData).readAsStringSync()) as Map); - logicalData = LogicalKeyData.fromJson( - json.decode(File(testLogicalData).readAsStringSync()) as Map); - const String platform = 'ios'; final PlatformCodeGenerator codeGenerator = IOSCodeGenerator( physicalData, @@ -120,14 +98,6 @@ void main() { checkCommonOutput(output); }); test('Generate Keycodes for Windows', () { - PhysicalKeyData physicalData; - LogicalKeyData logicalData; - - physicalData = PhysicalKeyData.fromJson( - json.decode(File(testPhysicalData).readAsStringSync()) as Map); - logicalData = LogicalKeyData.fromJson( - json.decode(File(testLogicalData).readAsStringSync()) as Map); - const String platform = 'windows'; final PlatformCodeGenerator codeGenerator = WindowsCodeGenerator( physicalData, @@ -143,14 +113,6 @@ void main() { checkCommonOutput(output); }); test('Generate Keycodes for Linux', () { - PhysicalKeyData physicalData; - LogicalKeyData logicalData; - - physicalData = PhysicalKeyData.fromJson( - json.decode(File(testPhysicalData).readAsStringSync()) as Map); - logicalData = LogicalKeyData.fromJson( - json.decode(File(testLogicalData).readAsStringSync()) as Map); - const String platform = 'gtk'; final PlatformCodeGenerator codeGenerator = GtkCodeGenerator( physicalData, @@ -166,14 +128,6 @@ void main() { checkCommonOutput(output); }); test('Generate Keycodes for Web', () { - PhysicalKeyData physicalData; - LogicalKeyData logicalData; - - physicalData = PhysicalKeyData.fromJson( - json.decode(File(testPhysicalData).readAsStringSync()) as Map); - logicalData = LogicalKeyData.fromJson( - json.decode(File(testLogicalData).readAsStringSync()) as Map); - const String platform = 'web'; final PlatformCodeGenerator codeGenerator = WebCodeGenerator( physicalData, @@ -188,4 +142,25 @@ void main() { expect(output, contains('kWebLogicalLocationMap')); checkCommonOutput(output); }); + test('LogicalKeyData', () async { + final List entries = logicalData.entries.toList(); + + // Regression tests for https://github.com/flutter/flutter/pull/87098 + + expect( + entries.indexWhere((LogicalKeyEntry entry) => entry.name == 'ShiftLeft'), + isNot(-1)); + expect( + entries.indexWhere((LogicalKeyEntry entry) => entry.webNames.contains('ShiftLeft')), + -1); + // 'Shift' maps to both 'ShiftLeft' and 'ShiftRight', and should be resolved + // by other ways. + expect( + entries.indexWhere((LogicalKeyEntry entry) => entry.webNames.contains('Shift')), + -1); + // Printable keys must not be added with Web key of their names. + expect( + entries.indexWhere((LogicalKeyEntry entry) => entry.webNames.contains('Slash')), + -1); + }); } diff --git a/packages/flutter/lib/src/services/keyboard_maps.dart b/packages/flutter/lib/src/services/keyboard_maps.dart index 74cea741ea..3827e9e774 100644 --- a/packages/flutter/lib/src/services/keyboard_maps.dart +++ b/packages/flutter/lib/src/services/keyboard_maps.dart @@ -2141,25 +2141,17 @@ const Map kLinuxToPhysicalKey = kWebToLogicalKey = { 'AVRInput': LogicalKeyboardKey.avrInput, 'AVRPower': LogicalKeyboardKey.avrPower, - 'Abort': LogicalKeyboardKey.abort, 'Accel': LogicalKeyboardKey.accel, 'Accept': LogicalKeyboardKey.accept, - 'Add': LogicalKeyboardKey.add, 'Again': LogicalKeyboardKey.again, 'AllCandidates': LogicalKeyboardKey.allCandidates, 'Alphanumeric': LogicalKeyboardKey.alphanumeric, - 'Alt': LogicalKeyboardKey.alt, 'AltGraph': LogicalKeyboardKey.altGraph, - 'AltLeft': LogicalKeyboardKey.altLeft, - 'AltRight': LogicalKeyboardKey.altRight, - 'Ampersand': LogicalKeyboardKey.ampersand, 'AppSwitch': LogicalKeyboardKey.appSwitch, 'ArrowDown': LogicalKeyboardKey.arrowDown, 'ArrowLeft': LogicalKeyboardKey.arrowLeft, 'ArrowRight': LogicalKeyboardKey.arrowRight, 'ArrowUp': LogicalKeyboardKey.arrowUp, - 'Asterisk': LogicalKeyboardKey.asterisk, - 'At': LogicalKeyboardKey.at, 'Attn': LogicalKeyboardKey.attn, 'AudioBalanceLeft': LogicalKeyboardKey.audioBalanceLeft, 'AudioBalanceRight': LogicalKeyboardKey.audioBalanceRight, @@ -2174,14 +2166,7 @@ const Map kWebToLogicalKey = kWebToLogicalKey = kWebToLogicalKey = kWebToLogicalKey = kWebToLogicalKey = kWebToLogicalKey = kWebToLogicalKey = kWebToLogicalKey = kWebToLogicalKey = kWebToLogicalKey = kWebToLogicalKey = kWebToLogicalKey = message) { - final RawKeyEventData data; String? character; - RawKeyEventData _dataFromWeb() { final String? key = message['key'] as String?; - if (key != null && key.isNotEmpty) { + if (key != null && key.isNotEmpty && key.length == 1) { character = key; } return RawKeyEventDataWeb( @@ -300,6 +298,8 @@ abstract class RawKeyEvent with Diagnosticable { metaState: message['metaState'] as int? ?? 0, ); } + + final RawKeyEventData data; if (kIsWeb) { data = _dataFromWeb(); } else { diff --git a/packages/flutter/lib/src/services/raw_keyboard_web.dart b/packages/flutter/lib/src/services/raw_keyboard_web.dart index 70e2b94ee2..4ae924e9d4 100644 --- a/packages/flutter/lib/src/services/raw_keyboard_web.dart +++ b/packages/flutter/lib/src/services/raw_keyboard_web.dart @@ -10,6 +10,13 @@ import 'keyboard_key.dart'; import 'keyboard_maps.dart'; import 'raw_keyboard.dart'; +String? _unicodeChar(String key) { + if (key.length == 1) { + return key.substring(0, 1); + } + return null; +} + /// Platform-specific key event data for Web. /// /// See also: @@ -74,7 +81,7 @@ class RawKeyEventDataWeb extends RawKeyEventData { final int metaState; @override - String get keyLabel => key == 'Unidentified' ? '' : key; + String get keyLabel => key == 'Unidentified' ? '' : _unicodeChar(key) ?? ''; @override PhysicalKeyboardKey get physicalKey { @@ -95,9 +102,14 @@ class RawKeyEventDataWeb extends RawKeyEventData { return newKey; } + final bool isPrintable = key.length == 1; + if (isPrintable) + return LogicalKeyboardKey(key.codeUnitAt(0)); + // This is a non-printable key that we don't know about, so we mint a new - // code. - return LogicalKeyboardKey(code.hashCode | LogicalKeyboardKey.webPlane); + // key from `code`. Don't mint with `key`, because the `key` will always be + // "Unidentified" . + return LogicalKeyboardKey(code.hashCode + LogicalKeyboardKey.webPlane); } @override diff --git a/packages/flutter/test/services/raw_keyboard_test.dart b/packages/flutter/test/services/raw_keyboard_test.dart index f5c227c80a..30a3941cb2 100644 --- a/packages/flutter/test/services/raw_keyboard_test.dart +++ b/packages/flutter/test/services/raw_keyboard_test.dart @@ -2400,6 +2400,7 @@ void main() { 'keymap': 'web', 'code': 'KeyA', 'key': 'a', + 'location': 0, 'metaState': 0x0, }); final RawKeyEventDataWeb data = keyAEvent.data as RawKeyEventDataWeb; @@ -2413,6 +2414,8 @@ void main() { 'type': 'keydown', 'keymap': 'web', 'code': 'Escape', + 'key': 'Escape', + 'location': 0, 'metaState': 0x0, }); final RawKeyEventDataWeb data = escapeKeyEvent.data as RawKeyEventDataWeb; @@ -2426,6 +2429,8 @@ void main() { 'type': 'keydown', 'keymap': 'web', 'code': 'ShiftLeft', + 'key': 'Shift', + 'location': 1, 'metaState': RawKeyEventDataWeb.modifierShift, }); final RawKeyEventDataWeb data = shiftKeyEvent.data as RawKeyEventDataWeb; @@ -2439,6 +2444,8 @@ void main() { 'type': 'keydown', 'keymap': 'web', 'code': 'ArrowDown', + 'key': 'ArrowDown', + 'location': 0, 'metaState': 0x0, }); final RawKeyEventDataWeb data = arrowKeyDown.data as RawKeyEventDataWeb; @@ -2447,6 +2454,25 @@ void main() { expect(data.keyLabel, isEmpty); }); + test('Unrecognized keys are mapped to Web plane', () { + final RawKeyEvent arrowKeyDown = RawKeyEvent.fromMessage(const { + 'type': 'keydown', + 'keymap': 'web', + 'code': 'Unrecog1', + 'key': 'Unrecog2', + 'location': 0, + 'metaState': 0x0, + }); + final RawKeyEventDataWeb data = arrowKeyDown.data as RawKeyEventDataWeb; + // This might be easily broken on Web if the code fails to acknowledge + // that JavaScript doesn't handle 64-bit bit-wise operation. + expect(data.physicalKey.usbHidUsage, greaterThan(0x01700000000)); + expect(data.physicalKey.usbHidUsage, lessThan(0x01800000000)); + expect(data.logicalKey.keyId, greaterThan(0x01700000000)); + expect(data.logicalKey.keyId, lessThan(0x01800000000)); + expect(data.keyLabel, isEmpty); + }); + test('data.toString', () { expect(RawKeyEvent.fromMessage(const { 'type': 'keydown', diff --git a/packages/flutter_test/lib/src/event_simulation.dart b/packages/flutter_test/lib/src/event_simulation.dart index 32f81dca86..c4e34e1ba4 100644 --- a/packages/flutter_test/lib/src/event_simulation.dart +++ b/packages/flutter_test/lib/src/event_simulation.dart @@ -14,6 +14,16 @@ import 'package:flutter_test/flutter_test.dart'; import 'binding.dart'; import 'test_async_utils.dart'; +// A tuple of `key` and `location` from Web's `KeyboardEvent` class. +// +// See [RawKeyEventDataWeb]'s `key` and `location` fields for details. +@immutable +class _WebKeyLocationPair { + const _WebKeyLocationPair(this.key, this.location); + final String key; + final int location; +} + // TODO(gspencergoog): Replace this with more robust key simulation code once // the new key event code is in. // https://github.com/flutter/flutter/issues/33521 @@ -145,8 +155,32 @@ class KeyEventSimulator { } } - static String _getWebKeyCode(LogicalKeyboardKey key) { + static PhysicalKeyboardKey _inferPhysicalKey(LogicalKeyboardKey key) { + PhysicalKeyboardKey? result; + for (final PhysicalKeyboardKey physicalKey in PhysicalKeyboardKey.knownPhysicalKeys) { + if (physicalKey.debugName == key.debugName) { + result = physicalKey; + break; + } + } + assert(result != null, 'Unable to infer physical key for $key'); + return result!; + } + + static _WebKeyLocationPair _getWebKeyLocation(LogicalKeyboardKey key, String keyLabel) { String? result; + for (final MapEntry> entry in kWebLocationMap.entries) { + final int foundIndex = entry.value.indexOf(key); + // If foundIndex is -1, then the key is not defined in kWebLocationMap. + // If foundIndex is 0, then the key is in the standard part of the keyboard, + // but we have to check `keyLabel` to see if it's remapped or modified. + if (foundIndex != -1 && foundIndex != 0) { + return _WebKeyLocationPair(entry.key, foundIndex); + } + } + if (keyLabel.isNotEmpty) { + return _WebKeyLocationPair(keyLabel, 0); + } for (final String code in kWebToLogicalKey.keys) { if (key.keyId == kWebToLogicalKey[code]!.keyId) { result = code; @@ -154,6 +188,18 @@ class KeyEventSimulator { } } assert(result != null, 'Key $key not found in web keyCode map'); + return _WebKeyLocationPair(result!, 0); + } + + static String _getWebCode(PhysicalKeyboardKey key) { + String? result; + for (final MapEntry entry in kWebToPhysicalKey.entries) { + if (entry.value.usbHidUsage == key.usbHidUsage) { + result = entry.key; + break; + } + } + assert(result != null, 'Key $key not found in web code map'); return result!; } @@ -215,8 +261,6 @@ class KeyEventSimulator { physicalKey ??= _findPhysicalKeyByPlatform(key, platform); assert(key.debugName != null); - final int keyCode = _getKeyCode(key, platform); - final int scanCode = _getScanCode(physicalKey, platform); final Map result = { 'type': isDown ? 'keydown' : 'keyup', @@ -225,14 +269,19 @@ class KeyEventSimulator { final String resultCharacter = character ?? _keyLabel(key) ?? ''; void assignWeb() { - result['code'] = _getWebKeyCode(key); - result['key'] = resultCharacter; + final _WebKeyLocationPair keyLocation = _getWebKeyLocation(key, resultCharacter); + final PhysicalKeyboardKey actualPhysicalKey = physicalKey ?? _inferPhysicalKey(key); + result['code'] = _getWebCode(actualPhysicalKey); + result['key'] = keyLocation.key; + result['location'] = keyLocation.location; result['metaState'] = _getWebModifierFlags(key, isDown); } if (kIsWeb) { assignWeb(); return result; } + final int keyCode = _getKeyCode(key, platform); + final int scanCode = _getScanCode(physicalKey, platform); switch (platform) { case 'android':