From f93d22c7813242dd95153191e06a723cdfae314e Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 14:56:23 -0800 Subject: [PATCH 01/34] Split out web --- lib/web_ui/lib/src/engine.dart | 2 + lib/web_ui/lib/src/engine/dom_renderer.dart | 1 + lib/web_ui/lib/src/engine/key_map.dart | 706 +++++++++++++ .../lib/src/engine/keyboard_binding.dart | 489 +++++++++ .../lib/src/engine/platform_dispatcher.dart | 26 +- lib/web_ui/lib/src/ui/key.dart | 66 ++ .../lib/src/ui/platform_dispatcher.dart | 6 +- lib/web_ui/lib/src/ui/window.dart | 5 + lib/web_ui/lib/ui.dart | 1 + lib/web_ui/test/keyboard_converter_test.dart | 927 ++++++++++++++++++ 10 files changed, 2227 insertions(+), 2 deletions(-) create mode 100644 lib/web_ui/lib/src/engine/key_map.dart create mode 100644 lib/web_ui/lib/src/engine/keyboard_binding.dart create mode 100644 lib/web_ui/lib/src/ui/key.dart create mode 100644 lib/web_ui/test/keyboard_converter_test.dart diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 42040eb1afa56..d8e42bafd00cb 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -96,7 +96,9 @@ part 'engine/html/surface.dart'; part 'engine/html/surface_stats.dart'; part 'engine/html/transform.dart'; part 'engine/html_image_codec.dart'; +part 'engine/keyboard_binding.dart'; part 'engine/keyboard.dart'; +part 'engine/key_map.dart'; part 'engine/mouse_cursor.dart'; part 'engine/onscreen_logging.dart'; part 'engine/picture.dart'; diff --git a/lib/web_ui/lib/src/engine/dom_renderer.dart b/lib/web_ui/lib/src/engine/dom_renderer.dart index b14d007ead10b..34b594b668721 100644 --- a/lib/web_ui/lib/src/engine/dom_renderer.dart +++ b/lib/web_ui/lib/src/engine/dom_renderer.dart @@ -421,6 +421,7 @@ flt-glass-pane * { glassPaneElement.insertBefore(_accesibilityPlaceholder, _sceneHostElement); PointerBinding.initInstance(glassPaneElement); + KeyboardBinding.initInstance(glassPaneElement); // Hide the DOM nodes used to render the scene from accessibility, because // the accessibility tree is built from the SemanticsNode tree as a parallel diff --git a/lib/web_ui/lib/src/engine/key_map.dart b/lib/web_ui/lib/src/engine/key_map.dart new file mode 100644 index 0000000000000..d3970549c7ca2 --- /dev/null +++ b/lib/web_ui/lib/src/engine/key_map.dart @@ -0,0 +1,706 @@ +// 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. + +// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT +// This file is generated by dev/tools/gen_keycodes/bin/gen_keycodes.dart and +// should not be edited directly. +// +// Edit the template dev/tools/gen_keycodes/data/web_key_map_dart.tmpl instead. +// See dev/tools/gen_keycodes/README.md for more information. + +// @dart = 2.12 +part of engine; + +/// Maps Web KeyboardEvent codes to the matching LogicalKeyboardKey id. +const Map kWebToLogicalKey = { + 'None': 0x0000000000, + 'Unidentified': 0x0000000001, + 'Backspace': 0x0000000008, + 'Tab': 0x0000000009, + 'Enter': 0x000000000d, + 'Escape': 0x000000001b, + 'Space': 0x0000000020, + 'Exclamation': 0x0000000021, + 'Quote': 0x0000000022, + 'NumberSign': 0x0000000023, + 'Dollar': 0x0000000024, + 'Ampersand': 0x0000000026, + 'QuoteSingle': 0x0000000027, + 'ParenthesisLeft': 0x0000000028, + 'ParenthesisRight': 0x0000000029, + 'Asterisk': 0x000000002a, + 'Add': 0x000000002b, + 'Comma': 0x000000002c, + 'Minus': 0x000000002d, + 'Period': 0x000000002e, + 'Slash': 0x000000002f, + 'Digit0': 0x0000000030, + 'Digit1': 0x0000000031, + 'Digit2': 0x0000000032, + 'Digit3': 0x0000000033, + 'Digit4': 0x0000000034, + 'Digit5': 0x0000000035, + 'Digit6': 0x0000000036, + 'Digit7': 0x0000000037, + 'Digit8': 0x0000000038, + 'Digit9': 0x0000000039, + 'Colon': 0x000000003a, + 'Semicolon': 0x000000003b, + 'Less': 0x000000003c, + 'Equal': 0x000000003d, + 'Greater': 0x000000003e, + 'Question': 0x000000003f, + 'At': 0x0000000040, + 'BracketLeft': 0x000000005b, + 'Backslash': 0x000000005c, + 'BracketRight': 0x000000005d, + 'Caret': 0x000000005e, + 'Underscore': 0x000000005f, + 'Backquote': 0x0000000060, + 'KeyA': 0x0000000061, + 'KeyB': 0x0000000062, + 'KeyC': 0x0000000063, + 'KeyD': 0x0000000064, + 'KeyE': 0x0000000065, + 'KeyF': 0x0000000066, + 'KeyG': 0x0000000067, + 'KeyH': 0x0000000068, + 'KeyI': 0x0000000069, + 'KeyJ': 0x000000006a, + 'KeyK': 0x000000006b, + 'KeyL': 0x000000006c, + 'KeyM': 0x000000006d, + 'KeyN': 0x000000006e, + 'KeyO': 0x000000006f, + 'KeyP': 0x0000000070, + 'KeyQ': 0x0000000071, + 'KeyR': 0x0000000072, + 'KeyS': 0x0000000073, + 'KeyT': 0x0000000074, + 'KeyU': 0x0000000075, + 'KeyV': 0x0000000076, + 'KeyW': 0x0000000077, + 'KeyX': 0x0000000078, + 'KeyY': 0x0000000079, + 'KeyZ': 0x000000007a, + 'BraceLeft': 0x000000007b, + 'Bar': 0x000000007c, + 'BraceRight': 0x000000007d, + 'Tilde': 0x000000007e, + 'Delete': 0x000000007f, + 'Accel': 0x0000000101, + 'AltGraph': 0x0000000103, + 'CapsLock': 0x0000000104, + 'Fn': 0x0000000106, + 'FnLock': 0x0000000107, + 'Hyper': 0x0000000108, + 'NumLock': 0x000000010a, + 'ScrollLock': 0x000000010c, + 'Super': 0x000000010e, + 'Symbol': 0x000000010f, + 'SymbolLock': 0x0000000110, + 'ShiftLevel5': 0x0000000111, + 'AltGraphLatch': 0x0000000112, + 'ArrowDown': 0x0000000301, + 'ArrowLeft': 0x0000000302, + 'ArrowRight': 0x0000000303, + 'ArrowUp': 0x0000000304, + 'End': 0x0000000305, + 'Home': 0x0000000306, + 'PageDown': 0x0000000307, + 'PageUp': 0x0000000308, + 'Clear': 0x0000000401, + 'Copy': 0x0000000402, + 'CrSel': 0x0000000403, + 'Cut': 0x0000000404, + 'EraseEof': 0x0000000405, + 'ExSel': 0x0000000406, + 'Insert': 0x0000000407, + 'Paste': 0x0000000408, + 'Redo': 0x0000000409, + 'Undo': 0x000000040a, + 'Accept': 0x0000000501, + 'Again': 0x0000000502, + 'Attn': 0x0000000503, + 'Cancel': 0x0000000504, + 'ContextMenu': 0x0000000505, + 'Execute': 0x0000000506, + 'Find': 0x0000000507, + 'Help': 0x0000000508, + 'Pause': 0x0000000509, + 'Play': 0x000000050a, + 'Props': 0x000000050b, + 'Select': 0x000000050c, + 'ZoomIn': 0x000000050d, + 'ZoomOut': 0x000000050e, + 'BrightnessDown': 0x0000000601, + 'BrightnessUp': 0x0000000602, + 'Camera': 0x0000000603, + 'Eject': 0x0000000604, + 'LogOff': 0x0000000605, + 'Power': 0x0000000606, + 'PowerOff': 0x0000000607, + 'PrintScreen': 0x0000000608, + 'Hibernate': 0x0000000609, + 'Standby': 0x000000060a, + 'WakeUp': 0x000000060b, + 'AllCandidates': 0x0000000701, + 'Alphanumeric': 0x0000000702, + 'CodeInput': 0x0000000703, + 'Compose': 0x0000000704, + 'Convert': 0x0000000705, + 'FinalMode': 0x0000000706, + 'GroupFirst': 0x0000000707, + 'GroupLast': 0x0000000708, + 'GroupNext': 0x0000000709, + 'GroupPrevious': 0x000000070a, + 'ModeChange': 0x000000070b, + 'NextCandidate': 0x000000070c, + 'NonConvert': 0x000000070d, + 'PreviousCandidate': 0x000000070e, + 'Process': 0x000000070f, + 'SingleCandidate': 0x0000000710, + 'HangulMode': 0x0000000711, + 'HanjaMode': 0x0000000712, + 'JunjaMode': 0x0000000713, + 'Eisu': 0x0000000714, + 'Hankaku': 0x0000000715, + 'Hiragana': 0x0000000716, + 'HiraganaKatakana': 0x0000000717, + 'KanaMode': 0x0000000718, + 'KanjiMode': 0x0000000719, + 'Katakana': 0x000000071a, + 'Romaji': 0x000000071b, + 'Zenkaku': 0x000000071c, + 'ZenkakuHankaku': 0x000000071d, + 'F1': 0x0000000801, + 'F2': 0x0000000802, + 'F3': 0x0000000803, + 'F4': 0x0000000804, + 'F5': 0x0000000805, + 'F6': 0x0000000806, + 'F7': 0x0000000807, + 'F8': 0x0000000808, + 'F9': 0x0000000809, + 'F10': 0x000000080a, + 'F11': 0x000000080b, + 'F12': 0x000000080c, + 'F13': 0x000000080d, + 'F14': 0x000000080e, + 'F15': 0x000000080f, + 'F16': 0x0000000810, + 'F17': 0x0000000811, + 'F18': 0x0000000812, + 'F19': 0x0000000813, + 'F20': 0x0000000814, + 'F21': 0x0000000815, + 'F22': 0x0000000816, + 'F23': 0x0000000817, + 'F24': 0x0000000818, + 'Soft1': 0x0000000901, + 'Soft2': 0x0000000902, + 'Soft3': 0x0000000903, + 'Soft4': 0x0000000904, + 'Soft5': 0x0000000905, + 'Soft6': 0x0000000906, + 'Soft7': 0x0000000907, + 'Soft8': 0x0000000908, + 'Close': 0x0000000a01, + 'MailForward': 0x0000000a02, + 'MailReply': 0x0000000a03, + 'MailSend': 0x0000000a04, + 'MediaPlayPause': 0x0000000a05, + 'MediaStop': 0x0000000a07, + 'MediaTrackNext': 0x0000000a08, + 'MediaTrackPrevious': 0x0000000a09, + 'New': 0x0000000a0a, + 'Open': 0x0000000a0b, + 'Print': 0x0000000a0c, + 'Save': 0x0000000a0d, + 'SpellCheck': 0x0000000a0e, + 'AudioVolumeDown': 0x0000000a0f, + 'AudioVolumeUp': 0x0000000a10, + 'AudioVolumeMute': 0x0000000a11, + 'LaunchApplication2': 0x0000000b01, + 'LaunchCalendar': 0x0000000b02, + 'LaunchMail': 0x0000000b03, + 'LaunchMediaPlayer': 0x0000000b04, + 'LaunchMusicPlayer': 0x0000000b05, + 'LaunchApplication1': 0x0000000b06, + 'LaunchScreenSaver': 0x0000000b07, + 'LaunchSpreadsheet': 0x0000000b08, + 'LaunchWebBrowser': 0x0000000b09, + 'LaunchWebCam': 0x0000000b0a, + 'LaunchWordProcessor': 0x0000000b0b, + 'LaunchContacts': 0x0000000b0c, + 'LaunchPhone': 0x0000000b0d, + 'LaunchAssistant': 0x0000000b0e, + 'LaunchControlPanel': 0x0000000b0f, + 'BrowserBack': 0x0000000c01, + 'BrowserFavorites': 0x0000000c02, + 'BrowserForward': 0x0000000c03, + 'BrowserHome': 0x0000000c04, + 'BrowserRefresh': 0x0000000c05, + 'BrowserSearch': 0x0000000c06, + 'BrowserStop': 0x0000000c07, + 'AudioBalanceLeft': 0x0000000d01, + 'AudioBalanceRight': 0x0000000d02, + 'AudioBassBoostDown': 0x0000000d03, + 'AudioBassBoostUp': 0x0000000d04, + 'AudioFaderFront': 0x0000000d05, + 'AudioFaderRear': 0x0000000d06, + 'AudioSurroundModeNext': 0x0000000d07, + 'AVRInput': 0x0000000d08, + 'AVRPower': 0x0000000d09, + 'ChannelDown': 0x0000000d0a, + 'ChannelUp': 0x0000000d0b, + 'ColorF0Red': 0x0000000d0c, + 'ColorF1Green': 0x0000000d0d, + 'ColorF2Yellow': 0x0000000d0e, + 'ColorF3Blue': 0x0000000d0f, + 'ColorF4Grey': 0x0000000d10, + 'ColorF5Brown': 0x0000000d11, + 'ClosedCaptionToggle': 0x0000000d12, + 'Dimmer': 0x0000000d13, + 'DisplaySwap': 0x0000000d14, + 'Exit': 0x0000000d15, + 'FavoriteClear0': 0x0000000d16, + 'FavoriteClear1': 0x0000000d17, + 'FavoriteClear2': 0x0000000d18, + 'FavoriteClear3': 0x0000000d19, + 'FavoriteRecall0': 0x0000000d1a, + 'FavoriteRecall1': 0x0000000d1b, + 'FavoriteRecall2': 0x0000000d1c, + 'FavoriteRecall3': 0x0000000d1d, + 'FavoriteStore0': 0x0000000d1e, + 'FavoriteStore1': 0x0000000d1f, + 'FavoriteStore2': 0x0000000d20, + 'FavoriteStore3': 0x0000000d21, + 'Guide': 0x0000000d22, + 'GuideNextDay': 0x0000000d23, + 'GuidePreviousDay': 0x0000000d24, + 'Info': 0x0000000d25, + 'InstantReplay': 0x0000000d26, + 'Link': 0x0000000d27, + 'ListProgram': 0x0000000d28, + 'LiveContent': 0x0000000d29, + 'Lock': 0x0000000d2a, + 'MediaApps': 0x0000000d2b, + 'MediaFastForward': 0x0000000d2c, + 'MediaLast': 0x0000000d2d, + 'MediaPause': 0x0000000d2e, + 'MediaPlay': 0x0000000d2f, + 'MediaRecord': 0x0000000d30, + 'MediaRewind': 0x0000000d31, + 'MediaSkip': 0x0000000d32, + 'NextFavoriteChannel': 0x0000000d33, + 'NextUserProfile': 0x0000000d34, + 'OnDemand': 0x0000000d35, + 'PinPDown': 0x0000000d36, + 'PinPMove': 0x0000000d37, + 'PinPToggle': 0x0000000d38, + 'PinPUp': 0x0000000d39, + 'PlaySpeedDown': 0x0000000d3a, + 'PlaySpeedReset': 0x0000000d3b, + 'PlaySpeedUp': 0x0000000d3c, + 'RandomToggle': 0x0000000d3d, + 'RcLowBattery': 0x0000000d3e, + 'RecordSpeedNext': 0x0000000d3f, + 'RfBypass': 0x0000000d40, + 'ScanChannelsToggle': 0x0000000d41, + 'ScreenModeNext': 0x0000000d42, + 'Settings': 0x0000000d43, + 'SplitScreenToggle': 0x0000000d44, + 'STBInput': 0x0000000d45, + 'STBPower': 0x0000000d46, + 'Subtitle': 0x0000000d47, + 'Teletext': 0x0000000d48, + 'TV': 0x0000000d49, + 'TVInput': 0x0000000d4a, + 'TVPower': 0x0000000d4b, + 'VideoModeNext': 0x0000000d4c, + 'Wink': 0x0000000d4d, + 'ZoomToggle': 0x0000000d4e, + 'DVR': 0x0000000d4f, + 'MediaAudioTrack': 0x0000000d50, + 'MediaSkipBackward': 0x0000000d51, + 'MediaSkipForward': 0x0000000d52, + 'MediaStepBackward': 0x0000000d53, + 'MediaStepForward': 0x0000000d54, + 'MediaTopMenu': 0x0000000d55, + 'NavigateIn': 0x0000000d56, + 'NavigateNext': 0x0000000d57, + 'NavigateOut': 0x0000000d58, + 'NavigatePrevious': 0x0000000d59, + 'Pairing': 0x0000000d5a, + 'MediaClose': 0x0000000d5b, + 'AudioBassBoostToggle': 0x0000000e02, + 'AudioTrebleDown': 0x0000000e04, + 'AudioTrebleUp': 0x0000000e05, + 'MicrophoneToggle': 0x0000000e06, + 'MicrophoneVolumeDown': 0x0000000e07, + 'MicrophoneVolumeUp': 0x0000000e08, + 'MicrophoneVolumeMute': 0x0000000e09, + 'SpeechCorrectionList': 0x0000000f01, + 'SpeechInputToggle': 0x0000000f02, + 'AppSwitch': 0x0000001001, + 'Call': 0x0000001002, + 'CameraFocus': 0x0000001003, + 'EndCall': 0x0000001004, + 'GoBack': 0x0000001005, + 'GoHome': 0x0000001006, + 'HeadsetHook': 0x0000001007, + 'LastNumberRedial': 0x0000001008, + 'Notification': 0x0000001009, + 'MannerMode': 0x000000100a, + 'VoiceDial': 0x000000100b, + 'TV3DMode': 0x0000001101, + 'TVAntennaCable': 0x0000001102, + 'TVAudioDescription': 0x0000001103, + 'TVAudioDescriptionMixDown': 0x0000001104, + 'TVAudioDescriptionMixUp': 0x0000001105, + 'TVContentsMenu': 0x0000001106, + 'TVDataService': 0x0000001107, + 'TVInputComponent1': 0x0000001108, + 'TVInputComponent2': 0x0000001109, + 'TVInputComposite1': 0x000000110a, + 'TVInputComposite2': 0x000000110b, + 'TVInputHDMI1': 0x000000110c, + 'TVInputHDMI2': 0x000000110d, + 'TVInputHDMI3': 0x000000110e, + 'TVInputHDMI4': 0x000000110f, + 'TVInputVGA1': 0x0000001110, + 'TVMediaContext': 0x0000001111, + 'TVNetwork': 0x0000001112, + 'TVNumberEntry': 0x0000001113, + 'TVRadioService': 0x0000001114, + 'TVSatellite': 0x0000001115, + 'TVSatelliteBS': 0x0000001116, + 'TVSatelliteCS': 0x0000001117, + 'TVSatelliteToggle': 0x0000001118, + 'TVTerrestrialAnalog': 0x0000001119, + 'TVTerrestrialDigital': 0x000000111a, + 'TVTimer': 0x000000111b, + 'Key11': 0x0000001201, + 'Key12': 0x0000001202, + 'GameButton1': 0x000005ff01, + 'GameButton2': 0x000005ff02, + 'GameButton3': 0x000005ff03, + 'GameButton4': 0x000005ff04, + 'GameButton5': 0x000005ff05, + 'GameButton6': 0x000005ff06, + 'GameButton7': 0x000005ff07, + 'GameButton8': 0x000005ff08, + 'GameButton9': 0x000005ff09, + 'GameButton10': 0x000005ff0a, + 'GameButton11': 0x000005ff0b, + 'GameButton12': 0x000005ff0c, + 'GameButton13': 0x000005ff0d, + 'GameButton14': 0x000005ff0e, + 'GameButton15': 0x000005ff0f, + 'GameButton16': 0x000005ff10, + 'GameButtonA': 0x000005ff11, + 'GameButtonB': 0x000005ff12, + 'GameButtonC': 0x000005ff13, + 'GameButtonLeft1': 0x000005ff14, + 'GameButtonLeft2': 0x000005ff15, + 'GameButtonMode': 0x000005ff16, + 'GameButtonRight1': 0x000005ff17, + 'GameButtonRight2': 0x000005ff18, + 'GameButtonSelect': 0x000005ff19, + 'GameButtonStart': 0x000005ff1a, + 'GameButtonThumbLeft': 0x000005ff1b, + 'GameButtonThumbRight': 0x000005ff1c, + 'GameButtonX': 0x000005ff1d, + 'GameButtonY': 0x000005ff1e, + 'GameButtonZ': 0x000005ff1f, + 'Suspend': 0x0100000014, + 'Resume': 0x0100000015, + 'Sleep': 0x0100010082, + 'IntlBackslash': 0x0100070064, + 'IntlRo': 0x0100070087, + 'IntlYen': 0x0100070089, + 'Lang1': 0x0100070090, + 'Lang2': 0x0100070091, + 'Lang3': 0x0100070092, + 'Lang4': 0x0100070093, + 'Lang5': 0x0100070094, + 'Abort': 0x010007009b, +}; + +/// Maps Web KeyboardEvent codes to the matching PhysicalKeyboardKey USB HID code. +const Map kWebToPhysicalKey = { + 'None': 0x00000000, + 'Hyper': 0x00000010, + 'Super': 0x00000011, + 'FnLock': 0x00000013, + 'Suspend': 0x00000014, + 'Resume': 0x00000015, + 'Turbo': 0x00000016, + 'PrivacyScreenToggle': 0x00000017, + 'Sleep': 0x00010082, + 'WakeUp': 0x00010083, + 'DisplayToggleIntExt': 0x000100b5, + 'KeyA': 0x00070004, + 'KeyB': 0x00070005, + 'KeyC': 0x00070006, + 'KeyD': 0x00070007, + 'KeyE': 0x00070008, + 'KeyF': 0x00070009, + 'KeyG': 0x0007000a, + 'KeyH': 0x0007000b, + 'KeyI': 0x0007000c, + 'KeyJ': 0x0007000d, + 'KeyK': 0x0007000e, + 'KeyL': 0x0007000f, + 'KeyM': 0x00070010, + 'KeyN': 0x00070011, + 'KeyO': 0x00070012, + 'KeyP': 0x00070013, + 'KeyQ': 0x00070014, + 'KeyR': 0x00070015, + 'KeyS': 0x00070016, + 'KeyT': 0x00070017, + 'KeyU': 0x00070018, + 'KeyV': 0x00070019, + 'KeyW': 0x0007001a, + 'KeyX': 0x0007001b, + 'KeyY': 0x0007001c, + 'KeyZ': 0x0007001d, + 'Digit1': 0x0007001e, + 'Digit2': 0x0007001f, + 'Digit3': 0x00070020, + 'Digit4': 0x00070021, + 'Digit5': 0x00070022, + 'Digit6': 0x00070023, + 'Digit7': 0x00070024, + 'Digit8': 0x00070025, + 'Digit9': 0x00070026, + 'Digit0': 0x00070027, + 'Enter': 0x00070028, + 'Escape': 0x00070029, + 'Backspace': 0x0007002a, + 'Tab': 0x0007002b, + 'Space': 0x0007002c, + 'Minus': 0x0007002d, + 'Equal': 0x0007002e, + 'BracketLeft': 0x0007002f, + 'BracketRight': 0x00070030, + 'Backslash': 0x00070031, + 'Semicolon': 0x00070033, + 'Quote': 0x00070034, + 'Backquote': 0x00070035, + 'Comma': 0x00070036, + 'Period': 0x00070037, + 'Slash': 0x00070038, + 'CapsLock': 0x00070039, + 'F1': 0x0007003a, + 'F2': 0x0007003b, + 'F3': 0x0007003c, + 'F4': 0x0007003d, + 'F5': 0x0007003e, + 'F6': 0x0007003f, + 'F7': 0x00070040, + 'F8': 0x00070041, + 'F9': 0x00070042, + 'F10': 0x00070043, + 'F11': 0x00070044, + 'F12': 0x00070045, + 'PrintScreen': 0x00070046, + 'ScrollLock': 0x00070047, + 'Pause': 0x00070048, + 'Insert': 0x00070049, + 'Home': 0x0007004a, + 'PageUp': 0x0007004b, + 'Delete': 0x0007004c, + 'End': 0x0007004d, + 'PageDown': 0x0007004e, + 'ArrowRight': 0x0007004f, + 'ArrowLeft': 0x00070050, + 'ArrowDown': 0x00070051, + 'ArrowUp': 0x00070052, + 'NumLock': 0x00070053, + 'NumpadDivide': 0x00070054, + 'NumpadMultiply': 0x00070055, + 'NumpadSubtract': 0x00070056, + 'NumpadAdd': 0x00070057, + 'NumpadEnter': 0x00070058, + 'Numpad1': 0x00070059, + 'Numpad2': 0x0007005a, + 'Numpad3': 0x0007005b, + 'Numpad4': 0x0007005c, + 'Numpad5': 0x0007005d, + 'Numpad6': 0x0007005e, + 'Numpad7': 0x0007005f, + 'Numpad8': 0x00070060, + 'Numpad9': 0x00070061, + 'Numpad0': 0x00070062, + 'NumpadDecimal': 0x00070063, + 'IntlBackslash': 0x00070064, + 'ContextMenu': 0x00070065, + 'Power': 0x00070066, + 'NumpadEqual': 0x00070067, + 'F13': 0x00070068, + 'F14': 0x00070069, + 'F15': 0x0007006a, + 'F16': 0x0007006b, + 'F17': 0x0007006c, + 'F18': 0x0007006d, + 'F19': 0x0007006e, + 'F20': 0x0007006f, + 'F21': 0x00070070, + 'F22': 0x00070071, + 'F23': 0x00070072, + 'F24': 0x00070073, + 'Open': 0x00070074, + 'Help': 0x00070075, + 'Select': 0x00070077, + 'Again': 0x00070079, + 'Undo': 0x0007007a, + 'Cut': 0x0007007b, + 'Copy': 0x0007007c, + 'Paste': 0x0007007d, + 'Find': 0x0007007e, + 'AudioVolumeMute': 0x0007007f, + 'AudioVolumeUp': 0x00070080, + 'AudioVolumeDown': 0x00070081, + 'NumpadComma': 0x00070085, + 'IntlRo': 0x00070087, + 'KanaMode': 0x00070088, + 'IntlYen': 0x00070089, + 'Convert': 0x0007008a, + 'NonConvert': 0x0007008b, + 'Lang1': 0x00070090, + 'Lang2': 0x00070091, + 'Lang3': 0x00070092, + 'Lang4': 0x00070093, + 'Lang5': 0x00070094, + 'Abort': 0x0007009b, + 'Props': 0x000700a3, + 'NumpadParenLeft': 0x000700b6, + 'NumpadParenRight': 0x000700b7, + 'NumpadBackspace': 0x000700bb, + 'NumpadMemoryStore': 0x000700d0, + 'NumpadMemoryRecall': 0x000700d1, + 'NumpadMemoryClear': 0x000700d2, + 'NumpadMemoryAdd': 0x000700d3, + 'NumpadMemorySubtract': 0x000700d4, + 'NumpadClear': 0x000700d8, + 'NumpadClearEntry': 0x000700d9, + 'ControlLeft': 0x000700e0, + 'ShiftLeft': 0x000700e1, + 'AltLeft': 0x000700e2, + 'MetaLeft': 0x000700e3, + 'ControlRight': 0x000700e4, + 'ShiftRight': 0x000700e5, + 'AltRight': 0x000700e6, + 'MetaRight': 0x000700e7, + 'BrightnessUp': 0x000c006f, + 'BrightnessDown': 0x000c0070, + 'MediaPlay': 0x000c00b0, + 'MediaPause': 0x000c00b1, + 'MediaRecord': 0x000c00b2, + 'MediaFastForward': 0x000c00b3, + 'MediaRewind': 0x000c00b4, + 'MediaTrackNext': 0x000c00b5, + 'MediaTrackPrevious': 0x000c00b6, + 'MediaStop': 0x000c00b7, + 'Eject': 0x000c00b8, + 'MediaPlayPause': 0x000c00cd, + 'MediaSelect': 0x000c0183, + 'LaunchMail': 0x000c018a, + 'LaunchApp2': 0x000c0192, + 'LaunchApp1': 0x000c0194, + 'LaunchControlPanel': 0x000c019f, + 'SelectTask': 0x000c01a2, + 'LaunchScreenSaver': 0x000c01b1, + 'LaunchAssistant': 0x000c01cb, + 'BrowserSearch': 0x000c0221, + 'BrowserHome': 0x000c0223, + 'BrowserBack': 0x000c0224, + 'BrowserForward': 0x000c0225, + 'BrowserStop': 0x000c0226, + 'BrowserRefresh': 0x000c0227, + 'BrowserFavorites': 0x000c022a, + 'ZoomToggle': 0x000c0232, + 'MailReply': 0x000c0289, + 'MailForward': 0x000c028b, + 'MailSend': 0x000c028c, + 'KeyboardLayoutSelect': 0x000c029d, + 'ShowAllWindows': 0x000c029f, + 'GameButton1': 0x0005ff01, + 'GameButton2': 0x0005ff02, + 'GameButton3': 0x0005ff03, + 'GameButton4': 0x0005ff04, + 'GameButton5': 0x0005ff05, + 'GameButton6': 0x0005ff06, + 'GameButton7': 0x0005ff07, + 'GameButton8': 0x0005ff08, + 'GameButton9': 0x0005ff09, + 'GameButton10': 0x0005ff0a, + 'GameButton11': 0x0005ff0b, + 'GameButton12': 0x0005ff0c, + 'GameButton13': 0x0005ff0d, + 'GameButton14': 0x0005ff0e, + 'GameButton15': 0x0005ff0f, + 'GameButton16': 0x0005ff10, + 'GameButtonA': 0x0005ff11, + 'GameButtonB': 0x0005ff12, + 'GameButtonC': 0x0005ff13, + 'GameButtonLeft1': 0x0005ff14, + 'GameButtonLeft2': 0x0005ff15, + 'GameButtonMode': 0x0005ff16, + 'GameButtonRight1': 0x0005ff17, + 'GameButtonRight2': 0x0005ff18, + 'GameButtonSelect': 0x0005ff19, + 'GameButtonStart': 0x0005ff1a, + 'GameButtonThumbLeft': 0x0005ff1b, + 'GameButtonThumbRight': 0x0005ff1c, + 'GameButtonX': 0x0005ff1d, + 'GameButtonY': 0x0005ff1e, + 'GameButtonZ': 0x0005ff1f, + 'Fn': 0x00000012, +}; + +/// Maps Web KeyboardEvent keys to Flutter logical IDs that depend on locations. +/// +/// `KeyboardEvent.location` is defined as: +/// +/// * 0: Standard +/// * 1: Left +/// * 2: Right +/// * 3: Numpad +const Map> kWebLogicalLocationMap = >{ + '0': [0x0000000030, null, null, 0x0200000030], + '1': [0x0000000031, null, null, 0x0200000031], + '2': [0x0000000032, null, null, 0x0200000032], + '3': [0x0000000033, null, null, 0x0200000033], + '4': [0x0000000034, null, null, 0x0200000034], + '5': [0x0000000035, null, null, 0x0200000035], + '6': [0x0000000036, null, null, 0x0200000036], + '7': [0x0000000037, null, null, 0x0200000037], + '8': [0x0000000038, null, null, 0x0200000038], + '9': [0x0000000039, null, null, 0x0200000039], + '.': [0x000000002e, null, null, 0x020000002e], + 'Insert': [0x0000000407, null, null, 0x0200000030], + 'End': [0x0000000305, null, null, 0x0200000031], + 'ArrowDown': [0x0000000301, null, null, 0x0200000032], + 'PageDown': [0x0000000307, null, null, 0x0200000033], + 'ArrowLeft': [0x0000000302, null, null, 0x0200000034], + 'Clear': [0x0000000401, null, null, 0x0200000035], + 'ArrowRight': [0x0000000303, null, null, 0x0200000036], + 'Home': [0x0000000306, null, null, 0x0200000037], + 'ArrowUp': [0x0000000304, null, null, 0x0200000038], + 'PageUp': [0x0000000308, null, null, 0x0200000039], + 'Delete': [0x000000007f, null, null, 0x020000002e], + '/': [0x000000002f, null, null, 0x020000002f], + '*': [0x000000002a, null, null, 0x020000002a], + '-': [0x000000002d, null, null, 0x020000002d], + '+': [0x000000002b, null, null, 0x020000002b], + 'Enter': [0x000000000d, null, null, 0x020000000d], + 'Shift': [null, 0x030000010d, 0x040000010d, null], + 'Control': [null, 0x0300000105, 0x0400000105, null], + 'Alt': [null, 0x0300000102, 0x0400000102, null], + 'Meta': [null, 0x0300000109, 0x0400000109, null], +}; diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart new file mode 100644 index 0000000000000..c0f734b10bebd --- /dev/null +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -0,0 +1,489 @@ +// Copyright 2013 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. + +// @dart = 2.12 +part of engine; + +typedef VoidCallback = void Function(); +typedef ValueGetter = T Function(); +typedef _ModifierGetter = bool Function(FlutterHtmlKeyboardEvent event); + +// Set this flag to true to see all the fired events in the console. +const bool _debugLogKeyEvents = false; + +const int _kLogicalAltLeft = 0x0300000102; +const int _kLogicalAltRight = 0x0400000102; +const int _kLogicalControlLeft = 0x0300000105; +const int _kLogicalControlRight = 0x0400000105; +const int _kLogicalShiftLeft = 0x030000010d; +const int _kLogicalShiftRight = 0x040000010d; +const int _kLogicalMetaLeft = 0x0300000109; +const int _kLogicalMetaRight = 0x0400000109; +// Map logical keys for modifier keys to the functions that can get their +// modifier flag out of an event. +final Map _kLogicalKeyToModifierGetter = { + _kLogicalAltLeft: (FlutterHtmlKeyboardEvent event) => event.altKey, + _kLogicalAltRight: (FlutterHtmlKeyboardEvent event) => event.altKey, + _kLogicalControlLeft: (FlutterHtmlKeyboardEvent event) => event.ctrlKey, + _kLogicalControlRight: (FlutterHtmlKeyboardEvent event) => event.ctrlKey, + _kLogicalShiftLeft: (FlutterHtmlKeyboardEvent event) => event.shiftKey, + _kLogicalShiftRight: (FlutterHtmlKeyboardEvent event) => event.shiftKey, + _kLogicalMetaLeft: (FlutterHtmlKeyboardEvent event) => event.metaKey, + _kLogicalMetaRight: (FlutterHtmlKeyboardEvent event) => event.metaKey, +}; + +// After a keydown is received, this is the duration we wait for a repeat event +// before we decide to synthesize a keyup event. +// +// On Linux and Windows, the typical ranges for keyboard repeat delay go up to +// 1000ms. On Mac, the range goes up to 2000ms. +const Duration _kKeydownCancelDurationNormal = Duration(milliseconds: 1000); +const Duration _kKeydownCancelDurationMacOs = Duration(milliseconds: 2000); + +late final int _kCharLowerA = 'a'.codeUnitAt(0); +late final int _kCharLowerZ = 'z'.codeUnitAt(0); +late final int _kCharUpperA = 'A'.codeUnitAt(0); +late final int _kCharUpperZ = 'Z'.codeUnitAt(0); +bool isAlphabet(int charCode) { + return (charCode >= _kCharLowerA && charCode <= _kCharLowerZ) + || (charCode >= _kCharUpperA && charCode <= _kCharUpperZ); +} + +const String _kPhysicalCapsLock = 'CapsLock'; + +const String _kLogicalDead = 'Dead'; + +const int _kWebKeyIdPlane = 0x00800000000; +const int _kAutogeneratedMask = 0x10000000000; + +// Bits in a Flutter logical event to generate the logical key for dead keys. +// +// Logical keys for dead keys are generated by annotating physical keys with +// modifiers (see `_getLogicalCode`). +const int _kDeadKeyCtrl = 0x100000000000; +const int _kDeadKeyShift = 0x200000000000; +const int _kDeadKeyAlt = 0x400000000000; +const int _kDeadKeyMeta = 0x800000000000; + +typedef DispatchKeyData = void Function(ui.KeyData data); + +/// Converts a floating number timestamp (in milliseconds) to a [Duration] by +/// splitting it into two integer components: milliseconds + microseconds. +Duration _eventTimeStampToDuration(num milliseconds) { + final int ms = milliseconds.toInt(); + final int micro = ((milliseconds - ms) * Duration.microsecondsPerMillisecond).toInt(); + return Duration(milliseconds: ms, microseconds: micro); +} + +class KeyboardBinding { + /// The singleton instance of this object. + static KeyboardBinding? get instance => _instance; + static KeyboardBinding? _instance; + + static void initInstance(html.Element glassPaneElement) { + if (_instance == null) { + _instance = KeyboardBinding._(glassPaneElement); + assert(() { + registerHotRestartListener(_instance!._reset); + return true; + }()); + } + } + + KeyboardBinding._(this.glassPaneElement) { + _setup(); + } + + final html.Element glassPaneElement; + late KeyboardConverter _converter; + final Map _listeners = {}; + + void _addEventListener(String eventName, html.EventListener handler) { + final html.EventListener loggedHandler = (html.Event event) { + if (_debugLogKeyEvents) { + print(event.type); + } + if (EngineSemanticsOwner.instance.receiveGlobalEvent(event)) { + handler(event); + } + }; + assert(!_listeners.containsKey(eventName)); + _listeners[eventName] = loggedHandler; + html.window.addEventListener(eventName, loggedHandler, true); + } + + /// Remove all active event listeners. + void _clearListeners() { + _listeners.forEach((String eventName, html.EventListener listener) { + html.window.removeEventListener(eventName, listener, true); + }); + _listeners.clear(); + } + void _onKeyData(ui.KeyData data) { + EnginePlatformDispatcher.instance.invokeOnKeyData(data); + } + + void _setup() { + _addEventListener('keydown', (html.Event event) { + return _converter.handleEvent(FlutterHtmlKeyboardEvent(event as html.KeyboardEvent)); + }); + _addEventListener('keyup', (html.Event event) { + return _converter.handleEvent(FlutterHtmlKeyboardEvent(event as html.KeyboardEvent)); + }); + _converter = KeyboardConverter(_onKeyData, onMacOs: operatingSystem == OperatingSystem.macOs); + } + + void _reset() { + _clearListeners(); + _converter.dispose(); + + _setup(); + } +} + +class AsyncKeyboardDispatching { + AsyncKeyboardDispatching({ + required this.keyData, + this.callback, + }); + + final ui.KeyData keyData; + final VoidCallback? callback; +} + +// A wrapper of [html.KeyboardEvent] with reduced methods delegated to the event +// for the convenience of testing. +class FlutterHtmlKeyboardEvent { + FlutterHtmlKeyboardEvent(this._event); + + final html.KeyboardEvent _event; + + String get type => _event.type; + String? get code => _event.code; + String? get key => _event.key; + bool? get repeat => _event.repeat; + int? get location => _event.location; + num? get timeStamp => _event.timeStamp; + bool get altKey => _event.altKey; + bool get ctrlKey => _event.ctrlKey; + bool get shiftKey => _event.shiftKey; + bool get metaKey => _event.metaKey; + + bool getModifierState(String key) => _event.getModifierState(key); + void preventDefault() => _event.preventDefault(); +} + +// Reads [html.KeyboardEvent], then [dispatches ui.KeyData] accordingly. +// +// The events are read through [handleEvent], and dispatched through the +// [dispatchKeyData] as given in the constructor. Some key data might be +// dispatched asynchronously. +class KeyboardConverter { + KeyboardConverter(this.dispatchKeyData, {this.onMacOs = false}); + + final DispatchKeyData dispatchKeyData; + final bool onMacOs; + + bool _disposed = false; + void dispose() { + _disposed = true; + } + + // On macOS, CapsLock behaves differently in that, a keydown event occurs when + // the key is pressed and the light turns on, while a keyup event occurs when the + // key is pressed and the light turns off. Flutter considers both events as + // key down, and synthesizes immediate cancel events following them. The state + // of "whether CapsLock is on" should be accessed by "activeLocks". + bool _shouldSynthesizeCapsLockUp() { + return onMacOs; + } + + Duration get _keydownCancelDuration => onMacOs ? _kKeydownCancelDurationMacOs : _kKeydownCancelDurationNormal; + + bool _shouldPreventDefault(FlutterHtmlKeyboardEvent event) { + switch (event.key) { + case 'Tab': + return true; + + default: + return false; + } + } + + static int _getPhysicalCode(String code) { + return kWebToPhysicalKey[code] ?? (code.hashCode + _kWebKeyIdPlane + _kAutogeneratedMask); + } + + static int _getModifierMask(FlutterHtmlKeyboardEvent event) { + final bool altDown = event.altKey; + final bool ctrlDown = event.ctrlKey; + final bool shiftDown = event.shiftKey; + final bool metaDown = event.metaKey; + return (altDown ? _kDeadKeyAlt : 0) + + (ctrlDown ? _kDeadKeyCtrl : 0) + + (shiftDown ? _kDeadKeyShift : 0) + + (metaDown ? _kDeadKeyMeta : 0); + } + + // Whether `event.key` should be considered a key name. + // + // The `event.key` can either be a key name or the printable character. If the + // first character is an alphabet, it must be either 'A' to 'Z' ( and return + // true), or be a key name (and return false). Otherwise, return true. + static bool _eventKeyIsKeyname(String key) { + assert(key.length > 0); + return isAlphabet(key.codeUnitAt(0)) && key.length > 1; + } + + static int _characterToLogicalKey(String key) { + // Assume the length being <= 2 to be sufficient in all cases. If not, + // extend the algorithm. + assert(key.length <= 2); + int result = key.codeUnitAt(0) & 0xffff; + if (key.length == 2) { + result += key.codeUnitAt(1) << 16; + } + // Convert upper letters to lower letters + if (result >= _kCharUpperA && result <= _kCharUpperZ) { + result = result + _kCharLowerA - _kCharUpperA; + } + return result; + } + + static int _deadKeyToLogicalKey(int physicalKey, FlutterHtmlKeyboardEvent event) { + // 'Dead' is used to represent dead keys, such as a diacritic to the + // following base letter (such as Option-e results in ´). + // + // Assume they can be told apart with the physical key and the modifiers + // pressed. + return physicalKey + _getModifierMask(event) + _kWebKeyIdPlane + _kAutogeneratedMask; + } + + static int _otherLogicalKey(String key) { + return kWebToLogicalKey[key] ?? (key.hashCode + _kWebKeyIdPlane + _kAutogeneratedMask); + } + + // Map from pressed physical key to corresponding pressed logical key. + // + // Multiple physical keys can be mapped to the same logical key, usually due + // to positioned keys (left/right/numpad) or multiple keyboards. + final Map _pressingRecords = {}; + + // Schedule the dispatching of an event in the future. The `callback` will + // invoked before that. + // + // Returns a callback that cancels the schedule. Disposal of + // `KeyBoardConverter` also cancels the shedule automatically. + VoidCallback _scheduleAsyncEvent(Duration duration, ValueGetter getData, VoidCallback callback) { + bool canceled = false; + Future.delayed(duration).then((_) { + if (!canceled && !_disposed) { + callback(); + dispatchKeyData(getData()); + } + }); + return () { canceled = true; }; + } + + // ## About Key guards + // + // When the user enters a browser/system shortcut (e.g. `cmd+alt+i`) the + // browser doesn't send a keyup for it. This puts the framework in a corrupt + // state because it thinks the key was never released. + // + // To avoid this, we rely on the fact that browsers send repeat events + // while the key is held down by the user. If we don't receive a repeat + // event within a specific duration ([_keydownCancelDuration]) we assume + // the user has released the key and we synthesize a keyup event. + final Map _keyGuards = {}; + // Call this method on the down or repeated event of a non-modifier key. + void _startGuardingKey(int physicalKey, int logicalKey, Duration currentTimeStamp) { + final VoidCallback cancelingCallback = _scheduleAsyncEvent( + _keydownCancelDuration, + () => ui.KeyData( + timeStamp: currentTimeStamp + _keydownCancelDuration, + change: ui.KeyChange.up, + physical: physicalKey, + logical: logicalKey, + character: null, + synthesized: true, + ), + () { + _pressingRecords.remove(physicalKey); + } + ); + _keyGuards.remove(physicalKey)?.call(); + _keyGuards[physicalKey] = cancelingCallback; + } + // Call this method on an up event event of a non-modifier key. + void _stopGuardingKey(int physicalKey) { + _keyGuards.remove(physicalKey)?.call(); + } + + // Parse the HTML event, update states, and dispatch Flutter key data through + // [dispatchKeyData]. + // + // * The method might dispatch some synthesized key data first to update states, + // results discarded. + // * Then it dispatches exactly one non-synthesized key data that corresponds + // to the `event`, i.e. the main key data. The result of this dispatching is + // returned to indicate whether the event is processed by Flutter. + // * Some key data might be synthesized to update states after the main key + // data. They are always scheduled asynchronously with results discarded. + bool handleEvent(FlutterHtmlKeyboardEvent event) { + final Duration timeStamp = _eventTimeStampToDuration(event.timeStamp!); + + if (_shouldPreventDefault(event)) { + event.preventDefault(); + } + final String eventKey = event.key!; + + final int physicalKey = _getPhysicalCode(event.code!); + final bool logicalKeyIsCharacter = !_eventKeyIsKeyname(eventKey); + final String? character = logicalKeyIsCharacter ? eventKey : null; + final int logicalKey = () { + if (kWebLogicalLocationMap.containsKey(event.key!)) { + final int? result = kWebLogicalLocationMap[event.key!]?[event.location!]; + assert(result != null, 'Invalid modifier location: ${event.key}, ${event.location}'); + return result!; + } + if (character != null) + return _characterToLogicalKey(character); + if (eventKey == _kLogicalDead) + return _deadKeyToLogicalKey(physicalKey, event); + return _otherLogicalKey(eventKey); + }(); + + assert(event.type == 'keydown' || event.type == 'keyup'); + final bool isPhysicalDown = event.type == 'keydown' || + // On macOS, both keydown and keyup events of CapsLock should be considered keydown, + // followed by an immediate cancel event. + (_shouldSynthesizeCapsLockUp() && event.code! == _kPhysicalCapsLock); + + final int? lastLogicalRecord = _pressingRecords[physicalKey]; + + ui.KeyChange change; + + if (_shouldSynthesizeCapsLockUp() && event.code! == _kPhysicalCapsLock) { + // Case 1: Handle CapsLock on macOS + // + // On macOS, both keydown and keyup events of CapsLock are considered + // keydown, followed by an immediate synchronized up event. + + _scheduleAsyncEvent( + Duration.zero, + () => ui.KeyData( + timeStamp: timeStamp, + change: ui.KeyChange.up, + physical: physicalKey, + logical: logicalKey, + character: null, + synthesized: true, + ), + () { + _pressingRecords.remove(physicalKey); + } + ); + change = ui.KeyChange.down; + + } else if (isPhysicalDown) { + // Case 2: Handle key down of normal keys + change = ui.KeyChange.down; + if (lastLogicalRecord != null) { + // This physical key is being pressed according to the record. + if (event.repeat ?? false) { + // A normal repeated key. + change = ui.KeyChange.repeat; + } else { + // A non-repeated key has been pressed that has the exact physical key as + // a currently pressed one, usually indicating multiple keyboards are + // pressing keys with the same physical key, or the up event was lost + // during a loss of focus. The down event is ignored. + return false; + } + } else { + // This physical key is not being pressed according to the record. It's a + // normal down event, whether the system event is a repeat or not. + } + + } else { // isPhysicalDown is false and not CapsLock + // Case 2: Handle key up of normal keys + if (lastLogicalRecord == null) { + // The physical key has been released before. It indicates multiple + // keyboards pressed keys with the same physical key. Ignore the up event. + return false; + } + + change = ui.KeyChange.up; + } + + late final int? nextLogicalRecord; + switch (change) { + case ui.KeyChange.down: + assert(lastLogicalRecord == null); + nextLogicalRecord = logicalKey; + break; + case ui.KeyChange.up: + assert(lastLogicalRecord != null); + nextLogicalRecord = null; + break; + case ui.KeyChange.repeat: + assert(lastLogicalRecord != null); + nextLogicalRecord = lastLogicalRecord; + break; + } + if (nextLogicalRecord == null) { + _pressingRecords.remove(physicalKey); + } else { + _pressingRecords[physicalKey] = nextLogicalRecord; + } + + // After updating _pressingRecords, synchronize modifier states. The + // `event.***Key` fields can be used to reduce some omitted modifier key + // events. We can deduce key cancel events if they are false. Key sync + // events can not be deduced since we don't know which physical key they + // represent. + _kLogicalKeyToModifierGetter.forEach((int logicalKey, _ModifierGetter getModifier) { + // print(_pressingRecords); + if (_pressingRecords.containsValue(logicalKey) && !getModifier(event)) { + _pressingRecords.removeWhere((int physicalKey, int logicalRecord) { + if (logicalRecord != logicalKey) + return false; + + dispatchKeyData(ui.KeyData( + timeStamp: timeStamp, + change: ui.KeyChange.up, + physical: physicalKey, + logical: logicalKey, + character: null, + synthesized: true, + )); + + return true; + }); + } + }); + + // Update key guards + if (logicalKeyIsCharacter) { + if (nextLogicalRecord != null) { + _startGuardingKey(physicalKey, logicalKey, timeStamp); + } else { + _stopGuardingKey(physicalKey); + } + } + + final ui.KeyData keyData = ui.KeyData( + timeStamp: timeStamp, + change: change, + physical: physicalKey, + logical: lastLogicalRecord ?? logicalKey, + character: change == ui.KeyChange.up ? null : character, + synthesized: false, + ); + + dispatchKeyData(keyData); + return true; + } +} diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index 7f6ce039c5a5c..83ebe95725adf 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -170,6 +170,31 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { invoke1(_onPointerDataPacket, _onPointerDataPacketZone, dataPacket); } + /// A callback that is invoked when pointer data is available. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + /// + /// See also: + /// + /// * [GestureBinding], the Flutter framework class which manages pointer + /// events. + @override + ui.KeyDataCallback? get onKeyData => _onKeyData; + ui.KeyDataCallback? _onKeyData; + Zone? _onKeyDataZone; + @override + set onKeyData(ui.KeyDataCallback? callback) { + _onKeyData = callback; + _onKeyDataZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnKeyData(ui.KeyData dataPacket) { + invoke1(_onKeyData, _onKeyDataZone, dataPacket); + } + /// A callback that is invoked to report the [FrameTiming] of recently /// rasterized frames. /// @@ -947,4 +972,3 @@ void invoke3(void Function(A1 a1, A2 a2, A3 a3)? callback, Zone? zon }); } } - diff --git a/lib/web_ui/lib/src/ui/key.dart b/lib/web_ui/lib/src/ui/key.dart new file mode 100644 index 0000000000000..ffb8d4e81064b --- /dev/null +++ b/lib/web_ui/lib/src/ui/key.dart @@ -0,0 +1,66 @@ +// Copyright 2013 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. + +// @dart = 2.12 + +part of ui; + +/// How the key has changed since the last report. +enum KeyChange { + /// The key is pressed. + down, + + /// The key is released. + up, + + /// The key is held, causing a repeated key input. + repeat, +} + +/// Information about the change of a key. +class KeyData { + /// Creates an object that represents the change of a key. + const KeyData({ + required this.timeStamp, + required this.change, + required this.physical, + required this.logical, + required this.character, + required this.synthesized, + }); + + /// Time of event dispatch, relative to an arbitrary timeline. + /// + /// For [KeyChange.synchronize] and [KeyChange.cancel] events, the [timeStamp] + /// might not be the actual time that the key press or release happens. + final Duration timeStamp; + + /// How the key has changed since the last report. + final KeyChange change; + + /// The key code for the physical key that has changed. + final int physical; + + /// The key code for the logical key that has changed. + final int logical; + + /// Character input from the event. + /// + /// Not available to up events. + final String? character; + + /// Whether the event is generated by Flutter, hence not corresponding to a + /// native event. + final bool synthesized; + + @override + String toString() => 'KeyData(timeStamp: $timeStamp, change: $change, physical: $physical, ' + 'logical: $logical, character: $character, synthesized: $synthesized)'; + + /// Returns a complete textual description of the information in this object. + String toStringFull() { + return '$runtimeType(' + ')'; + } +} diff --git a/lib/web_ui/lib/src/ui/platform_dispatcher.dart b/lib/web_ui/lib/src/ui/platform_dispatcher.dart index 122e2befd336c..96aec518b4f0b 100644 --- a/lib/web_ui/lib/src/ui/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/ui/platform_dispatcher.dart @@ -9,6 +9,7 @@ typedef VoidCallback = void Function(); typedef FrameCallback = void Function(Duration duration); typedef TimingsCallback = void Function(List timings); typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); +typedef KeyDataCallback = void Function(KeyData packet); typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args); typedef PlatformMessageResponseCallback = void Function(ByteData? data); typedef PlatformMessageCallback = void Function( @@ -36,6 +37,9 @@ abstract class PlatformDispatcher { PointerDataPacketCallback? get onPointerDataPacket; set onPointerDataPacket(PointerDataPacketCallback? callback); + KeyDataCallback? get onKeyData; + set onKeyData(KeyDataCallback? callback); + TimingsCallback? get onReportTimings; set onReportTimings(TimingsCallback? callback); @@ -417,4 +421,4 @@ class Locale { } return out.toString(); } -} \ No newline at end of file +} diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index 6fc8b901cd742..ef3452947b5a1 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -81,6 +81,11 @@ abstract class SingletonFlutterWindow extends FlutterWindow { platformDispatcher.onPointerDataPacket = callback; } + KeyDataCallback? get onKeyData => platformDispatcher.onKeyData; + set onKeyData(KeyDataCallback? callback) { + platformDispatcher.onKeyData = callback; + } + String get defaultRouteName => platformDispatcher.defaultRouteName; void scheduleFrame() => platformDispatcher.scheduleFrame(); diff --git a/lib/web_ui/lib/ui.dart b/lib/web_ui/lib/ui.dart index c64360fec5403..334887d873c3d 100644 --- a/lib/web_ui/lib/ui.dart +++ b/lib/web_ui/lib/ui.dart @@ -24,6 +24,7 @@ part 'src/ui/compositing.dart'; part 'src/ui/geometry.dart'; part 'src/ui/hash_codes.dart'; part 'src/ui/initialization.dart'; +part 'src/ui/key.dart'; part 'src/ui/lerp.dart'; part 'src/ui/natives.dart'; part 'src/ui/painting.dart'; diff --git a/lib/web_ui/test/keyboard_converter_test.dart b/lib/web_ui/test/keyboard_converter_test.dart new file mode 100644 index 0000000000000..eb5bcf5563dfd --- /dev/null +++ b/lib/web_ui/test/keyboard_converter_test.dart @@ -0,0 +1,927 @@ +// Copyright 2013 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. + +// @dart = 2.10 + +import 'package:quiver/testing/async.dart'; +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; +import 'package:meta/meta.dart' show isTest; + +const int kLocationLeft = 1; +const int kLocationRight = 2; +const int kLocationNumpad = 3; + +const int kPhysicalKeyA = 0x00070004; +const int kPhysicalKeyE = 0x00070008; +const int kPhysicalKeyU = 0x00070018; +const int kPhysicalDigit1 = 0x0007001e; +const int kPhysicalNumpad1 = 0x00070059; +const int kPhysicalShiftLeft = 0x000700e1; +const int kPhysicalShiftRight = 0x000700e5; +const int kPhysicalMetaLeft = 0x000700e3; +const int kPhysicalTab = 0x0007002b; +const int kPhysicalCapsLock = 0x00070039; +const int kPhysicalScrollLock = 0x00070047; + +const int kLogicalKeyA = 0x00000000041; +const int kLogicalKeyU = 0x00000000055; +const int kLogicalDigit1 = 0x00000000031; +const int kLogicalNumpad1 = 0x00200000031; +const int kLogicalShiftLeft = 0x030000010d; +const int kLogicalShiftRight = 0x040000010d; +const int kLogicalMetaLeft = 0x0300000109; +const int kLogicalTab = 0x0000000009; +const int kLogicalCapsLock = 0x00000000104; +const int kLogicalScrollLock = 0x0000000010c; + +typedef VoidCallback = void Function(); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + test('Single key press, repeat, and release', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + bool preventedDefault = false; + final onPreventDefault = () { preventedDefault = true; }; + + converter.handleEvent(keyDownEvent('KeyA', 'a') + ..timeStamp = 1 + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + timeStamp: Duration(milliseconds: 1), + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + expect(preventedDefault, false); + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a') + ..timeStamp = 1.5 + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + timeStamp: Duration(milliseconds: 1, microseconds: 500), + change: ui.KeyChange.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + expect(preventedDefault, false); + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a') + ..timeStamp = 1500 + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + timeStamp: Duration(seconds: 1, milliseconds: 500), + change: ui.KeyChange.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + expect(preventedDefault, false); + + converter.handleEvent(keyUpEvent('KeyA', 'a') + ..timeStamp = 2000.5 + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + timeStamp: Duration(seconds: 2, microseconds: 500), + change: ui.KeyChange.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + expect(preventedDefault, false); + }); + + test('Release modifier during a repeated sequence', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + + converter.handleEvent(keyDownEvent('KeyA', 'A', kShift)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'A', + ); + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'A', kShift)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'A', + ); + + converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + + converter.handleEvent(keyUpEvent('KeyA', 'a')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + }); + + test('Distinguish between left and right modifiers', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + + converter.handleEvent(keyDownEvent('ShiftRight', 'Shift', kShift, kLocationRight)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalShiftRight, + logical: kLogicalShiftRight, + character: null, + ); + + converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + + converter.handleEvent(keyUpEvent('ShiftRight', 'Shift', 0, kLocationRight)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalShiftRight, + logical: kLogicalShiftRight, + character: null, + ); + }); + + test('Distinguish between normal and numpad digits', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + converter.handleEvent(keyDownEvent('Digit1', '1', 0, 0)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalDigit1, + logical: kLogicalDigit1, + character: '1', + ); + + converter.handleEvent(keyDownEvent('Numpad1', '1', 0, kLocationNumpad)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalNumpad1, + logical: kLogicalNumpad1, + character: '1', + ); + + converter.handleEvent(keyUpEvent('Digit1', '1', 0, 0)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalDigit1, + logical: kLogicalDigit1, + character: null, + ); + + converter.handleEvent(keyUpEvent('Numpad1', '1', 0, kLocationNumpad)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalNumpad1, + logical: kLogicalNumpad1, + character: null, + ); + }); + + test('Prevents default when pressing Tab', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + bool preventedDefault = false; + final onPreventDefault = () { preventedDefault = true; }; + + converter.handleEvent(keyDownEvent('Tab', 'Tab')..onPreventDefault = onPreventDefault); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalTab, + logical: kLogicalTab, + character: null, + ); + expect(preventedDefault, true); + preventedDefault = false; + + converter.handleEvent(keyUpEvent('Tab', 'Tab')..onPreventDefault = onPreventDefault); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalTab, + logical: kLogicalTab, + character: null, + ); + expect(preventedDefault, true); + preventedDefault = false; + }); + + test('Dead keys are distinguishable', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + // The absolute values of the following logical keys are not guaranteed. + const int kLogicalAltE = 0x410800070008; + const int kLogicalAltU = 0x410800070018; + const int kLogicalAltShiftE = 0x610800070008; + // The values must be distinguishable. + expect(kLogicalAltE, isNot(equals(kLogicalAltU))); + expect(kLogicalAltE, isNot(equals(kLogicalAltShiftE))); + + converter.handleEvent(keyDownEvent('AltLeft', 'Alt', kAlt, kLocationLeft)); + + converter.handleEvent(keyDownEvent('KeyE', 'Dead', kAlt)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalKeyE, + logical: kLogicalAltE, + character: null, + ); + + converter.handleEvent(keyUpEvent('KeyE', 'Dead', kAlt)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalKeyE, + logical: kLogicalAltE, + character: null, + ); + + converter.handleEvent(keyDownEvent('KeyU', 'Dead', kAlt)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalKeyU, + logical: kLogicalAltU, + character: null, + ); + + converter.handleEvent(keyUpEvent('KeyU', 'Dead', kAlt)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalKeyU, + logical: kLogicalAltU, + character: null, + ); + + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kAlt | kShift, kLocationLeft)); + + // This does not actually produce a Dead key on macOS (US layout); just for + // testing. + converter.handleEvent(keyDownEvent('KeyE', 'Dead', kAlt | kShift)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalKeyE, + logical: kLogicalAltShiftE, + character: null, + ); + + converter.handleEvent(keyUpEvent('AltLeft', 'Alt', kShift, kLocationLeft)); + + converter.handleEvent(keyUpEvent('KeyE', 'e', kShift)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalKeyE, + logical: kLogicalAltShiftE, + character: null, + ); + + converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); + }); + + test('Duplicate down is ignored', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + // A KeyUp of ShiftLeft is missed due to loss of focus. + + keyDataList.clear(); + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + expect(keyDataList, isEmpty); + + converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + }); + + test('Duplicate ups are skipped', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + // A KeyDown of ShiftRight is missed due to loss of focus. + converter.handleEvent(keyUpEvent('ShiftRight', 'Shift', 0, kLocationRight)); + expect(keyDataList, isEmpty); + }); + + test('Conflict from multiple keyboards do not crash', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + // Same layout + converter.handleEvent(keyDownEvent('KeyA', 'a')); + converter.handleEvent(keyDownEvent('KeyA', 'a')); + converter.handleEvent(keyUpEvent('KeyA', 'a')); + converter.handleEvent(keyUpEvent('KeyA', 'a')); + + // Different layout + converter.handleEvent(keyDownEvent('KeyA', 'a')); + converter.handleEvent(keyDownEvent('KeyA', 'u')); + converter.handleEvent(keyUpEvent('KeyA', 'u')); + converter.handleEvent(keyUpEvent('KeyA', 'a')); + + // Passes if there's no crash, and states are reset after everything is released. + keyDataList.clear(); + converter.handleEvent(keyDownEvent('KeyA', 'a')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + + converter.handleEvent(keyDownEvent('KeyU', 'u')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalKeyU, + logical: kLogicalKeyU, + character: 'u', + ); + }); + + testFakeAsync('CapsLock down synthesizes an immediate cancel on macOS', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }, onMacOs: true); + + converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + keyDataList.clear(); + + async.elapse(Duration(microseconds: 1)); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + synthesized: true, + ); + keyDataList.clear(); + + converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + keyDataList.clear(); + + async.elapse(Duration(microseconds: 1)); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + synthesized: true, + ); + keyDataList.clear(); + + // Another key down works + converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + keyDataList.clear(); + + + // Schedules are canceled after disposal + converter.dispose(); + async.elapse(Duration(seconds: 10)); + expect(keyDataList, isEmpty); + }); + + testFakeAsync('CapsLock behaves normally on non-macOS', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }, onMacOs: false); + + converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + keyDataList.clear(); + + async.elapse(Duration(seconds: 10)); + expect(keyDataList, isEmpty); + + converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + keyDataList.clear(); + + async.elapse(Duration(seconds: 10)); + expect(keyDataList, isEmpty); + + converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + + converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + }); + + testFakeAsync('Key guards: key down events are guarded', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100); + async.elapse(Duration(milliseconds: 100)); + + converter.handleEvent(keyDownEvent('KeyA', 'a', kMeta)..timeStamp = 200); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 200), + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + keyDataList.clear(); + + // Keyup of KeyA is omitted due to being a shortcut. + + async.elapse(Duration(milliseconds: 2500)); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 1200), + change: ui.KeyChange.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + synthesized: true, + ); + keyDataList.clear(); + + converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 2700); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 2700), + change: ui.KeyChange.up, + physical: kPhysicalMetaLeft, + logical: kLogicalMetaLeft, + character: null, + ); + async.elapse(Duration(milliseconds: 100)); + + // Key A states are cleared + converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 2800); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 2800), + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + async.elapse(Duration(milliseconds: 100)); + + converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2900); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 2900), + change: ui.KeyChange.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + }); + + testFakeAsync('Key guards: key repeated down events refreshes guards', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100); + async.elapse(Duration(milliseconds: 100)); + + converter.handleEvent(keyDownEvent('KeyA', 'a', kMeta)..timeStamp = 200); + async.elapse(Duration(milliseconds: 400)); + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a', kMeta)..timeStamp = 600); + async.elapse(Duration(milliseconds: 50)); + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a', kMeta)..timeStamp = 650); + async.elapse(Duration(milliseconds: 50)); + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a', kMeta)..timeStamp = 700); + + // Keyup of KeyA is omitted due to being a shortcut. + + async.elapse(Duration(milliseconds: 2500)); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 1700), + change: ui.KeyChange.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + synthesized: true, + ); + keyDataList.clear(); + + converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 3200); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 3200), + change: ui.KeyChange.up, + physical: kPhysicalMetaLeft, + logical: kLogicalMetaLeft, + character: null, + ); + async.elapse(Duration(milliseconds: 100)); + + // Key A states are cleared + converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 3300); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 3300), + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + async.elapse(Duration(milliseconds: 100)); + + converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 3400); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 3400), + change: ui.KeyChange.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + }); + + testFakeAsync('Key guards: cleared by keyups', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100); + async.elapse(Duration(milliseconds: 100)); + + converter.handleEvent(keyDownEvent('KeyA', 'a', kCtrl)..timeStamp = 200); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 200), + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + keyDataList.clear(); + async.elapse(Duration(milliseconds: 500)); + + converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 700); + async.elapse(Duration(milliseconds: 100)); + + converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 800); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 800), + change: ui.KeyChange.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + keyDataList.clear(); + async.elapse(Duration(milliseconds: 2000)); + expect(keyDataList, isEmpty); + + // Key A states are cleared + converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 2800); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 2800), + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + async.elapse(Duration(milliseconds: 100)); + + converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2900); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 2900), + change: ui.KeyChange.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + }); + + testFakeAsync('Lock flags of other keys', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }, onMacOs: false); + + converter.handleEvent(keyDownEvent('ScrollLock', 'ScrollLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalScrollLock, + logical: kLogicalScrollLock, + character: null, + ); + keyDataList.clear(); + + async.elapse(Duration(seconds: 10)); + expect(keyDataList, isEmpty); + + converter.handleEvent(keyUpEvent('ScrollLock', 'ScrollLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalScrollLock, + logical: kLogicalScrollLock, + character: null, + ); + keyDataList.clear(); + + converter.handleEvent(keyDownEvent('ScrollLock', 'ScrollLock')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalScrollLock, + logical: kLogicalScrollLock, + character: null, + ); + + converter.handleEvent(keyUpEvent('ScrollLock', 'ScrollLock')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalScrollLock, + logical: kLogicalScrollLock, + character: null, + ); + }); + + test('Deduce modifier key cancel from modifier field', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }, onMacOs: false); + + converter.handleEvent(keyDownEvent('ShiftRight', 'Shift', kShift, kLocationRight)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalShiftRight, + logical: kLogicalShiftRight, + character: null, + ); + + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + keyDataList.clear(); + + // The release of the shift keys are omitted + + converter.handleEvent(keyDownEvent('KeyA', 'a')); + expect(keyDataList, hasLength(3)); + expectKeyData(keyDataList[0], + change: ui.KeyChange.up, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + synthesized: true, + ); + expectKeyData(keyDataList[1], + change: ui.KeyChange.up, + physical: kPhysicalShiftRight, + logical: kLogicalShiftRight, + character: null, + synthesized: true, + ); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + }); +} + +class MockKeyboardEvent implements FlutterHtmlKeyboardEvent { + MockKeyboardEvent({ + required this.type, + required this.code, + required this.key, + this.timeStamp = 0, + this.repeat = false, + this.altKey = false, + this.ctrlKey = false, + this.shiftKey = false, + this.metaKey = false, + this.location = 0, + this.onPreventDefault, + }); + + String type; + String? code; + String? key; + bool? repeat; + num? timeStamp; + bool altKey; + bool ctrlKey; + bool shiftKey; + bool metaKey; + int? location; + + bool getModifierState(String key) => modifierState.contains(key); + final Set modifierState = {}; + + void preventDefault() { onPreventDefault?.call(); } + VoidCallback? onPreventDefault; +} + +// Flags used for the `modifiers` argument of `key***Event` functions. +const kAlt = 0x1; +const kCtrl = 0x2; +const kShift = 0x4; +const kMeta = 0x8; + +// Utility functions to make code more concise. +// +// To add timeStamp or onPreventDefault, use syntax like `..timeStamp = `. +MockKeyboardEvent keyDownEvent(String code, String key, [int modifiers = 0, int location = 0]) { + return MockKeyboardEvent( + type: 'keydown', + code: code, + key: key, + altKey: modifiers & kAlt != 0, + ctrlKey: modifiers & kCtrl != 0, + shiftKey: modifiers & kShift != 0, + metaKey: modifiers & kMeta != 0, + location: location, + ); +} + +MockKeyboardEvent keyUpEvent(String code, String key, [int modifiers = 0, int location = 0]) { + return MockKeyboardEvent( + type: 'keyup', + code: code, + key: key, + altKey: modifiers & kAlt != 0, + ctrlKey: modifiers & kCtrl != 0, + shiftKey: modifiers & kShift != 0, + metaKey: modifiers & kMeta != 0, + location: location, + ); +} + +MockKeyboardEvent keyRepeatedDownEvent(String code, String key, [int modifiers = 0, int location = 0]) { + return MockKeyboardEvent( + type: 'keydown', + code: code, + key: key, + altKey: modifiers & kAlt != 0, + ctrlKey: modifiers & kCtrl != 0, + shiftKey: modifiers & kShift != 0, + metaKey: modifiers & kMeta != 0, + repeat: true, + location: location, + ); +} + +// Flags used for the `activeLocks` argument of expectKeyData. +const kCapsLock = 0x1; +const kNumlLock = 0x2; +const kScrollLock = 0x4; + +void expectKeyData( + ui.KeyData target, { + required ui.KeyChange change, + required int physical, + required int logical, + required String? character, + Duration? timeStamp, + bool synthesized = false, +}) { + expect(target.change, change); + expect(target.physical, physical); + expect(target.logical, logical); + expect(target.character, character); + expect(target.synthesized, synthesized); + if (timeStamp != null) + expect(target.timeStamp, equals(timeStamp)); +} + +typedef FakeAsyncTest = void Function(FakeAsync); + +@isTest +void testFakeAsync(String description, FakeAsyncTest fn) { + test(description, () { + FakeAsync().run(fn); + }); +} From 08c95070cfcdfdbaed501d13880b2ef71879a95c Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 17:32:36 -0800 Subject: [PATCH 02/34] Include embedder changes --- ci/licenses_golden/licenses_flutter | 2 + lib/ui/dart_ui.gni | 1 + lib/ui/hooks.dart | 6 ++ lib/ui/key.dart | 68 +++++++++++++++++++ lib/ui/platform_dispatcher.dart | 61 ++++++++++++++++- lib/ui/ui.dart | 1 + lib/ui/window.dart | 9 +++ lib/ui/window/key_data.cc | 18 +++++ lib/ui/window/key_data.h | 48 +++++++++++++ lib/ui/window/key_data_packet.cc | 36 ++++++++++ lib/ui/window/key_data_packet.h | 65 ++++++++++++++++++ lib/ui/window/window.cc | 16 +++++ lib/ui/window/window.h | 2 + runtime/runtime_controller.cc | 10 +++ runtime/runtime_controller.h | 8 +++ shell/common/engine.cc | 7 ++ shell/common/engine.h | 11 +++ shell/common/platform_view.cc | 5 ++ shell/common/platform_view.h | 24 +++++++ shell/common/shell.cc | 15 ++++ shell/common/shell.h | 4 ++ shell/common/shell_unittests.cc | 3 + shell/platform/embedder/embedder.cc | 61 +++++++++++++++++ shell/platform/embedder/embedder.h | 40 +++++++++++ shell/platform/embedder/embedder_engine.cc | 15 ++++ shell/platform/embedder/embedder_engine.h | 2 + .../fuchsia/flutter/platform_view_unittest.cc | 3 + 27 files changed, 540 insertions(+), 1 deletion(-) create mode 100644 lib/ui/key.dart create mode 100644 lib/ui/window/key_data.cc create mode 100644 lib/ui/window/key_data.h create mode 100644 lib/ui/window/key_data_packet.cc create mode 100644 lib/ui/window/key_data_packet.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 12543e387ecfc..e868c7ca49eb8 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1091,6 +1091,7 @@ FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server.cc FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server.h FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.cc FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.h +FILE: ../../../flutter/lib/ui/key.dart FILE: ../../../flutter/lib/ui/lerp.dart FILE: ../../../flutter/lib/ui/natives.dart FILE: ../../../flutter/lib/ui/painting.dart @@ -1330,6 +1331,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/ui/compositing.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/geometry.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/hash_codes.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/initialization.dart +FILE: ../../../flutter/lib/web_ui/lib/src/ui/key.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/lerp.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/natives.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/painting.dart diff --git a/lib/ui/dart_ui.gni b/lib/ui/dart_ui.gni index 11fa74da53f84..96a7b06fbd918 100644 --- a/lib/ui/dart_ui.gni +++ b/lib/ui/dart_ui.gni @@ -10,6 +10,7 @@ dart_ui_files = [ "//flutter/lib/ui/hash_codes.dart", "//flutter/lib/ui/hooks.dart", "//flutter/lib/ui/isolate_name_server.dart", + "//flutter/lib/ui/key.dart", "//flutter/lib/ui/lerp.dart", "//flutter/lib/ui/natives.dart", "//flutter/lib/ui/painting.dart", diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index 6ffcea0f93d72..8d6770471ad71 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -96,6 +96,12 @@ void _dispatchPointerDataPacket(ByteData packet) { PlatformDispatcher.instance._dispatchPointerDataPacket(packet); } +@pragma('vm:entry-point') +// ignore: unused_element +void _dispatchKeyData(ByteData packet) { + PlatformDispatcher.instance._dispatchKeyData(packet); +} + @pragma('vm:entry-point') // ignore: unused_element void _dispatchSemanticsAction(int id, int action, ByteData? args) { diff --git a/lib/ui/key.dart b/lib/ui/key.dart new file mode 100644 index 0000000000000..e60bfbeb027f1 --- /dev/null +++ b/lib/ui/key.dart @@ -0,0 +1,68 @@ +// Copyright 2013 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. + +// @dart = 2.12 + +part of dart.ui; + +/// How the key has changed since the last report. +/// +// Must match the KeyChange enum in ui/window/key_data.h. +enum KeyChange { + /// The key is pressed. + down, + + /// The key is released. + up, + + /// The key is held, causing a repeated key input. + repeat, +} + +/// Information about the change of a key. +class KeyData { + /// Creates an object that represents the change of a key. + const KeyData({ + required this.timeStamp, + required this.change, + required this.physical, + required this.logical, + required this.character, + required this.synthesized, + }); + + /// Time of event dispatch, relative to an arbitrary timeline. + /// + /// For [KeyChange.synchronize] and [KeyChange.cancel] events, the [timeStamp] + /// might not be the actual time that the key press or release happens. + final Duration timeStamp; + + /// How the key has changed since the last report. + final KeyChange change; + + /// The key code for the physical key that has changed. + final int physical; + + /// The key code for the logical key that has changed. + final int logical; + + /// Character input from the event. + /// + /// Not available to up events. + final String? character; + + /// Whether the event is generated by Flutter, hence not corresponding to a + /// native event. + final bool synthesized; + + @override + String toString() => 'KeyData(timeStamp: $timeStamp, change: $change, physical: $physical, ' + 'logical: $logical, character: $character, synthesized: $synthesized)'; + + /// Returns a complete textual description of the information in this object. + String toStringFull() { + return '$runtimeType(' + ')'; + } +} diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index dc7223b79cc5d..c93fe976cc518 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -28,6 +28,9 @@ typedef TimingsCallback = void Function(List timings); /// Signature for [PlatformDispatcher.onPointerDataPacket]. typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); +/// Signature for [PlatformDispatcher.onKeyData]. +typedef KeyDataCallback = void Function(KeyData data); + /// Signature for [PlatformDispatcher.onSemanticsAction]. typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args); @@ -332,6 +335,62 @@ class PlatformDispatcher { return PointerDataPacket(data: data); } + /// A callback that is invoked when key data is available. + /// + /// The framework invokes this callback in the same zone in which the callback + /// was set. + KeyDataCallback? get onKeyData => _onKeyData; + KeyDataCallback? _onKeyData; + Zone _onKeyDataZone = Zone.root; + set onKeyData(KeyDataCallback? callback) { + _onKeyData = callback; + _onKeyDataZone = Zone.current; + } + + // Called from the engine, via hooks.dart + void _dispatchKeyData(ByteData packet) { + print('dispatch: onKeyData $onKeyData'); + if (onKeyData != null) { + _invoke1( + onKeyData, + _onKeyDataZone, + _unpackKeyData(packet), + ); + } + } + + // If this value changes, update the encoding code in the following files: + // + // * key_data.h + // * key.dart (ui) + // * key.dart (web_ui) + // * HardwareKeyboard.java + static const int _kKeyDataFieldCount = 5; + + // KeyData packet structure: + // | CharDataSize | (1 field) + // | Key Data | (_kKeyDataFieldCount fields) + // | CharData | (CharDataSize bits) + static KeyData _unpackKeyData(ByteData packet) { + const int kStride = Int64List.bytesPerElement; + + int offset = 0; + final int charDataSize = packet.getUint64(kStride * offset++, _kFakeHostEndian); + final String? character = charDataSize == 0 ? null : utf8.decoder.convert( + packet.buffer.asUint8List(kStride * (offset + _kKeyDataFieldCount), charDataSize)); + + final KeyData keyData = KeyData( + timeStamp: Duration(microseconds: packet.getUint64(kStride * offset++, _kFakeHostEndian)), + change: KeyChange.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], + physical: packet.getUint64(kStride * offset++, _kFakeHostEndian), + logical: packet.getUint64(kStride * offset++, _kFakeHostEndian), + character: character, + synthesized: packet.getUint64(kStride * offset++, _kFakeHostEndian) != 0, + ); + + return keyData; + } + /// A callback that is invoked to report the [FrameTiming] of recently /// rasterized frames. /// @@ -1547,4 +1606,4 @@ class Locale { out.write('$separator$countryCode'); return out.toString(); } -} \ No newline at end of file +} diff --git a/lib/ui/ui.dart b/lib/ui/ui.dart index ea966c88cd43a..f48b8162ba42c 100644 --- a/lib/ui/ui.dart +++ b/lib/ui/ui.dart @@ -30,6 +30,7 @@ part 'geometry.dart'; part 'hash_codes.dart'; part 'hooks.dart'; part 'isolate_name_server.dart'; +part 'key.dart'; part 'lerp.dart'; part 'natives.dart'; part 'painting.dart'; diff --git a/lib/ui/window.dart b/lib/ui/window.dart index ac984a2a6581c..da65c1d625a5a 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -556,6 +556,15 @@ class SingletonFlutterWindow extends FlutterWindow { platformDispatcher.onPointerDataPacket = callback; } + /// A callback that is invoked when key data is available. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + KeyDataCallback? get onKeyData => platformDispatcher.onKeyData; + set onKeyData(KeyDataCallback? callback) { + platformDispatcher.onKeyData = callback; + } + /// The route or path that the embedder requested when the application was /// launched. /// diff --git a/lib/ui/window/key_data.cc b/lib/ui/window/key_data.cc new file mode 100644 index 0000000000000..875f89f7522c2 --- /dev/null +++ b/lib/ui/window/key_data.cc @@ -0,0 +1,18 @@ +// Copyright 2013 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 "flutter/lib/ui/window/key_data.h" + +#include + +namespace flutter { + +static_assert(sizeof(KeyData) == kBytesPerKeyField * kKeyDataFieldCount, + "KeyData has the wrong size"); + +void KeyData::Clear() { + memset(this, 0, sizeof(KeyData)); +} + +} // namespace flutter diff --git a/lib/ui/window/key_data.h b/lib/ui/window/key_data.h new file mode 100644 index 0000000000000..d41c162f47f8a --- /dev/null +++ b/lib/ui/window/key_data.h @@ -0,0 +1,48 @@ +// Copyright 2013 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. + +#ifndef FLUTTER_LIB_UI_WINDOW_KEY_DATA_H_ +#define FLUTTER_LIB_UI_WINDOW_KEY_DATA_H_ + +#include + +namespace flutter { + +// If this value changes, update the key data unpacking code in hooks.dart. +static constexpr int kKeyDataFieldCount = 5; +static constexpr int kBytesPerKeyField = sizeof(int64_t); + +// The change of the key event, used by KeyData. +// +// Must match the KeyChange enum in ui/key.dart. +enum class KeyChange : int64_t { + kDown = 0, + kUp, + kRepeat, +}; + +// The fixed-length sections of a KeyDataPacket. +// +// KeyData does not contain `character`, for various-length data are stored in a +// different way in KeyDataPacket. +// +// This structure is unpacked by hooks.dart. +struct alignas(8) KeyData { + // Timestamp in microseconds from an arbitrary and consistant start point + uint64_t timestamp; + KeyChange change; + uint64_t physical; + uint64_t logical; + // True if the event does not correspond to a native event. + // + // The value is 1 for true, and 0 for false. + uint64_t synthesized; + + // Set all contents of `Keydata` to 0. + void Clear(); +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_WINDOW_POINTER_DATA_H_ diff --git a/lib/ui/window/key_data_packet.cc b/lib/ui/window/key_data_packet.cc new file mode 100644 index 0000000000000..0a22b04f13d9d --- /dev/null +++ b/lib/ui/window/key_data_packet.cc @@ -0,0 +1,36 @@ +// Copyright 2013 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 "flutter/lib/ui/window/key_data_packet.h" +#include "flutter/fml/logging.h" + +namespace flutter { + +KeyDataPacket::KeyDataPacket(uint8_t* data, size_t num_bytes) + : data_(data, data + num_bytes) {} + +KeyDataPacket::KeyDataPacket(size_t num_bytes) : data_(num_bytes) {} + +KeyDataPacket::~KeyDataPacket() = default; + +KeyDataPacketBuilder::KeyDataPacketBuilder(size_t character_data_size) + : KeyDataPacket(sizeof(uint64_t) + sizeof(KeyData) + character_data_size) { + uint64_t size64 = character_data_size; + memcpy(&data()[CharacterSizeStart_()], &size64, sizeof(size64)); +} + +KeyDataPacketBuilder::~KeyDataPacketBuilder() = default; + +void KeyDataPacketBuilder::SetKeyData(const KeyData& event) { + memcpy(&data()[KeyDataStart_()], &event, sizeof(KeyData)); +} + +void KeyDataPacketBuilder::SetCharacter(const char* character) { + if (character != nullptr) { + memcpy(data().data() + CharacterStart_(), character, + data().size() - CharacterStart_()); + } +} + +} // namespace flutter diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_packet.h new file mode 100644 index 0000000000000..fa48a60f7b676 --- /dev/null +++ b/lib/ui/window/key_data_packet.h @@ -0,0 +1,65 @@ +// Copyright 2013 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. + +#ifndef FLUTTER_LIB_UI_WINDOW_KEY_DATA_PACKET_H_ +#define FLUTTER_LIB_UI_WINDOW_KEY_DATA_PACKET_H_ + +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/lib/ui/window/key_data.h" + +namespace flutter { + +// Bitstream that contains a KeyData. +// +// This class provides interface to read the opaque bits. For constructing such +// a bitstream from KeyData, checkout KeyDataPacketBuilder. +class KeyDataPacket { + public: + KeyDataPacket(uint8_t* data, size_t num_bytes); + + protected: + KeyDataPacket(size_t num_bytes); + + std::vector& data() { return data_; } + + public: + ~KeyDataPacket(); + + const std::vector& data() const { return data_; } + + private: + std::vector data_; + + FML_DISALLOW_COPY_AND_ASSIGN(KeyDataPacket); +}; + +// Build a KeyDataPacket bitstream gradually. +class KeyDataPacketBuilder : public KeyDataPacket { + public: + // Build a KeyDataPacket by incrementally fill in data. + // + // The `character_data_size` is number of bytes to contain the character data. + KeyDataPacketBuilder(size_t character_data_size); + ~KeyDataPacketBuilder(); + + void SetKeyData(const KeyData& event); + + // Set character data to the proper position, which should not be terminated + // by a null character (length controled by character_data_size). + void SetCharacter(const char* characters); + + private: + size_t CharacterSizeStart_() { return 0; } + size_t KeyDataStart_() { return CharacterSizeStart_() + sizeof(uint64_t); } + size_t CharacterStart_() { return KeyDataStart_() + sizeof(KeyData); } + + FML_DISALLOW_COPY_AND_ASSIGN(KeyDataPacketBuilder); +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_WINDOW_POINTER_DATA_PACKET_H_ diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index 082df1b823d32..a6cbf7f35a40c 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -36,6 +36,22 @@ void Window::DispatchPointerDataPacket(const PointerDataPacket& packet) { library_.value(), "_dispatchPointerDataPacket", {data_handle})); } +void Window::DispatchKeyDataPacket(const KeyDataPacket& packet) { + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) + return; + tonic::DartState::Scope scope(dart_state); + + const std::vector& buffer = packet.data(); + Dart_Handle data_handle = + tonic::DartByteData::Create(buffer.data(), buffer.size()); + if (Dart_IsError(data_handle)) { + return; + } + tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_dispatchKeyData", + {data_handle})); +} + void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { viewport_metrics_ = metrics; diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index b6fa2555b04d7..d81e7e95aaa86 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -9,6 +9,7 @@ #include #include +#include "flutter/lib/ui/window/key_data_packet.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/viewport_metrics.h" @@ -27,6 +28,7 @@ class Window final { const ViewportMetrics& viewport_metrics() const { return viewport_metrics_; } void DispatchPointerDataPacket(const PointerDataPacket& packet); + void DispatchKeyDataPacket(const KeyDataPacket& packet); void UpdateWindowMetrics(const ViewportMetrics& metrics); private: diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 4998a9aeb3f50..b8a0327a393a9 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -243,6 +243,16 @@ bool RuntimeController::DispatchPointerDataPacket( return false; } +bool RuntimeController::DispatchKeyDataPacket(const KeyDataPacket& packet) { + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { + TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataPacket", "mode", + "basic"); + platform_configuration->get_window(0)->DispatchKeyDataPacket(packet); + return true; + } + return false; +} + bool RuntimeController::DispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) { diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 5e2c26da52726..6f42f440c4b38 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -408,6 +408,14 @@ class RuntimeController : public PlatformConfigurationClient { /// bool DispatchPointerDataPacket(const PointerDataPacket& packet); + //---------------------------------------------------------------------------- + /// @brief Dispatch the specified pointer data message to the running + /// root isolate. + /// + /// @param[in] packet The key data message to dispatch to the isolate. + /// + bool DispatchKeyDataPacket(const KeyDataPacket& packet); + //---------------------------------------------------------------------------- /// @brief Dispatch the semantics action to the specified accessibility /// node. diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 84de3e25bf92c..6b49eab9badcd 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -384,6 +384,13 @@ void Engine::DispatchPointerDataPacket( pointer_data_dispatcher_->DispatchPacket(std::move(packet), trace_flow_id); } +void Engine::DispatchKeyDataPacket(std::unique_ptr packet) { + TRACE_EVENT0("flutter", "Engine::DispatchKeyDataPacket"); + if (runtime_controller_) { + runtime_controller_->DispatchKeyDataPacket(*packet); + } +} + void Engine::DispatchSemanticsAction(int id, SemanticsAction action, std::vector args) { diff --git a/shell/common/engine.h b/shell/common/engine.h index 8041e14199add..cf2234ac6c07d 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -710,6 +710,17 @@ class Engine final : public RuntimeDelegate, void DispatchPointerDataPacket(std::unique_ptr packet, uint64_t trace_flow_id); + //---------------------------------------------------------------------------- + /// @brief Notifies the engine that the embedder has sent it a key data + /// packet. A key data packet contains one physical key event and + /// one or multiple logical key events. This call originates in + /// the platform view and the shell has forwarded the same to the + /// engine on the UI task runner here. + /// + /// @param[in] packet The key data packet. + /// + void DispatchKeyDataPacket(std::unique_ptr packet); + //---------------------------------------------------------------------------- /// @brief Notifies the engine that the embedder encountered an /// accessibility related action on the specified node. This call diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 22c2b395ba76e..168087be13a35 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -42,6 +42,11 @@ void PlatformView::DispatchPointerDataPacket( pointer_data_packet_converter_.Convert(std::move(packet))); } +void PlatformView::DispatchKeyDataPacket( + std::unique_ptr packet) { + delegate_.OnPlatformViewDispatchKeyDataPacket(std::move(packet)); +} + void PlatformView::DispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) { diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 92e670677c25a..d8beccd37d6b1 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -16,6 +16,7 @@ #include "flutter/fml/memory/weak_ptr.h" #include "flutter/lib/ui/semantics/custom_accessibility_action.h" #include "flutter/lib/ui/semantics/semantics_node.h" +#include "flutter/lib/ui/window/key_data_packet.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/pointer_data_packet_converter.h" @@ -125,6 +126,19 @@ class PlatformView { virtual void OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) = 0; + //-------------------------------------------------------------------------- + /// @brief Notifies the delegate that the platform view has encountered + /// a key event. This key event needs to be forwarded to + /// the running root isolate hosted by the engine on the UI + /// thread. + /// + /// @param[in] packet The key data packet containing one physical key + /// event + /// and multiple logical key events. + /// + virtual void OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet) = 0; + //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view has encountered /// an accessibility related action on the specified node. This @@ -575,6 +589,16 @@ class PlatformView { /// void DispatchPointerDataPacket(std::unique_ptr packet); + //---------------------------------------------------------------------------- + /// @brief Dispatches key events from the embedder to the framework. Each + /// key data packet contains one physical event and multiple + /// logical key events. Each call to this method wakes up the UI + /// thread. + /// + /// @param[in] packet The key data packet to dispatch to the framework. + /// + void DispatchKeyDataPacket(std::unique_ptr packet); + //-------------------------------------------------------------------------- /// @brief Used by the embedder to specify a texture that it wants the /// rasterizer to composite within the Flutter layer tree. All diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 98d8f43f2813c..db0938ed8ce0b 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -875,6 +875,21 @@ void Shell::OnPlatformViewDispatchPointerDataPacket( next_pointer_flow_id_++; } +// |PlatformView::Delegate| +void Shell::OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet) { + TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchKeyDataPacket"); + FML_DCHECK(is_setup_); + FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable( + [engine = weak_engine_, packet = std::move(packet)]() mutable { + if (engine) { + engine->DispatchKeyDataPacket(std::move(packet)); + } + })); +} + // |PlatformView::Delegate| void Shell::OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, diff --git a/shell/common/shell.h b/shell/common/shell.h index 21214f0013f34..480d8077daaa6 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -456,6 +456,10 @@ class Shell final : public PlatformView::Delegate, void OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) override; + // |PlatformView::Delegate| + void OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet) override; + // |PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction( int32_t id, diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 98621f2f0f75e..d32aad267d912 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -62,6 +62,9 @@ class MockPlatformViewDelegate : public PlatformView::Delegate { MOCK_METHOD1(OnPlatformViewDispatchPointerDataPacket, void(std::unique_ptr packet)); + MOCK_METHOD1(OnPlatformViewDispatchKeyDataPacket, + void(std::unique_ptr packet)); + MOCK_METHOD3(OnPlatformViewDispatchSemanticsAction, void(int32_t id, SemanticsAction action, diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index b2b9f26a53909..f580eb8e67fc8 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1492,6 +1492,66 @@ FlutterEngineResult FlutterEngineSendPointerEvent( "running Flutter application."); } +inline flutter::KeyChange ToKeyChange(FlutterKeyEventKind key_change) { + switch (key_change) { + case kFlutterKeyEventKindUp: + return flutter::KeyChange::kUp; + case kFlutterKeyEventKindDown: + return flutter::KeyChange::kDown; + case kFlutterKeyEventKindRepeat: + return flutter::KeyChange::kRepeat; + } + return flutter::KeyChange::kUp; +} + +// The number of bytes that should be able to fully store character data. +// +// This is an arbitrary number that is considered sufficient, used as an +// upperbound in strnlen. +// +// Many platforms assert the character to be less than 2 int16's, i.e. 4 bytes, +// therefore the character data is asserted to be less than double the amount, +// i.e. 8 bytes. +constexpr size_t kKeyEventCharacterMaxBytes = 8; + +FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) + engine, + const FlutterKeyEvent* event) { + if (engine == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); + } + + if (event == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid key event."); + } + + const char* character = SAFE_ACCESS(event, character, nullptr); + + size_t character_data_size = + character == nullptr ? 0 : strnlen(character, kKeyEventCharacterMaxBytes); + + auto packet = + std::make_unique(character_data_size); + + flutter::KeyData key_data; + key_data.Clear(); + key_data.timestamp = (uint64_t)SAFE_ACCESS(event, timestamp, 0); + key_data.change = ToKeyChange( + SAFE_ACCESS(event, kind, FlutterKeyEventKind::kFlutterKeyEventKindUp)); + key_data.physical = SAFE_ACCESS(event, physical, 0); + key_data.logical = SAFE_ACCESS(event, logical, 0); + key_data.synthesized = !!SAFE_ACCESS(event, synthesized, false); + packet->SetKeyData(key_data); + packet->SetCharacter(character); + + return reinterpret_cast(engine) + ->DispatchKeyDataPacket(std::move(packet)) + ? kSuccess + : LOG_EMBEDDER_ERROR(kInternalInconsistency, + "Could not dispatch the key event to the " + "running Flutter application."); +} + FlutterEngineResult FlutterEngineSendPlatformMessage( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessage* flutter_message) { @@ -2166,6 +2226,7 @@ FlutterEngineResult FlutterEngineGetProcAddresses( SET_PROC(RunInitialized, FlutterEngineRunInitialized); SET_PROC(SendWindowMetricsEvent, FlutterEngineSendWindowMetricsEvent); SET_PROC(SendPointerEvent, FlutterEngineSendPointerEvent); + SET_PROC(SendKeyEvent, FlutterEngineSendKeyEvent); SET_PROC(SendPlatformMessage, FlutterEngineSendPlatformMessage); SET_PROC(PlatformMessageCreateResponseHandle, FlutterPlatformMessageCreateResponseHandle); diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 71228696308cd..9f02e3cabebf3 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -611,6 +611,36 @@ typedef struct { int64_t buttons; } FlutterPointerEvent; +typedef enum { + kFlutterKeyEventKindUp = 1, + kFlutterKeyEventKindDown, + kFlutterKeyEventKindRepeat, +} FlutterKeyEventKind; + +typedef struct { + /// The size of this struct. Must be sizeof(FlutterKeyEvent). + size_t struct_size; + // Timestamp in microseconds. + double timestamp; + // The event kind. + FlutterKeyEventKind kind; + // The physical key of the event, distinguished by the HID code. + uint64_t physical; + // The logical key of the event, usually the effect of the key without + // modifier, but might include modifier if the information is not available. + // Can be 0 for empty. + uint64_t logical; + // Null-terminated character input from the event. Can be null. Not available + // to up events. + const char* character; + // Whether the event is synthesized by Flutter. + bool synthesized; +} FlutterKeyEvent; + +struct _FlutterPlatformKeyEventResponseHandle; +typedef struct _FlutterPlatformKeyEventResponseHandle + FlutterPlatformKeyEventResponseHandle; + struct _FlutterPlatformMessageResponseHandle; typedef struct _FlutterPlatformMessageResponseHandle FlutterPlatformMessageResponseHandle; @@ -1549,6 +1579,12 @@ FlutterEngineResult FlutterEngineSendPointerEvent( const FlutterPointerEvent* events, size_t events_count); +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineSendKeyEvent( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterKeyEvent* event, + FlutterPlatformKeyEventResponseHandle** response_out); + FLUTTER_EXPORT FlutterEngineResult FlutterEngineSendPlatformMessage( FLUTTER_API_SYMBOL(FlutterEngine) engine, @@ -2062,6 +2098,9 @@ typedef FlutterEngineResult (*FlutterEngineSendPointerEventFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPointerEvent* events, size_t events_count); +typedef FlutterEngineResult (*FlutterEngineSendKeyEventFnPtr)( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterKeyEvent* event); typedef FlutterEngineResult (*FlutterEngineSendPlatformMessageFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessage* message); @@ -2155,6 +2194,7 @@ typedef struct { FlutterEngineRunInitializedFnPtr RunInitialized; FlutterEngineSendWindowMetricsEventFnPtr SendWindowMetricsEvent; FlutterEngineSendPointerEventFnPtr SendPointerEvent; + FlutterEngineSendKeyEventFnPtr SendKeyEvent; FlutterEngineSendPlatformMessageFnPtr SendPlatformMessage; FlutterEnginePlatformMessageCreateResponseHandleFnPtr PlatformMessageCreateResponseHandle; diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index bb0aa08bb42d5..32685c7b00ca0 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -136,6 +136,21 @@ bool EmbedderEngine::DispatchPointerDataPacket( return true; } +bool EmbedderEngine::DispatchKeyDataPacket( + std::unique_ptr packet) { + if (!IsValid() || !packet) { + return false; + } + + auto platform_view = shell_->GetPlatformView(); + if (!platform_view) { + return false; + } + + platform_view->DispatchKeyDataPacket(std::move(packet)); + return true; +} + bool EmbedderEngine::SendPlatformMessage( fml::RefPtr message) { if (!IsValid() || !message) { diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index 4f38ba4410cd0..53a7b315c219a 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -60,6 +60,8 @@ class EmbedderEngine { bool DispatchPointerDataPacket( std::unique_ptr packet); + bool DispatchKeyDataPacket(std::unique_ptr packet); + bool SendPlatformMessage(fml::RefPtr message); bool RegisterTexture(int64_t texture); diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 7fabcbd11d1ba..76239b254a566 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -81,6 +81,9 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { void OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) {} // |flutter::PlatformView::Delegate| + void OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet) {} + // |flutter::PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction(int32_t id, flutter::SemanticsAction action, std::vector args) {} From aac6837fb42bc47eb40c38c24326cc373cbfa03c Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 17:55:43 -0800 Subject: [PATCH 03/34] Fix buildgn for embedder --- lib/ui/BUILD.gn | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index fa31f9fe5575e..18521e8818a97 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -93,6 +93,10 @@ source_set("ui") { "ui_dart_state.h", "volatile_path_tracker.cc", "volatile_path_tracker.h", + "window/key_data.cc", + "window/key_data.h", + "window/key_data_packet.cc", + "window/key_data_packet.h", "window/platform_configuration.cc", "window/platform_configuration.h", "window/platform_message.cc", From a2d0c78b35c9d5f860f53875db07bc59d3fe8ee7 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 18:06:37 -0800 Subject: [PATCH 04/34] Fix license --- lib/web_ui/lib/src/engine/key_map.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/engine/key_map.dart b/lib/web_ui/lib/src/engine/key_map.dart index d3970549c7ca2..03ef664c1dec0 100644 --- a/lib/web_ui/lib/src/engine/key_map.dart +++ b/lib/web_ui/lib/src/engine/key_map.dart @@ -1,4 +1,4 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. +// Copyright 2013 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. From 226cd56ea5790ebf2f6f79804eaa5439c1fb1b42 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 20:16:00 -0800 Subject: [PATCH 05/34] Fix license --- ci/licenses_golden/licenses_flutter | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index e868c7ca49eb8..ab9a23bfc46fe 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1179,6 +1179,10 @@ FILE: ../../../flutter/lib/ui/ui_dart_state.h FILE: ../../../flutter/lib/ui/volatile_path_tracker.cc FILE: ../../../flutter/lib/ui/volatile_path_tracker.h FILE: ../../../flutter/lib/ui/window.dart +FILE: ../../../flutter/lib/ui/window/key_data.cc +FILE: ../../../flutter/lib/ui/window/key_data.h +FILE: ../../../flutter/lib/ui/window/key_data_packet.cc +FILE: ../../../flutter/lib/ui/window/key_data_packet.h FILE: ../../../flutter/lib/ui/window/platform_configuration.cc FILE: ../../../flutter/lib/ui/window/platform_configuration.h FILE: ../../../flutter/lib/ui/window/platform_configuration_unittests.cc @@ -1271,7 +1275,9 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/surface.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/surface_stats.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/transform.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html_image_codec.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/key_map.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/keyboard.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/keyboard_binding.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/mouse_cursor.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/navigation/history.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/navigation/js_url_strategy.dart From 38280559ca4a606b56924de624edfce27ef1a445 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 23:46:00 -0800 Subject: [PATCH 06/34] Fix VoidCallback --- lib/web_ui/lib/src/engine/keyboard_binding.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart index c0f734b10bebd..13630a65837cd 100644 --- a/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -5,7 +5,7 @@ // @dart = 2.12 part of engine; -typedef VoidCallback = void Function(); +typedef _VoidCallback = void Function(); typedef ValueGetter = T Function(); typedef _ModifierGetter = bool Function(FlutterHtmlKeyboardEvent event); @@ -149,7 +149,7 @@ class AsyncKeyboardDispatching { }); final ui.KeyData keyData; - final VoidCallback? callback; + final _VoidCallback? callback; } // A wrapper of [html.KeyboardEvent] with reduced methods delegated to the event @@ -275,7 +275,7 @@ class KeyboardConverter { // // Returns a callback that cancels the schedule. Disposal of // `KeyBoardConverter` also cancels the shedule automatically. - VoidCallback _scheduleAsyncEvent(Duration duration, ValueGetter getData, VoidCallback callback) { + _VoidCallback _scheduleAsyncEvent(Duration duration, ValueGetter getData, _VoidCallback callback) { bool canceled = false; Future.delayed(duration).then((_) { if (!canceled && !_disposed) { @@ -296,10 +296,10 @@ class KeyboardConverter { // while the key is held down by the user. If we don't receive a repeat // event within a specific duration ([_keydownCancelDuration]) we assume // the user has released the key and we synthesize a keyup event. - final Map _keyGuards = {}; + final Map _keyGuards = {}; // Call this method on the down or repeated event of a non-modifier key. void _startGuardingKey(int physicalKey, int logicalKey, Duration currentTimeStamp) { - final VoidCallback cancelingCallback = _scheduleAsyncEvent( + final _VoidCallback cancelingCallback = _scheduleAsyncEvent( _keydownCancelDuration, () => ui.KeyData( timeStamp: currentTimeStamp + _keydownCancelDuration, From 99cd26c70aeb30da68bbf70dcbe2e48b8a575282 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 7 Jan 2021 15:30:29 -0800 Subject: [PATCH 07/34] Fix tests --- lib/web_ui/lib/src/engine/keyboard_binding.dart | 10 +++++----- lib/web_ui/test/keyboard_converter_test.dart | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart index 13630a65837cd..4107eed9978e0 100644 --- a/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -41,10 +41,11 @@ final Map _kLogicalKeyToModifierGetter = { const Duration _kKeydownCancelDurationNormal = Duration(milliseconds: 1000); const Duration _kKeydownCancelDurationMacOs = Duration(milliseconds: 2000); -late final int _kCharLowerA = 'a'.codeUnitAt(0); -late final int _kCharLowerZ = 'z'.codeUnitAt(0); -late final int _kCharUpperA = 'A'.codeUnitAt(0); -late final int _kCharUpperZ = 'Z'.codeUnitAt(0); +// ASCII for a, z, A, and Z +const int _kCharLowerA = 0x61; +const int _kCharLowerZ = 0x7a; +const int _kCharUpperA = 0x41; +const int _kCharUpperZ = 0x5a; bool isAlphabet(int charCode) { return (charCode >= _kCharLowerA && charCode <= _kCharLowerZ) || (charCode >= _kCharUpperA && charCode <= _kCharUpperZ); @@ -445,7 +446,6 @@ class KeyboardConverter { // events can not be deduced since we don't know which physical key they // represent. _kLogicalKeyToModifierGetter.forEach((int logicalKey, _ModifierGetter getModifier) { - // print(_pressingRecords); if (_pressingRecords.containsValue(logicalKey) && !getModifier(event)) { _pressingRecords.removeWhere((int physicalKey, int logicalRecord) { if (logicalRecord != logicalKey) diff --git a/lib/web_ui/test/keyboard_converter_test.dart b/lib/web_ui/test/keyboard_converter_test.dart index eb5bcf5563dfd..3b0003a1edbc8 100644 --- a/lib/web_ui/test/keyboard_converter_test.dart +++ b/lib/web_ui/test/keyboard_converter_test.dart @@ -27,8 +27,8 @@ const int kPhysicalTab = 0x0007002b; const int kPhysicalCapsLock = 0x00070039; const int kPhysicalScrollLock = 0x00070047; -const int kLogicalKeyA = 0x00000000041; -const int kLogicalKeyU = 0x00000000055; +const int kLogicalKeyA = 0x00000000061; +const int kLogicalKeyU = 0x00000000075; const int kLogicalDigit1 = 0x00000000031; const int kLogicalNumpad1 = 0x00200000031; const int kLogicalShiftLeft = 0x030000010d; From 09ccc39b36d97927ad5842cd7c6474f07e247f27 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 7 Jan 2021 17:05:16 -0800 Subject: [PATCH 08/34] Fix compile --- testing/dart/window_hooks_integration_test.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/testing/dart/window_hooks_integration_test.dart b/testing/dart/window_hooks_integration_test.dart index 91c2036dbda91..3a1aac710f9f9 100644 --- a/testing/dart/window_hooks_integration_test.dart +++ b/testing/dart/window_hooks_integration_test.dart @@ -24,6 +24,7 @@ part '../../lib/ui/compositing.dart'; part '../../lib/ui/geometry.dart'; part '../../lib/ui/hash_codes.dart'; part '../../lib/ui/hooks.dart'; +part '../../lib/ui/key.dart'; part '../../lib/ui/lerp.dart'; part '../../lib/ui/natives.dart'; part '../../lib/ui/painting.dart'; From c10ca64fe968511b89ac4127c6e95c20815f696f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 7 Jan 2021 17:56:32 -0800 Subject: [PATCH 09/34] Fix compile: implement virtual methods --- .../darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm | 1 + .../darwin/ios/framework/Source/FlutterPlatformViewsTest.mm | 1 + .../darwin/ios/framework/Source/accessibility_bridge_test.mm | 1 + 3 files changed, 3 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm index c81910f892767..129cce47c8199 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm @@ -25,6 +25,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 8f4b3e7a972c4..631f98c30c887 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -95,6 +95,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index 08f01b7c705ed..a446224495925 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -78,6 +78,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} From 7a3dd539101c2834eefe77c2d2f1b944cd711d85 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 8 Jan 2021 02:16:36 -0800 Subject: [PATCH 10/34] Better toString --- lib/ui/key.dart | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/ui/key.dart b/lib/ui/key.dart index e60bfbeb027f1..35cfd76501153 100644 --- a/lib/ui/key.dart +++ b/lib/ui/key.dart @@ -57,12 +57,29 @@ class KeyData { final bool synthesized; @override - String toString() => 'KeyData(timeStamp: $timeStamp, change: $change, physical: $physical, ' - 'logical: $logical, character: $character, synthesized: $synthesized)'; + String toString() => 'KeyData(change: ${_changeToString(change)}, physical: 0x${physical.toRadixString(16)}, ' + 'logical: 0x${logical.toRadixString(16)}, character: $character)'; /// Returns a complete textual description of the information in this object. String toStringFull() { return '$runtimeType(' + 'change: ${_changeToString(change)}, ' + 'timeStamp: $timeStamp, ' + 'physical: 0x${physical.toRadixString(16)}, ' + 'logical: 0x${logical.toRadixString(16)}, ' + 'character: $character, ' + 'synthesized: $synthesized' ')'; } + + static String _changeToString(KeyChange change) { + switch (change) { + case KeyChange.up: + return 'up'; + case KeyChange.down: + return 'down'; + case KeyChange.repeat: + return 'repeat'; + } + } } From 736d4f6f1cda02e23cab4d35cb360611c2a6ccc2 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 19 Jan 2021 14:45:13 -0800 Subject: [PATCH 11/34] Fix doc. Add async callback --- ci/licenses_golden/licenses_flutter | 4 +- lib/ui/BUILD.gn | 4 +- lib/ui/hooks.dart | 4 +- lib/ui/key.dart | 35 +++++++-- lib/ui/platform_dispatcher.dart | 28 ++++--- lib/ui/window/key_data.h | 6 +- lib/ui/window/key_data_message.cc | 29 +++++++ .../{key_data_packet.h => key_data_message.h} | 48 ++++-------- lib/ui/window/key_data_packet.cc | 36 --------- lib/ui/window/platform_configuration.cc | 32 ++++++++ lib/ui/window/platform_configuration.h | 26 +++++++ lib/ui/window/window.cc | 6 +- lib/ui/window/window.h | 5 +- .../lib/src/engine/keyboard_binding.dart | 4 +- .../lib/src/engine/platform_dispatcher.dart | 9 ++- lib/web_ui/lib/src/ui/key.dart | 52 +++++++++++-- .../lib/src/ui/platform_dispatcher.dart | 2 +- runtime/runtime_controller.cc | 9 ++- runtime/runtime_controller.h | 3 +- shell/common/engine.cc | 8 +- shell/common/engine.h | 3 +- shell/common/platform_view.cc | 7 +- shell/common/platform_view.h | 10 ++- shell/common/shell.cc | 13 ++-- shell/common/shell.h | 5 +- shell/common/shell_unittests.cc | 5 +- .../Source/FlutterEnginePlatformViewTest.mm | 2 +- .../Source/FlutterPlatformViewsTest.mm | 2 +- .../Source/accessibility_bridge_test.mm | 2 +- shell/platform/embedder/embedder.cc | 25 +++--- shell/platform/embedder/embedder.h | 78 +++++++++++++++---- shell/platform/embedder/embedder_engine.cc | 9 ++- shell/platform/embedder/embedder_engine.h | 3 +- .../fuchsia/flutter/platform_view_unittest.cc | 4 +- 34 files changed, 352 insertions(+), 166 deletions(-) create mode 100644 lib/ui/window/key_data_message.cc rename lib/ui/window/{key_data_packet.h => key_data_message.h} (54%) delete mode 100644 lib/ui/window/key_data_packet.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index f26e78aa510a8..6562b0cc6bdda 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -412,8 +412,8 @@ FILE: ../../../flutter/lib/ui/volatile_path_tracker.h FILE: ../../../flutter/lib/ui/window.dart FILE: ../../../flutter/lib/ui/window/key_data.cc FILE: ../../../flutter/lib/ui/window/key_data.h -FILE: ../../../flutter/lib/ui/window/key_data_packet.cc -FILE: ../../../flutter/lib/ui/window/key_data_packet.h +FILE: ../../../flutter/lib/ui/window/key_data_message.cc +FILE: ../../../flutter/lib/ui/window/key_data_message.h FILE: ../../../flutter/lib/ui/window/platform_configuration.cc FILE: ../../../flutter/lib/ui/window/platform_configuration.h FILE: ../../../flutter/lib/ui/window/platform_configuration_unittests.cc diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index 18521e8818a97..4156d7491a395 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -95,8 +95,8 @@ source_set("ui") { "volatile_path_tracker.h", "window/key_data.cc", "window/key_data.h", - "window/key_data_packet.cc", - "window/key_data_packet.h", + "window/key_data_message.cc", + "window/key_data_message.h", "window/platform_configuration.cc", "window/platform_configuration.h", "window/platform_message.cc", diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index 8d6770471ad71..cd1746f401d74 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -98,8 +98,8 @@ void _dispatchPointerDataPacket(ByteData packet) { @pragma('vm:entry-point') // ignore: unused_element -void _dispatchKeyData(ByteData packet) { - PlatformDispatcher.instance._dispatchKeyData(packet); +void _dispatchKeyData(ByteData packet, int responseId) { + PlatformDispatcher.instance._dispatchKeyData(packet, responseId); } @pragma('vm:entry-point') diff --git a/lib/ui/key.dart b/lib/ui/key.dart index 35cfd76501153..ba714c008d7fc 100644 --- a/lib/ui/key.dart +++ b/lib/ui/key.dart @@ -7,7 +7,6 @@ part of dart.ui; /// How the key has changed since the last report. -/// // Must match the KeyChange enum in ui/window/key_data.h. enum KeyChange { /// The key is pressed. @@ -34,8 +33,8 @@ class KeyData { /// Time of event dispatch, relative to an arbitrary timeline. /// - /// For [KeyChange.synchronize] and [KeyChange.cancel] events, the [timeStamp] - /// might not be the actual time that the key press or release happens. + /// For synthesized events, the [timeStamp] might not be the actual time that + /// the key press or release happens. final Duration timeStamp; /// How the key has changed since the last report. @@ -49,11 +48,35 @@ class KeyData { /// Character input from the event. /// - /// Not available to up events. + /// Ignored for up events. final String? character; - /// Whether the event is generated by Flutter, hence not corresponding to a - /// native event. + /// If [synthesized] is true, this event does not correspond to a native event. + /// + /// Although most of Flutter's keyboard events are transformed from native + /// events, some events are not based on native events, and are synthesized + /// only to conform Flutter's key event model (as documented in + /// the `HardwareKeyboard` class in the framework). + /// + /// For example, some key downs or ups might be lost when the window loses + /// focus. Some platforms provides ways to query whether a key is being held. + /// If Flutter detects an inconsistancy between the state Flutter records and + /// the state returned by the system, Flutter will synthesize a corresponding + /// event to synchronize the state without breaking the event model. + /// + /// As another example, macOS treats CapsLock in a special way by sending + /// down and up events at the down of alterate presses to indicate the + /// direction in which the lock is toggled instead of that the physical key is + /// going. Flutter normalizes the behavior by converting a native down event + /// into a down event followed immediately by a synthesized up event, and + /// the native up event also into a down event followed immediately by a + /// synthesized up event. + /// + /// Synthesized events do not have a trustworthy [timeStamp], and should not be + /// processed as if the key actually went down or up at the time of the + /// callback. + /// + /// [KeyRepeatEvent] is never synthesized. final bool synthesized; @override diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index c93fe976cc518..73faab867736e 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -28,8 +28,11 @@ typedef TimingsCallback = void Function(List timings); /// Signature for [PlatformDispatcher.onPointerDataPacket]. typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); +// Signature for the response to KeyDataCallback. +typedef _KeyDataResponseCallback = void Function(int responseId, bool handled); + /// Signature for [PlatformDispatcher.onKeyData]. -typedef KeyDataCallback = void Function(KeyData data); +typedef KeyDataCallback = bool Function(KeyData data); /// Signature for [PlatformDispatcher.onSemanticsAction]. typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args); @@ -335,6 +338,10 @@ class PlatformDispatcher { return PointerDataPacket(data: data); } + /// Called by [_dispatchKeyData]. + void _respondToKeyData(int responseId, bool handled) + native 'PlatformConfiguration_respondToKeyData'; + /// A callback that is invoked when key data is available. /// /// The framework invokes this callback in the same zone in which the callback @@ -348,15 +355,15 @@ class PlatformDispatcher { } // Called from the engine, via hooks.dart - void _dispatchKeyData(ByteData packet) { - print('dispatch: onKeyData $onKeyData'); - if (onKeyData != null) { - _invoke1( - onKeyData, - _onKeyDataZone, - _unpackKeyData(packet), - ); - } + void _dispatchKeyData(ByteData packet, int responseId) { + _invoke2( + (KeyData data, _KeyDataResponseCallback callback) { + callback(responseId, onKeyData == null ? false : onKeyData!(data)); + }, + _onKeyDataZone, + _unpackKeyData(packet), + _respondToKeyData, + ); } // If this value changes, update the encoding code in the following files: @@ -368,6 +375,7 @@ class PlatformDispatcher { static const int _kKeyDataFieldCount = 5; // KeyData packet structure: + // | ResponseId | (1 field) // | CharDataSize | (1 field) // | Key Data | (_kKeyDataFieldCount fields) // | CharData | (CharDataSize bits) diff --git a/lib/ui/window/key_data.h b/lib/ui/window/key_data.h index d41c162f47f8a..783e9e6092a32 100644 --- a/lib/ui/window/key_data.h +++ b/lib/ui/window/key_data.h @@ -22,10 +22,10 @@ enum class KeyChange : int64_t { kRepeat, }; -// The fixed-length sections of a KeyDataPacket. +// The fixed-length sections of a KeyDataMessage. // -// KeyData does not contain `character`, for various-length data are stored in a -// different way in KeyDataPacket. +// KeyData does not contain `character`, for variable-length data are stored in a +// different way in KeyDataMessage. // // This structure is unpacked by hooks.dart. struct alignas(8) KeyData { diff --git a/lib/ui/window/key_data_message.cc b/lib/ui/window/key_data_message.cc new file mode 100644 index 0000000000000..cdcf6f25d9255 --- /dev/null +++ b/lib/ui/window/key_data_message.cc @@ -0,0 +1,29 @@ +// Copyright 2013 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 "flutter/lib/ui/window/key_data_message.h" +#include "flutter/fml/logging.h" + +namespace flutter { + +KeyDataMessage::KeyDataMessage(size_t character_data_size) + : data_(sizeof(uint64_t) + sizeof(KeyData) + character_data_size) { + uint64_t size64 = character_data_size; + memcpy(&data()[CharacterSizeStart_()], &size64, sizeof(size64)); +} + +KeyDataMessage::~KeyDataMessage() = default; + +void KeyDataMessage::SetKeyData(const KeyData& event) { + memcpy(&data()[KeyDataStart_()], &event, sizeof(KeyData)); +} + +void KeyDataMessage::SetCharacter(const char* character) { + if (character != nullptr) { + memcpy(data().data() + CharacterStart_(), character, + data().size() - CharacterStart_()); + } +} + +} // namespace flutter diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_message.h similarity index 54% rename from lib/ui/window/key_data_packet.h rename to lib/ui/window/key_data_message.h index fa48a60f7b676..ba77786505cac 100644 --- a/lib/ui/window/key_data_packet.h +++ b/lib/ui/window/key_data_message.h @@ -2,49 +2,29 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_LIB_UI_WINDOW_KEY_DATA_PACKET_H_ -#define FLUTTER_LIB_UI_WINDOW_KEY_DATA_PACKET_H_ +#ifndef FLUTTER_LIB_UI_WINDOW_KEY_DATA_MESSAGE_H_ +#define FLUTTER_LIB_UI_WINDOW_KEY_DATA_MESSAGE_H_ #include #include +#include #include "flutter/fml/macros.h" #include "flutter/lib/ui/window/key_data.h" namespace flutter { -// Bitstream that contains a KeyData. -// -// This class provides interface to read the opaque bits. For constructing such -// a bitstream from KeyData, checkout KeyDataPacketBuilder. -class KeyDataPacket { - public: - KeyDataPacket(uint8_t* data, size_t num_bytes); - - protected: - KeyDataPacket(size_t num_bytes); - - std::vector& data() { return data_; } +typedef std::function KeyDataMessageCallback; +class KeyDataMessage { public: - ~KeyDataPacket(); - - const std::vector& data() const { return data_; } - - private: - std::vector data_; - - FML_DISALLOW_COPY_AND_ASSIGN(KeyDataPacket); -}; - -// Build a KeyDataPacket bitstream gradually. -class KeyDataPacketBuilder : public KeyDataPacket { - public: - // Build a KeyDataPacket by incrementally fill in data. + // Build a KeyDataMessage by incrementally fill in data. // // The `character_data_size` is number of bytes to contain the character data. - KeyDataPacketBuilder(size_t character_data_size); - ~KeyDataPacketBuilder(); + KeyDataMessage(size_t character_data_size); + ~KeyDataMessage(); + + const std::vector& data() const { return data_; } void SetKeyData(const KeyData& event); @@ -53,13 +33,17 @@ class KeyDataPacketBuilder : public KeyDataPacket { void SetCharacter(const char* characters); private: + std::vector& data() { return data_; } + size_t CharacterSizeStart_() { return 0; } size_t KeyDataStart_() { return CharacterSizeStart_() + sizeof(uint64_t); } size_t CharacterStart_() { return KeyDataStart_() + sizeof(KeyData); } - FML_DISALLOW_COPY_AND_ASSIGN(KeyDataPacketBuilder); + std::vector data_; + + FML_DISALLOW_COPY_AND_ASSIGN(KeyDataMessage); }; } // namespace flutter -#endif // FLUTTER_LIB_UI_WINDOW_POINTER_DATA_PACKET_H_ +#endif // FLUTTER_LIB_UI_WINDOW_POINTER_DATA_MESSAGE_H_ diff --git a/lib/ui/window/key_data_packet.cc b/lib/ui/window/key_data_packet.cc deleted file mode 100644 index 0a22b04f13d9d..0000000000000 --- a/lib/ui/window/key_data_packet.cc +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2013 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 "flutter/lib/ui/window/key_data_packet.h" -#include "flutter/fml/logging.h" - -namespace flutter { - -KeyDataPacket::KeyDataPacket(uint8_t* data, size_t num_bytes) - : data_(data, data + num_bytes) {} - -KeyDataPacket::KeyDataPacket(size_t num_bytes) : data_(num_bytes) {} - -KeyDataPacket::~KeyDataPacket() = default; - -KeyDataPacketBuilder::KeyDataPacketBuilder(size_t character_data_size) - : KeyDataPacket(sizeof(uint64_t) + sizeof(KeyData) + character_data_size) { - uint64_t size64 = character_data_size; - memcpy(&data()[CharacterSizeStart_()], &size64, sizeof(size64)); -} - -KeyDataPacketBuilder::~KeyDataPacketBuilder() = default; - -void KeyDataPacketBuilder::SetKeyData(const KeyData& event) { - memcpy(&data()[KeyDataStart_()], &event, sizeof(KeyData)); -} - -void KeyDataPacketBuilder::SetCharacter(const char* character) { - if (character != nullptr) { - memcpy(data().data() + CharacterStart_(), character, - data().size() - CharacterStart_()); - } -} - -} // namespace flutter diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index ccf9cc877001f..230ed49826062 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -181,6 +181,16 @@ void GetPersistentIsolateData(Dart_NativeArguments args) { persistent_isolate_data->GetSize())); } +void RespondToKeyData(Dart_Handle window, int response_id, bool handled) { + UIDartState::Current() + ->platform_configuration() + ->CompleteKeyDataResponse(response_id, handled); +} + +void _RespondToKeyData(Dart_NativeArguments args) { + tonic::DartCallStatic(&RespondToKeyData, args); +} + Dart_Handle ToByteData(const std::vector& buffer) { return tonic::DartByteData::Create(buffer.data(), buffer.size()); } @@ -349,6 +359,14 @@ void PlatformConfiguration::DispatchSemanticsAction(int32_t id, args_handle})); } +uint64_t PlatformConfiguration::RegisterKeyDataResponse( + KeyDataMessageCallback callback) { + uint64_t response_id = next_key_response_id_++; + pending_key_responses_[response_id] = std::move(callback); + return response_id; +} + + void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime) { std::shared_ptr dart_state = begin_frame_.dart_state().lock(); @@ -424,6 +442,19 @@ void PlatformConfiguration::CompletePlatformMessageResponse( response->Complete(std::make_unique(std::move(data))); } +void PlatformConfiguration::CompleteKeyDataResponse(uint64_t response_id, bool handled) { + if (!response_id) { + return; + } + auto it = pending_key_responses_.find(response_id); + if (it == pending_key_responses_.end()) { + return; + } + auto callback = std::move(it->second); + pending_key_responses_.erase(it); + callback(handled); +} + Dart_Handle ComputePlatformResolvedLocale(Dart_Handle supportedLocalesHandle) { std::vector supportedLocales = tonic::DartConverter>::FromDart( @@ -454,6 +485,7 @@ void PlatformConfiguration::RegisterNatives( true}, {"PlatformConfiguration_respondToPlatformMessage", _RespondToPlatformMessage, 3, true}, + {"PlatformConfiguration_respondToKeyData", _RespondToKeyData, 2, true}, {"PlatformConfiguration_render", Render, 3, true}, {"PlatformConfiguration_updateSemantics", UpdateSemantics, 2, true}, {"PlatformConfiguration_setIsolateDebugName", SetIsolateDebugName, 2, diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index f61d47494ec1c..458c38134345c 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -323,6 +323,16 @@ class PlatformConfiguration final { SemanticsAction action, std::vector args); + //---------------------------------------------------------------------------- + /// @brief Notifies the PlatformConfiguration that the client has sent + /// it a message. This call originates in the platform view and + /// has been forwarded through the engine to here. + /// + /// @param[in] message The message sent from the embedder to the Dart + /// application. + /// + uint64_t RegisterKeyDataResponse(KeyDataMessageCallback callback); + //---------------------------------------------------------------------------- /// @brief Notifies the framework that it is time to begin working on a /// new frame previously scheduled via a call to @@ -411,6 +421,16 @@ class PlatformConfiguration final { /// void CompletePlatformMessageEmptyResponse(int response_id); + //---------------------------------------------------------------------------- + /// @brief Responds to a previous key data message to the engine from the + /// framework. + /// + /// @param[in] response_id The unique id that identifies the original platform + /// message to respond to. + /// @param[in] handled Whether the key is handled. + /// + void CompleteKeyDataResponse(uint64_t response_id, bool handled); + private: PlatformConfigurationClient* client_; tonic::DartPersistentValue update_locales_; @@ -419,6 +439,7 @@ class PlatformConfiguration final { tonic::DartPersistentValue update_semantics_enabled_; tonic::DartPersistentValue update_accessibility_features_; tonic::DartPersistentValue dispatch_platform_message_; + tonic::DartPersistentValue dispatch_key_message_; tonic::DartPersistentValue dispatch_semantics_action_; tonic::DartPersistentValue begin_frame_; tonic::DartPersistentValue draw_frame_; @@ -430,6 +451,11 @@ class PlatformConfiguration final { int next_response_id_ = 1; std::unordered_map> pending_responses_; + + // We use id 0 to mean that no response is expected. + uint64_t next_key_response_id_ = 1; + std::unordered_map + pending_key_responses_; }; } // namespace flutter diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index a6cbf7f35a40c..52ae9c0ed3a7f 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -36,20 +36,20 @@ void Window::DispatchPointerDataPacket(const PointerDataPacket& packet) { library_.value(), "_dispatchPointerDataPacket", {data_handle})); } -void Window::DispatchKeyDataPacket(const KeyDataPacket& packet) { +void Window::DispatchKeyDataMessage(const KeyDataMessage& message, uint64_t response_id) { std::shared_ptr dart_state = library_.dart_state().lock(); if (!dart_state) return; tonic::DartState::Scope scope(dart_state); - const std::vector& buffer = packet.data(); + const std::vector buffer(message.data().size()); Dart_Handle data_handle = tonic::DartByteData::Create(buffer.data(), buffer.size()); if (Dart_IsError(data_handle)) { return; } tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_dispatchKeyData", - {data_handle})); + {data_handle, tonic::ToDart(response_id)})); } void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index d81e7e95aaa86..0573a8a579162 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -8,8 +8,9 @@ #include #include #include +#include -#include "flutter/lib/ui/window/key_data_packet.h" +#include "flutter/lib/ui/window/key_data_message.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/viewport_metrics.h" @@ -28,7 +29,7 @@ class Window final { const ViewportMetrics& viewport_metrics() const { return viewport_metrics_; } void DispatchPointerDataPacket(const PointerDataPacket& packet); - void DispatchKeyDataPacket(const KeyDataPacket& packet); + void DispatchKeyDataMessage(const KeyDataMessage& message, uint64_t response_id); void UpdateWindowMetrics(const ViewportMetrics& metrics); private: diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart index 4107eed9978e0..ef35962121b7c 100644 --- a/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -138,8 +138,6 @@ class KeyboardBinding { void _reset() { _clearListeners(); _converter.dispose(); - - _setup(); } } @@ -419,7 +417,7 @@ class KeyboardConverter { change = ui.KeyChange.up; } - late final int? nextLogicalRecord; + final int? nextLogicalRecord; switch (change) { case ui.KeyChange.down: assert(lastLogicalRecord == null); diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index 83ebe95725adf..c719e8fbbc18a 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -10,6 +10,8 @@ part of engine; /// This may be overridden in tests, for example, to pump fake frames. ui.VoidCallback? scheduleFrameCallback; +typedef _KeyDataResponseCallback = void Function(bool handled); + /// Platform event dispatcher. /// /// This is the central entry point for platform messages and configuration @@ -191,8 +193,11 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// Engine code should use this method instead of the callback directly. /// Otherwise zones won't work properly. - void invokeOnKeyData(ui.KeyData dataPacket) { - invoke1(_onKeyData, _onKeyDataZone, dataPacket); + void invokeOnKeyData(ui.KeyData data, _KeyDataResponseCallback callback) { + invoke( + () { callback(onKeyData == null ? false : onKeyData!(data)); }, + _onKeyDataZone, + ); } /// A callback that is invoked to report the [FrameTiming] of recently diff --git a/lib/web_ui/lib/src/ui/key.dart b/lib/web_ui/lib/src/ui/key.dart index ffb8d4e81064b..2e44a316552a8 100644 --- a/lib/web_ui/lib/src/ui/key.dart +++ b/lib/web_ui/lib/src/ui/key.dart @@ -7,6 +7,7 @@ part of ui; /// How the key has changed since the last report. +// Must match the KeyChange enum in ui/window/key_data.h. enum KeyChange { /// The key is pressed. down, @@ -47,20 +48,61 @@ class KeyData { /// Character input from the event. /// - /// Not available to up events. + /// Ignored for up events. final String? character; - /// Whether the event is generated by Flutter, hence not corresponding to a - /// native event. + /// If [synthesized] is true, this event does not correspond to a native event. + /// + /// Although most of Flutter's keyboard events are transformed from native + /// events, some events are not based on native events, and are synthesized + /// only to conform Flutter's key event model (as documented in + /// the `HardwareKeyboard` class in the framework). + /// + /// For example, some key downs or ups might be lost when the window loses + /// focus. Some platforms provides ways to query whether a key is being held. + /// If Flutter detects an inconsistancy between the state Flutter records and + /// the state returned by the system, Flutter will synthesize a corresponding + /// event to synchronize the state without breaking the event model. + /// + /// As another example, macOS treats CapsLock in a special way by sending + /// down and up events at the down of alterate presses to indicate the + /// direction in which the lock is toggled instead of that the physical key is + /// going. Flutter normalizes the behavior by converting a native down event + /// into a down event followed immediately by a synthesized up event, and + /// the native up event also into a down event followed immediately by a + /// synthesized up event. + /// + /// Synthesized events do not have a trustworthy [timeStamp], and should not be + /// processed as if the key actually went down or up at the time of the + /// callback. + /// + /// [KeyRepeatEvent] is never synthesized. final bool synthesized; @override - String toString() => 'KeyData(timeStamp: $timeStamp, change: $change, physical: $physical, ' - 'logical: $logical, character: $character, synthesized: $synthesized)'; + String toString() => 'KeyData(change: ${_changeToString(change)}, physical: 0x${physical.toRadixString(16)}, ' + 'logical: 0x${logical.toRadixString(16)}, character: $character)'; /// Returns a complete textual description of the information in this object. String toStringFull() { return '$runtimeType(' + 'change: ${_changeToString(change)}, ' + 'timeStamp: $timeStamp, ' + 'physical: 0x${physical.toRadixString(16)}, ' + 'logical: 0x${logical.toRadixString(16)}, ' + 'character: $character, ' + 'synthesized: $synthesized' ')'; } + + static String _changeToString(KeyChange change) { + switch (change) { + case KeyChange.up: + return 'up'; + case KeyChange.down: + return 'down'; + case KeyChange.repeat: + return 'repeat'; + } + } } diff --git a/lib/web_ui/lib/src/ui/platform_dispatcher.dart b/lib/web_ui/lib/src/ui/platform_dispatcher.dart index 96aec518b4f0b..87ee101c4d242 100644 --- a/lib/web_ui/lib/src/ui/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/ui/platform_dispatcher.dart @@ -9,7 +9,7 @@ typedef VoidCallback = void Function(); typedef FrameCallback = void Function(Duration duration); typedef TimingsCallback = void Function(List timings); typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); -typedef KeyDataCallback = void Function(KeyData packet); +typedef KeyDataCallback = bool Function(KeyData packet); typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args); typedef PlatformMessageResponseCallback = void Function(ByteData? data); typedef PlatformMessageCallback = void Function( diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index b8a0327a393a9..049dc40847673 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -243,11 +243,14 @@ bool RuntimeController::DispatchPointerDataPacket( return false; } -bool RuntimeController::DispatchKeyDataPacket(const KeyDataPacket& packet) { +bool RuntimeController::DispatchKeyDataMessage( + const KeyDataMessage& message, + KeyDataMessageCallback callback) { if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { - TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataPacket", "mode", + TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataMessage", "mode", "basic"); - platform_configuration->get_window(0)->DispatchKeyDataPacket(packet); + uint64_t response_id = platform_configuration->RegisterKeyDataResponse(std::move(callback)); + platform_configuration->get_window(0)->DispatchKeyDataMessage(message, response_id); return true; } return false; diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 6f42f440c4b38..9a1a61fd51b43 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -414,7 +414,8 @@ class RuntimeController : public PlatformConfigurationClient { /// /// @param[in] packet The key data message to dispatch to the isolate. /// - bool DispatchKeyDataPacket(const KeyDataPacket& packet); + bool DispatchKeyDataMessage(const KeyDataMessage& message, + KeyDataMessageCallback callback); //---------------------------------------------------------------------------- /// @brief Dispatch the semantics action to the specified accessibility diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 6b49eab9badcd..3a6155bfd5c4a 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -384,10 +384,12 @@ void Engine::DispatchPointerDataPacket( pointer_data_dispatcher_->DispatchPacket(std::move(packet), trace_flow_id); } -void Engine::DispatchKeyDataPacket(std::unique_ptr packet) { - TRACE_EVENT0("flutter", "Engine::DispatchKeyDataPacket"); +void Engine::DispatchKeyDataMessage( + std::unique_ptr message, + KeyDataMessageCallback callback) { + TRACE_EVENT0("flutter", "Engine::DispatchKeyDataMessage"); if (runtime_controller_) { - runtime_controller_->DispatchKeyDataPacket(*packet); + runtime_controller_->DispatchKeyDataMessage(*message, std::move(callback)); } } diff --git a/shell/common/engine.h b/shell/common/engine.h index cf2234ac6c07d..5be4b5c8d995b 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -719,7 +719,8 @@ class Engine final : public RuntimeDelegate, /// /// @param[in] packet The key data packet. /// - void DispatchKeyDataPacket(std::unique_ptr packet); + void DispatchKeyDataMessage(std::unique_ptr message, + KeyDataMessageCallback callback); //---------------------------------------------------------------------------- /// @brief Notifies the engine that the embedder encountered an diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 168087be13a35..2d8801daef0b6 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -42,9 +42,10 @@ void PlatformView::DispatchPointerDataPacket( pointer_data_packet_converter_.Convert(std::move(packet))); } -void PlatformView::DispatchKeyDataPacket( - std::unique_ptr packet) { - delegate_.OnPlatformViewDispatchKeyDataPacket(std::move(packet)); +void PlatformView::DispatchKeyDataMessage( + std::unique_ptr message, + KeyDataMessageCallback callback) { + delegate_.OnPlatformViewDispatchKeyDataMessage(std::move(message), std::move(callback)); } void PlatformView::DispatchSemanticsAction(int32_t id, diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index d8beccd37d6b1..b14635bc63cf5 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -16,7 +16,7 @@ #include "flutter/fml/memory/weak_ptr.h" #include "flutter/lib/ui/semantics/custom_accessibility_action.h" #include "flutter/lib/ui/semantics/semantics_node.h" -#include "flutter/lib/ui/window/key_data_packet.h" +#include "flutter/lib/ui/window/key_data_message.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/pointer_data_packet_converter.h" @@ -136,8 +136,9 @@ class PlatformView { /// event /// and multiple logical key events. /// - virtual void OnPlatformViewDispatchKeyDataPacket( - std::unique_ptr packet) = 0; + virtual void OnPlatformViewDispatchKeyDataMessage( + std::unique_ptr message, + KeyDataMessageCallback callback) = 0; //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view has encountered @@ -597,7 +598,8 @@ class PlatformView { /// /// @param[in] packet The key data packet to dispatch to the framework. /// - void DispatchKeyDataPacket(std::unique_ptr packet); + void DispatchKeyDataMessage(std::unique_ptr message, + KeyDataMessageCallback callback); //-------------------------------------------------------------------------- /// @brief Used by the embedder to specify a texture that it wants the diff --git a/shell/common/shell.cc b/shell/common/shell.cc index db0938ed8ce0b..46864ded709c0 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -876,16 +876,19 @@ void Shell::OnPlatformViewDispatchPointerDataPacket( } // |PlatformView::Delegate| -void Shell::OnPlatformViewDispatchKeyDataPacket( - std::unique_ptr packet) { - TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchKeyDataPacket"); +void Shell::OnPlatformViewDispatchKeyDataMessage( + std::unique_ptr message, + KeyDataMessageCallback callback) { + TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchKeyDataMessage"); FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable( - [engine = weak_engine_, packet = std::move(packet)]() mutable { + [engine = weak_engine_, + message = std::move(message), + callback = std::move(callback)]() mutable { if (engine) { - engine->DispatchKeyDataPacket(std::move(packet)); + engine->DispatchKeyDataMessage(std::move(message), std::move(callback)); } })); } diff --git a/shell/common/shell.h b/shell/common/shell.h index 480d8077daaa6..67bdb8d5c497b 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -457,8 +457,9 @@ class Shell final : public PlatformView::Delegate, std::unique_ptr packet) override; // |PlatformView::Delegate| - void OnPlatformViewDispatchKeyDataPacket( - std::unique_ptr packet) override; + void OnPlatformViewDispatchKeyDataMessage( + std::unique_ptr message, + KeyDataMessageCallback callback) override; // |PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction( diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index d32aad267d912..ee581fe07df57 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -62,8 +62,9 @@ class MockPlatformViewDelegate : public PlatformView::Delegate { MOCK_METHOD1(OnPlatformViewDispatchPointerDataPacket, void(std::unique_ptr packet)); - MOCK_METHOD1(OnPlatformViewDispatchKeyDataPacket, - void(std::unique_ptr packet)); + MOCK_METHOD2(OnPlatformViewDispatchKeyDataMessage, + void(std::unique_ptr message, + KeyDataMessageCallback callback)); MOCK_METHOD3(OnPlatformViewDispatchSemanticsAction, void(int32_t id, diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm index 129cce47c8199..552480face9d2 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm @@ -25,7 +25,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} + void OnPlatformViewDispatchKeyDataMessage(std::unique_ptr message) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 631f98c30c887..e99fa24fdd658 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -95,7 +95,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} + void OnPlatformViewDispatchKeyDataMessage(std::unique_ptr message) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index a446224495925..33ff62d5e17a6 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -78,7 +78,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} + void OnPlatformViewDispatchKeyDataMessage(std::unique_ptr message) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index f580eb8e67fc8..4d5917638148d 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1514,9 +1514,11 @@ inline flutter::KeyChange ToKeyChange(FlutterKeyEventKind key_change) { // i.e. 8 bytes. constexpr size_t kKeyEventCharacterMaxBytes = 8; -FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) - engine, - const FlutterKeyEvent* event) { +FlutterEngineResult FlutterEngineSendKeyEvent( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterKeyEvent* event, + FlutterKeyEventCallback callback, + void* user_data) { if (engine == nullptr) { return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); } @@ -1530,8 +1532,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) size_t character_data_size = character == nullptr ? 0 : strnlen(character, kKeyEventCharacterMaxBytes); - auto packet = - std::make_unique(character_data_size); + auto message = std::make_unique(character_data_size); flutter::KeyData key_data; key_data.Clear(); @@ -1540,12 +1541,18 @@ FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) SAFE_ACCESS(event, kind, FlutterKeyEventKind::kFlutterKeyEventKindUp)); key_data.physical = SAFE_ACCESS(event, physical, 0); key_data.logical = SAFE_ACCESS(event, logical, 0); - key_data.synthesized = !!SAFE_ACCESS(event, synthesized, false); - packet->SetKeyData(key_data); - packet->SetCharacter(character); + key_data.synthesized = SAFE_ACCESS(event, synthesized, false); + message->SetKeyData(key_data); + message->SetCharacter(character); + + auto response = [callback, user_data](bool handled) { + callback(handled, user_data); + }; return reinterpret_cast(engine) - ->DispatchKeyDataPacket(std::move(packet)) + ->DispatchKeyDataMessage( + std::move(message), + response) ? kSuccess : LOG_EMBEDDER_ERROR(kInternalInconsistency, "Could not dispatch the key event to the " diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 9f02e3cabebf3..19b534aa09f0f 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -617,29 +617,58 @@ typedef enum { kFlutterKeyEventKindRepeat, } FlutterKeyEventKind; +/// A structure to represent a change of state of a key. +/// +/// Sending `FlutterKeyEvent` via `FlutterEngineSendKeyEvent` results in a +/// corresponding `FlutterKeyEvent` to be dispatched in the framework. It is +/// embedder's responsibility to ensure the regularity of sent events, since the +/// framework only performs simple one-to-one mapping. The events must conform +/// the following rules: +/// +/// * Each key press sequence shall consist of one key down event (`kind` being +/// `kFlutterKeyEventKindDown`), zero or more repeat events, and one key up +/// event, representing a physical key button being pressed, held, and released. +/// * All events throughout a key press sequence shall have the same `physical` +/// and `logical`. Having different `character`s is allowed. typedef struct { /// The size of this struct. Must be sizeof(FlutterKeyEvent). size_t struct_size; - // Timestamp in microseconds. + /// The timestamp at which the pointer event was generated. The timestamp + /// should be specified in microseconds and the clock should be the same as + /// that used by `FlutterEngineGetCurrentTime`. double timestamp; - // The event kind. + /// The event kind. FlutterKeyEventKind kind; - // The physical key of the event, distinguished by the HID code. + /// The USB HID code for the physical key of the event. + /// + /// For the full definition and list of pre-defined physical keys, see + /// `PhysicalKeyboardKey` from the framework. uint64_t physical; - // The logical key of the event, usually the effect of the key without - // modifier, but might include modifier if the information is not available. - // Can be 0 for empty. + /// The key ID for the logical key of this event. + /// + /// For the full definition and a list of pre-defined logical keys, see + /// `LogicalKeyboardKey` from the framework. uint64_t logical; - // Null-terminated character input from the event. Can be null. Not available - // to up events. + /// Null-terminated character input from the event. Can be null. Ignored for + /// up events. const char* character; - // Whether the event is synthesized by Flutter. + /// True if this event does not correspond to a native event. + /// + /// The embedder is likely to skip events and/or construct new events that do + /// not correspond to any native events in order to conform the regularity + /// of events (as documented in `FlutterKeyEvent`). An example is when a key up + /// is missed due to loss of window focus, on a platform that provides query to + /// key pressing status, the embedder might realize that the key has been + /// released at the next key event, and should construct a synthesized up event + /// immediately before the actual event. + /// + /// An event being synthesized means that the framework will not trust the + /// `timestamp` of the event. bool synthesized; } FlutterKeyEvent; -struct _FlutterPlatformKeyEventResponseHandle; -typedef struct _FlutterPlatformKeyEventResponseHandle - FlutterPlatformKeyEventResponseHandle; +typedef void (*FlutterKeyEventCallback)(bool /* handled */, + void* /* user_data */); struct _FlutterPlatformMessageResponseHandle; typedef struct _FlutterPlatformMessageResponseHandle @@ -1579,11 +1608,30 @@ FlutterEngineResult FlutterEngineSendPointerEvent( const FlutterPointerEvent* events, size_t events_count); +//------------------------------------------------------------------------------ +/// @brief Send a key event to the engine, causing the framework to +/// dispatch a key event that is simply transformed from this one. +/// The framework will decide whether to handle this event in a +/// synchronous fashion, although due to technical limitation, the +/// result is always reported asynchronously. The `callback` is +/// guaranteed to be called exactly once. +/// +/// @param[in] engine A running engine instance. +/// @param[in] event The event data to be sent. This function will no +/// longer access `event` after returning. +/// @param[in] callback The callback invoked by the engine when the +/// Flutter application has decided whether it handles +/// this event. +/// @param[in] user_data The context associated with the callback. +/// +/// @return The result of the call. +/// FLUTTER_EXPORT FlutterEngineResult FlutterEngineSendKeyEvent( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterKeyEvent* event, - FlutterPlatformKeyEventResponseHandle** response_out); + FlutterKeyEventCallback callback, + void* user_data); FLUTTER_EXPORT FlutterEngineResult FlutterEngineSendPlatformMessage( @@ -2100,7 +2148,9 @@ typedef FlutterEngineResult (*FlutterEngineSendPointerEventFnPtr)( size_t events_count); typedef FlutterEngineResult (*FlutterEngineSendKeyEventFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, - const FlutterKeyEvent* event); + const FlutterKeyEvent* event, + FlutterKeyEventCallback callback, + void* user_data); typedef FlutterEngineResult (*FlutterEngineSendPlatformMessageFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessage* message); diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index 32685c7b00ca0..852c26b957fc1 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -136,9 +136,10 @@ bool EmbedderEngine::DispatchPointerDataPacket( return true; } -bool EmbedderEngine::DispatchKeyDataPacket( - std::unique_ptr packet) { - if (!IsValid() || !packet) { +bool EmbedderEngine::DispatchKeyDataMessage( + std::unique_ptr message, + KeyDataMessageCallback callback) { + if (!IsValid() || !message) { return false; } @@ -147,7 +148,7 @@ bool EmbedderEngine::DispatchKeyDataPacket( return false; } - platform_view->DispatchKeyDataPacket(std::move(packet)); + platform_view->DispatchKeyDataMessage(std::move(message), std::move(callback)); return true; } diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index 53a7b315c219a..b41ca4d0df0a4 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -60,7 +60,8 @@ class EmbedderEngine { bool DispatchPointerDataPacket( std::unique_ptr packet); - bool DispatchKeyDataPacket(std::unique_ptr packet); + bool DispatchKeyDataMessage(std::unique_ptr message, + KeyDataMessageCallback callback); bool SendPlatformMessage(fml::RefPtr message); diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 1f6b793f1d160..a1d88d1298d8d 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -81,8 +81,8 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { void OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) {} // |flutter::PlatformView::Delegate| - void OnPlatformViewDispatchKeyDataPacket( - std::unique_ptr packet) {} + void OnPlatformViewDispatchKeyDataMessage( + std::unique_ptr message) {} // |flutter::PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction(int32_t id, flutter::SemanticsAction action, From 4be1c0f747b4a4bb444087a44da06f793cafeba7 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 19 Jan 2021 14:51:44 -0800 Subject: [PATCH 12/34] Rename back to KeyDataPacket --- ci/licenses_golden/licenses_flutter | 4 ++-- lib/ui/BUILD.gn | 4 ++-- lib/ui/window/key_data.h | 4 ++-- .../{key_data_message.cc => key_data_packet.cc} | 10 +++++----- .../window/{key_data_message.h => key_data_packet.h} | 12 ++++++------ lib/ui/window/platform_configuration.cc | 2 +- lib/ui/window/platform_configuration.h | 4 ++-- lib/ui/window/window.cc | 4 ++-- lib/ui/window/window.h | 4 ++-- runtime/runtime_controller.cc | 10 +++++----- runtime/runtime_controller.h | 4 ++-- shell/common/engine.cc | 10 +++++----- shell/common/engine.h | 4 ++-- shell/common/platform_view.cc | 8 ++++---- shell/common/platform_view.h | 12 ++++++------ shell/common/shell.cc | 12 ++++++------ shell/common/shell.h | 6 +++--- shell/common/shell_unittests.cc | 6 +++--- .../Source/FlutterEnginePlatformViewTest.mm | 2 +- .../ios/framework/Source/FlutterPlatformViewsTest.mm | 2 +- .../framework/Source/accessibility_bridge_test.mm | 2 +- shell/platform/embedder/embedder.cc | 4 ++-- shell/platform/embedder/embedder_engine.cc | 10 +++++----- shell/platform/embedder/embedder_engine.h | 4 ++-- .../fuchsia/flutter/platform_view_unittest.cc | 4 ++-- 25 files changed, 74 insertions(+), 74 deletions(-) rename lib/ui/window/{key_data_message.cc => key_data_packet.cc} (70%) rename lib/ui/window/{key_data_message.h => key_data_packet.h} (82%) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 6562b0cc6bdda..f26e78aa510a8 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -412,8 +412,8 @@ FILE: ../../../flutter/lib/ui/volatile_path_tracker.h FILE: ../../../flutter/lib/ui/window.dart FILE: ../../../flutter/lib/ui/window/key_data.cc FILE: ../../../flutter/lib/ui/window/key_data.h -FILE: ../../../flutter/lib/ui/window/key_data_message.cc -FILE: ../../../flutter/lib/ui/window/key_data_message.h +FILE: ../../../flutter/lib/ui/window/key_data_packet.cc +FILE: ../../../flutter/lib/ui/window/key_data_packet.h FILE: ../../../flutter/lib/ui/window/platform_configuration.cc FILE: ../../../flutter/lib/ui/window/platform_configuration.h FILE: ../../../flutter/lib/ui/window/platform_configuration_unittests.cc diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index 4156d7491a395..18521e8818a97 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -95,8 +95,8 @@ source_set("ui") { "volatile_path_tracker.h", "window/key_data.cc", "window/key_data.h", - "window/key_data_message.cc", - "window/key_data_message.h", + "window/key_data_packet.cc", + "window/key_data_packet.h", "window/platform_configuration.cc", "window/platform_configuration.h", "window/platform_message.cc", diff --git a/lib/ui/window/key_data.h b/lib/ui/window/key_data.h index 783e9e6092a32..0237350b1bf7e 100644 --- a/lib/ui/window/key_data.h +++ b/lib/ui/window/key_data.h @@ -22,10 +22,10 @@ enum class KeyChange : int64_t { kRepeat, }; -// The fixed-length sections of a KeyDataMessage. +// The fixed-length sections of a KeyDataPacket. // // KeyData does not contain `character`, for variable-length data are stored in a -// different way in KeyDataMessage. +// different way in KeyDataPacket. // // This structure is unpacked by hooks.dart. struct alignas(8) KeyData { diff --git a/lib/ui/window/key_data_message.cc b/lib/ui/window/key_data_packet.cc similarity index 70% rename from lib/ui/window/key_data_message.cc rename to lib/ui/window/key_data_packet.cc index cdcf6f25d9255..a049251a94e37 100644 --- a/lib/ui/window/key_data_message.cc +++ b/lib/ui/window/key_data_packet.cc @@ -2,24 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/lib/ui/window/key_data_message.h" +#include "flutter/lib/ui/window/key_data_packet.h" #include "flutter/fml/logging.h" namespace flutter { -KeyDataMessage::KeyDataMessage(size_t character_data_size) +KeyDataPacket::KeyDataPacket(size_t character_data_size) : data_(sizeof(uint64_t) + sizeof(KeyData) + character_data_size) { uint64_t size64 = character_data_size; memcpy(&data()[CharacterSizeStart_()], &size64, sizeof(size64)); } -KeyDataMessage::~KeyDataMessage() = default; +KeyDataPacket::~KeyDataPacket() = default; -void KeyDataMessage::SetKeyData(const KeyData& event) { +void KeyDataPacket::SetKeyData(const KeyData& event) { memcpy(&data()[KeyDataStart_()], &event, sizeof(KeyData)); } -void KeyDataMessage::SetCharacter(const char* character) { +void KeyDataPacket::SetCharacter(const char* character) { if (character != nullptr) { memcpy(data().data() + CharacterStart_(), character, data().size() - CharacterStart_()); diff --git a/lib/ui/window/key_data_message.h b/lib/ui/window/key_data_packet.h similarity index 82% rename from lib/ui/window/key_data_message.h rename to lib/ui/window/key_data_packet.h index ba77786505cac..c3be35ab6f702 100644 --- a/lib/ui/window/key_data_message.h +++ b/lib/ui/window/key_data_packet.h @@ -14,15 +14,15 @@ namespace flutter { -typedef std::function KeyDataMessageCallback; +typedef std::function KeyDataPacketCallback; -class KeyDataMessage { +class KeyDataPacket { public: - // Build a KeyDataMessage by incrementally fill in data. + // Build a KeyDataPacket by incrementally fill in data. // // The `character_data_size` is number of bytes to contain the character data. - KeyDataMessage(size_t character_data_size); - ~KeyDataMessage(); + KeyDataPacket(size_t character_data_size); + ~KeyDataPacket(); const std::vector& data() const { return data_; } @@ -41,7 +41,7 @@ class KeyDataMessage { std::vector data_; - FML_DISALLOW_COPY_AND_ASSIGN(KeyDataMessage); + FML_DISALLOW_COPY_AND_ASSIGN(KeyDataPacket); }; } // namespace flutter diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 230ed49826062..7cc8edcf5becc 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -360,7 +360,7 @@ void PlatformConfiguration::DispatchSemanticsAction(int32_t id, } uint64_t PlatformConfiguration::RegisterKeyDataResponse( - KeyDataMessageCallback callback) { + KeyDataPacketCallback callback) { uint64_t response_id = next_key_response_id_++; pending_key_responses_[response_id] = std::move(callback); return response_id; diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 458c38134345c..fa1de9a2ad8bd 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -331,7 +331,7 @@ class PlatformConfiguration final { /// @param[in] message The message sent from the embedder to the Dart /// application. /// - uint64_t RegisterKeyDataResponse(KeyDataMessageCallback callback); + uint64_t RegisterKeyDataResponse(KeyDataPacketCallback callback); //---------------------------------------------------------------------------- /// @brief Notifies the framework that it is time to begin working on a @@ -454,7 +454,7 @@ class PlatformConfiguration final { // We use id 0 to mean that no response is expected. uint64_t next_key_response_id_ = 1; - std::unordered_map + std::unordered_map pending_key_responses_; }; diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index 52ae9c0ed3a7f..630785b5fc3bc 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -36,13 +36,13 @@ void Window::DispatchPointerDataPacket(const PointerDataPacket& packet) { library_.value(), "_dispatchPointerDataPacket", {data_handle})); } -void Window::DispatchKeyDataMessage(const KeyDataMessage& message, uint64_t response_id) { +void Window::DispatchKeyDataPacket(const KeyDataPacket& packet, uint64_t response_id) { std::shared_ptr dart_state = library_.dart_state().lock(); if (!dart_state) return; tonic::DartState::Scope scope(dart_state); - const std::vector buffer(message.data().size()); + const std::vector buffer(packet.data().size()); Dart_Handle data_handle = tonic::DartByteData::Create(buffer.data(), buffer.size()); if (Dart_IsError(data_handle)) { diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index 0573a8a579162..051caf0c76ccb 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -10,7 +10,7 @@ #include #include -#include "flutter/lib/ui/window/key_data_message.h" +#include "flutter/lib/ui/window/key_data_packet.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/viewport_metrics.h" @@ -29,7 +29,7 @@ class Window final { const ViewportMetrics& viewport_metrics() const { return viewport_metrics_; } void DispatchPointerDataPacket(const PointerDataPacket& packet); - void DispatchKeyDataMessage(const KeyDataMessage& message, uint64_t response_id); + void DispatchKeyDataPacket(const KeyDataPacket& packet, uint64_t response_id); void UpdateWindowMetrics(const ViewportMetrics& metrics); private: diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 049dc40847673..c8925cc3e3742 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -243,14 +243,14 @@ bool RuntimeController::DispatchPointerDataPacket( return false; } -bool RuntimeController::DispatchKeyDataMessage( - const KeyDataMessage& message, - KeyDataMessageCallback callback) { +bool RuntimeController::DispatchKeyDataPacket( + const KeyDataPacket& packet, + KeyDataPacketCallback callback) { if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { - TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataMessage", "mode", + TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataPacket", "mode", "basic"); uint64_t response_id = platform_configuration->RegisterKeyDataResponse(std::move(callback)); - platform_configuration->get_window(0)->DispatchKeyDataMessage(message, response_id); + platform_configuration->get_window(0)->DispatchKeyDataPacket(packet, response_id); return true; } return false; diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 9a1a61fd51b43..557f0e2293bd3 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -414,8 +414,8 @@ class RuntimeController : public PlatformConfigurationClient { /// /// @param[in] packet The key data message to dispatch to the isolate. /// - bool DispatchKeyDataMessage(const KeyDataMessage& message, - KeyDataMessageCallback callback); + bool DispatchKeyDataPacket(const KeyDataPacket& packet, + KeyDataPacketCallback callback); //---------------------------------------------------------------------------- /// @brief Dispatch the semantics action to the specified accessibility diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 3a6155bfd5c4a..c0258afe882b5 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -384,12 +384,12 @@ void Engine::DispatchPointerDataPacket( pointer_data_dispatcher_->DispatchPacket(std::move(packet), trace_flow_id); } -void Engine::DispatchKeyDataMessage( - std::unique_ptr message, - KeyDataMessageCallback callback) { - TRACE_EVENT0("flutter", "Engine::DispatchKeyDataMessage"); +void Engine::DispatchKeyDataPacket( + std::unique_ptr packet, + KeyDataPacketCallback callback) { + TRACE_EVENT0("flutter", "Engine::DispatchKeyDataPacket"); if (runtime_controller_) { - runtime_controller_->DispatchKeyDataMessage(*message, std::move(callback)); + runtime_controller_->DispatchKeyDataPacket(*packet, std::move(callback)); } } diff --git a/shell/common/engine.h b/shell/common/engine.h index 5be4b5c8d995b..3c1aa17aaf710 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -719,8 +719,8 @@ class Engine final : public RuntimeDelegate, /// /// @param[in] packet The key data packet. /// - void DispatchKeyDataMessage(std::unique_ptr message, - KeyDataMessageCallback callback); + void DispatchKeyDataPacket(std::unique_ptr packet, + KeyDataPacketCallback callback); //---------------------------------------------------------------------------- /// @brief Notifies the engine that the embedder encountered an diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 2d8801daef0b6..e83e10dd42a03 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -42,10 +42,10 @@ void PlatformView::DispatchPointerDataPacket( pointer_data_packet_converter_.Convert(std::move(packet))); } -void PlatformView::DispatchKeyDataMessage( - std::unique_ptr message, - KeyDataMessageCallback callback) { - delegate_.OnPlatformViewDispatchKeyDataMessage(std::move(message), std::move(callback)); +void PlatformView::DispatchKeyDataPacket( + std::unique_ptr packet, + KeyDataPacketCallback callback) { + delegate_.OnPlatformViewDispatchKeyDataPacket(std::move(packet), std::move(callback)); } void PlatformView::DispatchSemanticsAction(int32_t id, diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index b14635bc63cf5..1e37d4006795e 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -16,7 +16,7 @@ #include "flutter/fml/memory/weak_ptr.h" #include "flutter/lib/ui/semantics/custom_accessibility_action.h" #include "flutter/lib/ui/semantics/semantics_node.h" -#include "flutter/lib/ui/window/key_data_message.h" +#include "flutter/lib/ui/window/key_data_packet.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/pointer_data_packet_converter.h" @@ -136,9 +136,9 @@ class PlatformView { /// event /// and multiple logical key events. /// - virtual void OnPlatformViewDispatchKeyDataMessage( - std::unique_ptr message, - KeyDataMessageCallback callback) = 0; + virtual void OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet, + KeyDataPacketCallback callback) = 0; //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view has encountered @@ -598,8 +598,8 @@ class PlatformView { /// /// @param[in] packet The key data packet to dispatch to the framework. /// - void DispatchKeyDataMessage(std::unique_ptr message, - KeyDataMessageCallback callback); + void DispatchKeyDataPacket(std::unique_ptr packet, + KeyDataPacketCallback callback); //-------------------------------------------------------------------------- /// @brief Used by the embedder to specify a texture that it wants the diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 46864ded709c0..cbbbc3cd60d06 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -876,19 +876,19 @@ void Shell::OnPlatformViewDispatchPointerDataPacket( } // |PlatformView::Delegate| -void Shell::OnPlatformViewDispatchKeyDataMessage( - std::unique_ptr message, - KeyDataMessageCallback callback) { - TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchKeyDataMessage"); +void Shell::OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet, + KeyDataPacketCallback callback) { + TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchKeyDataPacket"); FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable( [engine = weak_engine_, - message = std::move(message), + packet = std::move(packet), callback = std::move(callback)]() mutable { if (engine) { - engine->DispatchKeyDataMessage(std::move(message), std::move(callback)); + engine->DispatchKeyDataPacket(std::move(packet), std::move(callback)); } })); } diff --git a/shell/common/shell.h b/shell/common/shell.h index 67bdb8d5c497b..6b75be235d3b3 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -457,9 +457,9 @@ class Shell final : public PlatformView::Delegate, std::unique_ptr packet) override; // |PlatformView::Delegate| - void OnPlatformViewDispatchKeyDataMessage( - std::unique_ptr message, - KeyDataMessageCallback callback) override; + void OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet, + KeyDataPacketCallback callback) override; // |PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction( diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index ee581fe07df57..5a9b13fe026ad 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -62,9 +62,9 @@ class MockPlatformViewDelegate : public PlatformView::Delegate { MOCK_METHOD1(OnPlatformViewDispatchPointerDataPacket, void(std::unique_ptr packet)); - MOCK_METHOD2(OnPlatformViewDispatchKeyDataMessage, - void(std::unique_ptr message, - KeyDataMessageCallback callback)); + MOCK_METHOD2(OnPlatformViewDispatchKeyDataPacket, + void(std::unique_ptr packet, + KeyDataPacketCallback callback)); MOCK_METHOD3(OnPlatformViewDispatchSemanticsAction, void(int32_t id, diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm index 552480face9d2..129cce47c8199 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm @@ -25,7 +25,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataMessage(std::unique_ptr message) override {} + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index e99fa24fdd658..631f98c30c887 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -95,7 +95,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataMessage(std::unique_ptr message) override {} + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index 33ff62d5e17a6..a446224495925 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -78,7 +78,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataMessage(std::unique_ptr message) override {} + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 4d5917638148d..4d0cac898a231 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1532,7 +1532,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent( size_t character_data_size = character == nullptr ? 0 : strnlen(character, kKeyEventCharacterMaxBytes); - auto message = std::make_unique(character_data_size); + auto message = std::make_unique(character_data_size); flutter::KeyData key_data; key_data.Clear(); @@ -1550,7 +1550,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent( }; return reinterpret_cast(engine) - ->DispatchKeyDataMessage( + ->DispatchKeyDataPacket( std::move(message), response) ? kSuccess diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index 852c26b957fc1..4c14cd9a42a3e 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -136,10 +136,10 @@ bool EmbedderEngine::DispatchPointerDataPacket( return true; } -bool EmbedderEngine::DispatchKeyDataMessage( - std::unique_ptr message, - KeyDataMessageCallback callback) { - if (!IsValid() || !message) { +bool EmbedderEngine::DispatchKeyDataPacket( + std::unique_ptr packet, + KeyDataPacketCallback callback) { + if (!IsValid() || !packet) { return false; } @@ -148,7 +148,7 @@ bool EmbedderEngine::DispatchKeyDataMessage( return false; } - platform_view->DispatchKeyDataMessage(std::move(message), std::move(callback)); + platform_view->DispatchKeyDataPacket(std::move(packet), std::move(callback)); return true; } diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index b41ca4d0df0a4..f2ca2c68d5ad8 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -60,8 +60,8 @@ class EmbedderEngine { bool DispatchPointerDataPacket( std::unique_ptr packet); - bool DispatchKeyDataMessage(std::unique_ptr message, - KeyDataMessageCallback callback); + bool DispatchKeyDataPacket(std::unique_ptr packet, + KeyDataPacketCallback callback); bool SendPlatformMessage(fml::RefPtr message); diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index a1d88d1298d8d..1f6b793f1d160 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -81,8 +81,8 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { void OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) {} // |flutter::PlatformView::Delegate| - void OnPlatformViewDispatchKeyDataMessage( - std::unique_ptr message) {} + void OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet) {} // |flutter::PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction(int32_t id, flutter::SemanticsAction action, From 15388e606a79238395d774f8d463dc09fff99b5b Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 20 Jan 2021 17:10:38 -0800 Subject: [PATCH 13/34] Unit test --- lib/ui/ui.dart | 1 - lib/ui/window/platform_configuration.cc | 1 + lib/ui/window/platform_configuration.h | 31 ++++-- runtime/runtime_controller.h | 9 +- shell/common/engine.h | 6 +- shell/common/platform_view.h | 14 +-- shell/platform/embedder/BUILD.gn | 1 + shell/platform/embedder/embedder.h | 4 +- shell/platform/embedder/embedder_engine.h | 2 +- shell/platform/embedder/fixtures/main.dart | 39 +++++++- .../tests/embedder_unittests_key_event.cc | 98 +++++++++++++++++++ 11 files changed, 182 insertions(+), 24 deletions(-) create mode 100644 shell/platform/embedder/tests/embedder_unittests_key_event.cc diff --git a/lib/ui/ui.dart b/lib/ui/ui.dart index f48b8162ba42c..23d85835c1d92 100644 --- a/lib/ui/ui.dart +++ b/lib/ui/ui.dart @@ -12,7 +12,6 @@ // @dart = 2.12 library dart.ui; -import 'dart:_internal' hide Symbol; // ignore: unused_import import 'dart:async'; import 'dart:collection' as collection; import 'dart:convert'; diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 7cc8edcf5becc..e8358ed22c868 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -447,6 +447,7 @@ void PlatformConfiguration::CompleteKeyDataResponse(uint64_t response_id, bool h return; } auto it = pending_key_responses_.find(response_id); + FML_DCHECK(it != pending_key_responses_.end()); if (it == pending_key_responses_.end()) { return; } diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index fa1de9a2ad8bd..8a9f30f9c04d5 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -324,12 +324,20 @@ class PlatformConfiguration final { std::vector args); //---------------------------------------------------------------------------- - /// @brief Notifies the PlatformConfiguration that the client has sent - /// it a message. This call originates in the platform view and - /// has been forwarded through the engine to here. + /// @brief Register a callback to be invoked when the framework has + /// decided whether to handle an event. This callback originates + /// in the platform view and has been forwarded through the engine + /// to here. /// - /// @param[in] message The message sent from the embedder to the Dart - /// application. + /// This method will move and store the `callback`, associate it + /// with a self-incrementing identifier, the response ID, then + /// return the ID, which is typically used by + /// Window::DispatchKeyDataPacket. + /// + /// @param[in] callback The callback to be registered. + /// + /// @return The response ID to be associated with the callback. Using this + /// ID in CompleteKeyDataResponse will invoke the callback. /// uint64_t RegisterKeyDataResponse(KeyDataPacketCallback callback); @@ -422,12 +430,17 @@ class PlatformConfiguration final { void CompletePlatformMessageEmptyResponse(int response_id); //---------------------------------------------------------------------------- - /// @brief Responds to a previous key data message to the engine from the - /// framework. + /// @brief Responds to a previously registered key data message from the + /// framework to the engine. + /// + /// For each response_id, this method should be called exactly + /// once. Responding to a response_id that has not been registered + /// or has been invoked will lead to a fatal error. /// /// @param[in] response_id The unique id that identifies the original platform - /// message to respond to. - /// @param[in] handled Whether the key is handled. + /// message to respond to, created by + /// RegisterKeyDataResponse. + /// @param[in] handled Whether the key data is handled. /// void CompleteKeyDataResponse(uint64_t response_id, bool handled); diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 72f05a4cf809c..1057eb35bb682 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -428,10 +428,15 @@ class RuntimeController : public PlatformConfigurationClient { /// @brief Dispatch the specified pointer data message to the running /// root isolate. /// - /// @param[in] packet The key data message to dispatch to the isolate. + /// @param[in] packet The key data message to dispatch to the isolate. + /// @param[in] callback Called when the framework has decided whether + /// to handle this key data. + /// + /// @return If the key data message was dispatched. This may fail is + /// an isolate is not running. /// bool DispatchKeyDataPacket(const KeyDataPacket& packet, - KeyDataPacketCallback callback); + KeyDataPacketCallback callback); //---------------------------------------------------------------------------- /// @brief Dispatch the semantics action to the specified accessibility diff --git a/shell/common/engine.h b/shell/common/engine.h index 02aada947ed87..ae86e330f5d29 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -735,10 +735,12 @@ class Engine final : public RuntimeDelegate, /// the platform view and the shell has forwarded the same to the /// engine on the UI task runner here. /// - /// @param[in] packet The key data packet. + /// @param[in] packet The key data packet. + /// @param[in] callback Called when the framework has decided whether + /// to handle this key data. /// void DispatchKeyDataPacket(std::unique_ptr packet, - KeyDataPacketCallback callback); + KeyDataPacketCallback callback); //---------------------------------------------------------------------------- /// @brief Notifies the engine that the embedder encountered an diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 1e37d4006795e..eeba64c94f65f 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -128,13 +128,13 @@ class PlatformView { //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view has encountered - /// a key event. This key event needs to be forwarded to - /// the running root isolate hosted by the engine on the UI - /// thread. + /// a key event. This key event and the callback needs to be + /// forwarded to the running root isolate hosted by the engine + /// on the UI thread. /// - /// @param[in] packet The key data packet containing one physical key - /// event - /// and multiple logical key events. + /// @param[in] packet The key data packet containing one key event. + /// @param[in] callback Called when the framework has decided whether + /// to handle this key data. /// virtual void OnPlatformViewDispatchKeyDataPacket( std::unique_ptr packet, @@ -599,7 +599,7 @@ class PlatformView { /// @param[in] packet The key data packet to dispatch to the framework. /// void DispatchKeyDataPacket(std::unique_ptr packet, - KeyDataPacketCallback callback); + KeyDataPacketCallback callback); //-------------------------------------------------------------------------- /// @brief Used by the embedder to specify a texture that it wants the diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index 7cbba62bbc5c5..193ebc348bd3b 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -192,6 +192,7 @@ if (enable_unittests) { "tests/embedder_test_context_software.cc", "tests/embedder_test_context_software.h", "tests/embedder_unittests.cc", + "tests/embedder_unittests_key_event.cc", "tests/embedder_unittests_util.cc", ] diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 19b534aa09f0f..d13858c03065d 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -1622,7 +1622,9 @@ FlutterEngineResult FlutterEngineSendPointerEvent( /// @param[in] callback The callback invoked by the engine when the /// Flutter application has decided whether it handles /// this event. -/// @param[in] user_data The context associated with the callback. +/// @param[in] user_data The context associated with the callback. Should +/// not be released until `callback` is invoked. Can +/// be null. /// /// @return The result of the call. /// diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index f2ca2c68d5ad8..b25f76e9f5466 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -61,7 +61,7 @@ class EmbedderEngine { std::unique_ptr packet); bool DispatchKeyDataPacket(std::unique_ptr packet, - KeyDataPacketCallback callback); + KeyDataPacketCallback callback); bool SendPlatformMessage(fml::RefPtr message); diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 81fc2e0c35bd6..54494a97baeb3 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -12,6 +12,8 @@ import 'dart:ffi'; import 'dart:core'; import 'dart:convert'; +import '../../../../lib/ui/ui.dart'; + void main() {} @pragma('vm:entry-point') @@ -495,6 +497,41 @@ Picture CreateGradientBox(Size size) { return baseRecorder.endRecording(); } +void _echoKeyEvent( + int change, + int timestamp, + int physical, + int logical, + int charCode, + bool synthesized) + native 'EchoKeyEvent'; + +int _serializeKeyChange(KeyChange change) { + switch(change) { + case KeyChange.up: + return 1; + case KeyChange.down: + return 2; + case KeyChange.repeat: + return 3; + } +} + +@pragma('vm:entry-point') +void key_data_echo() async { // ignore: non_constant_identifier_names + PlatformDispatcher.instance.onKeyData = (KeyData data) { + _echoKeyEvent( + _serializeKeyChange(data.change), + data.timeStamp.inMilliseconds, + data.physical, + data.logical, + data.character == null ? 0 : data.character!.codeUnitAt(0), + data.synthesized, + ); + return true; + }; +} + @pragma('vm:entry-point') void render_gradient() { PlatformDispatcher.instance.onBeginFrame = (Duration duration) { @@ -587,7 +624,7 @@ void can_display_platform_view_with_pixel_ratio() { @pragma('vm:entry-point') void can_receive_locale_updates() { PlatformDispatcher.instance.onLocaleChanged = (){ - signalNativeCount(PlatformDispatcher.instance.locales!.length); + signalNativeCount(PlatformDispatcher.instance.locales.length); }; signalNativeTest(); } diff --git a/shell/platform/embedder/tests/embedder_unittests_key_event.cc b/shell/platform/embedder/tests/embedder_unittests_key_event.cc new file mode 100644 index 0000000000000..54044d2a7bd2c --- /dev/null +++ b/shell/platform/embedder/tests/embedder_unittests_key_event.cc @@ -0,0 +1,98 @@ +// Copyright 2013 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 "flutter/shell/platform/embedder/embedder.h" + +#include + +#include "flutter/testing/testing.h" + +#ifdef _WIN32 +// winbase.h defines GetCurrentTime as a macro. +#undef GetCurrentTime +#endif + +namespace flutter { +namespace testing { + +FlutterKeyEventKind unserializeKind(uint64_t kindInt) { + switch(kindInt) { + case 1: + return kFlutterKeyEventKindUp; + case 2: + return kFlutterKeyEventKindDown; + case 3: + return kFlutterKeyEventKindRepeat; + default: + FML_UNREACHABLE(); + return kFlutterKeyEventKindUp; + } +} + +TEST(EmbedderKeyEvent, CorrectlySerializeKeyData) { + auto message_latch = std::make_shared(); + char echoed_string[2] = "m\n"; // Dummy string that holds one char. + FlutterKeyEvent echoed_event; + + auto native_echo_event = [&message_latch](Dart_NativeArguments args) { + auto handle = Dart_GetNativeArgument(args, 0); + echoed_event.type = unserializeKind( + tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 1))); + echoed_event.timestamp = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 2)); + echoed_event.physical = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 3)); + echoed_event.logical = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 4)); + echoed_string[0] = (char)tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 5)); + echoed_event.synthesized = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 6)); + + message_latch->Signal(); + }; + + context.AddNativeCallback("EchoKeyEvent", CREATE_NATIVE_ENTRY(native_echo_event)); + + auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext); + + // fml::AutoResetWaitableEvent latch; + // context.AddNativeCallback( + // "SignalNativeTest", CREATE_NATIVE_ENTRY(([&latch](Dart_NativeArguments) { + // }))); + + EmbedderConfigBuilder builder(context); + builder.SetSoftwareRendererConfig(); + builder.SetDartEntrypoint("key_data_echo"); + + auto engine = builder.LaunchEngine(); + ASSERT_TRUE(engine.is_valid()); + + const FlutterKeyEvent downEventUpperA { + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1, + .kind = kFlutterKeyEventKindDown, + .physical = 0x00070004, + .logical = 0x00000000061, + .character = "A", + .synthesized = false, + }; + FlutterEngineSendKeyEvent(engine, &downEventUpperA, + [](bool handled, void* user_data){ + }, + null); + message_latch.Wait(); + + EXPECT_EQ(echoed_event.timestamp, downEventUpperA.timestamp); + EXPECT_EQ(echoed_event.kind, downEventUpperA.kind); + EXPECT_EQ(echoed_event.physical, downEventUpperA.physical); + EXPECT_EQ(echoed_event.logical, downEventUpperA.logical); + EXPECT_STREQ(echoed_event.character, downEventUpperA.character); + EXPECT_EQ(echoed_event.synthesized, downEventUpperA.synthesized); +} + + +} // namespace testing +} // namespace flutter From 8baf82ba21a1c24dbec2a6724d7e0c92d8b72cbc Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 20 Jan 2021 17:46:05 -0800 Subject: [PATCH 14/34] Fix compile --- shell/platform/embedder/fixtures/main.dart | 4 +-- .../tests/embedder_unittests_key_event.cc | 32 ++++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 54494a97baeb3..1819f0035cd2c 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -12,8 +12,6 @@ import 'dart:ffi'; import 'dart:core'; import 'dart:convert'; -import '../../../../lib/ui/ui.dart'; - void main() {} @pragma('vm:entry-point') @@ -624,7 +622,7 @@ void can_display_platform_view_with_pixel_ratio() { @pragma('vm:entry-point') void can_receive_locale_updates() { PlatformDispatcher.instance.onLocaleChanged = (){ - signalNativeCount(PlatformDispatcher.instance.locales.length); + signalNativeCount(PlatformDispatcher.instance.locales!.length); }; signalNativeTest(); } diff --git a/shell/platform/embedder/tests/embedder_unittests_key_event.cc b/shell/platform/embedder/tests/embedder_unittests_key_event.cc index 54044d2a7bd2c..37358ec1ee53c 100644 --- a/shell/platform/embedder/tests/embedder_unittests_key_event.cc +++ b/shell/platform/embedder/tests/embedder_unittests_key_event.cc @@ -2,10 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/shell/platform/embedder/embedder.h" - #include +#include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/embedder/tests/embedder_config_builder.h" +#include "flutter/shell/platform/embedder/tests/embedder_test.h" +#include "third_party/dart/runtime/include/dart_api.h" +#include "third_party/tonic/converter/dart_converter.h" #include "flutter/testing/testing.h" #ifdef _WIN32 @@ -18,11 +22,11 @@ namespace testing { FlutterKeyEventKind unserializeKind(uint64_t kindInt) { switch(kindInt) { - case 1: + case 1: return kFlutterKeyEventKindUp; - case 2: + case 2: return kFlutterKeyEventKindDown; - case 3: + case 3: return kFlutterKeyEventKindRepeat; default: FML_UNREACHABLE(); @@ -30,14 +34,13 @@ FlutterKeyEventKind unserializeKind(uint64_t kindInt) { } } -TEST(EmbedderKeyEvent, CorrectlySerializeKeyData) { +TEST_F(EmbedderTest, CorrectlySerializeKeyData) { auto message_latch = std::make_shared(); - char echoed_string[2] = "m\n"; // Dummy string that holds one char. + char echoed_string[2] = "m"; // Dummy string that holds one char. FlutterKeyEvent echoed_event; - auto native_echo_event = [&message_latch](Dart_NativeArguments args) { - auto handle = Dart_GetNativeArgument(args, 0); - echoed_event.type = unserializeKind( + auto native_echo_event = [&](Dart_NativeArguments args) { + echoed_event.kind = unserializeKind( tonic::DartConverter::FromDart( Dart_GetNativeArgument(args, 1))); echoed_event.timestamp = tonic::DartConverter::FromDart( @@ -54,9 +57,8 @@ TEST(EmbedderKeyEvent, CorrectlySerializeKeyData) { message_latch->Signal(); }; - context.AddNativeCallback("EchoKeyEvent", CREATE_NATIVE_ENTRY(native_echo_event)); - auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext); + context.AddNativeCallback("EchoKeyEvent", CREATE_NATIVE_ENTRY(native_echo_event)); // fml::AutoResetWaitableEvent latch; // context.AddNativeCallback( @@ -79,11 +81,11 @@ TEST(EmbedderKeyEvent, CorrectlySerializeKeyData) { .character = "A", .synthesized = false, }; - FlutterEngineSendKeyEvent(engine, &downEventUpperA, + FlutterEngineSendKeyEvent(engine.get(), &downEventUpperA, [](bool handled, void* user_data){ }, - null); - message_latch.Wait(); + nullptr); + message_latch->Wait(); EXPECT_EQ(echoed_event.timestamp, downEventUpperA.timestamp); EXPECT_EQ(echoed_event.kind, downEventUpperA.kind); From d63c9f21212d1bd4d70c1e28352217cb8f637a80 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 20 Jan 2021 19:02:40 -0800 Subject: [PATCH 15/34] First unit test passed --- lib/ui/window/key_data_packet.cc | 8 ++--- lib/ui/window/key_data_packet.h | 2 -- lib/ui/window/platform_configuration.cc | 2 +- lib/ui/window/window.cc | 2 +- shell/platform/embedder/embedder.cc | 8 ++--- shell/platform/embedder/fixtures/main.dart | 3 +- .../tests/embedder_unittests_key_event.cc | 34 +++++++++++-------- 7 files changed, 31 insertions(+), 28 deletions(-) diff --git a/lib/ui/window/key_data_packet.cc b/lib/ui/window/key_data_packet.cc index a049251a94e37..a476be497a3e7 100644 --- a/lib/ui/window/key_data_packet.cc +++ b/lib/ui/window/key_data_packet.cc @@ -10,19 +10,19 @@ namespace flutter { KeyDataPacket::KeyDataPacket(size_t character_data_size) : data_(sizeof(uint64_t) + sizeof(KeyData) + character_data_size) { uint64_t size64 = character_data_size; - memcpy(&data()[CharacterSizeStart_()], &size64, sizeof(size64)); + memcpy(&data_[CharacterSizeStart_()], &size64, sizeof(size64)); } KeyDataPacket::~KeyDataPacket() = default; void KeyDataPacket::SetKeyData(const KeyData& event) { - memcpy(&data()[KeyDataStart_()], &event, sizeof(KeyData)); + memcpy(&data_[KeyDataStart_()], &event, sizeof(KeyData)); } void KeyDataPacket::SetCharacter(const char* character) { if (character != nullptr) { - memcpy(data().data() + CharacterStart_(), character, - data().size() - CharacterStart_()); + memcpy(data_.data() + CharacterStart_(), character, + data_.size() - CharacterStart_()); } } diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_packet.h index c3be35ab6f702..735bebd1d2cc1 100644 --- a/lib/ui/window/key_data_packet.h +++ b/lib/ui/window/key_data_packet.h @@ -33,8 +33,6 @@ class KeyDataPacket { void SetCharacter(const char* characters); private: - std::vector& data() { return data_; } - size_t CharacterSizeStart_() { return 0; } size_t KeyDataStart_() { return CharacterSizeStart_() + sizeof(uint64_t); } size_t CharacterStart_() { return KeyDataStart_() + sizeof(KeyData); } diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index e8358ed22c868..0eee18ce4a8f5 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -486,7 +486,7 @@ void PlatformConfiguration::RegisterNatives( true}, {"PlatformConfiguration_respondToPlatformMessage", _RespondToPlatformMessage, 3, true}, - {"PlatformConfiguration_respondToKeyData", _RespondToKeyData, 2, true}, + {"PlatformConfiguration_respondToKeyData", _RespondToKeyData, 3, true}, {"PlatformConfiguration_render", Render, 3, true}, {"PlatformConfiguration_updateSemantics", UpdateSemantics, 2, true}, {"PlatformConfiguration_setIsolateDebugName", SetIsolateDebugName, 2, diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index 630785b5fc3bc..650fe0b37bbec 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -42,7 +42,7 @@ void Window::DispatchKeyDataPacket(const KeyDataPacket& packet, uint64_t respons return; tonic::DartState::Scope scope(dart_state); - const std::vector buffer(packet.data().size()); + const std::vector& buffer = packet.data(); Dart_Handle data_handle = tonic::DartByteData::Create(buffer.data(), buffer.size()); if (Dart_IsError(data_handle)) { diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 4d0cac898a231..f2b0a09716baf 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1532,7 +1532,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent( size_t character_data_size = character == nullptr ? 0 : strnlen(character, kKeyEventCharacterMaxBytes); - auto message = std::make_unique(character_data_size); + auto packet = std::make_unique(character_data_size); flutter::KeyData key_data; key_data.Clear(); @@ -1542,8 +1542,8 @@ FlutterEngineResult FlutterEngineSendKeyEvent( key_data.physical = SAFE_ACCESS(event, physical, 0); key_data.logical = SAFE_ACCESS(event, logical, 0); key_data.synthesized = SAFE_ACCESS(event, synthesized, false); - message->SetKeyData(key_data); - message->SetCharacter(character); + packet->SetKeyData(key_data); + packet->SetCharacter(character); auto response = [callback, user_data](bool handled) { callback(handled, user_data); @@ -1551,7 +1551,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent( return reinterpret_cast(engine) ->DispatchKeyDataPacket( - std::move(message), + std::move(packet), response) ? kSuccess : LOG_EMBEDDER_ERROR(kInternalInconsistency, diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 1819f0035cd2c..c46a6d7153a84 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -520,7 +520,7 @@ void key_data_echo() async { // ignore: non_constant_identifier_names PlatformDispatcher.instance.onKeyData = (KeyData data) { _echoKeyEvent( _serializeKeyChange(data.change), - data.timeStamp.inMilliseconds, + data.timeStamp.inMicroseconds, data.physical, data.logical, data.character == null ? 0 : data.character!.codeUnitAt(0), @@ -528,6 +528,7 @@ void key_data_echo() async { // ignore: non_constant_identifier_names ); return true; }; + signalNativeTest(); } @pragma('vm:entry-point') diff --git a/shell/platform/embedder/tests/embedder_unittests_key_event.cc b/shell/platform/embedder/tests/embedder_unittests_key_event.cc index 37358ec1ee53c..270aa83611086 100644 --- a/shell/platform/embedder/tests/embedder_unittests_key_event.cc +++ b/shell/platform/embedder/tests/embedder_unittests_key_event.cc @@ -37,40 +37,44 @@ FlutterKeyEventKind unserializeKind(uint64_t kindInt) { TEST_F(EmbedderTest, CorrectlySerializeKeyData) { auto message_latch = std::make_shared(); char echoed_string[2] = "m"; // Dummy string that holds one char. - FlutterKeyEvent echoed_event; + FlutterKeyEvent echoed_event { + .character = echoed_string, + }; auto native_echo_event = [&](Dart_NativeArguments args) { echoed_event.kind = unserializeKind( tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 1))); + Dart_GetNativeArgument(args, 0))); echoed_event.timestamp = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 2)); + Dart_GetNativeArgument(args, 1)); echoed_event.physical = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 3)); + Dart_GetNativeArgument(args, 2)); echoed_event.logical = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 4)); + Dart_GetNativeArgument(args, 3)); echoed_string[0] = (char)tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 5)); + Dart_GetNativeArgument(args, 4)); echoed_event.synthesized = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 6)); + Dart_GetNativeArgument(args, 5)); message_latch->Signal(); }; - auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext); - context.AddNativeCallback("EchoKeyEvent", CREATE_NATIVE_ENTRY(native_echo_event)); - - // fml::AutoResetWaitableEvent latch; - // context.AddNativeCallback( - // "SignalNativeTest", CREATE_NATIVE_ENTRY(([&latch](Dart_NativeArguments) { - // }))); - + auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext); EmbedderConfigBuilder builder(context); builder.SetSoftwareRendererConfig(); builder.SetDartEntrypoint("key_data_echo"); + context.AddNativeCallback("EchoKeyEvent", CREATE_NATIVE_ENTRY(native_echo_event)); + + fml::AutoResetWaitableEvent ready; + context.AddNativeCallback( + "SignalNativeTest", + CREATE_NATIVE_ENTRY( + [&ready](Dart_NativeArguments args) { ready.Signal(); })); + auto engine = builder.LaunchEngine(); ASSERT_TRUE(engine.is_valid()); + ready.Wait(); const FlutterKeyEvent downEventUpperA { .struct_size = sizeof(FlutterKeyEvent), From 091765c1571b869ee8d4b6606c8c3d6c22c79aa3 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 20 Jan 2021 22:10:39 -0800 Subject: [PATCH 16/34] Complete tests --- shell/platform/embedder/BUILD.gn | 1 - shell/platform/embedder/embedder.h | 14 +- shell/platform/embedder/fixtures/main.dart | 3 +- .../embedder/tests/embedder_unittests.cc | 200 ++++++++++++++++++ .../tests/embedder_unittests_key_event.cc | 104 --------- 5 files changed, 209 insertions(+), 113 deletions(-) delete mode 100644 shell/platform/embedder/tests/embedder_unittests_key_event.cc diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index 193ebc348bd3b..7cbba62bbc5c5 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -192,7 +192,6 @@ if (enable_unittests) { "tests/embedder_test_context_software.cc", "tests/embedder_test_context_software.h", "tests/embedder_unittests.cc", - "tests/embedder_unittests_key_event.cc", "tests/embedder_unittests_util.cc", ] diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index d13858c03065d..a0f4588552460 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -618,7 +618,7 @@ typedef enum { } FlutterKeyEventKind; /// A structure to represent a change of state of a key. -/// +/// /// Sending `FlutterKeyEvent` via `FlutterEngineSendKeyEvent` results in a /// corresponding `FlutterKeyEvent` to be dispatched in the framework. It is /// embedder's responsibility to ensure the regularity of sent events, since the @@ -641,12 +641,12 @@ typedef struct { FlutterKeyEventKind kind; /// The USB HID code for the physical key of the event. /// - /// For the full definition and list of pre-defined physical keys, see + /// For the full definition and list of pre-defined physical keys, see /// `PhysicalKeyboardKey` from the framework. uint64_t physical; /// The key ID for the logical key of this event. /// - /// For the full definition and a list of pre-defined logical keys, see + /// For the full definition and a list of pre-defined logical keys, see /// `LogicalKeyboardKey` from the framework. uint64_t logical; /// Null-terminated character input from the event. Can be null. Ignored for @@ -1618,13 +1618,13 @@ FlutterEngineResult FlutterEngineSendPointerEvent( /// /// @param[in] engine A running engine instance. /// @param[in] event The event data to be sent. This function will no -/// longer access `event` after returning. +/// longer access `event` after returning. /// @param[in] callback The callback invoked by the engine when the /// Flutter application has decided whether it handles /// this event. -/// @param[in] user_data The context associated with the callback. Should -/// not be released until `callback` is invoked. Can -/// be null. +/// @param[in] user_data The context associated with the callback. The exact +/// same value will used to invoke `callback`. Accepts +/// nullptr or a non-pointer value. /// /// @return The result of the call. /// diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index c46a6d7153a84..d6afa3b68dcb4 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -515,6 +515,7 @@ int _serializeKeyChange(KeyChange change) { } } +// Echo the event data with `_echoKeyEvent`, and returns synthesized as handled. @pragma('vm:entry-point') void key_data_echo() async { // ignore: non_constant_identifier_names PlatformDispatcher.instance.onKeyData = (KeyData data) { @@ -526,7 +527,7 @@ void key_data_echo() async { // ignore: non_constant_identifier_names data.character == null ? 0 : data.character!.codeUnitAt(0), data.synthesized, ); - return true; + return data.synthesized; }; signalNativeTest(); } diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 2fe1cb19122d9..574faec8fa00f 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1191,5 +1191,205 @@ TEST_F(EmbedderTest, CanLaunchAndShutdownWithAValidElfSource) { engine.reset(); } +//------------------------------------------------------------------------------ +// Key Data +//------------------------------------------------------------------------------ + +typedef struct { + std::shared_ptr latch; + bool returned; +} KeyEventUserData; + +FlutterKeyEventKind unserializeKeyEventKind(uint64_t kindInt) { + switch(kindInt) { + case 1: + return kFlutterKeyEventKindUp; + case 2: + return kFlutterKeyEventKindDown; + case 3: + return kFlutterKeyEventKindRepeat; + default: + FML_UNREACHABLE(); + return kFlutterKeyEventKindUp; + } +} + +void expect_key_event_eq(const FlutterKeyEvent& subject, const FlutterKeyEvent& baseline) { + EXPECT_EQ(subject.timestamp, baseline.timestamp); + EXPECT_EQ(subject.kind, baseline.kind); + EXPECT_EQ(subject.physical, baseline.physical); + EXPECT_EQ(subject.logical, baseline.logical); + EXPECT_EQ(subject.synthesized, baseline.synthesized); +} + +TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { + auto message_latch = std::make_shared(); + uint64_t echoed_char; + FlutterKeyEvent echoed_event; + + auto native_echo_event = [&](Dart_NativeArguments args) { + echoed_event.kind = unserializeKeyEventKind( + tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 0))); + echoed_event.timestamp = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 1)); + echoed_event.physical = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 2)); + echoed_event.logical = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 3)); + echoed_char = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 4)); + echoed_event.synthesized = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 5)); + + message_latch->Signal(); + }; + + auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext); + EmbedderConfigBuilder builder(context); + builder.SetSoftwareRendererConfig(); + builder.SetDartEntrypoint("key_data_echo"); + fml::AutoResetWaitableEvent ready; + context.AddNativeCallback( + "SignalNativeTest", + CREATE_NATIVE_ENTRY( + [&ready](Dart_NativeArguments args) { ready.Signal(); })); + + context.AddNativeCallback("EchoKeyEvent", CREATE_NATIVE_ENTRY(native_echo_event)); + + auto engine = builder.LaunchEngine(); + ASSERT_TRUE(engine.is_valid()); + ready.Wait(); + + // A normal down event + const FlutterKeyEvent downEventUpperA { + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1, + .kind = kFlutterKeyEventKindDown, + .physical = 0x00070004, + .logical = 0x00000000061, + .character = "A", + .synthesized = false, + }; + FlutterEngineSendKeyEvent(engine.get(), &downEventUpperA, + [](bool handled, void* user_data){}, nullptr); + message_latch->Wait(); + + expect_key_event_eq(echoed_event, downEventUpperA); + EXPECT_EQ(echoed_char, 0x41llu); + + // A repeat event with multi-byte character + const FlutterKeyEvent repeatEventWideChar { + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1000, + .kind = kFlutterKeyEventKindRepeat, + .physical = 0x00070005, + .logical = 0x00000000062, + .character = "∆", + .synthesized = false, + }; + FlutterEngineSendKeyEvent(engine.get(), &repeatEventWideChar, + [](bool handled, void* user_data){}, nullptr); + message_latch->Wait(); + + expect_key_event_eq(echoed_event, repeatEventWideChar); + EXPECT_EQ(echoed_char, 0x2206llu); + + // An up event with no character, synthesized + const FlutterKeyEvent upEvent { + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1000000, + .kind = kFlutterKeyEventKindUp, + .physical = 0x00070006, + .logical = 0x00000000063, + .character = nullptr, + .synthesized = true, + }; + FlutterEngineSendKeyEvent(engine.get(), &upEvent, + [](bool handled, void* user_data){}, nullptr); + message_latch->Wait(); + + expect_key_event_eq(echoed_event, upEvent); + EXPECT_EQ(echoed_char, 0llu); +} + +TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { + auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext); + EmbedderConfigBuilder builder(context); + builder.SetSoftwareRendererConfig(); + builder.SetDartEntrypoint("key_data_echo"); + fml::AutoResetWaitableEvent ready; + context.AddNativeCallback( + "SignalNativeTest", + CREATE_NATIVE_ENTRY( + [&ready](Dart_NativeArguments args) { ready.Signal(); })); + + context.AddNativeCallback("EchoKeyEvent", + CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {})); + + auto engine = builder.LaunchEngine(); + ASSERT_TRUE(engine.is_valid()); + ready.Wait(); + + // Dispatch a single event + FlutterKeyEvent event { + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1000, + .kind = kFlutterKeyEventKindDown, + .physical = 0x00070005, + .logical = 0x00000000062, + .character = nullptr, + }; + + KeyEventUserData user_data1 { + .latch = std::make_shared(), + }; + // Entrypoint `key_data_echo` uses `event.synthesized` as `handled`. + event.synthesized = true; + FlutterEngineSendKeyEvent( + engine.get(), + &event, + [](bool handled, void* untyped_user_data){ + KeyEventUserData* user_data = reinterpret_cast(untyped_user_data); + EXPECT_EQ(handled, true); + user_data->latch->Signal(); + }, + &user_data1); + user_data1.latch->Wait(); + + // Dispatch two events back to back, using the same callback on different + // user_data + KeyEventUserData user_data2 { + .latch = std::make_shared(), + .returned = false, + }; + KeyEventUserData user_data3 { + .latch = std::make_shared(), + .returned = false, + }; + auto callback23 = [](bool handled, void* untyped_user_data){ + KeyEventUserData* user_data = reinterpret_cast(untyped_user_data); + EXPECT_EQ(handled, false); + user_data->latch->Signal(); + user_data->returned = true; + }; + + event.synthesized = false; + FlutterEngineSendKeyEvent( + engine.get(), + &event, + callback23, + &user_data2); + FlutterEngineSendKeyEvent( + engine.get(), + &event, + callback23, + &user_data3); + user_data2.latch->Wait(); + user_data3.latch->Wait(); + EXPECT_TRUE(user_data2.returned); + EXPECT_TRUE(user_data3.returned); +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/embedder/tests/embedder_unittests_key_event.cc b/shell/platform/embedder/tests/embedder_unittests_key_event.cc deleted file mode 100644 index 270aa83611086..0000000000000 --- a/shell/platform/embedder/tests/embedder_unittests_key_event.cc +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2013 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 - -#include "flutter/fml/synchronization/waitable_event.h" -#include "flutter/shell/platform/embedder/embedder.h" -#include "flutter/shell/platform/embedder/tests/embedder_config_builder.h" -#include "flutter/shell/platform/embedder/tests/embedder_test.h" -#include "third_party/dart/runtime/include/dart_api.h" -#include "third_party/tonic/converter/dart_converter.h" -#include "flutter/testing/testing.h" - -#ifdef _WIN32 -// winbase.h defines GetCurrentTime as a macro. -#undef GetCurrentTime -#endif - -namespace flutter { -namespace testing { - -FlutterKeyEventKind unserializeKind(uint64_t kindInt) { - switch(kindInt) { - case 1: - return kFlutterKeyEventKindUp; - case 2: - return kFlutterKeyEventKindDown; - case 3: - return kFlutterKeyEventKindRepeat; - default: - FML_UNREACHABLE(); - return kFlutterKeyEventKindUp; - } -} - -TEST_F(EmbedderTest, CorrectlySerializeKeyData) { - auto message_latch = std::make_shared(); - char echoed_string[2] = "m"; // Dummy string that holds one char. - FlutterKeyEvent echoed_event { - .character = echoed_string, - }; - - auto native_echo_event = [&](Dart_NativeArguments args) { - echoed_event.kind = unserializeKind( - tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 0))); - echoed_event.timestamp = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 1)); - echoed_event.physical = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 2)); - echoed_event.logical = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 3)); - echoed_string[0] = (char)tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 4)); - echoed_event.synthesized = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 5)); - - message_latch->Signal(); - }; - - auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext); - EmbedderConfigBuilder builder(context); - builder.SetSoftwareRendererConfig(); - builder.SetDartEntrypoint("key_data_echo"); - - context.AddNativeCallback("EchoKeyEvent", CREATE_NATIVE_ENTRY(native_echo_event)); - - fml::AutoResetWaitableEvent ready; - context.AddNativeCallback( - "SignalNativeTest", - CREATE_NATIVE_ENTRY( - [&ready](Dart_NativeArguments args) { ready.Signal(); })); - - auto engine = builder.LaunchEngine(); - ASSERT_TRUE(engine.is_valid()); - ready.Wait(); - - const FlutterKeyEvent downEventUpperA { - .struct_size = sizeof(FlutterKeyEvent), - .timestamp = 1, - .kind = kFlutterKeyEventKindDown, - .physical = 0x00070004, - .logical = 0x00000000061, - .character = "A", - .synthesized = false, - }; - FlutterEngineSendKeyEvent(engine.get(), &downEventUpperA, - [](bool handled, void* user_data){ - }, - nullptr); - message_latch->Wait(); - - EXPECT_EQ(echoed_event.timestamp, downEventUpperA.timestamp); - EXPECT_EQ(echoed_event.kind, downEventUpperA.kind); - EXPECT_EQ(echoed_event.physical, downEventUpperA.physical); - EXPECT_EQ(echoed_event.logical, downEventUpperA.logical); - EXPECT_STREQ(echoed_event.character, downEventUpperA.character); - EXPECT_EQ(echoed_event.synthesized, downEventUpperA.synthesized); -} - - -} // namespace testing -} // namespace flutter From f4c7b411153e8c8de61fe973d2653ae9fbf28c2f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 13:04:55 -0800 Subject: [PATCH 17/34] Rename to type --- lib/ui/key.dart | 26 ++-- lib/ui/platform_dispatcher.dart | 3 +- lib/ui/window/key_data.h | 6 +- .../lib/src/engine/keyboard_binding.dart | 24 ++-- lib/web_ui/lib/src/ui/key.dart | 18 +-- lib/web_ui/test/keyboard_converter_test.dart | 124 +++++++++--------- shell/common/fixtures/shell_test.dart | 2 +- shell/platform/embedder/embedder.cc | 12 +- shell/platform/embedder/fixtures/main.dart | 10 +- 9 files changed, 112 insertions(+), 113 deletions(-) diff --git a/lib/ui/key.dart b/lib/ui/key.dart index ba714c008d7fc..19641476bd1ed 100644 --- a/lib/ui/key.dart +++ b/lib/ui/key.dart @@ -7,8 +7,8 @@ part of dart.ui; /// How the key has changed since the last report. -// Must match the KeyChange enum in ui/window/key_data.h. -enum KeyChange { +// Must match the KeyEventType enum in ui/window/key_data.h. +enum KeyEventType { /// The key is pressed. down, @@ -19,12 +19,12 @@ enum KeyChange { repeat, } -/// Information about the change of a key. +/// Information about a key event. class KeyData { /// Creates an object that represents the change of a key. const KeyData({ required this.timeStamp, - required this.change, + required this.type, required this.physical, required this.logical, required this.character, @@ -37,8 +37,8 @@ class KeyData { /// the key press or release happens. final Duration timeStamp; - /// How the key has changed since the last report. - final KeyChange change; + /// The type of the event. + final KeyEventType type; /// The key code for the physical key that has changed. final int physical; @@ -64,7 +64,7 @@ class KeyData { /// the state returned by the system, Flutter will synthesize a corresponding /// event to synchronize the state without breaking the event model. /// - /// As another example, macOS treats CapsLock in a special way by sending + /// As another example, macOS treats CapsLock in a special way by sending /// down and up events at the down of alterate presses to indicate the /// direction in which the lock is toggled instead of that the physical key is /// going. Flutter normalizes the behavior by converting a native down event @@ -80,13 +80,13 @@ class KeyData { final bool synthesized; @override - String toString() => 'KeyData(change: ${_changeToString(change)}, physical: 0x${physical.toRadixString(16)}, ' + String toString() => 'KeyData(change: ${_changeToString(type)}, physical: 0x${physical.toRadixString(16)}, ' 'logical: 0x${logical.toRadixString(16)}, character: $character)'; /// Returns a complete textual description of the information in this object. String toStringFull() { return '$runtimeType(' - 'change: ${_changeToString(change)}, ' + 'change: ${_changeToString(type)}, ' 'timeStamp: $timeStamp, ' 'physical: 0x${physical.toRadixString(16)}, ' 'logical: 0x${logical.toRadixString(16)}, ' @@ -95,13 +95,13 @@ class KeyData { ')'; } - static String _changeToString(KeyChange change) { + static String _changeToString(KeyEventType change) { switch (change) { - case KeyChange.up: + case KeyEventType.up: return 'up'; - case KeyChange.down: + case KeyEventType.down: return 'down'; - case KeyChange.repeat: + case KeyEventType.repeat: return 'repeat'; } } diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index 73faab867736e..703486093cee5 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -375,7 +375,6 @@ class PlatformDispatcher { static const int _kKeyDataFieldCount = 5; // KeyData packet structure: - // | ResponseId | (1 field) // | CharDataSize | (1 field) // | Key Data | (_kKeyDataFieldCount fields) // | CharData | (CharDataSize bits) @@ -389,7 +388,7 @@ class PlatformDispatcher { final KeyData keyData = KeyData( timeStamp: Duration(microseconds: packet.getUint64(kStride * offset++, _kFakeHostEndian)), - change: KeyChange.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], + type: KeyEventType.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], physical: packet.getUint64(kStride * offset++, _kFakeHostEndian), logical: packet.getUint64(kStride * offset++, _kFakeHostEndian), character: character, diff --git a/lib/ui/window/key_data.h b/lib/ui/window/key_data.h index 0237350b1bf7e..1dd1892acab6c 100644 --- a/lib/ui/window/key_data.h +++ b/lib/ui/window/key_data.h @@ -15,8 +15,8 @@ static constexpr int kBytesPerKeyField = sizeof(int64_t); // The change of the key event, used by KeyData. // -// Must match the KeyChange enum in ui/key.dart. -enum class KeyChange : int64_t { +// Must match the KeyEventType enum in ui/key.dart. +enum class KeyEventType : int64_t { kDown = 0, kUp, kRepeat, @@ -31,7 +31,7 @@ enum class KeyChange : int64_t { struct alignas(8) KeyData { // Timestamp in microseconds from an arbitrary and consistant start point uint64_t timestamp; - KeyChange change; + KeyEventType change; uint64_t physical; uint64_t logical; // True if the event does not correspond to a native event. diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart index ef35962121b7c..1a373e66a20bf 100644 --- a/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -302,7 +302,7 @@ class KeyboardConverter { _keydownCancelDuration, () => ui.KeyData( timeStamp: currentTimeStamp + _keydownCancelDuration, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: physicalKey, logical: logicalKey, character: null, @@ -362,7 +362,7 @@ class KeyboardConverter { final int? lastLogicalRecord = _pressingRecords[physicalKey]; - ui.KeyChange change; + ui.KeyEventType change; if (_shouldSynthesizeCapsLockUp() && event.code! == _kPhysicalCapsLock) { // Case 1: Handle CapsLock on macOS @@ -374,7 +374,7 @@ class KeyboardConverter { Duration.zero, () => ui.KeyData( timeStamp: timeStamp, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: physicalKey, logical: logicalKey, character: null, @@ -384,16 +384,16 @@ class KeyboardConverter { _pressingRecords.remove(physicalKey); } ); - change = ui.KeyChange.down; + change = ui.KeyEventType.down; } else if (isPhysicalDown) { // Case 2: Handle key down of normal keys - change = ui.KeyChange.down; + change = ui.KeyEventType.down; if (lastLogicalRecord != null) { // This physical key is being pressed according to the record. if (event.repeat ?? false) { // A normal repeated key. - change = ui.KeyChange.repeat; + change = ui.KeyEventType.repeat; } else { // A non-repeated key has been pressed that has the exact physical key as // a currently pressed one, usually indicating multiple keyboards are @@ -414,20 +414,20 @@ class KeyboardConverter { return false; } - change = ui.KeyChange.up; + change = ui.KeyEventType.up; } final int? nextLogicalRecord; switch (change) { - case ui.KeyChange.down: + case ui.KeyEventType.down: assert(lastLogicalRecord == null); nextLogicalRecord = logicalKey; break; - case ui.KeyChange.up: + case ui.KeyEventType.up: assert(lastLogicalRecord != null); nextLogicalRecord = null; break; - case ui.KeyChange.repeat: + case ui.KeyEventType.repeat: assert(lastLogicalRecord != null); nextLogicalRecord = lastLogicalRecord; break; @@ -451,7 +451,7 @@ class KeyboardConverter { dispatchKeyData(ui.KeyData( timeStamp: timeStamp, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: physicalKey, logical: logicalKey, character: null, @@ -477,7 +477,7 @@ class KeyboardConverter { change: change, physical: physicalKey, logical: lastLogicalRecord ?? logicalKey, - character: change == ui.KeyChange.up ? null : character, + character: change == ui.KeyEventType.up ? null : character, synthesized: false, ); diff --git a/lib/web_ui/lib/src/ui/key.dart b/lib/web_ui/lib/src/ui/key.dart index 2e44a316552a8..9ae86116ca6a2 100644 --- a/lib/web_ui/lib/src/ui/key.dart +++ b/lib/web_ui/lib/src/ui/key.dart @@ -7,8 +7,8 @@ part of ui; /// How the key has changed since the last report. -// Must match the KeyChange enum in ui/window/key_data.h. -enum KeyChange { +// Must match the KeyEventType enum in ui/window/key_data.h. +enum KeyEventType { /// The key is pressed. down, @@ -33,12 +33,12 @@ class KeyData { /// Time of event dispatch, relative to an arbitrary timeline. /// - /// For [KeyChange.synchronize] and [KeyChange.cancel] events, the [timeStamp] + /// For [KeyEventType.synchronize] and [KeyEventType.cancel] events, the [timeStamp] /// might not be the actual time that the key press or release happens. final Duration timeStamp; /// How the key has changed since the last report. - final KeyChange change; + final KeyEventType change; /// The key code for the physical key that has changed. final int physical; @@ -64,7 +64,7 @@ class KeyData { /// the state returned by the system, Flutter will synthesize a corresponding /// event to synchronize the state without breaking the event model. /// - /// As another example, macOS treats CapsLock in a special way by sending + /// As another example, macOS treats CapsLock in a special way by sending /// down and up events at the down of alterate presses to indicate the /// direction in which the lock is toggled instead of that the physical key is /// going. Flutter normalizes the behavior by converting a native down event @@ -95,13 +95,13 @@ class KeyData { ')'; } - static String _changeToString(KeyChange change) { + static String _changeToString(KeyEventType change) { switch (change) { - case KeyChange.up: + case KeyEventType.up: return 'up'; - case KeyChange.down: + case KeyEventType.down: return 'down'; - case KeyChange.repeat: + case KeyEventType.repeat: return 'repeat'; } } diff --git a/lib/web_ui/test/keyboard_converter_test.dart b/lib/web_ui/test/keyboard_converter_test.dart index 3b0003a1edbc8..daa09e9dd89c5 100644 --- a/lib/web_ui/test/keyboard_converter_test.dart +++ b/lib/web_ui/test/keyboard_converter_test.dart @@ -59,7 +59,7 @@ void testMain() { ); expectKeyData(keyDataList.last, timeStamp: Duration(milliseconds: 1), - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -72,7 +72,7 @@ void testMain() { ); expectKeyData(keyDataList.last, timeStamp: Duration(milliseconds: 1, microseconds: 500), - change: ui.KeyChange.repeat, + change: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -85,7 +85,7 @@ void testMain() { ); expectKeyData(keyDataList.last, timeStamp: Duration(seconds: 1, milliseconds: 500), - change: ui.KeyChange.repeat, + change: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -98,7 +98,7 @@ void testMain() { ); expectKeyData(keyDataList.last, timeStamp: Duration(seconds: 2, microseconds: 500), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -114,7 +114,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -122,7 +122,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'A', kShift)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'A', @@ -130,7 +130,7 @@ void testMain() { converter.handleEvent(keyRepeatedDownEvent('KeyA', 'A', kShift)); expectKeyData(keyDataList.last, - change: ui.KeyChange.repeat, + change: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'A', @@ -138,7 +138,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -146,7 +146,7 @@ void testMain() { converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); expectKeyData(keyDataList.last, - change: ui.KeyChange.repeat, + change: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -154,7 +154,7 @@ void testMain() { converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); expectKeyData(keyDataList.last, - change: ui.KeyChange.repeat, + change: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -162,7 +162,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -177,7 +177,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -185,7 +185,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ShiftRight', 'Shift', kShift, kLocationRight)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalShiftRight, logical: kLogicalShiftRight, character: null, @@ -193,7 +193,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -201,7 +201,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ShiftRight', 'Shift', 0, kLocationRight)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalShiftRight, logical: kLogicalShiftRight, character: null, @@ -216,7 +216,7 @@ void testMain() { converter.handleEvent(keyDownEvent('Digit1', '1', 0, 0)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalDigit1, logical: kLogicalDigit1, character: '1', @@ -224,7 +224,7 @@ void testMain() { converter.handleEvent(keyDownEvent('Numpad1', '1', 0, kLocationNumpad)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalNumpad1, logical: kLogicalNumpad1, character: '1', @@ -232,7 +232,7 @@ void testMain() { converter.handleEvent(keyUpEvent('Digit1', '1', 0, 0)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalDigit1, logical: kLogicalDigit1, character: null, @@ -240,7 +240,7 @@ void testMain() { converter.handleEvent(keyUpEvent('Numpad1', '1', 0, kLocationNumpad)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalNumpad1, logical: kLogicalNumpad1, character: null, @@ -257,7 +257,7 @@ void testMain() { converter.handleEvent(keyDownEvent('Tab', 'Tab')..onPreventDefault = onPreventDefault); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalTab, logical: kLogicalTab, character: null, @@ -267,7 +267,7 @@ void testMain() { converter.handleEvent(keyUpEvent('Tab', 'Tab')..onPreventDefault = onPreventDefault); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalTab, logical: kLogicalTab, character: null, @@ -294,7 +294,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyE', 'Dead', kAlt)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyE, logical: kLogicalAltE, character: null, @@ -302,7 +302,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyE', 'Dead', kAlt)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyE, logical: kLogicalAltE, character: null, @@ -310,7 +310,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyU', 'Dead', kAlt)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyU, logical: kLogicalAltU, character: null, @@ -318,7 +318,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyU', 'Dead', kAlt)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyU, logical: kLogicalAltU, character: null, @@ -330,7 +330,7 @@ void testMain() { // testing. converter.handleEvent(keyDownEvent('KeyE', 'Dead', kAlt | kShift)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyE, logical: kLogicalAltShiftE, character: null, @@ -340,7 +340,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyE', 'e', kShift)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyE, logical: kLogicalAltShiftE, character: null, @@ -365,7 +365,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -405,7 +405,7 @@ void testMain() { keyDataList.clear(); converter.handleEvent(keyDownEvent('KeyA', 'a')); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -413,7 +413,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyU', 'u')); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyU, logical: kLogicalKeyU, character: 'u', @@ -429,7 +429,7 @@ void testMain() { converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -439,7 +439,7 @@ void testMain() { async.elapse(Duration(microseconds: 1)); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -450,7 +450,7 @@ void testMain() { converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -460,7 +460,7 @@ void testMain() { async.elapse(Duration(microseconds: 1)); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -472,7 +472,7 @@ void testMain() { converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -495,7 +495,7 @@ void testMain() { converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -508,7 +508,7 @@ void testMain() { converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -520,7 +520,7 @@ void testMain() { converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -528,7 +528,7 @@ void testMain() { converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -547,7 +547,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a', kMeta)..timeStamp = 200); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 200), - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -559,7 +559,7 @@ void testMain() { async.elapse(Duration(milliseconds: 2500)); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 1200), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -570,7 +570,7 @@ void testMain() { converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 2700); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2700), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalMetaLeft, logical: kLogicalMetaLeft, character: null, @@ -581,7 +581,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 2800); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2800), - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -591,7 +591,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2900); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2900), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -621,7 +621,7 @@ void testMain() { async.elapse(Duration(milliseconds: 2500)); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 1700), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -632,7 +632,7 @@ void testMain() { converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 3200); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 3200), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalMetaLeft, logical: kLogicalMetaLeft, character: null, @@ -643,7 +643,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 3300); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 3300), - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -653,7 +653,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 3400); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 3400), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -672,7 +672,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a', kCtrl)..timeStamp = 200); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 200), - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -686,7 +686,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 800); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 800), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -699,7 +699,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 2800); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2800), - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -709,7 +709,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2900); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2900), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -725,7 +725,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ScrollLock', 'ScrollLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalScrollLock, logical: kLogicalScrollLock, character: null, @@ -738,7 +738,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ScrollLock', 'ScrollLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalScrollLock, logical: kLogicalScrollLock, character: null, @@ -747,7 +747,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ScrollLock', 'ScrollLock')); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalScrollLock, logical: kLogicalScrollLock, character: null, @@ -755,7 +755,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ScrollLock', 'ScrollLock')); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalScrollLock, logical: kLogicalScrollLock, character: null, @@ -770,7 +770,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ShiftRight', 'Shift', kShift, kLocationRight)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalShiftRight, logical: kLogicalShiftRight, character: null, @@ -778,7 +778,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -790,21 +790,21 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')); expect(keyDataList, hasLength(3)); expectKeyData(keyDataList[0], - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, synthesized: true, ); expectKeyData(keyDataList[1], - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalShiftRight, logical: kLogicalShiftRight, character: null, synthesized: true, ); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -901,7 +901,7 @@ const kScrollLock = 0x4; void expectKeyData( ui.KeyData target, { - required ui.KeyChange change, + required ui.KeyEventType change, required int physical, required int logical, required String? character, diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index 408e53bf7a713..9be515458d956 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -40,7 +40,7 @@ void onPointerDataPacketMain() { PlatformDispatcher.instance.onPointerDataPacket = (PointerDataPacket packet) { List sequence = []; for (PointerData data in packet.data) { - sequence.add(PointerChange.values.indexOf(data.change)); + sequence.add(PointerChange.values.indexOf(data.type)); } nativeOnPointerDataPacket(sequence); }; diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index f2b0a09716baf..e5a789a224d15 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1492,16 +1492,16 @@ FlutterEngineResult FlutterEngineSendPointerEvent( "running Flutter application."); } -inline flutter::KeyChange ToKeyChange(FlutterKeyEventKind key_change) { +inline flutter::KeyEventType ToKeyEventType(FlutterKeyEventKind key_change) { switch (key_change) { case kFlutterKeyEventKindUp: - return flutter::KeyChange::kUp; + return flutter::KeyEventType::kUp; case kFlutterKeyEventKindDown: - return flutter::KeyChange::kDown; + return flutter::KeyEventType::kDown; case kFlutterKeyEventKindRepeat: - return flutter::KeyChange::kRepeat; + return flutter::KeyEventType::kRepeat; } - return flutter::KeyChange::kUp; + return flutter::KeyEventType::kUp; } // The number of bytes that should be able to fully store character data. @@ -1537,7 +1537,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent( flutter::KeyData key_data; key_data.Clear(); key_data.timestamp = (uint64_t)SAFE_ACCESS(event, timestamp, 0); - key_data.change = ToKeyChange( + key_data.change = ToKeyEventType( SAFE_ACCESS(event, kind, FlutterKeyEventKind::kFlutterKeyEventKindUp)); key_data.physical = SAFE_ACCESS(event, physical, 0); key_data.logical = SAFE_ACCESS(event, logical, 0); diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index d6afa3b68dcb4..4fa2fb4dab6c1 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -504,13 +504,13 @@ void _echoKeyEvent( bool synthesized) native 'EchoKeyEvent'; -int _serializeKeyChange(KeyChange change) { +int _serializeKeyEventType(KeyEventType change) { switch(change) { - case KeyChange.up: + case KeyEventType.up: return 1; - case KeyChange.down: + case KeyEventType.down: return 2; - case KeyChange.repeat: + case KeyEventType.repeat: return 3; } } @@ -520,7 +520,7 @@ int _serializeKeyChange(KeyChange change) { void key_data_echo() async { // ignore: non_constant_identifier_names PlatformDispatcher.instance.onKeyData = (KeyData data) { _echoKeyEvent( - _serializeKeyChange(data.change), + _serializeKeyEventType(data.type), data.timeStamp.inMicroseconds, data.physical, data.logical, From 7de3628639165050c961bab43afdf5813d2e47ff Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 14:23:46 -0800 Subject: [PATCH 18/34] Rename callback --- lib/ui/key.dart | 27 +++++++++++----------- lib/ui/window/key_data.cc | 2 +- lib/ui/window/key_data.h | 6 ++--- lib/ui/window/key_data_packet.cc | 1 + lib/ui/window/key_data_packet.h | 3 --- lib/ui/window/platform_configuration.cc | 2 +- lib/ui/window/platform_configuration.h | 7 ++++-- lib/web_ui/lib/src/ui/key.dart | 20 ++++++++-------- runtime/runtime_controller.cc | 2 +- runtime/runtime_controller.h | 2 +- shell/common/engine.cc | 2 +- shell/common/engine.h | 2 +- shell/common/fixtures/shell_test.dart | 2 +- shell/common/platform_view.cc | 2 +- shell/common/platform_view.h | 6 +++-- shell/common/shell.cc | 2 +- shell/common/shell.h | 2 +- shell/common/shell_unittests.cc | 2 +- shell/platform/embedder/embedder.cc | 6 ++--- shell/platform/embedder/embedder_engine.cc | 2 +- shell/platform/embedder/embedder_engine.h | 2 +- 21 files changed, 53 insertions(+), 49 deletions(-) diff --git a/lib/ui/key.dart b/lib/ui/key.dart index 19641476bd1ed..e6a349d1a2c9d 100644 --- a/lib/ui/key.dart +++ b/lib/ui/key.dart @@ -6,7 +6,7 @@ part of dart.ui; -/// How the key has changed since the last report. +/// The type of a key event. // Must match the KeyEventType enum in ui/window/key_data.h. enum KeyEventType { /// The key is pressed. @@ -21,7 +21,7 @@ enum KeyEventType { /// Information about a key event. class KeyData { - /// Creates an object that represents the change of a key. + /// Creates an object that represents a key event. const KeyData({ required this.timeStamp, required this.type, @@ -60,17 +60,18 @@ class KeyData { /// /// For example, some key downs or ups might be lost when the window loses /// focus. Some platforms provides ways to query whether a key is being held. - /// If Flutter detects an inconsistancy between the state Flutter records and - /// the state returned by the system, Flutter will synthesize a corresponding - /// event to synchronize the state without breaking the event model. + /// If the embedder detects an inconsistancy between its internal record and + /// the state returned by the system, the embedder will synthesize a + /// corresponding event to synchronize the state without breaking the event + /// model. /// /// As another example, macOS treats CapsLock in a special way by sending /// down and up events at the down of alterate presses to indicate the /// direction in which the lock is toggled instead of that the physical key is - /// going. Flutter normalizes the behavior by converting a native down event - /// into a down event followed immediately by a synthesized up event, and - /// the native up event also into a down event followed immediately by a - /// synthesized up event. + /// going. A macOS embedder should normalize the behavior by converting a + /// native down event into a down event followed immediately by a synthesized + /// up event, and the native up event also into a down event followed + /// immediately by a synthesized up event. /// /// Synthesized events do not have a trustworthy [timeStamp], and should not be /// processed as if the key actually went down or up at the time of the @@ -80,13 +81,13 @@ class KeyData { final bool synthesized; @override - String toString() => 'KeyData(change: ${_changeToString(type)}, physical: 0x${physical.toRadixString(16)}, ' + String toString() => 'KeyData(type: ${_typeToString(type)}, physical: 0x${physical.toRadixString(16)}, ' 'logical: 0x${logical.toRadixString(16)}, character: $character)'; /// Returns a complete textual description of the information in this object. String toStringFull() { return '$runtimeType(' - 'change: ${_changeToString(type)}, ' + 'type: ${_typeToString(type)}, ' 'timeStamp: $timeStamp, ' 'physical: 0x${physical.toRadixString(16)}, ' 'logical: 0x${logical.toRadixString(16)}, ' @@ -95,8 +96,8 @@ class KeyData { ')'; } - static String _changeToString(KeyEventType change) { - switch (change) { + static String _typeToString(KeyEventType type) { + switch (type) { case KeyEventType.up: return 'up'; case KeyEventType.down: diff --git a/lib/ui/window/key_data.cc b/lib/ui/window/key_data.cc index 875f89f7522c2..2dacd8d7bea43 100644 --- a/lib/ui/window/key_data.cc +++ b/lib/ui/window/key_data.cc @@ -4,7 +4,7 @@ #include "flutter/lib/ui/window/key_data.h" -#include +#include namespace flutter { diff --git a/lib/ui/window/key_data.h b/lib/ui/window/key_data.h index 1dd1892acab6c..a9220a7c225cb 100644 --- a/lib/ui/window/key_data.h +++ b/lib/ui/window/key_data.h @@ -5,7 +5,7 @@ #ifndef FLUTTER_LIB_UI_WINDOW_KEY_DATA_H_ #define FLUTTER_LIB_UI_WINDOW_KEY_DATA_H_ -#include +#include namespace flutter { @@ -31,7 +31,7 @@ enum class KeyEventType : int64_t { struct alignas(8) KeyData { // Timestamp in microseconds from an arbitrary and consistant start point uint64_t timestamp; - KeyEventType change; + KeyEventType type; uint64_t physical; uint64_t logical; // True if the event does not correspond to a native event. @@ -39,7 +39,7 @@ struct alignas(8) KeyData { // The value is 1 for true, and 0 for false. uint64_t synthesized; - // Set all contents of `Keydata` to 0. + // Sets all contents of `Keydata` to 0. void Clear(); }; diff --git a/lib/ui/window/key_data_packet.cc b/lib/ui/window/key_data_packet.cc index a476be497a3e7..2b67c43fd303c 100644 --- a/lib/ui/window/key_data_packet.cc +++ b/lib/ui/window/key_data_packet.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/lib/ui/window/key_data_packet.h" + #include "flutter/fml/logging.h" namespace flutter { diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_packet.h index 735bebd1d2cc1..aae2507e0a2a6 100644 --- a/lib/ui/window/key_data_packet.h +++ b/lib/ui/window/key_data_packet.h @@ -5,7 +5,6 @@ #ifndef FLUTTER_LIB_UI_WINDOW_KEY_DATA_MESSAGE_H_ #define FLUTTER_LIB_UI_WINDOW_KEY_DATA_MESSAGE_H_ -#include #include #include @@ -14,8 +13,6 @@ namespace flutter { -typedef std::function KeyDataPacketCallback; - class KeyDataPacket { public: // Build a KeyDataPacket by incrementally fill in data. diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 0eee18ce4a8f5..70219eb9a687d 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -360,7 +360,7 @@ void PlatformConfiguration::DispatchSemanticsAction(int32_t id, } uint64_t PlatformConfiguration::RegisterKeyDataResponse( - KeyDataPacketCallback callback) { + KeyDataResponse callback) { uint64_t response_id = next_key_response_id_++; pending_key_responses_[response_id] = std::move(callback); return response_id; diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 8a9f30f9c04d5..0a30c7620ba02 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_LIB_UI_WINDOW_PLATFORM_CONFIGURATION_H_ #define FLUTTER_LIB_UI_WINDOW_PLATFORM_CONFIGURATION_H_ +#include #include #include #include @@ -22,6 +23,8 @@ class FontCollection; class PlatformMessage; class Scene; +typedef std::function KeyDataResponse; + //-------------------------------------------------------------------------- /// @brief An enum for defining the different kinds of accessibility features /// that can be enabled by the platform. @@ -339,7 +342,7 @@ class PlatformConfiguration final { /// @return The response ID to be associated with the callback. Using this /// ID in CompleteKeyDataResponse will invoke the callback. /// - uint64_t RegisterKeyDataResponse(KeyDataPacketCallback callback); + uint64_t RegisterKeyDataResponse(KeyDataResponse callback); //---------------------------------------------------------------------------- /// @brief Notifies the framework that it is time to begin working on a @@ -467,7 +470,7 @@ class PlatformConfiguration final { // We use id 0 to mean that no response is expected. uint64_t next_key_response_id_ = 1; - std::unordered_map + std::unordered_map pending_key_responses_; }; diff --git a/lib/web_ui/lib/src/ui/key.dart b/lib/web_ui/lib/src/ui/key.dart index 9ae86116ca6a2..14fb39393f904 100644 --- a/lib/web_ui/lib/src/ui/key.dart +++ b/lib/web_ui/lib/src/ui/key.dart @@ -6,7 +6,7 @@ part of ui; -/// How the key has changed since the last report. +/// The type of a key event. // Must match the KeyEventType enum in ui/window/key_data.h. enum KeyEventType { /// The key is pressed. @@ -19,12 +19,12 @@ enum KeyEventType { repeat, } -/// Information about the change of a key. +/// Information about a key event. class KeyData { - /// Creates an object that represents the change of a key. + /// Creates an object that represents a key event. const KeyData({ required this.timeStamp, - required this.change, + required this.type, required this.physical, required this.logical, required this.character, @@ -37,8 +37,8 @@ class KeyData { /// might not be the actual time that the key press or release happens. final Duration timeStamp; - /// How the key has changed since the last report. - final KeyEventType change; + /// The type of the event. + final KeyEventType type; /// The key code for the physical key that has changed. final int physical; @@ -80,13 +80,13 @@ class KeyData { final bool synthesized; @override - String toString() => 'KeyData(change: ${_changeToString(change)}, physical: 0x${physical.toRadixString(16)}, ' + String toString() => 'KeyData(type: ${_typeToString(type)}, physical: 0x${physical.toRadixString(16)}, ' 'logical: 0x${logical.toRadixString(16)}, character: $character)'; /// Returns a complete textual description of the information in this object. String toStringFull() { return '$runtimeType(' - 'change: ${_changeToString(change)}, ' + 'type: ${_typeToString(type)}, ' 'timeStamp: $timeStamp, ' 'physical: 0x${physical.toRadixString(16)}, ' 'logical: 0x${logical.toRadixString(16)}, ' @@ -95,8 +95,8 @@ class KeyData { ')'; } - static String _changeToString(KeyEventType change) { - switch (change) { + static String _typeToString(KeyEventType type) { + switch (type) { case KeyEventType.up: return 'up'; case KeyEventType.down: diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 91d214e25ea37..45c7050abf690 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -264,7 +264,7 @@ bool RuntimeController::DispatchPointerDataPacket( bool RuntimeController::DispatchKeyDataPacket( const KeyDataPacket& packet, - KeyDataPacketCallback callback) { + KeyDataResponse callback) { if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataPacket", "mode", "basic"); diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 1057eb35bb682..6793b1a32f3b1 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -436,7 +436,7 @@ class RuntimeController : public PlatformConfigurationClient { /// an isolate is not running. /// bool DispatchKeyDataPacket(const KeyDataPacket& packet, - KeyDataPacketCallback callback); + KeyDataResponse callback); //---------------------------------------------------------------------------- /// @brief Dispatch the semantics action to the specified accessibility diff --git a/shell/common/engine.cc b/shell/common/engine.cc index e44fef37f9af7..7fa1dbe38a440 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -417,7 +417,7 @@ void Engine::DispatchPointerDataPacket( void Engine::DispatchKeyDataPacket( std::unique_ptr packet, - KeyDataPacketCallback callback) { + KeyDataResponse callback) { TRACE_EVENT0("flutter", "Engine::DispatchKeyDataPacket"); if (runtime_controller_) { runtime_controller_->DispatchKeyDataPacket(*packet, std::move(callback)); diff --git a/shell/common/engine.h b/shell/common/engine.h index ae86e330f5d29..43d56b822eda4 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -740,7 +740,7 @@ class Engine final : public RuntimeDelegate, /// to handle this key data. /// void DispatchKeyDataPacket(std::unique_ptr packet, - KeyDataPacketCallback callback); + KeyDataResponse callback); //---------------------------------------------------------------------------- /// @brief Notifies the engine that the embedder encountered an diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index 9be515458d956..408e53bf7a713 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -40,7 +40,7 @@ void onPointerDataPacketMain() { PlatformDispatcher.instance.onPointerDataPacket = (PointerDataPacket packet) { List sequence = []; for (PointerData data in packet.data) { - sequence.add(PointerChange.values.indexOf(data.type)); + sequence.add(PointerChange.values.indexOf(data.change)); } nativeOnPointerDataPacket(sequence); }; diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index e83e10dd42a03..53a3726aaf0ca 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -44,7 +44,7 @@ void PlatformView::DispatchPointerDataPacket( void PlatformView::DispatchKeyDataPacket( std::unique_ptr packet, - KeyDataPacketCallback callback) { + KeyDataResponse callback) { delegate_.OnPlatformViewDispatchKeyDataPacket(std::move(packet), std::move(callback)); } diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index eeba64c94f65f..ac8e7338dd925 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -6,6 +6,7 @@ #define COMMON_PLATFORM_VIEW_H_ #include +#include #include "flow/embedded_views.h" #include "flutter/common/graphics/texture.h" @@ -53,6 +54,7 @@ class PlatformView { /// class Delegate { public: + using KeyDataResponse = std::function; //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view was created /// with the given render surface. This surface is platform @@ -138,7 +140,7 @@ class PlatformView { /// virtual void OnPlatformViewDispatchKeyDataPacket( std::unique_ptr packet, - KeyDataPacketCallback callback) = 0; + std::function callback) = 0; //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view has encountered @@ -599,7 +601,7 @@ class PlatformView { /// @param[in] packet The key data packet to dispatch to the framework. /// void DispatchKeyDataPacket(std::unique_ptr packet, - KeyDataPacketCallback callback); + Delegate::KeyDataResponse callback); //-------------------------------------------------------------------------- /// @brief Used by the embedder to specify a texture that it wants the diff --git a/shell/common/shell.cc b/shell/common/shell.cc index b69feca3e1ff4..d1adb4eedc1a7 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -964,7 +964,7 @@ void Shell::OnPlatformViewDispatchPointerDataPacket( // |PlatformView::Delegate| void Shell::OnPlatformViewDispatchKeyDataPacket( std::unique_ptr packet, - KeyDataPacketCallback callback) { + std::function callback) { TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchKeyDataPacket"); FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); diff --git a/shell/common/shell.h b/shell/common/shell.h index c221c5d89282b..716f5c72d3aaf 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -507,7 +507,7 @@ class Shell final : public PlatformView::Delegate, // |PlatformView::Delegate| void OnPlatformViewDispatchKeyDataPacket( std::unique_ptr packet, - KeyDataPacketCallback callback) override; + std::function callback) override; // |PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction( diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index a7d19dc749aa2..0b9a98f211567 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -64,7 +64,7 @@ class MockPlatformViewDelegate : public PlatformView::Delegate { MOCK_METHOD2(OnPlatformViewDispatchKeyDataPacket, void(std::unique_ptr packet, - KeyDataPacketCallback callback)); + KeyDataResponse callback)); MOCK_METHOD3(OnPlatformViewDispatchSemanticsAction, void(int32_t id, diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index e5a789a224d15..3aa3d9d182d48 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1492,8 +1492,8 @@ FlutterEngineResult FlutterEngineSendPointerEvent( "running Flutter application."); } -inline flutter::KeyEventType ToKeyEventType(FlutterKeyEventKind key_change) { - switch (key_change) { +inline flutter::KeyEventType ToKeyEventType(FlutterKeyEventKind event_kind) { + switch (event_kind) { case kFlutterKeyEventKindUp: return flutter::KeyEventType::kUp; case kFlutterKeyEventKindDown: @@ -1537,7 +1537,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent( flutter::KeyData key_data; key_data.Clear(); key_data.timestamp = (uint64_t)SAFE_ACCESS(event, timestamp, 0); - key_data.change = ToKeyEventType( + key_data.type = ToKeyEventType( SAFE_ACCESS(event, kind, FlutterKeyEventKind::kFlutterKeyEventKindUp)); key_data.physical = SAFE_ACCESS(event, physical, 0); key_data.logical = SAFE_ACCESS(event, logical, 0); diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index 4c14cd9a42a3e..c1e40cc41b04d 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -138,7 +138,7 @@ bool EmbedderEngine::DispatchPointerDataPacket( bool EmbedderEngine::DispatchKeyDataPacket( std::unique_ptr packet, - KeyDataPacketCallback callback) { + KeyDataResponse callback) { if (!IsValid() || !packet) { return false; } diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index b25f76e9f5466..a06d74e383472 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -61,7 +61,7 @@ class EmbedderEngine { std::unique_ptr packet); bool DispatchKeyDataPacket(std::unique_ptr packet, - KeyDataPacketCallback callback); + KeyDataResponse callback); bool SendPlatformMessage(fml::RefPtr message); From 86c4e9b5a3f64ac26a5c9b1e85b221876bc43b00 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 14:52:07 -0800 Subject: [PATCH 19/34] Simplify KeyDataPacket's API. --- lib/ui/platform_dispatcher.dart | 5 +---- lib/ui/window/key_data_packet.cc | 20 ++++++++------------ lib/ui/window/key_data_packet.h | 18 +++++++++--------- shell/platform/embedder/embedder.cc | 10 +++------- 4 files changed, 21 insertions(+), 32 deletions(-) diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index 703486093cee5..612de46431bb0 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -374,10 +374,7 @@ class PlatformDispatcher { // * HardwareKeyboard.java static const int _kKeyDataFieldCount = 5; - // KeyData packet structure: - // | CharDataSize | (1 field) - // | Key Data | (_kKeyDataFieldCount fields) - // | CharData | (CharDataSize bits) + // The packet structure is described in `key_data_packet.h`. static KeyData _unpackKeyData(ByteData packet) { const int kStride = Int64List.bytesPerElement; diff --git a/lib/ui/window/key_data_packet.cc b/lib/ui/window/key_data_packet.cc index 2b67c43fd303c..ca54e23ad5517 100644 --- a/lib/ui/window/key_data_packet.cc +++ b/lib/ui/window/key_data_packet.cc @@ -4,27 +4,23 @@ #include "flutter/lib/ui/window/key_data_packet.h" +#include + #include "flutter/fml/logging.h" namespace flutter { -KeyDataPacket::KeyDataPacket(size_t character_data_size) - : data_(sizeof(uint64_t) + sizeof(KeyData) + character_data_size) { - uint64_t size64 = character_data_size; - memcpy(&data_[CharacterSizeStart_()], &size64, sizeof(size64)); -} - -KeyDataPacket::~KeyDataPacket() = default; - -void KeyDataPacket::SetKeyData(const KeyData& event) { +KeyDataPacket::KeyDataPacket(const KeyData& event, const char* character) { + uint64_t char_size = character == nullptr ? 0 : strlen(character); + data_.resize(sizeof(uint64_t) + sizeof(KeyData) + char_size); + memcpy(&data_[CharacterSizeStart_()], &char_size, sizeof(char_size)); memcpy(&data_[KeyDataStart_()], &event, sizeof(KeyData)); -} - -void KeyDataPacket::SetCharacter(const char* character) { if (character != nullptr) { memcpy(data_.data() + CharacterStart_(), character, data_.size() - CharacterStart_()); } } +KeyDataPacket::~KeyDataPacket() = default; + } // namespace flutter diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_packet.h index aae2507e0a2a6..2d80a17ef4fb8 100644 --- a/lib/ui/window/key_data_packet.h +++ b/lib/ui/window/key_data_packet.h @@ -13,23 +13,23 @@ namespace flutter { +// A byte stream representing a key event, to be sent to the framework. class KeyDataPacket { public: - // Build a KeyDataPacket by incrementally fill in data. + // Build the key data packet by providing information. // - // The `character_data_size` is number of bytes to contain the character data. - KeyDataPacket(size_t character_data_size); + // The `character` is a nullable C-string that ends with a '\0'. + KeyDataPacket(const KeyData& event, const char* character); ~KeyDataPacket(); const std::vector& data() const { return data_; } - void SetKeyData(const KeyData& event); - - // Set character data to the proper position, which should not be terminated - // by a null character (length controled by character_data_size). - void SetCharacter(const char* characters); - private: + // Packet structure: + // | CharDataSize | (1 field) + // | Key Data | (kKeyDataFieldCount fields) + // | CharData | (CharDataSize bits) + size_t CharacterSizeStart_() { return 0; } size_t KeyDataStart_() { return CharacterSizeStart_() + sizeof(uint64_t); } size_t CharacterStart_() { return KeyDataStart_() + sizeof(KeyData); } diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 3aa3d9d182d48..b87acebe37ad6 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1529,11 +1529,6 @@ FlutterEngineResult FlutterEngineSendKeyEvent( const char* character = SAFE_ACCESS(event, character, nullptr); - size_t character_data_size = - character == nullptr ? 0 : strnlen(character, kKeyEventCharacterMaxBytes); - - auto packet = std::make_unique(character_data_size); - flutter::KeyData key_data; key_data.Clear(); key_data.timestamp = (uint64_t)SAFE_ACCESS(event, timestamp, 0); @@ -1542,8 +1537,9 @@ FlutterEngineResult FlutterEngineSendKeyEvent( key_data.physical = SAFE_ACCESS(event, physical, 0); key_data.logical = SAFE_ACCESS(event, logical, 0); key_data.synthesized = SAFE_ACCESS(event, synthesized, false); - packet->SetKeyData(key_data); - packet->SetCharacter(character); + + auto packet = std::make_unique( + key_data, character); auto response = [callback, user_data](bool handled) { callback(handled, user_data); From b367ce3c97c016a5aadadbfd789a6e5537ffc292 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 15:34:34 -0800 Subject: [PATCH 20/34] More comments --- lib/ui/window/key_data_packet.cc | 10 +++++----- lib/ui/window/key_data_packet.h | 12 +++++++----- lib/ui/window/platform_configuration.cc | 4 ++-- lib/ui/window/platform_configuration.h | 6 +++--- lib/ui/window/window.h | 11 ++++++++++- lib/web_ui/lib/src/engine/platform_dispatcher.dart | 2 +- shell/platform/embedder/embedder.cc | 7 ++++--- shell/platform/embedder/embedder.h | 4 ++-- 8 files changed, 34 insertions(+), 22 deletions(-) diff --git a/lib/ui/window/key_data_packet.cc b/lib/ui/window/key_data_packet.cc index ca54e23ad5517..fe24af210cd67 100644 --- a/lib/ui/window/key_data_packet.cc +++ b/lib/ui/window/key_data_packet.cc @@ -11,13 +11,13 @@ namespace flutter { KeyDataPacket::KeyDataPacket(const KeyData& event, const char* character) { - uint64_t char_size = character == nullptr ? 0 : strlen(character); + size_t char_size = character == nullptr ? 0 : strlen(character); + uint64_t char_size_64 = char_size; data_.resize(sizeof(uint64_t) + sizeof(KeyData) + char_size); - memcpy(&data_[CharacterSizeStart_()], &char_size, sizeof(char_size)); - memcpy(&data_[KeyDataStart_()], &event, sizeof(KeyData)); + memcpy(CharacterSizeStart(), &char_size_64, sizeof(char_size)); + memcpy(KeyDataStart(), &event, sizeof(KeyData)); if (character != nullptr) { - memcpy(data_.data() + CharacterStart_(), character, - data_.size() - CharacterStart_()); + memcpy(CharacterStart(), character, char_size); } } diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_packet.h index 2d80a17ef4fb8..f340a891d0a29 100644 --- a/lib/ui/window/key_data_packet.h +++ b/lib/ui/window/key_data_packet.h @@ -22,6 +22,10 @@ class KeyDataPacket { KeyDataPacket(const KeyData& event, const char* character); ~KeyDataPacket(); + // Prevent copying. + KeyDataPacket(KeyDataPacket const&) = delete; + KeyDataPacket& operator=(KeyDataPacket const&) = delete; + const std::vector& data() const { return data_; } private: @@ -30,13 +34,11 @@ class KeyDataPacket { // | Key Data | (kKeyDataFieldCount fields) // | CharData | (CharDataSize bits) - size_t CharacterSizeStart_() { return 0; } - size_t KeyDataStart_() { return CharacterSizeStart_() + sizeof(uint64_t); } - size_t CharacterStart_() { return KeyDataStart_() + sizeof(KeyData); } + uint8_t* CharacterSizeStart() { return data_.data(); } + uint8_t* KeyDataStart() { return CharacterSizeStart() + sizeof(uint64_t); } + uint8_t* CharacterStart() { return KeyDataStart() + sizeof(KeyData); } std::vector data_; - - FML_DISALLOW_COPY_AND_ASSIGN(KeyDataPacket); }; } // namespace flutter diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 70219eb9a687d..024041e820699 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -443,7 +443,7 @@ void PlatformConfiguration::CompletePlatformMessageResponse( } void PlatformConfiguration::CompleteKeyDataResponse(uint64_t response_id, bool handled) { - if (!response_id) { + if (response_id == 0) { return; } auto it = pending_key_responses_.find(response_id); @@ -451,7 +451,7 @@ void PlatformConfiguration::CompleteKeyDataResponse(uint64_t response_id, bool h if (it == pending_key_responses_.end()) { return; } - auto callback = std::move(it->second); + KeyDataResponse callback = std::move(it->second); pending_key_responses_.erase(it); callback(handled); } diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 0a30c7620ba02..2d8a800bd8b76 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -327,7 +327,7 @@ class PlatformConfiguration final { std::vector args); //---------------------------------------------------------------------------- - /// @brief Register a callback to be invoked when the framework has + /// @brief Registers a callback to be invoked when the framework has /// decided whether to handle an event. This callback originates /// in the platform view and has been forwarded through the engine /// to here. @@ -463,12 +463,12 @@ class PlatformConfiguration final { std::unordered_map> windows_; - // We use id 0 to mean that no response is expected. + // ID starts at 1 because an ID of 0 indicates that no response is expected. int next_response_id_ = 1; std::unordered_map> pending_responses_; - // We use id 0 to mean that no response is expected. + // ID starts at 1 because an ID of 0 indicates that no response is expected. uint64_t next_key_response_id_ = 1; std::unordered_map pending_key_responses_; diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index 051caf0c76ccb..aae68604ffdeb 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -5,10 +5,10 @@ #ifndef FLUTTER_LIB_UI_WINDOW_WINDOW_H_ #define FLUTTER_LIB_UI_WINDOW_WINDOW_H_ +#include #include #include #include -#include #include "flutter/lib/ui/window/key_data_packet.h" #include "flutter/lib/ui/window/platform_message.h" @@ -28,7 +28,16 @@ class Window final { const ViewportMetrics& viewport_metrics() const { return viewport_metrics_; } + // Dispatch a packet to the framework that indicates one or a few pointer + // events. void DispatchPointerDataPacket(const PointerDataPacket& packet); + // Dispatch a packet to the framework that indicates a key event. + // + // The `response_id` is used to label the response of whether the key event + // is handled by the framework, typically the return value of + // PlatformConfiguration::RegisterKeyDataResponse. + // It should be used later in + // PlatformConfiguration::CompleteKeyDataResponse. void DispatchKeyDataPacket(const KeyDataPacket& packet, uint64_t response_id); void UpdateWindowMetrics(const ViewportMetrics& metrics); diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index c719e8fbbc18a..10faac0b9af5f 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -172,7 +172,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { invoke1(_onPointerDataPacket, _onPointerDataPacketZone, dataPacket); } - /// A callback that is invoked when pointer data is available. + /// A callback that is invoked when key data is available. /// /// The framework invokes this callback in the same zone in which the /// callback was set. diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index b87acebe37ad6..7120da5947b5f 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1512,7 +1512,7 @@ inline flutter::KeyEventType ToKeyEventType(FlutterKeyEventKind event_kind) { // Many platforms assert the character to be less than 2 int16's, i.e. 4 bytes, // therefore the character data is asserted to be less than double the amount, // i.e. 8 bytes. -constexpr size_t kKeyEventCharacterMaxBytes = 8; +static constexpr size_t kKeyEventCharacterMaxBytes = 8; FlutterEngineResult FlutterEngineSendKeyEvent( FLUTTER_API_SYMBOL(FlutterEngine) engine, @@ -1542,8 +1542,9 @@ FlutterEngineResult FlutterEngineSendKeyEvent( key_data, character); auto response = [callback, user_data](bool handled) { - callback(handled, user_data); - }; + if (callback != nullptr) + callback(handled, user_data); + }; return reinterpret_cast(engine) ->DispatchKeyDataPacket( diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index a0f4588552460..79572a6cb1e4a 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -617,7 +617,7 @@ typedef enum { kFlutterKeyEventKindRepeat, } FlutterKeyEventKind; -/// A structure to represent a change of state of a key. +/// A structure to represent a key event. /// /// Sending `FlutterKeyEvent` via `FlutterEngineSendKeyEvent` results in a /// corresponding `FlutterKeyEvent` to be dispatched in the framework. It is @@ -1621,7 +1621,7 @@ FlutterEngineResult FlutterEngineSendPointerEvent( /// longer access `event` after returning. /// @param[in] callback The callback invoked by the engine when the /// Flutter application has decided whether it handles -/// this event. +/// this event. This can be null. /// @param[in] user_data The context associated with the callback. The exact /// same value will used to invoke `callback`. Accepts /// nullptr or a non-pointer value. From 855ffdeb012f0396a3d69a05516b3dab4511c26f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 15:37:29 -0800 Subject: [PATCH 21/34] Rename embedder kind -> type --- shell/platform/embedder/embedder.cc | 12 +++++----- shell/platform/embedder/embedder.h | 12 +++++----- .../embedder/tests/embedder_unittests.cc | 22 +++++++++---------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 7120da5947b5f..a3d3189b04fae 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1492,13 +1492,13 @@ FlutterEngineResult FlutterEngineSendPointerEvent( "running Flutter application."); } -inline flutter::KeyEventType ToKeyEventType(FlutterKeyEventKind event_kind) { +static inline flutter::KeyEventType MapKeyEventType(FlutterKeyEventType event_kind) { switch (event_kind) { - case kFlutterKeyEventKindUp: + case kFlutterKeyEventTypeUp: return flutter::KeyEventType::kUp; - case kFlutterKeyEventKindDown: + case kFlutterKeyEventTypeDown: return flutter::KeyEventType::kDown; - case kFlutterKeyEventKindRepeat: + case kFlutterKeyEventTypeRepeat: return flutter::KeyEventType::kRepeat; } return flutter::KeyEventType::kUp; @@ -1532,8 +1532,8 @@ FlutterEngineResult FlutterEngineSendKeyEvent( flutter::KeyData key_data; key_data.Clear(); key_data.timestamp = (uint64_t)SAFE_ACCESS(event, timestamp, 0); - key_data.type = ToKeyEventType( - SAFE_ACCESS(event, kind, FlutterKeyEventKind::kFlutterKeyEventKindUp)); + key_data.type = MapKeyEventType( + SAFE_ACCESS(event, type, FlutterKeyEventType::kFlutterKeyEventTypeUp)); key_data.physical = SAFE_ACCESS(event, physical, 0); key_data.logical = SAFE_ACCESS(event, logical, 0); key_data.synthesized = SAFE_ACCESS(event, synthesized, false); diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 79572a6cb1e4a..1566a88a9730f 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -612,10 +612,10 @@ typedef struct { } FlutterPointerEvent; typedef enum { - kFlutterKeyEventKindUp = 1, - kFlutterKeyEventKindDown, - kFlutterKeyEventKindRepeat, -} FlutterKeyEventKind; + kFlutterKeyEventTypeUp = 1, + kFlutterKeyEventTypeDown, + kFlutterKeyEventTypeRepeat, +} FlutterKeyEventType; /// A structure to represent a key event. /// @@ -626,7 +626,7 @@ typedef enum { /// the following rules: /// /// * Each key press sequence shall consist of one key down event (`kind` being -/// `kFlutterKeyEventKindDown`), zero or more repeat events, and one key up +/// `kFlutterKeyEventTypeDown`), zero or more repeat events, and one key up /// event, representing a physical key button being pressed, held, and released. /// * All events throughout a key press sequence shall have the same `physical` /// and `logical`. Having different `character`s is allowed. @@ -638,7 +638,7 @@ typedef struct { /// that used by `FlutterEngineGetCurrentTime`. double timestamp; /// The event kind. - FlutterKeyEventKind kind; + FlutterKeyEventType type; /// The USB HID code for the physical key of the event. /// /// For the full definition and list of pre-defined physical keys, see diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 25763cf1af7af..914ce86763fb7 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1200,23 +1200,23 @@ typedef struct { bool returned; } KeyEventUserData; -FlutterKeyEventKind unserializeKeyEventKind(uint64_t kindInt) { +FlutterKeyEventType unserializeKeyEventKind(uint64_t kindInt) { switch(kindInt) { case 1: - return kFlutterKeyEventKindUp; + return kFlutterKeyEventTypeUp; case 2: - return kFlutterKeyEventKindDown; + return kFlutterKeyEventTypeDown; case 3: - return kFlutterKeyEventKindRepeat; + return kFlutterKeyEventTypeRepeat; default: FML_UNREACHABLE(); - return kFlutterKeyEventKindUp; + return kFlutterKeyEventTypeUp; } } void expect_key_event_eq(const FlutterKeyEvent& subject, const FlutterKeyEvent& baseline) { EXPECT_EQ(subject.timestamp, baseline.timestamp); - EXPECT_EQ(subject.kind, baseline.kind); + EXPECT_EQ(subject.type, baseline.type); EXPECT_EQ(subject.physical, baseline.physical); EXPECT_EQ(subject.logical, baseline.logical); EXPECT_EQ(subject.synthesized, baseline.synthesized); @@ -1228,7 +1228,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { FlutterKeyEvent echoed_event; auto native_echo_event = [&](Dart_NativeArguments args) { - echoed_event.kind = unserializeKeyEventKind( + echoed_event.type = unserializeKeyEventKind( tonic::DartConverter::FromDart( Dart_GetNativeArgument(args, 0))); echoed_event.timestamp = tonic::DartConverter::FromDart( @@ -1265,7 +1265,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { const FlutterKeyEvent downEventUpperA { .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1, - .kind = kFlutterKeyEventKindDown, + .type = kFlutterKeyEventTypeDown, .physical = 0x00070004, .logical = 0x00000000061, .character = "A", @@ -1282,7 +1282,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { const FlutterKeyEvent repeatEventWideChar { .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1000, - .kind = kFlutterKeyEventKindRepeat, + .type = kFlutterKeyEventTypeRepeat, .physical = 0x00070005, .logical = 0x00000000062, .character = "∆", @@ -1299,7 +1299,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { const FlutterKeyEvent upEvent { .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1000000, - .kind = kFlutterKeyEventKindUp, + .type = kFlutterKeyEventTypeUp, .physical = 0x00070006, .logical = 0x00000000063, .character = nullptr, @@ -1335,7 +1335,7 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { FlutterKeyEvent event { .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1000, - .kind = kFlutterKeyEventKindDown, + .type = kFlutterKeyEventTypeDown, .physical = 0x00070005, .logical = 0x00000000062, .character = nullptr, From 8cbf7e190aa666bf4f805531faea300b35be9126 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 16:28:38 -0800 Subject: [PATCH 22/34] All comments --- shell/common/engine.h | 9 +++--- shell/platform/embedder/embedder.h | 21 ++++++------ shell/platform/embedder/embedder_engine.h | 12 +++++++ shell/platform/embedder/fixtures/main.dart | 4 +++ .../embedder/tests/embedder_unittests.cc | 32 +++++++++++-------- 5 files changed, 50 insertions(+), 28 deletions(-) diff --git a/shell/common/engine.h b/shell/common/engine.h index 43d56b822eda4..4249ceb2eba01 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -730,10 +730,11 @@ class Engine final : public RuntimeDelegate, //---------------------------------------------------------------------------- /// @brief Notifies the engine that the embedder has sent it a key data - /// packet. A key data packet contains one physical key event and - /// one or multiple logical key events. This call originates in - /// the platform view and the shell has forwarded the same to the - /// engine on the UI task runner here. + /// packet. A key data packet contains one key event. This call + /// originates in the platform view and the shell has forwarded + /// the same to the engine on the UI task runner here. The engine + /// will decide whether to handle this event, and send the + /// result using `callback`, which will be called exactly once. /// /// @param[in] packet The key data packet. /// @param[in] callback Called when the framework has decided whether diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 1566a88a9730f..428d7d08ff9f3 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -633,9 +633,9 @@ typedef enum { typedef struct { /// The size of this struct. Must be sizeof(FlutterKeyEvent). size_t struct_size; - /// The timestamp at which the pointer event was generated. The timestamp - /// should be specified in microseconds and the clock should be the same as - /// that used by `FlutterEngineGetCurrentTime`. + /// The timestamp at which the key event was generated. The timestamp should be + /// specified in microseconds and the clock should be the same as that used by + /// `FlutterEngineGetCurrentTime`. double timestamp; /// The event kind. FlutterKeyEventType type; @@ -1609,22 +1609,21 @@ FlutterEngineResult FlutterEngineSendPointerEvent( size_t events_count); //------------------------------------------------------------------------------ -/// @brief Send a key event to the engine, causing the framework to -/// dispatch a key event that is simply transformed from this one. -/// The framework will decide whether to handle this event in a -/// synchronous fashion, although due to technical limitation, the -/// result is always reported asynchronously. The `callback` is -/// guaranteed to be called exactly once. +/// @brief Sends a key event to the engine. The framework will decide +/// whether to handle this event in a synchronous fashion, although +/// due to technical limitation, the result is always reported +/// asynchronously. The `callback` is guaranteed to be called +/// exactly once. /// /// @param[in] engine A running engine instance. /// @param[in] event The event data to be sent. This function will no /// longer access `event` after returning. /// @param[in] callback The callback invoked by the engine when the /// Flutter application has decided whether it handles -/// this event. This can be null. +/// this event. Accepts nullptr. /// @param[in] user_data The context associated with the callback. The exact /// same value will used to invoke `callback`. Accepts -/// nullptr or a non-pointer value. +/// nullptr. /// /// @return The result of the call. /// diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index a06d74e383472..bdff752a9eceb 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -60,6 +60,18 @@ class EmbedderEngine { bool DispatchPointerDataPacket( std::unique_ptr packet); + //---------------------------------------------------------------------------- + /// @brief Notifies the platform view that the embedder has sent it a key + /// data packet. A key data packet contains one key event. This + /// call originates in the platform view and the shell has forwarded + /// the same to the engine on the UI task runner here. The platform + /// view will decide whether to handle this event, and send the + /// result using `callback`, which will be called exactly once. + /// + /// @param[in] packet The key data packet. + /// @param[in] callback Called when the framework has decided whether + /// to handle this key data. + /// bool DispatchKeyDataPacket(std::unique_ptr packet, KeyDataResponse callback); diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 4fa2fb4dab6c1..9d0458af6fdcd 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -504,6 +504,10 @@ void _echoKeyEvent( bool synthesized) native 'EchoKeyEvent'; +// Convert `kind` in enum form to its integer form. +// +// It performs a revesed mapping from `unserializeKeyEventKind` +// in shell/platform/embedder/tests/embedder_unittests.cc. int _serializeKeyEventType(KeyEventType change) { switch(change) { case KeyEventType.up: diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 914ce86763fb7..faad0634141fd 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1200,8 +1200,12 @@ typedef struct { bool returned; } KeyEventUserData; -FlutterKeyEventType unserializeKeyEventKind(uint64_t kindInt) { - switch(kindInt) { +// Convert `kind` in integer form to its enum form. +// +// It performs a revesed mapping from `_serializeKeyEventType` +// in shell/platform/embedder/fixtures/main.dart. +FlutterKeyEventType UnserializeKeyEventKind(uint64_t kind) { + switch(kind) { case 1: return kFlutterKeyEventTypeUp; case 2: @@ -1214,7 +1218,9 @@ FlutterKeyEventType unserializeKeyEventKind(uint64_t kindInt) { } } -void expect_key_event_eq(const FlutterKeyEvent& subject, const FlutterKeyEvent& baseline) { +// Checks the equality of two `FlutterKeyEvent` by each of their members except +// for `character`. The `character` must be checked separately. +void ExpectKeyEventEq(const FlutterKeyEvent& subject, const FlutterKeyEvent& baseline) { EXPECT_EQ(subject.timestamp, baseline.timestamp); EXPECT_EQ(subject.type, baseline.type); EXPECT_EQ(subject.physical, baseline.physical); @@ -1228,7 +1234,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { FlutterKeyEvent echoed_event; auto native_echo_event = [&](Dart_NativeArguments args) { - echoed_event.type = unserializeKeyEventKind( + echoed_event.type = UnserializeKeyEventKind( tonic::DartConverter::FromDart( Dart_GetNativeArgument(args, 0))); echoed_event.timestamp = tonic::DartConverter::FromDart( @@ -1262,7 +1268,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { ready.Wait(); // A normal down event - const FlutterKeyEvent downEventUpperA { + const FlutterKeyEvent down_event_upper_a { .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1, .type = kFlutterKeyEventTypeDown, @@ -1271,15 +1277,15 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { .character = "A", .synthesized = false, }; - FlutterEngineSendKeyEvent(engine.get(), &downEventUpperA, + FlutterEngineSendKeyEvent(engine.get(), &down_event_upper_a, [](bool handled, void* user_data){}, nullptr); message_latch->Wait(); - expect_key_event_eq(echoed_event, downEventUpperA); + ExpectKeyEventEq(echoed_event, down_event_upper_a); EXPECT_EQ(echoed_char, 0x41llu); // A repeat event with multi-byte character - const FlutterKeyEvent repeatEventWideChar { + const FlutterKeyEvent repeat_event_wide_char { .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1000, .type = kFlutterKeyEventTypeRepeat, @@ -1288,15 +1294,15 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { .character = "∆", .synthesized = false, }; - FlutterEngineSendKeyEvent(engine.get(), &repeatEventWideChar, + FlutterEngineSendKeyEvent(engine.get(), &repeat_event_wide_char, [](bool handled, void* user_data){}, nullptr); message_latch->Wait(); - expect_key_event_eq(echoed_event, repeatEventWideChar); + ExpectKeyEventEq(echoed_event, repeat_event_wide_char); EXPECT_EQ(echoed_char, 0x2206llu); // An up event with no character, synthesized - const FlutterKeyEvent upEvent { + const FlutterKeyEvent up_event { .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1000000, .type = kFlutterKeyEventTypeUp, @@ -1305,11 +1311,11 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { .character = nullptr, .synthesized = true, }; - FlutterEngineSendKeyEvent(engine.get(), &upEvent, + FlutterEngineSendKeyEvent(engine.get(), &up_event, [](bool handled, void* user_data){}, nullptr); message_latch->Wait(); - expect_key_event_eq(echoed_event, upEvent); + ExpectKeyEventEq(echoed_event, up_event); EXPECT_EQ(echoed_char, 0llu); } From 54479e38f3c2c847707e16087ff70a7efbc1f0e1 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 16:49:01 -0800 Subject: [PATCH 23/34] Format --- lib/ui/window/key_data.h | 4 +- lib/ui/window/key_data_packet.h | 4 +- lib/ui/window/platform_configuration.cc | 9 +- lib/ui/window/platform_configuration.h | 3 +- lib/ui/window/window.cc | 8 +- lib/ui/window/window.h | 2 +- runtime/runtime_controller.cc | 11 +- shell/common/engine.cc | 5 +- shell/common/platform_view.cc | 8 +- shell/common/platform_view.h | 6 +- shell/common/shell.cc | 9 +- shell/common/shell.h | 2 +- shell/platform/embedder/embedder.cc | 20 ++- shell/platform/embedder/embedder.h | 39 +++--- shell/platform/embedder/embedder_engine.h | 9 +- .../embedder/tests/embedder_unittests.cc | 125 +++++++++--------- 16 files changed, 130 insertions(+), 134 deletions(-) diff --git a/lib/ui/window/key_data.h b/lib/ui/window/key_data.h index a9220a7c225cb..5c84bf0cb58c8 100644 --- a/lib/ui/window/key_data.h +++ b/lib/ui/window/key_data.h @@ -24,8 +24,8 @@ enum class KeyEventType : int64_t { // The fixed-length sections of a KeyDataPacket. // -// KeyData does not contain `character`, for variable-length data are stored in a -// different way in KeyDataPacket. +// KeyData does not contain `character`, for variable-length data are stored in +// a different way in KeyDataPacket. // // This structure is unpacked by hooks.dart. struct alignas(8) KeyData { diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_packet.h index f340a891d0a29..e8efdf17334df 100644 --- a/lib/ui/window/key_data_packet.h +++ b/lib/ui/window/key_data_packet.h @@ -5,8 +5,8 @@ #ifndef FLUTTER_LIB_UI_WINDOW_KEY_DATA_MESSAGE_H_ #define FLUTTER_LIB_UI_WINDOW_KEY_DATA_MESSAGE_H_ -#include #include +#include #include "flutter/fml/macros.h" #include "flutter/lib/ui/window/key_data.h" @@ -16,7 +16,7 @@ namespace flutter { // A byte stream representing a key event, to be sent to the framework. class KeyDataPacket { public: - // Build the key data packet by providing information. + // Build the key data packet by providing information. // // The `character` is a nullable C-string that ends with a '\0'. KeyDataPacket(const KeyData& event, const char* character); diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 024041e820699..44f389a880272 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -182,9 +182,8 @@ void GetPersistentIsolateData(Dart_NativeArguments args) { } void RespondToKeyData(Dart_Handle window, int response_id, bool handled) { - UIDartState::Current() - ->platform_configuration() - ->CompleteKeyDataResponse(response_id, handled); + UIDartState::Current()->platform_configuration()->CompleteKeyDataResponse( + response_id, handled); } void _RespondToKeyData(Dart_NativeArguments args) { @@ -366,7 +365,6 @@ uint64_t PlatformConfiguration::RegisterKeyDataResponse( return response_id; } - void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime) { std::shared_ptr dart_state = begin_frame_.dart_state().lock(); @@ -442,7 +440,8 @@ void PlatformConfiguration::CompletePlatformMessageResponse( response->Complete(std::make_unique(std::move(data))); } -void PlatformConfiguration::CompleteKeyDataResponse(uint64_t response_id, bool handled) { +void PlatformConfiguration::CompleteKeyDataResponse(uint64_t response_id, + bool handled) { if (response_id == 0) { return; } diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 2d8a800bd8b76..9b5f1b5135fa6 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -470,8 +470,7 @@ class PlatformConfiguration final { // ID starts at 1 because an ID of 0 indicates that no response is expected. uint64_t next_key_response_id_ = 1; - std::unordered_map - pending_key_responses_; + std::unordered_map pending_key_responses_; }; } // namespace flutter diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index 650fe0b37bbec..994eca2ab5499 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -36,7 +36,8 @@ void Window::DispatchPointerDataPacket(const PointerDataPacket& packet) { library_.value(), "_dispatchPointerDataPacket", {data_handle})); } -void Window::DispatchKeyDataPacket(const KeyDataPacket& packet, uint64_t response_id) { +void Window::DispatchKeyDataPacket(const KeyDataPacket& packet, + uint64_t response_id) { std::shared_ptr dart_state = library_.dart_state().lock(); if (!dart_state) return; @@ -48,8 +49,9 @@ void Window::DispatchKeyDataPacket(const KeyDataPacket& packet, uint64_t respons if (Dart_IsError(data_handle)) { return; } - tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_dispatchKeyData", - {data_handle, tonic::ToDart(response_id)})); + tonic::LogIfError( + tonic::DartInvokeField(library_.value(), "_dispatchKeyData", + {data_handle, tonic::ToDart(response_id)})); } void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index aae68604ffdeb..9a6847d3c46a6 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -34,7 +34,7 @@ class Window final { // Dispatch a packet to the framework that indicates a key event. // // The `response_id` is used to label the response of whether the key event - // is handled by the framework, typically the return value of + // is handled by the framework, typically the return value of // PlatformConfiguration::RegisterKeyDataResponse. // It should be used later in // PlatformConfiguration::CompleteKeyDataResponse. diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 45c7050abf690..33b977a7d9d8e 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -262,14 +262,15 @@ bool RuntimeController::DispatchPointerDataPacket( return false; } -bool RuntimeController::DispatchKeyDataPacket( - const KeyDataPacket& packet, - KeyDataResponse callback) { +bool RuntimeController::DispatchKeyDataPacket(const KeyDataPacket& packet, + KeyDataResponse callback) { if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataPacket", "mode", "basic"); - uint64_t response_id = platform_configuration->RegisterKeyDataResponse(std::move(callback)); - platform_configuration->get_window(0)->DispatchKeyDataPacket(packet, response_id); + uint64_t response_id = + platform_configuration->RegisterKeyDataResponse(std::move(callback)); + platform_configuration->get_window(0)->DispatchKeyDataPacket(packet, + response_id); return true; } return false; diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 7fa1dbe38a440..669a7cf878567 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -415,9 +415,8 @@ void Engine::DispatchPointerDataPacket( pointer_data_dispatcher_->DispatchPacket(std::move(packet), trace_flow_id); } -void Engine::DispatchKeyDataPacket( - std::unique_ptr packet, - KeyDataResponse callback) { +void Engine::DispatchKeyDataPacket(std::unique_ptr packet, + KeyDataResponse callback) { TRACE_EVENT0("flutter", "Engine::DispatchKeyDataPacket"); if (runtime_controller_) { runtime_controller_->DispatchKeyDataPacket(*packet, std::move(callback)); diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 53a3726aaf0ca..27a398e5cdbcb 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -42,10 +42,10 @@ void PlatformView::DispatchPointerDataPacket( pointer_data_packet_converter_.Convert(std::move(packet))); } -void PlatformView::DispatchKeyDataPacket( - std::unique_ptr packet, - KeyDataResponse callback) { - delegate_.OnPlatformViewDispatchKeyDataPacket(std::move(packet), std::move(callback)); +void PlatformView::DispatchKeyDataPacket(std::unique_ptr packet, + KeyDataResponse callback) { + delegate_.OnPlatformViewDispatchKeyDataPacket(std::move(packet), + std::move(callback)); } void PlatformView::DispatchSemanticsAction(int32_t id, diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index ac8e7338dd925..ea75883702145 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -5,8 +5,8 @@ #ifndef COMMON_PLATFORM_VIEW_H_ #define COMMON_PLATFORM_VIEW_H_ -#include #include +#include #include "flow/embedded_views.h" #include "flutter/common/graphics/texture.h" @@ -54,7 +54,7 @@ class PlatformView { /// class Delegate { public: - using KeyDataResponse = std::function; + using KeyDataResponse = std::function; //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view was created /// with the given render surface. This surface is platform @@ -140,7 +140,7 @@ class PlatformView { /// virtual void OnPlatformViewDispatchKeyDataPacket( std::unique_ptr packet, - std::function callback) = 0; + std::function callback) = 0; //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view has encountered diff --git a/shell/common/shell.cc b/shell/common/shell.cc index d1adb4eedc1a7..fbf1e22cc89ae 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -964,15 +964,14 @@ void Shell::OnPlatformViewDispatchPointerDataPacket( // |PlatformView::Delegate| void Shell::OnPlatformViewDispatchKeyDataPacket( std::unique_ptr packet, - std::function callback) { + std::function callback) { TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchKeyDataPacket"); FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable( - [engine = weak_engine_, - packet = std::move(packet), - callback = std::move(callback)]() mutable { + task_runners_.GetUITaskRunner()->PostTask( + fml::MakeCopyable([engine = weak_engine_, packet = std::move(packet), + callback = std::move(callback)]() mutable { if (engine) { engine->DispatchKeyDataPacket(std::move(packet), std::move(callback)); } diff --git a/shell/common/shell.h b/shell/common/shell.h index 716f5c72d3aaf..fab937891f8d1 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -507,7 +507,7 @@ class Shell final : public PlatformView::Delegate, // |PlatformView::Delegate| void OnPlatformViewDispatchKeyDataPacket( std::unique_ptr packet, - std::function callback) override; + std::function callback) override; // |PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction( diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index a3d3189b04fae..436e7bdef28df 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1492,7 +1492,8 @@ FlutterEngineResult FlutterEngineSendPointerEvent( "running Flutter application."); } -static inline flutter::KeyEventType MapKeyEventType(FlutterKeyEventType event_kind) { +static inline flutter::KeyEventType MapKeyEventType( + FlutterKeyEventType event_kind) { switch (event_kind) { case kFlutterKeyEventTypeUp: return flutter::KeyEventType::kUp; @@ -1514,11 +1515,11 @@ static inline flutter::KeyEventType MapKeyEventType(FlutterKeyEventType event_ki // i.e. 8 bytes. static constexpr size_t kKeyEventCharacterMaxBytes = 8; -FlutterEngineResult FlutterEngineSendKeyEvent( - FLUTTER_API_SYMBOL(FlutterEngine) engine, - const FlutterKeyEvent* event, - FlutterKeyEventCallback callback, - void* user_data) { +FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) + engine, + const FlutterKeyEvent* event, + FlutterKeyEventCallback callback, + void* user_data) { if (engine == nullptr) { return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); } @@ -1538,8 +1539,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent( key_data.logical = SAFE_ACCESS(event, logical, 0); key_data.synthesized = SAFE_ACCESS(event, synthesized, false); - auto packet = std::make_unique( - key_data, character); + auto packet = std::make_unique(key_data, character); auto response = [callback, user_data](bool handled) { if (callback != nullptr) @@ -1547,9 +1547,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent( }; return reinterpret_cast(engine) - ->DispatchKeyDataPacket( - std::move(packet), - response) + ->DispatchKeyDataPacket(std::move(packet), response) ? kSuccess : LOG_EMBEDDER_ERROR(kInternalInconsistency, "Could not dispatch the key event to the " diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 428d7d08ff9f3..4c0ba35a34e23 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -627,15 +627,16 @@ typedef enum { /// /// * Each key press sequence shall consist of one key down event (`kind` being /// `kFlutterKeyEventTypeDown`), zero or more repeat events, and one key up -/// event, representing a physical key button being pressed, held, and released. +/// event, representing a physical key button being pressed, held, and +/// released. /// * All events throughout a key press sequence shall have the same `physical` /// and `logical`. Having different `character`s is allowed. typedef struct { /// The size of this struct. Must be sizeof(FlutterKeyEvent). size_t struct_size; - /// The timestamp at which the key event was generated. The timestamp should be - /// specified in microseconds and the clock should be the same as that used by - /// `FlutterEngineGetCurrentTime`. + /// The timestamp at which the key event was generated. The timestamp should + /// be specified in microseconds and the clock should be the same as that used + /// by `FlutterEngineGetCurrentTime`. double timestamp; /// The event kind. FlutterKeyEventType type; @@ -656,11 +657,11 @@ typedef struct { /// /// The embedder is likely to skip events and/or construct new events that do /// not correspond to any native events in order to conform the regularity - /// of events (as documented in `FlutterKeyEvent`). An example is when a key up - /// is missed due to loss of window focus, on a platform that provides query to - /// key pressing status, the embedder might realize that the key has been - /// released at the next key event, and should construct a synthesized up event - /// immediately before the actual event. + /// of events (as documented in `FlutterKeyEvent`). An example is when a key + /// up is missed due to loss of window focus, on a platform that provides + /// query to key pressing status, the embedder might realize that the key has + /// been released at the next key event, and should construct a synthesized up + /// event immediately before the actual event. /// /// An event being synthesized means that the framework will not trust the /// `timestamp` of the event. @@ -1619,20 +1620,20 @@ FlutterEngineResult FlutterEngineSendPointerEvent( /// @param[in] event The event data to be sent. This function will no /// longer access `event` after returning. /// @param[in] callback The callback invoked by the engine when the -/// Flutter application has decided whether it handles -/// this event. Accepts nullptr. -/// @param[in] user_data The context associated with the callback. The exact -/// same value will used to invoke `callback`. Accepts -/// nullptr. +/// Flutter application has decided whether it +/// handles this event. Accepts nullptr. +/// @param[in] user_data The context associated with the callback. The +/// exact same value will used to invoke `callback`. +/// Accepts nullptr. /// /// @return The result of the call. /// FLUTTER_EXPORT -FlutterEngineResult FlutterEngineSendKeyEvent( - FLUTTER_API_SYMBOL(FlutterEngine) engine, - const FlutterKeyEvent* event, - FlutterKeyEventCallback callback, - void* user_data); +FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) + engine, + const FlutterKeyEvent* event, + FlutterKeyEventCallback callback, + void* user_data); FLUTTER_EXPORT FlutterEngineResult FlutterEngineSendPlatformMessage( diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index bdff752a9eceb..55afebdba7367 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -63,10 +63,11 @@ class EmbedderEngine { //---------------------------------------------------------------------------- /// @brief Notifies the platform view that the embedder has sent it a key /// data packet. A key data packet contains one key event. This - /// call originates in the platform view and the shell has forwarded - /// the same to the engine on the UI task runner here. The platform - /// view will decide whether to handle this event, and send the - /// result using `callback`, which will be called exactly once. + /// call originates in the platform view and the shell has + /// forwarded the same to the engine on the UI task runner here. + /// The platform view will decide whether to handle this event, + /// and send the result using `callback`, which will be called + /// exactly once. /// /// @param[in] packet The key data packet. /// @param[in] callback Called when the framework has decided whether diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index faad0634141fd..bb53760433be4 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1205,7 +1205,7 @@ typedef struct { // It performs a revesed mapping from `_serializeKeyEventType` // in shell/platform/embedder/fixtures/main.dart. FlutterKeyEventType UnserializeKeyEventKind(uint64_t kind) { - switch(kind) { + switch (kind) { case 1: return kFlutterKeyEventTypeUp; case 2: @@ -1220,7 +1220,8 @@ FlutterKeyEventType UnserializeKeyEventKind(uint64_t kind) { // Checks the equality of two `FlutterKeyEvent` by each of their members except // for `character`. The `character` must be checked separately. -void ExpectKeyEventEq(const FlutterKeyEvent& subject, const FlutterKeyEvent& baseline) { +void ExpectKeyEventEq(const FlutterKeyEvent& subject, + const FlutterKeyEvent& baseline) { EXPECT_EQ(subject.timestamp, baseline.timestamp); EXPECT_EQ(subject.type, baseline.type); EXPECT_EQ(subject.physical, baseline.physical); @@ -1234,19 +1235,19 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { FlutterKeyEvent echoed_event; auto native_echo_event = [&](Dart_NativeArguments args) { - echoed_event.type = UnserializeKeyEventKind( - tonic::DartConverter::FromDart( + echoed_event.type = + UnserializeKeyEventKind(tonic::DartConverter::FromDart( Dart_GetNativeArgument(args, 0))); echoed_event.timestamp = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 1)); + Dart_GetNativeArgument(args, 1)); echoed_event.physical = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 2)); + Dart_GetNativeArgument(args, 2)); echoed_event.logical = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 3)); + Dart_GetNativeArgument(args, 3)); echoed_char = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 4)); - echoed_event.synthesized = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 5)); + Dart_GetNativeArgument(args, 4)); + echoed_event.synthesized = + tonic::DartConverter::FromDart(Dart_GetNativeArgument(args, 5)); message_latch->Signal(); }; @@ -1261,7 +1262,8 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { CREATE_NATIVE_ENTRY( [&ready](Dart_NativeArguments args) { ready.Signal(); })); - context.AddNativeCallback("EchoKeyEvent", CREATE_NATIVE_ENTRY(native_echo_event)); + context.AddNativeCallback("EchoKeyEvent", + CREATE_NATIVE_ENTRY(native_echo_event)); auto engine = builder.LaunchEngine(); ASSERT_TRUE(engine.is_valid()); @@ -1269,16 +1271,17 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { // A normal down event const FlutterKeyEvent down_event_upper_a { - .struct_size = sizeof(FlutterKeyEvent), - .timestamp = 1, - .type = kFlutterKeyEventTypeDown, - .physical = 0x00070004, - .logical = 0x00000000061, - .character = "A", - .synthesized = false, + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1, + .type = kFlutterKeyEventTypeDown, + .physical = 0x00070004, + .logical = 0x00000000061, + .character = "A", + .synthesized = false, }; - FlutterEngineSendKeyEvent(engine.get(), &down_event_upper_a, - [](bool handled, void* user_data){}, nullptr); + FlutterEngineSendKeyEvent( + engine.get(), &down_event_upper_a, [](bool handled, void* user_data){}, + nullptr); message_latch->Wait(); ExpectKeyEventEq(echoed_event, down_event_upper_a); @@ -1286,15 +1289,16 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { // A repeat event with multi-byte character const FlutterKeyEvent repeat_event_wide_char { - .struct_size = sizeof(FlutterKeyEvent), - .timestamp = 1000, - .type = kFlutterKeyEventTypeRepeat, - .physical = 0x00070005, - .logical = 0x00000000062, - .character = "∆", - .synthesized = false, + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1000, + .type = kFlutterKeyEventTypeRepeat, + .physical = 0x00070005, + .logical = 0x00000000062, + .character = "∆", + .synthesized = false, }; - FlutterEngineSendKeyEvent(engine.get(), &repeat_event_wide_char, + FlutterEngineSendKeyEvent( + engine.get(), &repeat_event_wide_char, [](bool handled, void* user_data){}, nullptr); message_latch->Wait(); @@ -1303,16 +1307,16 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { // An up event with no character, synthesized const FlutterKeyEvent up_event { - .struct_size = sizeof(FlutterKeyEvent), - .timestamp = 1000000, - .type = kFlutterKeyEventTypeUp, - .physical = 0x00070006, - .logical = 0x00000000063, - .character = nullptr, - .synthesized = true, + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1000000, + .type = kFlutterKeyEventTypeUp, + .physical = 0x00070006, + .logical = 0x00000000063, + .character = nullptr, + .synthesized = true, }; - FlutterEngineSendKeyEvent(engine.get(), &up_event, - [](bool handled, void* user_data){}, nullptr); + FlutterEngineSendKeyEvent( + engine.get(), &up_event, [](bool handled, void* user_data){}, nullptr); message_latch->Wait(); ExpectKeyEventEq(echoed_event, up_event); @@ -1330,8 +1334,8 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { CREATE_NATIVE_ENTRY( [&ready](Dart_NativeArguments args) { ready.Signal(); })); - context.AddNativeCallback("EchoKeyEvent", - CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {})); + context.AddNativeCallback( + "EchoKeyEvent", CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {})); auto engine = builder.LaunchEngine(); ASSERT_TRUE(engine.is_valid()); @@ -1339,24 +1343,24 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { // Dispatch a single event FlutterKeyEvent event { - .struct_size = sizeof(FlutterKeyEvent), - .timestamp = 1000, - .type = kFlutterKeyEventTypeDown, - .physical = 0x00070005, - .logical = 0x00000000062, - .character = nullptr, + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1000, + .type = kFlutterKeyEventTypeDown, + .physical = 0x00070005, + .logical = 0x00000000062, + .character = nullptr, }; KeyEventUserData user_data1 { - .latch = std::make_shared(), + .latch = std::make_shared(), }; // Entrypoint `key_data_echo` uses `event.synthesized` as `handled`. event.synthesized = true; FlutterEngineSendKeyEvent( - engine.get(), - &event, + engine.get(), &event, [](bool handled, void* untyped_user_data){ - KeyEventUserData* user_data = reinterpret_cast(untyped_user_data); + KeyEventUserData* user_data = + reinterpret_cast(untyped_user_data); EXPECT_EQ(handled, true); user_data->latch->Signal(); }, @@ -1366,31 +1370,24 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { // Dispatch two events back to back, using the same callback on different // user_data KeyEventUserData user_data2 { - .latch = std::make_shared(), - .returned = false, + .latch = std::make_shared(), + .returned = false, }; KeyEventUserData user_data3 { - .latch = std::make_shared(), - .returned = false, + .latch = std::make_shared(), + .returned = false, }; auto callback23 = [](bool handled, void* untyped_user_data){ - KeyEventUserData* user_data = reinterpret_cast(untyped_user_data); + KeyEventUserData* user_data = + reinterpret_cast(untyped_user_data); EXPECT_EQ(handled, false); user_data->latch->Signal(); user_data->returned = true; }; event.synthesized = false; - FlutterEngineSendKeyEvent( - engine.get(), - &event, - callback23, - &user_data2); - FlutterEngineSendKeyEvent( - engine.get(), - &event, - callback23, - &user_data3); + FlutterEngineSendKeyEvent(engine.get(), &event, callback23, &user_data2); + FlutterEngineSendKeyEvent(engine.get(), &event, callback23, &user_data3); user_data2.latch->Wait(); user_data3.latch->Wait(); EXPECT_TRUE(user_data2.returned); From a31d47512ccd2e2a177c3a12857d171238657bcf Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 16:55:48 -0800 Subject: [PATCH 24/34] Format --- lib/ui/window/platform_configuration.h | 2 +- .../embedder/tests/embedder_unittests.cc | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 9b5f1b5135fa6..e6dacbb535b57 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -23,7 +23,7 @@ class FontCollection; class PlatformMessage; class Scene; -typedef std::function KeyDataResponse; +typedef std::function KeyDataResponse; //-------------------------------------------------------------------------- /// @brief An enum for defining the different kinds of accessibility features diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index bb53760433be4..a8297a5a00056 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1270,7 +1270,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { ready.Wait(); // A normal down event - const FlutterKeyEvent down_event_upper_a { + const FlutterKeyEvent down_event_upper_a{ .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1, .type = kFlutterKeyEventTypeDown, @@ -1288,7 +1288,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { EXPECT_EQ(echoed_char, 0x41llu); // A repeat event with multi-byte character - const FlutterKeyEvent repeat_event_wide_char { + const FlutterKeyEvent repeat_event_wide_char{ .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1000, .type = kFlutterKeyEventTypeRepeat, @@ -1299,14 +1299,14 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { }; FlutterEngineSendKeyEvent( engine.get(), &repeat_event_wide_char, - [](bool handled, void* user_data){}, nullptr); + [](bool handled, void* user_data) {}, nullptr); message_latch->Wait(); ExpectKeyEventEq(echoed_event, repeat_event_wide_char); EXPECT_EQ(echoed_char, 0x2206llu); // An up event with no character, synthesized - const FlutterKeyEvent up_event { + const FlutterKeyEvent up_event{ .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1000000, .type = kFlutterKeyEventTypeUp, @@ -1316,7 +1316,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { .synthesized = true, }; FlutterEngineSendKeyEvent( - engine.get(), &up_event, [](bool handled, void* user_data){}, nullptr); + engine.get(), &up_event, [](bool handled, void* user_data) {}, nullptr); message_latch->Wait(); ExpectKeyEventEq(echoed_event, up_event); @@ -1342,7 +1342,7 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { ready.Wait(); // Dispatch a single event - FlutterKeyEvent event { + FlutterKeyEvent event{ .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1000, .type = kFlutterKeyEventTypeDown, @@ -1351,14 +1351,14 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { .character = nullptr, }; - KeyEventUserData user_data1 { + KeyEventUserData user_data1{ .latch = std::make_shared(), }; // Entrypoint `key_data_echo` uses `event.synthesized` as `handled`. event.synthesized = true; FlutterEngineSendKeyEvent( engine.get(), &event, - [](bool handled, void* untyped_user_data){ + [](bool handled, void* untyped_user_data) { KeyEventUserData* user_data = reinterpret_cast(untyped_user_data); EXPECT_EQ(handled, true); @@ -1369,11 +1369,11 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { // Dispatch two events back to back, using the same callback on different // user_data - KeyEventUserData user_data2 { + KeyEventUserData user_data2{ .latch = std::make_shared(), .returned = false, }; - KeyEventUserData user_data3 { + KeyEventUserData user_data3{ .latch = std::make_shared(), .returned = false, }; From 9f192ee5ffdaed9e6a9d0ae73f41bd71f20be4cb Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 17:00:13 -0800 Subject: [PATCH 25/34] Format3 --- shell/platform/embedder/tests/embedder_unittests.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index a8297a5a00056..6826805dcd45c 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1280,7 +1280,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { .synthesized = false, }; FlutterEngineSendKeyEvent( - engine.get(), &down_event_upper_a, [](bool handled, void* user_data){}, + engine.get(), &down_event_upper_a, [](bool handled, void* user_data) {}, nullptr); message_latch->Wait(); @@ -1377,7 +1377,7 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { .latch = std::make_shared(), .returned = false, }; - auto callback23 = [](bool handled, void* untyped_user_data){ + auto callback23 = [](bool handled, void* untyped_user_data) { KeyEventUserData* user_data = reinterpret_cast(untyped_user_data); EXPECT_EQ(handled, false); From bc406f97d509071bdfe2810392de09b8adf5c2e6 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 17:23:06 -0800 Subject: [PATCH 26/34] Remove unused --- shell/platform/embedder/embedder.cc | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 436e7bdef28df..00fd7633a2611 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1505,16 +1505,6 @@ static inline flutter::KeyEventType MapKeyEventType( return flutter::KeyEventType::kUp; } -// The number of bytes that should be able to fully store character data. -// -// This is an arbitrary number that is considered sufficient, used as an -// upperbound in strnlen. -// -// Many platforms assert the character to be less than 2 int16's, i.e. 4 bytes, -// therefore the character data is asserted to be less than double the amount, -// i.e. 8 bytes. -static constexpr size_t kKeyEventCharacterMaxBytes = 8; - FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterKeyEvent* event, From c6d5221469b9aaf6d0a7aebaf0d9a54e96e0c0e2 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 17:44:23 -0800 Subject: [PATCH 27/34] Fix web compile --- .../lib/src/engine/keyboard_binding.dart | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart index 1a373e66a20bf..147077c5ac868 100644 --- a/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -302,7 +302,7 @@ class KeyboardConverter { _keydownCancelDuration, () => ui.KeyData( timeStamp: currentTimeStamp + _keydownCancelDuration, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: physicalKey, logical: logicalKey, character: null, @@ -362,7 +362,7 @@ class KeyboardConverter { final int? lastLogicalRecord = _pressingRecords[physicalKey]; - ui.KeyEventType change; + ui.KeyEventType type; if (_shouldSynthesizeCapsLockUp() && event.code! == _kPhysicalCapsLock) { // Case 1: Handle CapsLock on macOS @@ -374,7 +374,7 @@ class KeyboardConverter { Duration.zero, () => ui.KeyData( timeStamp: timeStamp, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: physicalKey, logical: logicalKey, character: null, @@ -384,16 +384,16 @@ class KeyboardConverter { _pressingRecords.remove(physicalKey); } ); - change = ui.KeyEventType.down; + type = ui.KeyEventType.down; } else if (isPhysicalDown) { // Case 2: Handle key down of normal keys - change = ui.KeyEventType.down; + type = ui.KeyEventType.down; if (lastLogicalRecord != null) { // This physical key is being pressed according to the record. if (event.repeat ?? false) { // A normal repeated key. - change = ui.KeyEventType.repeat; + type = ui.KeyEventType.repeat; } else { // A non-repeated key has been pressed that has the exact physical key as // a currently pressed one, usually indicating multiple keyboards are @@ -414,11 +414,11 @@ class KeyboardConverter { return false; } - change = ui.KeyEventType.up; + type = ui.KeyEventType.up; } final int? nextLogicalRecord; - switch (change) { + switch (type) { case ui.KeyEventType.down: assert(lastLogicalRecord == null); nextLogicalRecord = logicalKey; @@ -451,7 +451,7 @@ class KeyboardConverter { dispatchKeyData(ui.KeyData( timeStamp: timeStamp, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: physicalKey, logical: logicalKey, character: null, @@ -474,10 +474,10 @@ class KeyboardConverter { final ui.KeyData keyData = ui.KeyData( timeStamp: timeStamp, - change: change, + type: type, physical: physicalKey, logical: lastLogicalRecord ?? logicalKey, - character: change == ui.KeyEventType.up ? null : character, + character: type == ui.KeyEventType.up ? null : character, synthesized: false, ); From 54f3231a60eb2070b7365208654b75f7a66434ae Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 18:25:13 -0800 Subject: [PATCH 28/34] Add return value handling to web --- .../lib/src/engine/keyboard_binding.dart | 14 +- lib/web_ui/test/keyboard_converter_test.dart | 142 ++++++++++-------- 2 files changed, 87 insertions(+), 69 deletions(-) diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart index 147077c5ac868..7e6e46850e3a7 100644 --- a/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -67,7 +67,7 @@ const int _kDeadKeyShift = 0x200000000000; const int _kDeadKeyAlt = 0x400000000000; const int _kDeadKeyMeta = 0x800000000000; -typedef DispatchKeyData = void Function(ui.KeyData data); +typedef DispatchKeyData = bool Function(ui.KeyData data); /// Converts a floating number timestamp (in milliseconds) to a [Duration] by /// splitting it into two integer components: milliseconds + microseconds. @@ -106,7 +106,7 @@ class KeyboardBinding { print(event.type); } if (EngineSemanticsOwner.instance.receiveGlobalEvent(event)) { - handler(event); + return handler(event); } }; assert(!_listeners.containsKey(eventName)); @@ -121,8 +121,11 @@ class KeyboardBinding { }); _listeners.clear(); } - void _onKeyData(ui.KeyData data) { - EnginePlatformDispatcher.instance.invokeOnKeyData(data); + bool _onKeyData(ui.KeyData data) { + bool? result; + EnginePlatformDispatcher.instance.invokeOnKeyData(data, + (bool handled) { result = handled; }); + return result!; } void _setup() { @@ -481,7 +484,6 @@ class KeyboardConverter { synthesized: false, ); - dispatchKeyData(keyData); - return true; + return dispatchKeyData(keyData); } } diff --git a/lib/web_ui/test/keyboard_converter_test.dart b/lib/web_ui/test/keyboard_converter_test.dart index daa09e9dd89c5..4f63cd1f456b5 100644 --- a/lib/web_ui/test/keyboard_converter_test.dart +++ b/lib/web_ui/test/keyboard_converter_test.dart @@ -49,6 +49,7 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); bool preventedDefault = false; final onPreventDefault = () { preventedDefault = true; }; @@ -59,7 +60,7 @@ void testMain() { ); expectKeyData(keyDataList.last, timeStamp: Duration(milliseconds: 1), - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -72,7 +73,7 @@ void testMain() { ); expectKeyData(keyDataList.last, timeStamp: Duration(milliseconds: 1, microseconds: 500), - change: ui.KeyEventType.repeat, + type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -85,7 +86,7 @@ void testMain() { ); expectKeyData(keyDataList.last, timeStamp: Duration(seconds: 1, milliseconds: 500), - change: ui.KeyEventType.repeat, + type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -98,7 +99,7 @@ void testMain() { ); expectKeyData(keyDataList.last, timeStamp: Duration(seconds: 2, microseconds: 500), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -110,11 +111,12 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -122,7 +124,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'A', kShift)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'A', @@ -130,7 +132,7 @@ void testMain() { converter.handleEvent(keyRepeatedDownEvent('KeyA', 'A', kShift)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.repeat, + type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'A', @@ -138,7 +140,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -146,7 +148,7 @@ void testMain() { converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.repeat, + type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -154,7 +156,7 @@ void testMain() { converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.repeat, + type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -162,7 +164,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -173,11 +175,12 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -185,7 +188,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ShiftRight', 'Shift', kShift, kLocationRight)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalShiftRight, logical: kLogicalShiftRight, character: null, @@ -193,7 +196,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -201,7 +204,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ShiftRight', 'Shift', 0, kLocationRight)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalShiftRight, logical: kLogicalShiftRight, character: null, @@ -212,11 +215,12 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); converter.handleEvent(keyDownEvent('Digit1', '1', 0, 0)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalDigit1, logical: kLogicalDigit1, character: '1', @@ -224,7 +228,7 @@ void testMain() { converter.handleEvent(keyDownEvent('Numpad1', '1', 0, kLocationNumpad)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalNumpad1, logical: kLogicalNumpad1, character: '1', @@ -232,7 +236,7 @@ void testMain() { converter.handleEvent(keyUpEvent('Digit1', '1', 0, 0)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalDigit1, logical: kLogicalDigit1, character: null, @@ -240,7 +244,7 @@ void testMain() { converter.handleEvent(keyUpEvent('Numpad1', '1', 0, kLocationNumpad)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalNumpad1, logical: kLogicalNumpad1, character: null, @@ -251,13 +255,14 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); bool preventedDefault = false; final onPreventDefault = () { preventedDefault = true; }; converter.handleEvent(keyDownEvent('Tab', 'Tab')..onPreventDefault = onPreventDefault); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalTab, logical: kLogicalTab, character: null, @@ -267,7 +272,7 @@ void testMain() { converter.handleEvent(keyUpEvent('Tab', 'Tab')..onPreventDefault = onPreventDefault); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalTab, logical: kLogicalTab, character: null, @@ -280,6 +285,7 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); // The absolute values of the following logical keys are not guaranteed. @@ -294,7 +300,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyE', 'Dead', kAlt)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyE, logical: kLogicalAltE, character: null, @@ -302,7 +308,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyE', 'Dead', kAlt)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyE, logical: kLogicalAltE, character: null, @@ -310,7 +316,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyU', 'Dead', kAlt)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyU, logical: kLogicalAltU, character: null, @@ -318,7 +324,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyU', 'Dead', kAlt)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyU, logical: kLogicalAltU, character: null, @@ -330,7 +336,7 @@ void testMain() { // testing. converter.handleEvent(keyDownEvent('KeyE', 'Dead', kAlt | kShift)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyE, logical: kLogicalAltShiftE, character: null, @@ -340,7 +346,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyE', 'e', kShift)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyE, logical: kLogicalAltShiftE, character: null, @@ -353,6 +359,7 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); @@ -365,7 +372,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -376,6 +383,7 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); // A KeyDown of ShiftRight is missed due to loss of focus. @@ -387,6 +395,7 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); // Same layout @@ -405,7 +414,7 @@ void testMain() { keyDataList.clear(); converter.handleEvent(keyDownEvent('KeyA', 'a')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -413,7 +422,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyU', 'u')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyU, logical: kLogicalKeyU, character: 'u', @@ -424,12 +433,13 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }, onMacOs: true); converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -439,7 +449,7 @@ void testMain() { async.elapse(Duration(microseconds: 1)); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -450,7 +460,7 @@ void testMain() { converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -460,7 +470,7 @@ void testMain() { async.elapse(Duration(microseconds: 1)); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -472,7 +482,7 @@ void testMain() { converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -490,12 +500,13 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }, onMacOs: false); converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -508,7 +519,7 @@ void testMain() { converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -520,7 +531,7 @@ void testMain() { converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -528,7 +539,7 @@ void testMain() { converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -539,6 +550,7 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100); @@ -547,7 +559,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a', kMeta)..timeStamp = 200); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 200), - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -559,7 +571,7 @@ void testMain() { async.elapse(Duration(milliseconds: 2500)); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 1200), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -570,7 +582,7 @@ void testMain() { converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 2700); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2700), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalMetaLeft, logical: kLogicalMetaLeft, character: null, @@ -581,7 +593,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 2800); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2800), - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -591,7 +603,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2900); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2900), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -602,6 +614,7 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100); @@ -621,7 +634,7 @@ void testMain() { async.elapse(Duration(milliseconds: 2500)); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 1700), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -632,7 +645,7 @@ void testMain() { converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 3200); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 3200), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalMetaLeft, logical: kLogicalMetaLeft, character: null, @@ -643,7 +656,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 3300); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 3300), - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -653,7 +666,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 3400); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 3400), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -664,6 +677,7 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100); @@ -672,7 +686,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a', kCtrl)..timeStamp = 200); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 200), - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -686,7 +700,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 800); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 800), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -699,7 +713,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 2800); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2800), - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -709,7 +723,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2900); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2900), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -720,12 +734,13 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }, onMacOs: false); converter.handleEvent(keyDownEvent('ScrollLock', 'ScrollLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalScrollLock, logical: kLogicalScrollLock, character: null, @@ -738,7 +753,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ScrollLock', 'ScrollLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalScrollLock, logical: kLogicalScrollLock, character: null, @@ -747,7 +762,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ScrollLock', 'ScrollLock')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalScrollLock, logical: kLogicalScrollLock, character: null, @@ -755,7 +770,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ScrollLock', 'ScrollLock')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalScrollLock, logical: kLogicalScrollLock, character: null, @@ -766,11 +781,12 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }, onMacOs: false); converter.handleEvent(keyDownEvent('ShiftRight', 'Shift', kShift, kLocationRight)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalShiftRight, logical: kLogicalShiftRight, character: null, @@ -778,7 +794,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -790,21 +806,21 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')); expect(keyDataList, hasLength(3)); expectKeyData(keyDataList[0], - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, synthesized: true, ); expectKeyData(keyDataList[1], - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalShiftRight, logical: kLogicalShiftRight, character: null, synthesized: true, ); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -901,14 +917,14 @@ const kScrollLock = 0x4; void expectKeyData( ui.KeyData target, { - required ui.KeyEventType change, + required ui.KeyEventType type, required int physical, required int logical, required String? character, Duration? timeStamp, bool synthesized = false, }) { - expect(target.change, change); + expect(target.type, type); expect(target.physical, physical); expect(target.logical, logical); expect(target.character, character); From 5428e9942a1e03f2631747fb665ee79ee8cfdebd Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 19:05:29 -0800 Subject: [PATCH 29/34] Add some handling test --- .../lib/src/engine/keyboard_binding.dart | 28 ++--- lib/web_ui/test/keyboard_converter_test.dart | 118 +++++++++++------- 2 files changed, 81 insertions(+), 65 deletions(-) diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart index 7e6e46850e3a7..db98684332ae0 100644 --- a/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -203,16 +203,6 @@ class KeyboardConverter { Duration get _keydownCancelDuration => onMacOs ? _kKeydownCancelDurationMacOs : _kKeydownCancelDurationNormal; - bool _shouldPreventDefault(FlutterHtmlKeyboardEvent event) { - switch (event.key) { - case 'Tab': - return true; - - default: - return false; - } - } - static int _getPhysicalCode(String code) { return kWebToPhysicalKey[code] ?? (code.hashCode + _kWebKeyIdPlane + _kAutogeneratedMask); } @@ -329,16 +319,13 @@ class KeyboardConverter { // * The method might dispatch some synthesized key data first to update states, // results discarded. // * Then it dispatches exactly one non-synthesized key data that corresponds - // to the `event`, i.e. the main key data. The result of this dispatching is - // returned to indicate whether the event is processed by Flutter. + // to the `event`, i.e. the primary key data. If this dispatching returns + // true, then this event will be invoked `preventDefault`. // * Some key data might be synthesized to update states after the main key // data. They are always scheduled asynchronously with results discarded. - bool handleEvent(FlutterHtmlKeyboardEvent event) { + void handleEvent(FlutterHtmlKeyboardEvent event) { final Duration timeStamp = _eventTimeStampToDuration(event.timeStamp!); - if (_shouldPreventDefault(event)) { - event.preventDefault(); - } final String eventKey = event.key!; final int physicalKey = _getPhysicalCode(event.code!); @@ -402,7 +389,7 @@ class KeyboardConverter { // a currently pressed one, usually indicating multiple keyboards are // pressing keys with the same physical key, or the up event was lost // during a loss of focus. The down event is ignored. - return false; + return; } } else { // This physical key is not being pressed according to the record. It's a @@ -414,7 +401,7 @@ class KeyboardConverter { if (lastLogicalRecord == null) { // The physical key has been released before. It indicates multiple // keyboards pressed keys with the same physical key. Ignore the up event. - return false; + return; } type = ui.KeyEventType.up; @@ -484,6 +471,9 @@ class KeyboardConverter { synthesized: false, ); - return dispatchKeyData(keyData); + bool primaryHandled = dispatchKeyData(keyData); + if (primaryHandled) { + event.preventDefault(); + } } } diff --git a/lib/web_ui/test/keyboard_converter_test.dart b/lib/web_ui/test/keyboard_converter_test.dart index 4f63cd1f456b5..8c21267925a02 100644 --- a/lib/web_ui/test/keyboard_converter_test.dart +++ b/lib/web_ui/test/keyboard_converter_test.dart @@ -49,7 +49,8 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); - return true; + // Only handle down events + return key.type == ui.KeyEventType.down; }); bool preventedDefault = false; final onPreventDefault = () { preventedDefault = true; }; @@ -65,7 +66,8 @@ void testMain() { logical: kLogicalKeyA, character: 'a', ); - expect(preventedDefault, false); + expect(preventedDefault, true); + preventedDefault = false; converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a') ..timeStamp = 1.5 @@ -111,56 +113,79 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); - return true; + // Only handle down events + return key.type == ui.KeyEventType.down; }); + bool preventedDefault = false; + final onPreventDefault = () { preventedDefault = true; }; - converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft) + ..onPreventDefault = onPreventDefault + ); expectKeyData(keyDataList.last, type: ui.KeyEventType.down, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, ); + expect(preventedDefault, true); + preventedDefault = false; - converter.handleEvent(keyDownEvent('KeyA', 'A', kShift)); + converter.handleEvent(keyDownEvent('KeyA', 'A', kShift) + ..onPreventDefault = onPreventDefault + ); expectKeyData(keyDataList.last, type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'A', ); + expect(preventedDefault, true); + preventedDefault = false; - converter.handleEvent(keyRepeatedDownEvent('KeyA', 'A', kShift)); + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'A', kShift) + ..onPreventDefault = onPreventDefault + ); expectKeyData(keyDataList.last, type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'A', ); + expect(preventedDefault, false); - converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); + converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft) + ..onPreventDefault = onPreventDefault + ); expectKeyData(keyDataList.last, type: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, ); + expect(preventedDefault, false); - converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a') + ..onPreventDefault = onPreventDefault + ); expectKeyData(keyDataList.last, type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', ); + expect(preventedDefault, false); - converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a') + ..onPreventDefault = onPreventDefault + ); expectKeyData(keyDataList.last, type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', ); + expect(preventedDefault, false); converter.handleEvent(keyUpEvent('KeyA', 'a')); expectKeyData(keyDataList.last, @@ -169,6 +194,7 @@ void testMain() { logical: kLogicalKeyA, character: null, ); + expect(preventedDefault, false); }); test('Distinguish between left and right modifiers', () { @@ -251,36 +277,6 @@ void testMain() { ); }); - test('Prevents default when pressing Tab', () { - final List keyDataList = []; - final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { - keyDataList.add(key); - return true; - }); - bool preventedDefault = false; - final onPreventDefault = () { preventedDefault = true; }; - - converter.handleEvent(keyDownEvent('Tab', 'Tab')..onPreventDefault = onPreventDefault); - expectKeyData(keyDataList.last, - type: ui.KeyEventType.down, - physical: kPhysicalTab, - logical: kLogicalTab, - character: null, - ); - expect(preventedDefault, true); - preventedDefault = false; - - converter.handleEvent(keyUpEvent('Tab', 'Tab')..onPreventDefault = onPreventDefault); - expectKeyData(keyDataList.last, - type: ui.KeyEventType.up, - physical: kPhysicalTab, - logical: kLogicalTab, - character: null, - ); - expect(preventedDefault, true); - preventedDefault = false; - }); - test('Dead keys are distinguishable', () { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { @@ -361,15 +357,26 @@ void testMain() { keyDataList.add(key); return true; }); + bool preventedDefault = false; + final onPreventDefault = () { preventedDefault = true; }; - converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft) + ..onPreventDefault = onPreventDefault + ); + expect(preventedDefault, true); + preventedDefault = false; // A KeyUp of ShiftLeft is missed due to loss of focus. keyDataList.clear(); - converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft) + ..onPreventDefault = onPreventDefault + ); expect(keyDataList, isEmpty); + expect(preventedDefault, false); - converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); + converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft) + ..onPreventDefault = onPreventDefault + ); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, type: ui.KeyEventType.up, @@ -377,6 +384,7 @@ void testMain() { logical: kLogicalShiftLeft, character: null, ); + expect(preventedDefault, true); }); test('Duplicate ups are skipped', () { @@ -385,10 +393,15 @@ void testMain() { keyDataList.add(key); return true; }); + bool preventedDefault = false; + final onPreventDefault = () { preventedDefault = true; }; // A KeyDown of ShiftRight is missed due to loss of focus. - converter.handleEvent(keyUpEvent('ShiftRight', 'Shift', 0, kLocationRight)); + converter.handleEvent(keyUpEvent('ShiftRight', 'Shift', 0, kLocationRight) + ..onPreventDefault = onPreventDefault + ); expect(keyDataList, isEmpty); + expect(preventedDefault, false); }); test('Conflict from multiple keyboards do not crash', () { @@ -435,8 +448,13 @@ void testMain() { keyDataList.add(key); return true; }, onMacOs: true); + bool preventedDefault = false; + final onPreventDefault = () { preventedDefault = true; }; - converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); + // A KeyDown of ShiftRight is missed due to loss of focus. + converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock') + ..onPreventDefault = onPreventDefault + ); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, type: ui.KeyEventType.down, @@ -444,7 +462,9 @@ void testMain() { logical: kLogicalCapsLock, character: null, ); + expect(preventedDefault, true); keyDataList.clear(); + preventedDefault = false; async.elapse(Duration(microseconds: 1)); expect(keyDataList, hasLength(1)); @@ -455,9 +475,12 @@ void testMain() { character: null, synthesized: true, ); + expect(preventedDefault, false); keyDataList.clear(); - converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); + converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock') + ..onPreventDefault = onPreventDefault + ); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, type: ui.KeyEventType.down, @@ -465,7 +488,9 @@ void testMain() { logical: kLogicalCapsLock, character: null, ); + expect(preventedDefault, true); keyDataList.clear(); + preventedDefault = false; async.elapse(Duration(microseconds: 1)); expect(keyDataList, hasLength(1)); @@ -476,6 +501,7 @@ void testMain() { character: null, synthesized: true, ); + expect(preventedDefault, false); keyDataList.clear(); // Another key down works @@ -777,7 +803,7 @@ void testMain() { ); }); - test('Deduce modifier key cancel from modifier field', () { + test('Deduce modifier key up from modifier field', () { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); From f2fd10dff1b83ff35c02d8de318c2c4705768cfb Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 19:09:11 -0800 Subject: [PATCH 30/34] Fix signatures --- .../ios/framework/Source/FlutterEnginePlatformViewTest.mm | 3 ++- .../darwin/ios/framework/Source/FlutterPlatformViewsTest.mm | 3 ++- .../darwin/ios/framework/Source/accessibility_bridge_test.mm | 3 ++- shell/platform/fuchsia/flutter/platform_view_unittest.cc | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm index 129cce47c8199..53d8688077068 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm @@ -25,7 +25,8 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet, + std::function callback) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 631f98c30c887..c826a2e9723d2 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -95,7 +95,8 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet, + std::function callback) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index a446224495925..41f8072afd858 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -78,7 +78,8 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet, + std::function callback) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 1f6b793f1d160..608c133a2bd8c 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -82,7 +82,8 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { std::unique_ptr packet) {} // |flutter::PlatformView::Delegate| void OnPlatformViewDispatchKeyDataPacket( - std::unique_ptr packet) {} + std::unique_ptr packet, + std::function callback) {} // |flutter::PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction(int32_t id, flutter::SemanticsAction action, From bf643a58042146706bc637dcae8842e35e8e8e58 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 26 Jan 2021 13:11:36 -0800 Subject: [PATCH 31/34] Better doc --- shell/platform/embedder/embedder.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 4c0ba35a34e23..48ce6de82975e 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -663,8 +663,8 @@ typedef struct { /// been released at the next key event, and should construct a synthesized up /// event immediately before the actual event. /// - /// An event being synthesized means that the framework will not trust the - /// `timestamp` of the event. + /// An event being synthesized means that the `timestamp` might greatly deviate + /// from the actual time when the event occurs physically. bool synthesized; } FlutterKeyEvent; From 771dfe610cdbb4ef2de15ebadfe99e87566dcf9d Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 26 Jan 2021 13:16:55 -0800 Subject: [PATCH 32/34] format --- shell/platform/embedder/embedder.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 48ce6de82975e..6489edcf938ef 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -663,8 +663,8 @@ typedef struct { /// been released at the next key event, and should construct a synthesized up /// event immediately before the actual event. /// - /// An event being synthesized means that the `timestamp` might greatly deviate - /// from the actual time when the event occurs physically. + /// An event being synthesized means that the `timestamp` might greatly + /// deviate from the actual time when the event occurs physically. bool synthesized; } FlutterKeyEvent; From 749c16170e730f61915f9f345bb334a787d921a8 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 26 Jan 2021 13:33:36 -0800 Subject: [PATCH 33/34] Deflake --- shell/platform/embedder/tests/embedder_unittests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 6826805dcd45c..64b3bfed8fb87 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1381,8 +1381,8 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { KeyEventUserData* user_data = reinterpret_cast(untyped_user_data); EXPECT_EQ(handled, false); - user_data->latch->Signal(); user_data->returned = true; + user_data->latch->Signal(); }; event.synthesized = false; From 04b31e51616e511fab346cdc21102f51b275100d Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 26 Jan 2021 15:29:52 -0800 Subject: [PATCH 34/34] Explain the sync return --- lib/web_ui/lib/src/engine/keyboard_binding.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart index db98684332ae0..40e5a39fc4727 100644 --- a/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -123,6 +123,8 @@ class KeyboardBinding { } bool _onKeyData(ui.KeyData data) { bool? result; + // This callback is designed to be invoked synchronously. This is enforced + // by `result`, which starts null and is asserted non-null when returned. EnginePlatformDispatcher.instance.invokeOnKeyData(data, (bool handled) { result = handled; }); return result!;