blob: bdd121091f54e9b3acd7748a0a3efaa3c63139d9 [file] [log] [blame]
// 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