| // 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/glfw/key_event_handler.h" |
| |
| #include <iostream> |
| |
| #include "flutter/shell/platform/common/cpp/json_message_codec.h" |
| |
| static constexpr char kChannelName[] = "flutter/keyevent"; |
| |
| static constexpr char kKeyCodeKey[] = "keyCode"; |
| static constexpr char kKeyMapKey[] = "keymap"; |
| static constexpr char kScanCodeKey[] = "scanCode"; |
| static constexpr char kModifiersKey[] = "modifiers"; |
| static constexpr char kTypeKey[] = "type"; |
| static constexpr char kToolkitKey[] = "toolkit"; |
| static constexpr char kUnicodeScalarValues[] = "unicodeScalarValues"; |
| |
| static constexpr char kLinuxKeyMap[] = "linux"; |
| static constexpr char kGLFWKey[] = "glfw"; |
| |
| static constexpr char kKeyUp[] = "keyup"; |
| static constexpr char kKeyDown[] = "keydown"; |
| |
| // Masks used for UTF-8 to UTF-32 conversion. |
| static constexpr int kTwoByteMask = 0xC0; |
| static constexpr int kThreeByteMask = 0xE0; |
| static constexpr int kFourByteMask = 0xF0; |
| |
| namespace flutter { |
| |
| namespace { |
| |
| // Information about the UTF-8 encoded code point. |
| struct UTF8CodePointInfo { |
| // The bit-mask that determines the length of the code point. |
| int first_byte_mask; |
| // The number of bytes of the code point. |
| size_t length; |
| }; |
| |
| // Creates a [UTF8CodePointInfo] from a given byte. [first_byte] must be the |
| // first byte in the code point. |
| UTF8CodePointInfo GetUTF8CodePointInfo(int first_byte) { |
| UTF8CodePointInfo byte_info; |
| |
| // The order matters. Otherwise, it is possible that comparing against i.e. |
| // kThreeByteMask and kFourByteMask could be both true. |
| if ((first_byte & kFourByteMask) == kFourByteMask) { |
| byte_info.first_byte_mask = 0x07; |
| byte_info.length = 4; |
| } else if ((first_byte & kThreeByteMask) == kThreeByteMask) { |
| byte_info.first_byte_mask = 0x0F; |
| byte_info.length = 3; |
| } else if ((first_byte & kTwoByteMask) == kTwoByteMask) { |
| byte_info.first_byte_mask = 0x1F; |
| byte_info.length = 2; |
| } else { |
| byte_info.first_byte_mask = 0xFF; |
| byte_info.length = 1; |
| } |
| return byte_info; |
| } |
| |
| // Queries GLFW for the printable key name given a [key] and [scan_code] and |
| // converts it to UTF-32. The Flutter framework accepts only one code point, |
| // therefore, only the first code point will be used. There is unlikely to be |
| // more than one, but there is no guarantee that it won't happen. |
| bool GetUTF32CodePointFromGLFWKey(int key, |
| int scan_code, |
| uint32_t* code_point) { |
| // Get the name of the printable key, encoded as UTF-8. |
| // There's a known issue with glfwGetKeyName, where users with multiple |
| // layouts configured on their machines, will not always return the right |
| // value. See: https://github.com/glfw/glfw/issues/1462 |
| const char* utf8 = glfwGetKeyName(key, scan_code); |
| if (utf8 == nullptr) { |
| return false; |
| } |
| // The first byte determines the length of the whole code point. |
| const auto byte_info = GetUTF8CodePointInfo(utf8[0]); |
| // Tracks how many bits the current byte should shift to the left. |
| int shift = byte_info.length - 1; |
| |
| const int complement_mask = 0x3F; |
| uint32_t result = 0; |
| |
| size_t current_byte_index = 0; |
| while (current_byte_index < byte_info.length) { |
| const int current_byte = utf8[current_byte_index]; |
| const int mask = |
| current_byte_index == 0 ? byte_info.first_byte_mask : complement_mask; |
| current_byte_index++; |
| const int bits_to_shift = 6 * shift--; |
| result += (current_byte & mask) << bits_to_shift; |
| } |
| *code_point = result; |
| return true; |
| } |
| } // namespace |
| |
| KeyEventHandler::KeyEventHandler(flutter::BinaryMessenger* messenger) |
| : channel_( |
| std::make_unique<flutter::BasicMessageChannel<rapidjson::Document>>( |
| messenger, |
| kChannelName, |
| &flutter::JsonMessageCodec::GetInstance())) {} |
| |
| KeyEventHandler::~KeyEventHandler() = default; |
| |
| void KeyEventHandler::CharHook(GLFWwindow* window, unsigned int code_point) {} |
| |
| void KeyEventHandler::KeyboardHook(GLFWwindow* window, |
| int key, |
| int scancode, |
| int action, |
| int mods) { |
| // TODO: Translate to a cross-platform key code system rather than passing |
| // the native key code. |
| rapidjson::Document event(rapidjson::kObjectType); |
| auto& allocator = event.GetAllocator(); |
| event.AddMember(kKeyCodeKey, key, allocator); |
| event.AddMember(kKeyMapKey, kLinuxKeyMap, allocator); |
| event.AddMember(kScanCodeKey, scancode, allocator); |
| event.AddMember(kModifiersKey, mods, allocator); |
| event.AddMember(kToolkitKey, kGLFWKey, allocator); |
| |
| uint32_t unicodeInt; |
| bool result = GetUTF32CodePointFromGLFWKey(key, scancode, &unicodeInt); |
| if (result) { |
| event.AddMember(kUnicodeScalarValues, unicodeInt, allocator); |
| } |
| |
| switch (action) { |
| case GLFW_PRESS: |
| case GLFW_REPEAT: |
| event.AddMember(kTypeKey, kKeyDown, allocator); |
| break; |
| case GLFW_RELEASE: |
| event.AddMember(kTypeKey, kKeyUp, allocator); |
| break; |
| default: |
| std::cerr << "Unknown key event action: " << action << std::endl; |
| return; |
| } |
| channel_->Send(event); |
| } |
| |
| } // namespace flutter |