diff --git a/shell/platform/tizen/BUILD.gn b/shell/platform/tizen/BUILD.gn index 1951c6835a056..98c447774f1df 100644 --- a/shell/platform/tizen/BUILD.gn +++ b/shell/platform/tizen/BUILD.gn @@ -112,6 +112,7 @@ template("embedder") { "channels/app_control_channel.cc", "channels/feedback_manager.cc", "channels/key_event_channel.cc", + "channels/key_mapping.cc", "channels/lifecycle_channel.cc", "channels/navigation_channel.cc", "channels/platform_channel.cc", diff --git a/shell/platform/tizen/channels/key_event_channel.cc b/shell/platform/tizen/channels/key_event_channel.cc index 041056338f743..10c5dd6bbd5c0 100644 --- a/shell/platform/tizen/channels/key_event_channel.cc +++ b/shell/platform/tizen/channels/key_event_channel.cc @@ -5,10 +5,11 @@ #include "key_event_channel.h" #include -#include #include #include "flutter/shell/platform/common/json_message_codec.h" +#include "flutter/shell/platform/tizen/channels/key_mapping.h" +#include "flutter/shell/platform/tizen/logger.h" namespace flutter { @@ -30,211 +31,7 @@ constexpr char kKeyDown[] = "keydown"; constexpr char kGtkToolkit[] = "gtk"; constexpr char kLinuxKeyMap[] = "linux"; -// Mapping from TV remote control key symbols to standard XKB scan codes. -// -// The values are originally defined in: -// - xkb-tizen-data/tizen_key_layout.txt.tv -// - flutter/keyboard_maps.dart (kLinuxToPhysicalKey) -const std::map kSymbolToScanCode = { - {"XF86AudioPlay", 0x000000d7}, // mediaPlay - {"XF86AudioPlayPause", 0x000000ac}, // mediaPlayPause - {"XF86Menu", 0x00000087}, // contextMenu - {"XF86PlayBack", 0x000000ac}, // mediaPlayPause - {"XF86SysMenu", 0x00000087}, // contextMenu -}; - -// Mapping from physical scan codes to logical (GTK) key codes. -// -// The values are originally defined in: -// - flutter/keyboard_maps.dart (kLinuxToPhysicalKey) -// - flutter/keyboard_maps.dart (kGtkToLogicalKey) -const std::map kScanCodeToKeyCode = { - {0x00000009, 65307}, // escape - {0x0000000a, 49}, // digit1 - {0x0000000b, 50}, // digit2 - {0x0000000c, 51}, // digit3 - {0x0000000d, 52}, // digit4 - {0x0000000e, 53}, // digit5 - {0x0000000f, 54}, // digit6 - {0x00000010, 55}, // digit7 - {0x00000011, 56}, // digit8 - {0x00000012, 57}, // digit9 - {0x00000013, 48}, // digit0 - {0x00000014, 45}, // minus - {0x00000015, 61}, // equal - {0x00000016, 65288}, // backspace - {0x00000017, 65289}, // tab - {0x00000018, 81}, // keyQ - {0x00000019, 87}, // keyW - {0x0000001a, 69}, // keyE - {0x0000001b, 82}, // keyR - {0x0000001c, 84}, // keyT - {0x0000001d, 89}, // keyY - {0x0000001e, 85}, // keyU - {0x0000001f, 73}, // keyI - {0x00000020, 79}, // keyO - {0x00000021, 80}, // keyP - {0x00000022, 91}, // bracketLeft - {0x00000023, 93}, // bracketRight - {0x00000024, 65293}, // enter - {0x00000025, 65507}, // controlLeft - {0x00000026, 65}, // keyA - {0x00000027, 83}, // keyS - {0x00000028, 68}, // keyD - {0x00000029, 70}, // keyF - {0x0000002a, 71}, // keyG - {0x0000002b, 72}, // keyH - {0x0000002c, 74}, // keyJ - {0x0000002d, 75}, // keyK - {0x0000002e, 76}, // keyL - {0x0000002f, 59}, // semicolon - {0x00000030, 39}, // quote - {0x00000031, 96}, // backquote - {0x00000032, 65505}, // shiftLeft - {0x00000033, 92}, // backslash - {0x00000034, 90}, // keyZ - {0x00000035, 88}, // keyX - {0x00000036, 67}, // keyC - {0x00000037, 86}, // keyV - {0x00000038, 66}, // keyB - {0x00000039, 78}, // keyN - {0x0000003a, 77}, // keyM - {0x0000003b, 44}, // comma - {0x0000003c, 46}, // period - {0x0000003d, 47}, // slash - {0x0000003e, 65506}, // shiftRight - {0x0000003f, 65450}, // numpadMultiply - {0x00000040, 65513}, // altLeft - {0x00000041, 32}, // space - {0x00000042, 65509}, // capsLock - {0x00000043, 65470}, // f1 - {0x00000044, 65471}, // f2 - {0x00000045, 65472}, // f3 - {0x00000046, 65473}, // f4 - {0x00000047, 65474}, // f5 - {0x00000048, 65475}, // f6 - {0x00000049, 65476}, // f7 - {0x0000004a, 65477}, // f8 - {0x0000004b, 65478}, // f9 - {0x0000004c, 65479}, // f10 - {0x0000004d, 65407}, // numLock - {0x0000004e, 65300}, // scrollLock - {0x0000004f, 65463}, // numpad7 - {0x00000050, 65464}, // numpad8 - {0x00000051, 65465}, // numpad9 - {0x00000052, 65453}, // numpadSubtract - {0x00000053, 65460}, // numpad4 - {0x00000054, 65461}, // numpad5 - {0x00000055, 65462}, // numpad6 - {0x00000056, 65451}, // numpadAdd - {0x00000057, 65457}, // numpad1 - {0x00000058, 65458}, // numpad2 - {0x00000059, 65459}, // numpad3 - {0x0000005a, 65456}, // numpad0 - {0x0000005b, 65454}, // numpadDecimal - {0x0000005f, 65480}, // f11 - {0x00000060, 65481}, // f12 - {0x00000065, 65406}, // kanaMode - {0x00000068, 65421}, // numpadEnter - {0x00000069, 65508}, // controlRight - {0x0000006a, 65455}, // numpadDivide - {0x0000006b, 64797}, // printScreen - {0x0000006c, 65514}, // altRight - {0x0000006e, 65360}, // home - {0x0000006f, 65362}, // arrowUp - {0x00000070, 65365}, // pageUp - {0x00000071, 65361}, // arrowLeft - {0x00000072, 65363}, // arrowRight - {0x00000073, 65367}, // end - {0x00000074, 65364}, // arrowDown - {0x00000075, 65366}, // pageDown - {0x00000076, 65379}, // insert - {0x00000077, 65535}, // delete - {0x00000079, 269025042}, // audioVolumeMute - {0x0000007a, 269025041}, // audioVolumeDown - {0x0000007b, 269025043}, // audioVolumeUp - {0x0000007c, 269025066}, // power - {0x0000007d, 65469}, // numpadEqual - {0x0000007f, 65299}, // pause - {0x00000084, 165}, // intlYen - {0x00000085, 65511}, // metaLeft - {0x00000086, 65512}, // metaRight - {0x00000087, 65383}, // contextMenu - {0x00000088, 269025064}, // browserStop - {0x0000008b, 65381}, // undo - {0x0000008c, 65376}, // select - {0x0000008d, 269025111}, // copy - {0x0000008e, 269025131}, // open - {0x0000008f, 269025133}, // paste - {0x00000090, 65384}, // find - {0x00000092, 65386}, // help - {0x00000096, 269025071}, // sleep - {0x00000097, 269025067}, // wakeUp - {0x0000009e, 269025070}, // launchInternetBrowser - {0x000000a3, 269025049}, // launchMail - {0x000000a4, 269025072}, // browserFavorites - {0x000000a6, 269025062}, // browserBack - {0x000000a7, 269025063}, // browserForward - {0x000000a9, 269025068}, // eject - {0x000000ab, 269025047}, // mediaTrackNext - {0x000000ad, 269025046}, // mediaTrackPrevious - {0x000000ae, 269025045}, // mediaStop - {0x000000af, 269025052}, // mediaRecord - {0x000000b0, 269025086}, // mediaRewind - {0x000000b1, 269025134}, // launchPhone - {0x000000b4, 269025048}, // browserHome - {0x000000b5, 269025065}, // browserRefresh - {0x000000bd, 269025128}, // newKey - {0x000000be, 65382}, // redo - {0x000000bf, 65482}, // f13 - {0x000000c0, 65483}, // f14 - {0x000000c1, 65484}, // f15 - {0x000000c2, 65485}, // f16 - {0x000000c3, 65486}, // f17 - {0x000000c4, 65487}, // f18 - {0x000000c5, 65488}, // f19 - {0x000000c6, 65489}, // f20 - {0x000000c7, 65490}, // f21 - {0x000000c8, 65491}, // f22 - {0x000000c9, 65492}, // f23 - {0x000000ca, 65493}, // f24 - {0x000000d1, 269025073}, // mediaPause - {0x000000d6, 269025110}, // close - {0x000000d7, 269025044}, // mediaPlay - {0x000000d8, 269025175}, // mediaFastForward - {0x000000da, 65377}, // print - {0x000000e1, 269025051}, // browserSearch - {0x000000e8, 269025027}, // brightnessDown - {0x000000e9, 269025026}, // brightnessUp - {0x000000ed, 269025030}, // kbdIllumDown - {0x000000ee, 269025029}, // kbdIllumUp - {0x000000ef, 269025147}, // mailSend - {0x000000f0, 269025138}, // mailReply - {0x000000f1, 269025168}, // mailForward - {0x000000f2, 269025143}, // save - {0x00000190, 269025170}, // launchAudioBrowser - {0x00000195, 269025056}, // launchCalendar - {0x000001aa, 269025163}, // zoomIn - {0x000001ab, 269025164}, // zoomOut - {0x000001b8, 269025148}, // spellCheck - {0x000001b9, 269025121}, // logOff - {0x0000024d, 269025069}, // launchScreenSaver -}; - -// Mapping from Ecore modifiers to GTK modifiers. -// -// The values are originally defined in: -// - efl/Ecore_Input.h -// - flutter/raw_keyboard_linux.dart (GtkKeyHelper) -const std::map kEcoreModifierToGtkModifier = { - {0x0001, 1 << 0}, // SHIFT (modifierShift) - {0x0002, 1 << 2}, // CTRL (modifierControl) - {0x0004, 1 << 3}, // ALT (modifierMod1) - {0x0008, 1 << 26}, // WIN (modifierMeta) - {0x0010, 0}, // SCROLL (undefined) - {0x0020, 1 << 4}, // NUM (modifierMod2) - {0x0040, 1 << 1}, // CAPS (modifierCapsLock) -}; +constexpr size_t kMaxPendingEvents = 1000; uint32_t Utf8ToUtf32CodePoint(const char* utf8) { std::wstring_convert, wchar_t> converter; @@ -244,13 +41,36 @@ uint32_t Utf8ToUtf32CodePoint(const char* utf8) { return 0; } +uint64_t ApplyPlaneToId(uint64_t id, uint64_t plane) { + return (id & kValueMask) | plane; +} + +uint64_t GetPhysicalKey(uint32_t scan_code) { + auto iter = kScanCodeToPhysicalKeyCode.find(scan_code); + if (iter != kScanCodeToPhysicalKeyCode.end()) { + return iter->second; + } + return ApplyPlaneToId(scan_code, kTizenPlane); +} + +uint64_t GetLogicalKey(const char* key) { + auto iter = kSymbolToLogicalKeyCode.find(key); + if (iter != kSymbolToLogicalKeyCode.end()) { + return iter->second; + } + // No logical ID is available. Default to 0. + return ApplyPlaneToId(0, kTizenPlane); +} + } // namespace -KeyEventChannel::KeyEventChannel(BinaryMessenger* messenger) +KeyEventChannel::KeyEventChannel(BinaryMessenger* messenger, + SendEventHandler send_event) : channel_(std::make_unique>( messenger, kChannelName, - &JsonMessageCodec::GetInstance())) {} + &JsonMessageCodec::GetInstance())), + send_event_(send_event) {} KeyEventChannel::~KeyEventChannel() {} @@ -261,15 +81,44 @@ void KeyEventChannel::SendKey(const char* key, uint32_t scan_code, bool is_down, std::function callback) { - auto iter1 = kSymbolToScanCode.find(key); - if (iter1 != kSymbolToScanCode.end()) { - scan_code = iter1->second; + uint64_t sequence_id = last_sequence_id_++; + + PendingEvent pending; + // This event is sent through the embedder API (KeyEvent) and the platform + // channel (RawKeyEvent) simultaneously, and |callback| will be called once + // responses are received from both of them. + pending.unreplied = 2; + pending.any_handled = false; + pending.callback = std::move(callback); + + if (pending_events_.size() > kMaxPendingEvents) { + FT_LOG(Error) + << "There are " << pending_events_.size() + << " keyboard events that have not yet received a response from the " + << "framework. Are responses being sent?"; } + pending_events_[sequence_id] = std::make_unique(pending); + + SendEmbedderEvent(key, string, compose, modifiers, scan_code, is_down, + sequence_id); + // The channel-based API (RawKeyEvent) is deprecated and |SendChannelEvent| + // will be removed in the future. This class (KeyEventChannel) itself will + // also be renamed and refactored then. + SendChannelEvent(key, string, compose, modifiers, scan_code, is_down, + sequence_id); +} +void KeyEventChannel::SendChannelEvent(const char* key, + const char* string, + const char* compose, + uint32_t modifiers, + uint32_t scan_code, + bool is_down, + uint64_t sequence_id) { uint32_t key_code = 0; - auto iter2 = kScanCodeToKeyCode.find(scan_code); - if (iter2 != kScanCodeToKeyCode.end()) { - key_code = iter2->second; + auto iter = kScanCodeToGtkKeyCode.find(scan_code); + if (iter != kScanCodeToGtkKeyCode.end()) { + key_code = iter->second; } int gtk_modifiers = 0; @@ -280,8 +129,8 @@ void KeyEventChannel::SendKey(const char* key, } uint32_t unicode_scalar_values = 0; - if (compose) { - unicode_scalar_values = Utf8ToUtf32CodePoint(compose); + if (string) { + unicode_scalar_values = Utf8ToUtf32CodePoint(string); } rapidjson::Document event(rapidjson::kObjectType); @@ -297,15 +146,110 @@ void KeyEventChannel::SendKey(const char* key, } else { event.AddMember(kTypeKey, kKeyUp, allocator); } - channel_->Send(event, [callback = std::move(callback)](const uint8_t* reply, - size_t reply_size) { - if (reply != nullptr) { - std::unique_ptr decoded = - JsonMessageCodec::GetInstance().DecodeMessage(reply, reply_size); - bool handled = (*decoded)[kHandledKey].GetBool(); - callback(handled); + channel_->Send( + event, [this, sequence_id](const uint8_t* reply, size_t reply_size) { + if (reply != nullptr) { + std::unique_ptr decoded = + JsonMessageCodec::GetInstance().DecodeMessage(reply, reply_size); + bool handled = (*decoded)[kHandledKey].GetBool(); + ResolvePendingEvent(sequence_id, handled); + } + }); +} + +void KeyEventChannel::SendEmbedderEvent(const char* key, + const char* string, + const char* compose, + uint32_t modifiers, + uint32_t scan_code, + bool is_down, + uint64_t sequence_id) { + uint64_t physical_key = GetPhysicalKey(scan_code); + uint64_t logical_key = GetLogicalKey(key); + const char* character = is_down ? string : nullptr; + + uint64_t last_logical_record = 0; + auto iter = pressing_records_.find(physical_key); + if (iter != pressing_records_.end()) { + last_logical_record = iter->second; + } + + FlutterKeyEventType type; + if (is_down) { + if (last_logical_record) { + // A key has been pressed that has the exact physical key as a currently + // pressed one. This can happen during repeated events. + type = kFlutterKeyEventTypeRepeat; + } else { + type = kFlutterKeyEventTypeDown; + } + pressing_records_[physical_key] = logical_key; + } else { + if (!last_logical_record) { + // The physical key has been released before. It might indicate a missed + // event due to loss of focus, or multiple keyboards pressed keys with the + // same physical key. Ignore the up event. + FlutterKeyEvent empty_event = { + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = static_cast( + std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count()), + .type = kFlutterKeyEventTypeDown, + .physical = 0, + .logical = 0, + .character = "", + .synthesized = false, + }; + send_event_(empty_event, nullptr, nullptr); + ResolvePendingEvent(sequence_id, true); + return; + } else { + type = kFlutterKeyEventTypeUp; + } + pressing_records_.erase(physical_key); + } + + FlutterKeyEvent event = {}; + event.struct_size = sizeof(FlutterKeyEvent), + event.timestamp = static_cast( + std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count()); + event.type = type; + event.physical = physical_key; + event.logical = last_logical_record != 0 ? last_logical_record : logical_key; + event.character = character; + event.synthesized = false; + + send_event_( + event, + [](bool handled, void* user_data) { + auto* callback = + reinterpret_cast*>(user_data); + (*callback)(handled); + delete callback; + }, + new std::function([this, sequence_id](bool handled) { + ResolvePendingEvent(sequence_id, handled); + })); +} + +void KeyEventChannel::ResolvePendingEvent(uint64_t sequence_id, bool handled) { + auto iter = pending_events_.find(sequence_id); + if (iter != pending_events_.end()) { + PendingEvent* event = iter->second.get(); + event->any_handled = event->any_handled || handled; + event->unreplied -= 1; + // If all handlers have replied, report if any of them handled the event. + if (event->unreplied == 0) { + event->callback(event->any_handled); + pending_events_.erase(iter); } - }); + return; + } + // The pending event should always be found. + FT_ASSERT_NOT_REACHED(); } } // namespace flutter diff --git a/shell/platform/tizen/channels/key_event_channel.h b/shell/platform/tizen/channels/key_event_channel.h index c22d8c2eb46fc..65352483e9eb3 100644 --- a/shell/platform/tizen/channels/key_event_channel.h +++ b/shell/platform/tizen/channels/key_event_channel.h @@ -5,17 +5,25 @@ #ifndef EMBEDDER_KEY_EVENT_CHANNEL_H_ #define EMBEDDER_KEY_EVENT_CHANNEL_H_ +#include +#include #include #include "flutter/shell/platform/common/client_wrapper/include/flutter/basic_message_channel.h" #include "flutter/shell/platform/common/client_wrapper/include/flutter/binary_messenger.h" +#include "flutter/shell/platform/embedder/embedder.h" #include "rapidjson/document.h" namespace flutter { class KeyEventChannel { public: - explicit KeyEventChannel(BinaryMessenger* messenger); + using SendEventHandler = std::function; + + explicit KeyEventChannel(BinaryMessenger* messenger, + SendEventHandler send_event); virtual ~KeyEventChannel(); void SendKey(const char* key, @@ -28,6 +36,46 @@ class KeyEventChannel { private: std::unique_ptr> channel_; + SendEventHandler send_event_; + + struct PendingEvent { + // The number of handlers that haven't replied. + size_t unreplied; + // Whether any replied handlers reported true (handled). + bool any_handled; + // Where to report the handlers' result to. + std::function callback; + }; + + // Key events that have been sent to the framework but have not yet received + // a response, indexed by sequence IDs. + std::map> pending_events_; + + // A self-incrementing integer used as the ID for the next entry for + // |pending_events_|. + uint64_t last_sequence_id_ = 0; + + // A map from physical keys to logical keys, each entry indicating a pressed + // key. + std::map pressing_records_; + + void SendEmbedderEvent(const char* key, + const char* string, + const char* compose, + uint32_t modifiers, + uint32_t scan_code, + bool is_down, + uint64_t sequence_id); + + void SendChannelEvent(const char* key, + const char* string, + const char* compose, + uint32_t modifiers, + uint32_t scan_code, + bool is_down, + uint64_t sequence_id); + + void ResolvePendingEvent(uint64_t sequence_id, bool handled); }; } // namespace flutter diff --git a/shell/platform/tizen/channels/key_mapping.cc b/shell/platform/tizen/channels/key_mapping.cc new file mode 100644 index 0000000000000..35c0f3687fb7c --- /dev/null +++ b/shell/platform/tizen/channels/key_mapping.cc @@ -0,0 +1,668 @@ +// Copyright 2022 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "key_mapping.h" + +std::map kScanCodeToGtkKeyCode = { + {0x00000009, 65307}, // escape + {0x0000000a, 49}, // digit1 + {0x0000000b, 50}, // digit2 + {0x0000000c, 51}, // digit3 + {0x0000000d, 52}, // digit4 + {0x0000000e, 53}, // digit5 + {0x0000000f, 54}, // digit6 + {0x00000010, 55}, // digit7 + {0x00000011, 56}, // digit8 + {0x00000012, 57}, // digit9 + {0x00000013, 48}, // digit0 + {0x00000014, 45}, // minus + {0x00000015, 61}, // equal + {0x00000016, 65288}, // backspace + {0x00000017, 65289}, // tab + {0x00000018, 81}, // keyQ + {0x00000019, 87}, // keyW + {0x0000001a, 69}, // keyE + {0x0000001b, 82}, // keyR + {0x0000001c, 84}, // keyT + {0x0000001d, 89}, // keyY + {0x0000001e, 85}, // keyU + {0x0000001f, 73}, // keyI + {0x00000020, 79}, // keyO + {0x00000021, 80}, // keyP + {0x00000022, 91}, // bracketLeft + {0x00000023, 93}, // bracketRight + {0x00000024, 65293}, // enter + {0x00000025, 65507}, // controlLeft + {0x00000026, 65}, // keyA + {0x00000027, 83}, // keyS + {0x00000028, 68}, // keyD + {0x00000029, 70}, // keyF + {0x0000002a, 71}, // keyG + {0x0000002b, 72}, // keyH + {0x0000002c, 74}, // keyJ + {0x0000002d, 75}, // keyK + {0x0000002e, 76}, // keyL + {0x0000002f, 59}, // semicolon + {0x00000030, 39}, // quote + {0x00000031, 96}, // backquote + {0x00000032, 65505}, // shiftLeft + {0x00000033, 92}, // backslash + {0x00000034, 90}, // keyZ + {0x00000035, 88}, // keyX + {0x00000036, 67}, // keyC + {0x00000037, 86}, // keyV + {0x00000038, 66}, // keyB + {0x00000039, 78}, // keyN + {0x0000003a, 77}, // keyM + {0x0000003b, 44}, // comma + {0x0000003c, 46}, // period + {0x0000003d, 47}, // slash + {0x0000003e, 65506}, // shiftRight + {0x0000003f, 65450}, // numpadMultiply + {0x00000040, 65513}, // altLeft + {0x00000041, 32}, // space + {0x00000042, 65509}, // capsLock + {0x00000043, 65470}, // f1 + {0x00000044, 65471}, // f2 + {0x00000045, 65472}, // f3 + {0x00000046, 65473}, // f4 + {0x00000047, 65474}, // f5 + {0x00000048, 65475}, // f6 + {0x00000049, 65476}, // f7 + {0x0000004a, 65477}, // f8 + {0x0000004b, 65478}, // f9 + {0x0000004c, 65479}, // f10 + {0x0000004d, 65407}, // numLock + {0x0000004e, 65300}, // scrollLock + {0x0000004f, 65463}, // numpad7 + {0x00000050, 65464}, // numpad8 + {0x00000051, 65465}, // numpad9 + {0x00000052, 65453}, // numpadSubtract + {0x00000053, 65460}, // numpad4 + {0x00000054, 65461}, // numpad5 + {0x00000055, 65462}, // numpad6 + {0x00000056, 65451}, // numpadAdd + {0x00000057, 65457}, // numpad1 + {0x00000058, 65458}, // numpad2 + {0x00000059, 65459}, // numpad3 + {0x0000005a, 65456}, // numpad0 + {0x0000005b, 65454}, // numpadDecimal + {0x0000005f, 65480}, // f11 + {0x00000060, 65481}, // f12 + {0x00000065, 65406}, // kanaMode + {0x00000068, 65421}, // numpadEnter + {0x00000069, 65508}, // controlRight + {0x0000006a, 65455}, // numpadDivide + {0x0000006b, 64797}, // printScreen + {0x0000006c, 65514}, // altRight + {0x0000006e, 65360}, // home + {0x0000006f, 65362}, // arrowUp + {0x00000070, 65365}, // pageUp + {0x00000071, 65361}, // arrowLeft + {0x00000072, 65363}, // arrowRight + {0x00000073, 65367}, // end + {0x00000074, 65364}, // arrowDown + {0x00000075, 65366}, // pageDown + {0x00000076, 65379}, // insert + {0x00000077, 65535}, // delete + {0x00000079, 269025042}, // audioVolumeMute + {0x0000007a, 269025041}, // audioVolumeDown + {0x0000007b, 269025043}, // audioVolumeUp + {0x0000007c, 269025066}, // power + {0x0000007d, 65469}, // numpadEqual + {0x0000007f, 65299}, // pause + {0x00000084, 165}, // intlYen + {0x00000085, 65511}, // metaLeft + {0x00000086, 65512}, // metaRight + {0x00000087, 65383}, // contextMenu + {0x00000088, 269025064}, // browserStop + {0x0000008b, 65381}, // undo + {0x0000008c, 65376}, // select + {0x0000008d, 269025111}, // copy + {0x0000008e, 269025131}, // open + {0x0000008f, 269025133}, // paste + {0x00000090, 65384}, // find + {0x00000092, 65386}, // help + {0x00000096, 269025071}, // sleep + {0x00000097, 269025067}, // wakeUp + {0x0000009e, 269025070}, // launchInternetBrowser + {0x000000a3, 269025049}, // launchMail + {0x000000a4, 269025072}, // browserFavorites + {0x000000a6, 269025062}, // browserBack + {0x000000a7, 269025063}, // browserForward + {0x000000a9, 269025068}, // eject + {0x000000ab, 269025047}, // mediaTrackNext + {0x000000ad, 269025046}, // mediaTrackPrevious + {0x000000ae, 269025045}, // mediaStop + {0x000000af, 269025052}, // mediaRecord + {0x000000b0, 269025086}, // mediaRewind + {0x000000b1, 269025134}, // launchPhone + {0x000000b4, 269025048}, // browserHome + {0x000000b5, 269025065}, // browserRefresh + {0x000000bd, 269025128}, // newKey + {0x000000be, 65382}, // redo + {0x000000bf, 65482}, // f13 + {0x000000c0, 65483}, // f14 + {0x000000c1, 65484}, // f15 + {0x000000c2, 65485}, // f16 + {0x000000c3, 65486}, // f17 + {0x000000c4, 65487}, // f18 + {0x000000c5, 65488}, // f19 + {0x000000c6, 65489}, // f20 + {0x000000c7, 65490}, // f21 + {0x000000c8, 65491}, // f22 + {0x000000c9, 65492}, // f23 + {0x000000ca, 65493}, // f24 + {0x000000d1, 269025073}, // mediaPause + {0x000000d6, 269025110}, // close + {0x000000d7, 269025044}, // mediaPlay + {0x000000d8, 269025175}, // mediaFastForward + {0x000000da, 65377}, // print + {0x000000e1, 269025051}, // browserSearch + {0x000000e8, 269025027}, // brightnessDown + {0x000000e9, 269025026}, // brightnessUp + {0x000000ed, 269025030}, // kbdIllumDown + {0x000000ee, 269025029}, // kbdIllumUp + {0x000000ef, 269025147}, // mailSend + {0x000000f0, 269025138}, // mailReply + {0x000000f1, 269025168}, // mailForward + {0x000000f2, 269025143}, // save + {0x00000190, 269025170}, // launchAudioBrowser + {0x00000195, 269025056}, // launchCalendar + {0x000001aa, 269025163}, // zoomIn + {0x000001ab, 269025164}, // zoomOut + {0x000001b8, 269025148}, // spellCheck + {0x000001b9, 269025121}, // logOff + {0x0000024d, 269025069}, // launchScreenSaver +}; + +std::map kEcoreModifierToGtkModifier = { + {0x0001, 1 << 0}, // SHIFT -> modifierShift + {0x0002, 1 << 2}, // CTRL -> modifierControl + {0x0004, 1 << 3}, // ALT -> modifierMod1 + {0x0008, 1 << 26}, // WIN -> modifierMeta + {0x0010, 0}, // SCROLL -> undefined + {0x0020, 1 << 4}, // NUM -> modifierMod2 + {0x0040, 1 << 1}, // CAPS -> modifierCapsLock +}; + +std::map kScanCodeToPhysicalKeyCode = { + {0x00000009, 0x00070029}, // escape + {0x0000000a, 0x0007001e}, // digit1 + {0x0000000b, 0x0007001f}, // digit2 + {0x0000000c, 0x00070020}, // digit3 + {0x0000000d, 0x00070021}, // digit4 + {0x0000000e, 0x00070022}, // digit5 + {0x0000000f, 0x00070023}, // digit6 + {0x00000010, 0x00070024}, // digit7 + {0x00000011, 0x00070025}, // digit8 + {0x00000012, 0x00070026}, // digit9 + {0x00000013, 0x00070027}, // digit0 + {0x00000014, 0x0007002d}, // minus + {0x00000015, 0x0007002e}, // equal + {0x00000016, 0x0007002a}, // backspace + {0x00000017, 0x0007002b}, // tab + {0x00000018, 0x00070014}, // keyQ + {0x00000019, 0x0007001a}, // keyW + {0x0000001a, 0x00070008}, // keyE + {0x0000001b, 0x00070015}, // keyR + {0x0000001c, 0x00070017}, // keyT + {0x0000001d, 0x0007001c}, // keyY + {0x0000001e, 0x00070018}, // keyU + {0x0000001f, 0x0007000c}, // keyI + {0x00000020, 0x00070012}, // keyO + {0x00000021, 0x00070013}, // keyP + {0x00000022, 0x0007002f}, // bracketLeft + {0x00000023, 0x00070030}, // bracketRight + {0x00000024, 0x00070028}, // enter + {0x00000025, 0x000700e0}, // controlLeft + {0x00000026, 0x00070004}, // keyA + {0x00000027, 0x00070016}, // keyS + {0x00000028, 0x00070007}, // keyD + {0x00000029, 0x00070009}, // keyF + {0x0000002a, 0x0007000a}, // keyG + {0x0000002b, 0x0007000b}, // keyH + {0x0000002c, 0x0007000d}, // keyJ + {0x0000002d, 0x0007000e}, // keyK + {0x0000002e, 0x0007000f}, // keyL + {0x0000002f, 0x00070033}, // semicolon + {0x00000030, 0x00070034}, // quote + {0x00000031, 0x00070035}, // backquote + {0x00000032, 0x000700e1}, // shiftLeft + {0x00000033, 0x00070031}, // backslash + {0x00000034, 0x0007001d}, // keyZ + {0x00000035, 0x0007001b}, // keyX + {0x00000036, 0x00070006}, // keyC + {0x00000037, 0x00070019}, // keyV + {0x00000038, 0x00070005}, // keyB + {0x00000039, 0x00070011}, // keyN + {0x0000003a, 0x00070010}, // keyM + {0x0000003b, 0x00070036}, // comma + {0x0000003c, 0x00070037}, // period + {0x0000003d, 0x00070038}, // slash + {0x0000003e, 0x000700e5}, // shiftRight + {0x0000003f, 0x00070055}, // numpadMultiply + {0x00000040, 0x000700e2}, // altLeft + {0x00000041, 0x0007002c}, // space + {0x00000042, 0x00070039}, // capsLock + {0x00000043, 0x0007003a}, // f1 + {0x00000044, 0x0007003b}, // f2 + {0x00000045, 0x0007003c}, // f3 + {0x00000046, 0x0007003d}, // f4 + {0x00000047, 0x0007003e}, // f5 + {0x00000048, 0x0007003f}, // f6 + {0x00000049, 0x00070040}, // f7 + {0x0000004a, 0x00070041}, // f8 + {0x0000004b, 0x00070042}, // f9 + {0x0000004c, 0x00070043}, // f10 + {0x0000004d, 0x00070053}, // numLock + {0x0000004e, 0x00070047}, // scrollLock + {0x0000004f, 0x0007005f}, // numpad7 + {0x00000050, 0x00070060}, // numpad8 + {0x00000051, 0x00070061}, // numpad9 + {0x00000052, 0x00070056}, // numpadSubtract + {0x00000053, 0x0007005c}, // numpad4 + {0x00000054, 0x0007005d}, // numpad5 + {0x00000055, 0x0007005e}, // numpad6 + {0x00000056, 0x00070057}, // numpadAdd + {0x00000057, 0x00070059}, // numpad1 + {0x00000058, 0x0007005a}, // numpad2 + {0x00000059, 0x0007005b}, // numpad3 + {0x0000005a, 0x00070062}, // numpad0 + {0x0000005b, 0x00070063}, // numpadDecimal + {0x0000005d, 0x00070094}, // lang5 + {0x0000005e, 0x00070064}, // intlBackslash + {0x0000005f, 0x00070044}, // f11 + {0x00000060, 0x00070045}, // f12 + {0x00000061, 0x00070087}, // intlRo + {0x00000062, 0x00070092}, // lang3 + {0x00000063, 0x00070093}, // lang4 + {0x00000064, 0x0007008a}, // convert + {0x00000065, 0x00070088}, // kanaMode + {0x00000066, 0x0007008b}, // nonConvert + {0x00000068, 0x00070058}, // numpadEnter + {0x00000069, 0x000700e4}, // controlRight + {0x0000006a, 0x00070054}, // numpadDivide + {0x0000006b, 0x00070046}, // printScreen + {0x0000006c, 0x000700e6}, // altRight + {0x0000006e, 0x0007004a}, // home + {0x0000006f, 0x00070052}, // arrowUp + {0x00000070, 0x0007004b}, // pageUp + {0x00000071, 0x00070050}, // arrowLeft + {0x00000072, 0x0007004f}, // arrowRight + {0x00000073, 0x0007004d}, // end + {0x00000074, 0x00070051}, // arrowDown + {0x00000075, 0x0007004e}, // pageDown + {0x00000076, 0x00070049}, // insert + {0x00000077, 0x0007004c}, // delete + {0x00000079, 0x0007007f}, // audioVolumeMute + {0x0000007a, 0x00070081}, // audioVolumeDown + {0x0000007b, 0x00070080}, // audioVolumeUp + {0x0000007c, 0x00070066}, // power + {0x0000007d, 0x00070067}, // numpadEqual + {0x0000007e, 0x000700d7}, // numpadSignChange + {0x0000007f, 0x00070048}, // pause + {0x00000080, 0x000c029f}, // showAllWindows + {0x00000081, 0x00070085}, // numpadComma + {0x00000082, 0x00070090}, // lang1 + {0x00000083, 0x00070091}, // lang2 + {0x00000084, 0x00070089}, // intlYen + {0x00000085, 0x000700e3}, // metaLeft + {0x00000086, 0x000700e7}, // metaRight + {0x00000087, 0x00070065}, // contextMenu + {0x00000088, 0x000c0226}, // browserStop + {0x00000089, 0x00070079}, // again + {0x0000008b, 0x0007007a}, // undo + {0x0000008c, 0x00070077}, // select + {0x0000008d, 0x0007007c}, // copy + {0x0000008e, 0x00070074}, // open + {0x0000008f, 0x0007007d}, // paste + {0x00000090, 0x0007007e}, // find + {0x00000091, 0x0007007b}, // cut + {0x00000092, 0x00070075}, // help + {0x00000094, 0x000c0192}, // launchApp2 + {0x00000096, 0x00010082}, // sleep + {0x00000097, 0x00010083}, // wakeUp + {0x00000098, 0x000c0194}, // launchApp1 + {0x0000009e, 0x000c0196}, // launchInternetBrowser + {0x000000a0, 0x000c019e}, // lockScreen + {0x000000a3, 0x000c018a}, // launchMail + {0x000000a4, 0x000c022a}, // browserFavorites + {0x000000a6, 0x000c0224}, // browserBack + {0x000000a7, 0x000c0225}, // browserForward + {0x000000a9, 0x000c00b8}, // eject + {0x000000ab, 0x000c00b5}, // mediaTrackNext + {0x000000ac, 0x000c00cd}, // mediaPlayPause + {0x000000ad, 0x000c00b6}, // mediaTrackPrevious + {0x000000ae, 0x000c00b7}, // mediaStop + {0x000000af, 0x000c00b2}, // mediaRecord + {0x000000b0, 0x000c00b4}, // mediaRewind + {0x000000b1, 0x000c008c}, // launchPhone + {0x000000b3, 0x000c0183}, // mediaSelect + {0x000000b4, 0x000c0223}, // browserHome + {0x000000b5, 0x000c0227}, // browserRefresh + {0x000000b6, 0x000c0094}, // exit + {0x000000bb, 0x000700b6}, // numpadParenLeft + {0x000000bc, 0x000700b7}, // numpadParenRight + {0x000000bd, 0x000c0201}, // newKey + {0x000000be, 0x000c0279}, // redo + {0x000000bf, 0x00070068}, // f13 + {0x000000c0, 0x00070069}, // f14 + {0x000000c1, 0x0007006a}, // f15 + {0x000000c2, 0x0007006b}, // f16 + {0x000000c3, 0x0007006c}, // f17 + {0x000000c4, 0x0007006d}, // f18 + {0x000000c5, 0x0007006e}, // f19 + {0x000000c6, 0x0007006f}, // f20 + {0x000000c7, 0x00070070}, // f21 + {0x000000c8, 0x00070071}, // f22 + {0x000000c9, 0x00070072}, // f23 + {0x000000ca, 0x00070073}, // f24 + {0x000000d1, 0x000c00b1}, // mediaPause + {0x000000d6, 0x000c0203}, // close + {0x000000d7, 0x000c00b0}, // mediaPlay + {0x000000d8, 0x000c00b3}, // mediaFastForward + {0x000000d9, 0x000c00e5}, // bassBoost + {0x000000da, 0x000c0208}, // print + {0x000000e1, 0x000c0221}, // browserSearch + {0x000000e8, 0x000c0070}, // brightnessDown + {0x000000e9, 0x000c006f}, // brightnessUp + {0x000000eb, 0x000100b5}, // displayToggleIntExt + {0x000000ed, 0x000c007a}, // kbdIllumDown + {0x000000ee, 0x000c0079}, // kbdIllumUp + {0x000000ef, 0x000c028c}, // mailSend + {0x000000f0, 0x000c0289}, // mailReply + {0x000000f1, 0x000c028b}, // mailForward + {0x000000f2, 0x000c0207}, // save + {0x000000f3, 0x000c01a7}, // launchDocuments + {0x000000fc, 0x000c0075}, // brightnessAuto + {0x0000016e, 0x000c0060}, // info + {0x00000172, 0x000c008d}, // programGuide + {0x0000017a, 0x000c0061}, // closedCaptionToggle + {0x0000017c, 0x000c0232}, // zoomToggle + {0x0000017e, 0x000c01ae}, // launchKeyboardLayout + {0x00000190, 0x000c01b7}, // launchAudioBrowser + {0x00000195, 0x000c018e}, // launchCalendar + {0x0000019d, 0x000c0083}, // mediaLast + {0x000001a2, 0x000c009c}, // channelUp + {0x000001a3, 0x000c009d}, // channelDown + {0x000001aa, 0x000c022d}, // zoomIn + {0x000001ab, 0x000c022e}, // zoomOut + {0x000001ad, 0x000c0184}, // launchWordProcessor + {0x000001af, 0x000c0186}, // launchSpreadsheet + {0x000001b5, 0x000c018d}, // launchContacts + {0x000001b7, 0x000c0072}, // brightnessToggle + {0x000001b8, 0x000c01ab}, // spellCheck + {0x000001b9, 0x000c019c}, // logOff + {0x0000024b, 0x000c019f}, // launchControlPanel + {0x0000024c, 0x000c01a2}, // selectTask + {0x0000024d, 0x000c01b1}, // launchScreenSaver + {0x0000024e, 0x000c00cf}, // speechInputToggle + {0x0000024f, 0x000c01cb}, // launchAssistant + {0x00000250, 0x000c029d}, // keyboardLayoutSelect + {0x00000258, 0x000c0073}, // brightnessMinimum + {0x00000259, 0x000c0074}, // brightnessMaximum + {0x00000281, 0x00000017}, // privacyScreenToggle +}; + +std::map kSymbolToLogicalKeyCode = { + {"space", 0x00000000020}, // space + {"exclam", 0x00000000021}, // exclamation + {"quotedbl", 0x00000000022}, // quote + {"numbersign", 0x00000000023}, // numberSign + {"dollar", 0x00000000024}, // dollar + {"percent", 0x00000000025}, // percent + {"ampersand", 0x00000000026}, // ampersand + {"apostrophe", 0x00000000027}, // quoteSingle + {"parenleft", 0x00000000028}, // parenthesisLeft + {"parenright", 0x00000000029}, // parenthesisRight + {"asterisk", 0x0000000002a}, // asterisk + {"plus", 0x0000000002b}, // add + {"comma", 0x0000000002c}, // comma + {"minus", 0x0000000002d}, // minus + {"period", 0x0000000002e}, // period + {"slash", 0x0000000002f}, // slash + {"0", 0x00000000030}, // digit0 + {"1", 0x00000000031}, // digit1 + {"2", 0x00000000032}, // digit2 + {"3", 0x00000000033}, // digit3 + {"4", 0x00000000034}, // digit4 + {"5", 0x00000000035}, // digit5 + {"6", 0x00000000036}, // digit6 + {"7", 0x00000000037}, // digit7 + {"8", 0x00000000038}, // digit8 + {"9", 0x00000000039}, // digit9 + {"colon", 0x0000000003a}, // colon + {"semicolon", 0x0000000003b}, // semicolon + {"less", 0x0000000003c}, // less + {"equal", 0x0000000003d}, // equal + {"greater", 0x0000000003e}, // greater + {"question", 0x0000000003f}, // question + {"at", 0x00000000040}, // at + {"bracketleft", 0x0000000005b}, // bracketLeft + {"backslash", 0x0000000005c}, // backslash + {"bracketright", 0x0000000005d}, // bracketRight + {"asciicircum", 0x0000000005e}, // caret + {"underscore", 0x0000000005f}, // underscore + {"grave", 0x00000000060}, // backquote + {"a", 0x00000000061}, // keyA + {"b", 0x00000000062}, // keyB + {"c", 0x00000000063}, // keyC + {"d", 0x00000000064}, // keyD + {"e", 0x00000000065}, // keyE + {"f", 0x00000000066}, // keyF + {"g", 0x00000000067}, // keyG + {"h", 0x00000000068}, // keyH + {"i", 0x00000000069}, // keyI + {"j", 0x0000000006a}, // keyJ + {"k", 0x0000000006b}, // keyK + {"l", 0x0000000006c}, // keyL + {"m", 0x0000000006d}, // keyM + {"n", 0x0000000006e}, // keyN + {"o", 0x0000000006f}, // keyO + {"p", 0x00000000070}, // keyP + {"q", 0x00000000071}, // keyQ + {"r", 0x00000000072}, // keyR + {"s", 0x00000000073}, // keyS + {"t", 0x00000000074}, // keyT + {"u", 0x00000000075}, // keyU + {"v", 0x00000000076}, // keyV + {"w", 0x00000000077}, // keyW + {"x", 0x00000000078}, // keyX + {"y", 0x00000000079}, // keyY + {"z", 0x0000000007a}, // keyZ + {"A", 0x00000000061}, // keyA + {"B", 0x00000000062}, // keyB + {"C", 0x00000000063}, // keyC + {"D", 0x00000000064}, // keyD + {"E", 0x00000000065}, // keyE + {"F", 0x00000000066}, // keyF + {"G", 0x00000000067}, // keyG + {"H", 0x00000000068}, // keyH + {"I", 0x00000000069}, // keyI + {"J", 0x0000000006a}, // keyJ + {"K", 0x0000000006b}, // keyK + {"L", 0x0000000006c}, // keyL + {"M", 0x0000000006d}, // keyM + {"N", 0x0000000006e}, // keyN + {"O", 0x0000000006f}, // keyO + {"P", 0x00000000070}, // keyP + {"Q", 0x00000000071}, // keyQ + {"R", 0x00000000072}, // keyR + {"S", 0x00000000073}, // keyS + {"T", 0x00000000074}, // keyT + {"U", 0x00000000075}, // keyU + {"V", 0x00000000076}, // keyV + {"W", 0x00000000077}, // keyW + {"X", 0x00000000078}, // keyX + {"Y", 0x00000000079}, // keyY + {"Z", 0x0000000007a}, // keyZ + {"braceleft", 0x0000000007b}, // braceLeft + {"bar", 0x0000000007c}, // bar + {"braceright", 0x0000000007d}, // braceRight + {"asciitilde", 0x0000000007e}, // tilde + {"BackSpace", 0x00100000008}, // backspace + {"Tab", 0x00100000009}, // tab + {"Return", 0x0010000000d}, // enter + {"XF86Back", 0x0010000001b}, // escape + {"Delete", 0x0010000007f}, // delete + {"Caps_Lock", 0x00100000104}, // capsLock + {"Hyper_L", 0x00100000108}, // hyper + {"Num_Lock", 0x0010000010a}, // numLock + {"Scroll_Lock", 0x0010000010c}, // scrollLock + {"Super_L", 0x0010000010e}, // superKey + {"Down", 0x00100000301}, // arrowDown + {"Left", 0x00100000302}, // arrowLeft + {"Right", 0x00100000303}, // arrowRight + {"Up", 0x00100000304}, // arrowUp + {"End", 0x00100000305}, // end + {"Home", 0x00100000306}, // home + {"Next", 0x00100000307}, // pageDown + {"Prior", 0x00100000308}, // pageUp + {"Clear", 0x00100000401}, // clear + {"Copy", 0x00100000402}, // copy + {"Cut", 0x00100000404}, // cut + {"Insert", 0x00100000407}, // insert + {"Paste", 0x00100000408}, // paste + {"Redo", 0x00100000409}, // redo + {"Undo", 0x0010000040a}, // undo + {"Again", 0x00100000502}, // again + {"Cancel", 0x00100000504}, // cancel + {"XF86ContextMenu", 0x00100000505}, // contextMenu + {"Execute", 0x00100000506}, // execute + {"Find", 0x00100000507}, // find + {"Help", 0x00100000508}, // help + {"Pause", 0x00100000509}, // pause + {"Props", 0x0010000050b}, // props + {"Select", 0x0010000050c}, // select + {"XF86ZoomIn", 0x0010000050d}, // zoomIn + {"XF86ZoomOut", 0x0010000050e}, // zoomOut + {"XF86MonBrightnessDown", 0x00100000601}, // brightnessDown + {"XF86MonBrightnessUp", 0x00100000602}, // brightnessUp + {"XF86Eject", 0x00100000604}, // eject + {"XF86LogOff", 0x00100000605}, // logOff + {"XF86PowerOff", 0x00100000607}, // powerOff + {"XF86Hibernate", 0x00100000609}, // hibernate + {"XF86Standby", 0x0010000060a}, // standby + {"WakeUp", 0x0010000060b}, // wakeUp + {"Hangul", 0x00100000711}, // hangulMode + {"Hankaku", 0x00100000715}, // hankaku + {"Hiragana", 0x00100000716}, // hiragana + {"Hiragana_Katakana", 0x00100000717}, // hiraganaKatakana + {"Katakana", 0x0010000071a}, // katakana + {"Romaji", 0x0010000071b}, // romaji + {"Zenkaku", 0x0010000071c}, // zenkaku + {"Zenkaku_Hankaku", 0x0010000071d}, // zenkakuHankaku + {"F1", 0x00100000801}, // f1 + {"F2", 0x00100000802}, // f2 + {"F3", 0x00100000803}, // f3 + {"F4", 0x00100000804}, // f4 + {"F5", 0x00100000805}, // f5 + {"F6", 0x00100000806}, // f6 + {"F7", 0x00100000807}, // f7 + {"F8", 0x00100000808}, // f8 + {"F9", 0x00100000809}, // f9 + {"F10", 0x0010000080a}, // f10 + {"F11", 0x0010000080b}, // f11 + {"F12", 0x0010000080c}, // f12 + {"F13", 0x0010000080d}, // f13 + {"F14", 0x0010000080e}, // f14 + {"F15", 0x0010000080f}, // f15 + {"F16", 0x00100000810}, // f16 + {"F17", 0x00100000811}, // f17 + {"F18", 0x00100000812}, // f18 + {"F19", 0x00100000813}, // f19 + {"F20", 0x00100000814}, // f20 + {"F21", 0x00100000815}, // f21 + {"F22", 0x00100000816}, // f22 + {"F23", 0x00100000817}, // f23 + {"F24", 0x00100000818}, // f24 + {"XF86Close", 0x00100000a01}, // close + {"XF86MailForward", 0x00100000a02}, // mailForward + {"XF86PlayBack", 0x00100000a05}, // mediaPlayPause + {"XF86AudioStop", 0x00100000a07}, // mediaStop + {"XF86AudioNext", 0x00100000a08}, // mediaTrackNext + {"XF86AudioPrev", 0x00100000a09}, // mediaTrackPrevious + {"XF86New", 0x00100000a0a}, // newKey + {"Open", 0x00100000a0b}, // open + {"Print", 0x00100000a0c}, // print + {"XF86Save", 0x00100000a0d}, // save + {"XF86AudioLowerVolume", 0x00100000a0f}, // audioVolumeDown + {"XF86AudioRaiseVolume", 0x00100000a10}, // audioVolumeUp + {"XF86AudioMute", 0x00100000a11}, // audioVolumeMute + {"XF86Calendar", 0x00100000b02}, // launchCalendar + {"XF86Mail", 0x00100000b03}, // launchMail + {"XF86Video", 0x00100000b04}, // launchMediaPlayer + {"XF86Music", 0x00100000b05}, // launchMusicPlayer + {"XF86ScreenSaver", 0x00100000b07}, // launchScreenSaver + {"XF86Excel", 0x00100000b08}, // launchSpreadsheet + {"XF86WWW", 0x00100000b09}, // launchWebBrowser + {"XF86WebCam", 0x00100000b0a}, // launchWebCam + {"XF86Word", 0x00100000b0b}, // launchWordProcessor + {"XF86Addressbook", 0x00100000b0c}, // launchContacts + {"XF86Phone", 0x00100000b0d}, // launchPhone + {"XF86Assistant", 0x00100000b0e}, // launchAssistant + {"XF86ControlPanel", 0x00100000b0f}, // launchControlPanel + {"XF86Favorites", 0x00100000c02}, // browserFavorites + {"XF86Forward", 0x00100000c03}, // browserForward + {"XF86HomePage", 0x00100000c04}, // browserHome + {"XF86Refresh", 0x00100000c05}, // browserRefresh + {"XF86Search", 0x00100000c06}, // browserSearch + {"XF86Stop", 0x00100000c07}, // browserStop + {"XF86ChannelDown", 0x00100000d0a}, // channelDown + {"XF86ChannelUp", 0x00100000d0b}, // channelUp + {"XF86Red", 0x00100000d0c}, // colorF0Red + {"XF86Green", 0x00100000d0d}, // colorF1Green + {"XF86Yellow", 0x00100000d0e}, // colorF2Yellow + {"XF86Blue", 0x00100000d0f}, // colorF3Blue + {"XF86Caption", 0x00100000d12}, // closedCaptionToggle + {"XF86Info", 0x00100000d25}, // info + {"XF86ChannelList", 0x00100000d28}, // listProgram + {"XF86AudioForward", 0x00100000d2c}, // mediaFastForward + {"XF86AudioPause", 0x00100000d2e}, // mediaPause + {"XF86AudioPlay", 0x00100000d2f}, // mediaPlay + {"XF86AudioRecord", 0x00100000d30}, // mediaRecord + {"XF86AudioRewind", 0x00100000d31}, // mediaRewind + {"XF86AudioRandomPlay", 0x00100000d3d}, // randomToggle + {"XF86Subtitle", 0x00100000d47}, // subtitle + {"XF86Display", 0x00100000d4a}, // tvInput + {"XF86MediaTopMenu", 0x00100000d55}, // mediaTopMenu + {"XF86PreviousChannel", 0x00100000d59}, // navigatePrevious + {"XF86Call", 0x00100001002}, // call + {"XF863D", 0x00100001101}, // tv3DMode + {"XF86Suspend", 0x00200000000}, // suspend + {"XF86Sleep", 0x00200000002}, // sleep + {"yen", 0x00200000022}, // intlYen + {"Control_L", 0x00200000100}, // controlLeft + {"Control_R", 0x00200000101}, // controlRight + {"Shift_L", 0x00200000102}, // shiftLeft + {"Shift_R", 0x00200000103}, // shiftRight + {"Alt_L", 0x00200000104}, // altLeft + {"Alt_R", 0x00200000105}, // altRight + {"Meta_L", 0x00200000106}, // metaLeft + {"Meta_R", 0x00200000107}, // metaRight + {"KP_Enter", 0x0020000020d}, // numpadEnter + {"KP_Multiply", 0x0020000022a}, // numpadMultiply + {"KP_Add", 0x0020000022b}, // numpadAdd + {"KP_Subtract", 0x0020000022d}, // numpadSubtract + {"KP_Decimal", 0x0020000022e}, // numpadDecimal + {"KP_Divide", 0x0020000022f}, // numpadDivide + {"KP_0", 0x00200000230}, // numpad0 + {"KP_1", 0x00200000231}, // numpad1 + {"KP_2", 0x00200000232}, // numpad2 + {"KP_3", 0x00200000233}, // numpad3 + {"KP_4", 0x00200000234}, // numpad4 + {"KP_5", 0x00200000235}, // numpad5 + {"KP_6", 0x00200000236}, // numpad6 + {"KP_7", 0x00200000237}, // numpad7 + {"KP_8", 0x00200000238}, // numpad8 + {"KP_9", 0x00200000239}, // numpad9 + {"KP_Equal", 0x0020000023d}, // numpadEqual + {"XF86Menu", 0x00100000505}, // contextMenu + {"XF86Home", 0x00100000306}, // home + {"XF86LowerChannel", 0x00100000d0a}, // channelDown + {"XF86RaiseChannel", 0x00100000d0b}, // channelUp + {"XF86ChannelGuide", 0x00100000d22}, // guide + {"XF86SimpleMenu", 0x00100000505}, // contextMenu +}; diff --git a/shell/platform/tizen/channels/key_mapping.h b/shell/platform/tizen/channels/key_mapping.h new file mode 100644 index 0000000000000..d2ec300bedacb --- /dev/null +++ b/shell/platform/tizen/channels/key_mapping.h @@ -0,0 +1,46 @@ +// Copyright 2022 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EMBEDDER_KEY_MAPPING_H_ +#define EMBEDDER_KEY_MAPPING_H_ + +#include +#include +#include + +// Maps XKB scan codes to GTK key codes. +// +// The values are originally defined in: +// - flutter/keyboard_maps.dart (kLinuxToPhysicalKey) +// - flutter/keyboard_maps.dart (kGtkToLogicalKey) +// +// Provided only for backward compatibility. This will be removed after the +// legacy RawKeyboard API is removed from the framework in the future. +extern std::map kScanCodeToGtkKeyCode; + +// Maps Ecore modifiers to GTK modifiers. +// +// The values are originally defined in: +// - efl/Ecore_Input.h +// - flutter/raw_keyboard_linux.dart (GtkKeyHelper) +// +// Provided only for backward compatibility. This will be removed after the +// legacy RawKeyboard API is removed from the framework in the future. +extern std::map kEcoreModifierToGtkModifier; + +// Maps XKB scan codes to Flutter's physical key codes. +// +// This is a copy of the Linux embedder's |xkb_to_physical_key_map|. +extern std::map kScanCodeToPhysicalKeyCode; + +// Maps Tizen key symbols to Flutter's logical key codes. +extern std::map kSymbolToLogicalKeyCode; + +// Mask for the 32-bit value portion of the key code. +const uint64_t kValueMask = 0x000ffffffff; + +// The plane value for private keys defined by the Tizen platform. +const uint64_t kTizenPlane = 0x02000000000; + +#endif // EMBEDDER_KEY_MAPPING_H_ diff --git a/shell/platform/tizen/flutter_tizen_engine.cc b/shell/platform/tizen/flutter_tizen_engine.cc index 7856f9303ec30..0b7b909da4759 100644 --- a/shell/platform/tizen/flutter_tizen_engine.cc +++ b/shell/platform/tizen/flutter_tizen_engine.cc @@ -238,7 +238,9 @@ bool FlutterTizenEngine::RunEngine() { if (IsHeaded()) { texture_registrar_ = std::make_unique(this); key_event_channel_ = std::make_unique( - internal_plugin_registrar_->messenger()); + internal_plugin_registrar_->messenger(), + [this](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, + void* user_data) { SendKeyEvent(event, callback, user_data); }); navigation_channel_ = std::make_unique( internal_plugin_registrar_->messenger()); platform_view_channel_ = std::make_unique( @@ -320,6 +322,12 @@ void FlutterTizenEngine::SendPlatformMessageResponse( embedder_api_.SendPlatformMessageResponse(engine_, handle, data, data_length); } +void FlutterTizenEngine::SendKeyEvent(const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, + void* user_data) { + embedder_api_.SendKeyEvent(engine_, &event, callback, user_data); +} + void FlutterTizenEngine::SendPointerEvent(const FlutterPointerEvent& event) { embedder_api_.SendPointerEvent(engine_, &event, 1); } diff --git a/shell/platform/tizen/flutter_tizen_engine.h b/shell/platform/tizen/flutter_tizen_engine.h index a63fee393c759..a6c052e0b5063 100644 --- a/shell/platform/tizen/flutter_tizen_engine.h +++ b/shell/platform/tizen/flutter_tizen_engine.h @@ -135,6 +135,11 @@ class FlutterTizenEngine { const uint8_t* data, size_t data_length); + // Informs the engine of an incoming key event. + void SendKeyEvent(const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, + void* user_data); + // Informs the engine of an incoming pointer event. void SendPointerEvent(const FlutterPointerEvent& event); diff --git a/shell/platform/tizen/flutter_tizen_view.cc b/shell/platform/tizen/flutter_tizen_view.cc index 8a74cf25f4bd3..c86a151e1b2ac 100644 --- a/shell/platform/tizen/flutter_tizen_view.cc +++ b/shell/platform/tizen/flutter_tizen_view.cc @@ -21,21 +21,22 @@ constexpr double kProfileFactor = 2.0; constexpr double kProfileFactor = 1.0; #endif +constexpr char kSysMenuKey[] = "XF86SysMenu"; constexpr char kBackKey[] = "XF86Back"; constexpr char kExitKey[] = "XF86Exit"; // Keys that should always be handled by the app first but not by the system. const std::vector kBindableSystemKeys = { - "XF86Menu", "XF86Back", "XF86AudioPlay", - "XF86AudioPause", "XF86AudioStop", "XF86AudioNext", - "XF86AudioPrev", "XF86AudioRewind", "XF86AudioForward", - "XF86AudioPlayPause", "XF86AudioRecord", "XF86LowerChannel", - "XF86RaiseChannel", "XF86ChannelList", "XF86PreviousChannel", - "XF86SysMenu", "XF86SimpleMenu", "XF86History", - "XF86Favorites", "XF86Info", "XF86Red", - "XF86Green", "XF86Yellow", "XF86Blue", - "XF86Subtitle", "XF86PlayBack", "XF86ChannelGuide", - "XF86Caption", "XF86Exit", + "XF86Menu", "XF86Back", "XF86AudioPlay", + "XF86AudioPause", "XF86AudioStop", "XF86AudioNext", + "XF86AudioPrev", "XF86AudioRewind", "XF86AudioForward", + "XF86AudioPlayPause", "XF86AudioRecord", "XF86LowerChannel", + "XF86RaiseChannel", "XF86ChannelList", "XF86PreviousChannel", + "XF86SimpleMenu", "XF86History", "XF86Favorites", + "XF86Info", "XF86Red", "XF86Green", + "XF86Yellow", "XF86Blue", "XF86Subtitle", + "XF86PlayBack", "XF86ChannelGuide", "XF86Caption", + "XF86Exit", }; } // namespace @@ -224,6 +225,11 @@ void FlutterTizenView::OnKey(const char* key, << std::setfill('0') << std::right << std::hex << scan_code; } + // Do not handle the TV system menu key. + if (strcmp(key, kSysMenuKey) == 0) { + return; + } + if (text_input_channel_) { if (text_input_channel_->SendKey(key, string, compose, modifiers, scan_code, is_down)) { diff --git a/shell/platform/tizen/flutter_tizen_view.h b/shell/platform/tizen/flutter_tizen_view.h index 790171455d599..78526f664c52b 100644 --- a/shell/platform/tizen/flutter_tizen_view.h +++ b/shell/platform/tizen/flutter_tizen_view.h @@ -7,6 +7,7 @@ #define EMBEDDER_FLUTTER_TIZEN_VIEW_H_ #include +#include #include "flutter/shell/platform/common/client_wrapper/include/flutter/plugin_registrar.h" #include "flutter/shell/platform/embedder/embedder.h" @@ -14,6 +15,7 @@ #include "flutter/shell/platform/tizen/channels/text_input_channel.h" #include "flutter/shell/platform/tizen/channels/window_channel.h" #include "flutter/shell/platform/tizen/flutter_tizen_engine.h" +#include "flutter/shell/platform/tizen/tizen_view_base.h" namespace flutter {