Split out FlKeyboardLayout into its own class (#55816)
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index ef13edb..4b7568d 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -44806,6 +44806,9 @@
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_handler.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_handler.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_handler_test.cc + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_layout.cc + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_layout.h + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_layout_test.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_pending_event.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_pending_event.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_view_delegate.cc + ../../../flutter/LICENSE
@@ -47713,6 +47716,9 @@
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_handler.cc
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_handler.h
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_handler_test.cc
+FILE: ../../../flutter/shell/platform/linux/fl_keyboard_layout.cc
+FILE: ../../../flutter/shell/platform/linux/fl_keyboard_layout.h
+FILE: ../../../flutter/shell/platform/linux/fl_keyboard_layout_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_pending_event.cc
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_pending_event.h
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_view_delegate.cc
diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn
index 9cbcc9f..22c192e 100644
--- a/shell/platform/linux/BUILD.gn
+++ b/shell/platform/linux/BUILD.gn
@@ -117,6 +117,7 @@
"fl_key_event.cc",
"fl_key_responder.cc",
"fl_keyboard_handler.cc",
+ "fl_keyboard_layout.cc",
"fl_keyboard_pending_event.cc",
"fl_keyboard_view_delegate.cc",
"fl_message_codec.cc",
@@ -215,6 +216,7 @@
"fl_key_channel_responder_test.cc",
"fl_key_embedder_responder_test.cc",
"fl_keyboard_handler_test.cc",
+ "fl_keyboard_layout_test.cc",
"fl_message_codec_test.cc",
"fl_method_channel_test.cc",
"fl_method_codec_test.cc",
diff --git a/shell/platform/linux/fl_keyboard_handler.cc b/shell/platform/linux/fl_keyboard_handler.cc
index 0b825cb..2901693 100644
--- a/shell/platform/linux/fl_keyboard_handler.cc
+++ b/shell/platform/linux/fl_keyboard_handler.cc
@@ -11,6 +11,7 @@
#include "flutter/shell/platform/linux/fl_key_channel_responder.h"
#include "flutter/shell/platform/linux/fl_key_embedder_responder.h"
+#include "flutter/shell/platform/linux/fl_keyboard_layout.h"
#include "flutter/shell/platform/linux/fl_keyboard_pending_event.h"
#include "flutter/shell/platform/linux/key_mapping.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
@@ -25,8 +26,6 @@
/* Declarations of private classes */
-#define FL_TYPE_KEYBOARD_HANDLER_USER_DATA \
- fl_keyboard_handler_user_data_get_type()
G_DECLARE_FINAL_TYPE(FlKeyboardHandlerUserData,
fl_keyboard_handler_user_data,
FL,
@@ -37,20 +36,6 @@
namespace {
-// The maxiumum keycode in a derived layout.
-//
-// Although X supports higher keycodes, Flutter only cares about standard keys,
-// which are below this.
-constexpr size_t kLayoutSize = 128;
-// Describes the derived layout of a keyboard group.
-//
-// Maps from keycode to logical key. Value being 0 stands for empty.
-typedef std::array<uint64_t, kLayoutSize> DerivedGroupLayout;
-// Describes the derived layout of the entire keyboard.
-//
-// Maps from group ID to group layout.
-typedef std::map<guint8, DerivedGroupLayout> DerivedLayout;
-
// Context variables for the foreach call used to dispatch events to responders.
typedef struct {
FlKeyEvent* event;
@@ -58,7 +43,7 @@
FlKeyboardHandlerUserData* user_data;
} DispatchToResponderLoopContext;
-bool is_eascii(uint16_t character) {
+static bool is_eascii(uint16_t character) {
return character < 256;
}
@@ -89,21 +74,6 @@
} // namespace
-static uint64_t get_logical_key_from_layout(FlKeyEvent* event,
- const DerivedLayout& layout) {
- guint8 group = fl_key_event_get_group(event);
- guint16 keycode = fl_key_event_get_keycode(event);
- if (keycode >= kLayoutSize) {
- return 0;
- }
-
- auto found_group_layout = layout.find(group);
- if (found_group_layout != layout.end()) {
- return found_group_layout->second[keycode];
- }
- return 0;
-}
-
/* Define FlKeyboardHandlerUserData */
/**
@@ -192,11 +162,13 @@
// It is cleared when the platform reports a layout switch. Each entry,
// which corresponds to a group, is only initialized on the arrival of the
// first event for that group that has a goal keycode.
- std::unique_ptr<DerivedLayout> derived_layout;
+ FlKeyboardLayout* derived_layout;
+
// A static map from keycodes to all layout goals.
//
// It is set up when the handler is initialized and is not changed ever after.
std::unique_ptr<std::map<uint16_t, const LayoutGoal*>> keycode_to_goals;
+
// A static map from logical keys to all mandatory layout goals.
//
// It is set up when the handler is initialized and is not changed ever after.
@@ -322,7 +294,7 @@
// if the event contains a goal keycode.
static void guarantee_layout(FlKeyboardHandler* self, FlKeyEvent* event) {
guint8 group = fl_key_event_get_group(event);
- if (self->derived_layout->find(group) != self->derived_layout->end()) {
+ if (fl_keyboard_layout_has_group(self->derived_layout, group)) {
return;
}
if (self->keycode_to_goals->find(fl_key_event_get_keycode(event)) ==
@@ -330,8 +302,6 @@
return;
}
- DerivedGroupLayout& layout = (*self->derived_layout)[group];
-
// Clone all mandatory goals. Each goal is removed from this cloned map when
// fulfilled, and the remaining ones will be assigned to a default position.
std::map<uint64_t, const LayoutGoal*> remaining_mandatory_goals =
@@ -371,8 +341,10 @@
auto matching_goal = remaining_mandatory_goals.find(clue);
if (matching_goal != remaining_mandatory_goals.end()) {
// Found a key that produces a mandatory char. Use it.
- g_return_if_fail(layout[keycode] == 0);
- layout[keycode] = clue;
+ g_return_if_fail(fl_keyboard_layout_get_logical_key(
+ self->derived_layout, group, keycode) == 0);
+ fl_keyboard_layout_set_logical_key(self->derived_layout, group, keycode,
+ clue);
remaining_mandatory_goals.erase(matching_goal);
break;
}
@@ -380,10 +352,14 @@
bool has_any_eascii =
is_eascii(this_key_clues[0]) || is_eascii(this_key_clues[1]);
// See if any produced char meets the requirement as a logical key.
- if (layout[keycode] == 0 && !has_any_eascii) {
+ if (fl_keyboard_layout_get_logical_key(self->derived_layout, group,
+ keycode) == 0 &&
+ !has_any_eascii) {
auto found_us_layout = self->keycode_to_goals->find(keycode);
if (found_us_layout != self->keycode_to_goals->end()) {
- layout[keycode] = found_us_layout->second->logical_key;
+ fl_keyboard_layout_set_logical_key(
+ self->derived_layout, group, keycode,
+ found_us_layout->second->logical_key);
}
}
}
@@ -391,7 +367,8 @@
// Ensure all mandatory goals are assigned.
for (const auto mandatory_goal_iter : remaining_mandatory_goals) {
const LayoutGoal* goal = mandatory_goal_iter.second;
- layout[goal->keycode] = goal->logical_key;
+ fl_keyboard_layout_set_logical_key(self->derived_layout, group,
+ goal->keycode, goal->logical_key);
}
}
@@ -460,7 +437,6 @@
self->view_delegate = nullptr;
}
- self->derived_layout.reset();
self->keycode_to_goals.reset();
self->logical_to_mandatory_goals.reset();
@@ -468,6 +444,7 @@
g_ptr_array_set_free_func(self->pending_responds, g_object_unref);
g_ptr_array_free(self->pending_responds, TRUE);
g_ptr_array_free(self->pending_redispatches, TRUE);
+ g_clear_object(&self->derived_layout);
G_OBJECT_CLASS(fl_keyboard_handler_parent_class)->dispose(object);
}
@@ -477,7 +454,7 @@
}
static void fl_keyboard_handler_init(FlKeyboardHandler* self) {
- self->derived_layout = std::make_unique<DerivedLayout>();
+ self->derived_layout = fl_keyboard_layout_new();
self->keycode_to_goals =
std::make_unique<std::map<uint16_t, const LayoutGoal*>>();
@@ -529,7 +506,10 @@
fl_keyboard_view_delegate_get_messenger(view_delegate))));
fl_keyboard_view_delegate_subscribe_to_layout_change(
- self->view_delegate, [self]() { self->derived_layout->clear(); });
+ self->view_delegate, [self]() {
+ g_clear_object(&self->derived_layout);
+ self->derived_layout = fl_keyboard_layout_new();
+ });
// Setup the flutter/keyboard channel.
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
@@ -561,8 +541,9 @@
self, fl_keyboard_pending_event_get_sequence_id(pending));
DispatchToResponderLoopContext data{
.event = event,
- .specified_logical_key =
- get_logical_key_from_layout(event, *self->derived_layout),
+ .specified_logical_key = fl_keyboard_layout_get_logical_key(
+ self->derived_layout, fl_key_event_get_group(event),
+ fl_key_event_get_keycode(event)),
.user_data = user_data,
};
g_ptr_array_foreach(self->responder_list, dispatch_to_responder, &data);
diff --git a/shell/platform/linux/fl_keyboard_layout.cc b/shell/platform/linux/fl_keyboard_layout.cc
new file mode 100644
index 0000000..5ac5d87
--- /dev/null
+++ b/shell/platform/linux/fl_keyboard_layout.cc
@@ -0,0 +1,80 @@
+// 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/linux/fl_keyboard_layout.h"
+
+// The maxiumum keycode in a derived layout.
+//
+// Although X supports higher keycodes, Flutter only cares about standard keys,
+// which are below this.
+constexpr size_t kLayoutSize = 128;
+
+struct _FlKeyboardLayout {
+ GObject parent_instance;
+
+ // Each keycode->logical key mapping per group.
+ GHashTable* groups;
+};
+
+G_DEFINE_TYPE(FlKeyboardLayout, fl_keyboard_layout, G_TYPE_OBJECT)
+
+static void fl_keyboard_layout_dispose(GObject* object) {
+ FlKeyboardLayout* self = FL_KEYBOARD_LAYOUT(object);
+
+ g_clear_pointer(&self->groups, g_hash_table_unref);
+
+ G_OBJECT_CLASS(fl_keyboard_layout_parent_class)->dispose(object);
+}
+
+static void fl_keyboard_layout_class_init(FlKeyboardLayoutClass* klass) {
+ G_OBJECT_CLASS(klass)->dispose = fl_keyboard_layout_dispose;
+}
+
+static void fl_keyboard_layout_init(FlKeyboardLayout* self) {
+ self->groups = g_hash_table_new_full(
+ g_direct_hash, g_direct_equal, nullptr,
+ reinterpret_cast<GDestroyNotify>(g_hash_table_unref));
+}
+
+FlKeyboardLayout* fl_keyboard_layout_new() {
+ return FL_KEYBOARD_LAYOUT(
+ g_object_new(fl_keyboard_layout_get_type(), nullptr));
+}
+
+gboolean fl_keyboard_layout_has_group(FlKeyboardLayout* self, uint8_t group) {
+ return g_hash_table_lookup(self->groups, GINT_TO_POINTER(group)) != nullptr;
+}
+
+void fl_keyboard_layout_set_logical_key(FlKeyboardLayout* self,
+ uint8_t group,
+ uint16_t keycode,
+ uint64_t logical_key) {
+ GHashTable* group_layout = static_cast<GHashTable*>(
+ g_hash_table_lookup(self->groups, GINT_TO_POINTER(group)));
+ if (group_layout == nullptr) {
+ group_layout =
+ g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr, nullptr);
+ g_hash_table_insert(self->groups, GINT_TO_POINTER(group), group_layout);
+ }
+
+ g_hash_table_insert(group_layout, GINT_TO_POINTER(keycode),
+ GINT_TO_POINTER(logical_key));
+}
+
+uint64_t fl_keyboard_layout_get_logical_key(FlKeyboardLayout* self,
+ uint8_t group,
+ uint16_t keycode) {
+ if (keycode >= kLayoutSize) {
+ return 0;
+ }
+
+ GHashTable* group_layout = static_cast<GHashTable*>(
+ g_hash_table_lookup(self->groups, GINT_TO_POINTER(group)));
+ if (group_layout == nullptr) {
+ return 0;
+ }
+
+ return GPOINTER_TO_INT(
+ g_hash_table_lookup(group_layout, GINT_TO_POINTER(keycode)));
+}
diff --git a/shell/platform/linux/fl_keyboard_layout.h b/shell/platform/linux/fl_keyboard_layout.h
new file mode 100644
index 0000000..1338ab5
--- /dev/null
+++ b/shell/platform/linux/fl_keyboard_layout.h
@@ -0,0 +1,73 @@
+// 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_SHELL_PLATFORM_LINUX_FL_KEYBOARD_LAYOUT_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_KEYBOARD_LAYOUT_H_
+
+#include <glib-object.h>
+#include <stdint.h>
+
+G_BEGIN_DECLS
+
+G_DECLARE_FINAL_TYPE(FlKeyboardLayout,
+ fl_keyboard_layout,
+ FL,
+ KEYBOARD_LAYOUT,
+ GObject);
+
+/**
+ * FlKeyboardLayout:
+ * Tracks keycode to to logical key mappings for #FlKeyboardHandler
+ */
+
+/**
+ * fl_keyboard_layout_new:
+ *
+ * Create a new #FlKeyboardLayout.
+ *
+ * Returns: a new #FlKeyboardLayout.
+ */
+FlKeyboardLayout* fl_keyboard_layout_new();
+
+/**
+ * fl_keyboard_layout_has_group:
+ * @layout: a #FlKeyboardLayout.
+ * @group: a key group.
+ *
+ * Checks if a group is present in this layout.
+ *
+ * Returns: %TRUE if this group is present.
+ */
+gboolean fl_keyboard_layout_has_group(FlKeyboardLayout* layout, uint8_t group);
+
+/**
+ * fl_keyboard_layout_has_group:
+ * @layout: a #FlKeyboardLayout.
+ * @group: a key group.
+ * @logical_key: a logical keycode.
+ *
+ * Sets the logical key for a given group and keycode.
+ */
+void fl_keyboard_layout_set_logical_key(FlKeyboardLayout* layout,
+ uint8_t group,
+ uint16_t keycode,
+ uint64_t logical_key);
+
+/**
+ * fl_keyboard_layout_get_logical_key:
+ * @layout: a #FlKeyboardLayout.
+ * @group: a key group.
+ * @keycode: a keycode.
+ *
+ * Gets the logical key for the given group and keycode.
+ *
+ * Returns: the logical keycode or 0 if not set.
+ */
+uint64_t fl_keyboard_layout_get_logical_key(FlKeyboardLayout* layout,
+ uint8_t group,
+ uint16_t keycode);
+
+G_END_DECLS
+
+#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_KEYBOARD_LAYOUT_H_
diff --git a/shell/platform/linux/fl_keyboard_layout_test.cc b/shell/platform/linux/fl_keyboard_layout_test.cc
new file mode 100644
index 0000000..0d6680ce
--- /dev/null
+++ b/shell/platform/linux/fl_keyboard_layout_test.cc
@@ -0,0 +1,41 @@
+// 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/linux/fl_keyboard_layout.h"
+
+#include "gtest/gtest.h"
+
+TEST(FlKeyboardLayoutTest, SetLogicalKey) {
+ g_autoptr(FlKeyboardLayout) layout = fl_keyboard_layout_new();
+
+ EXPECT_EQ(fl_keyboard_layout_get_logical_key(layout, 0, 42),
+ static_cast<uint64_t>(0));
+
+ fl_keyboard_layout_set_logical_key(layout, 0, 42, 1234);
+
+ EXPECT_EQ(fl_keyboard_layout_get_logical_key(layout, 0, 42),
+ static_cast<uint64_t>(1234));
+}
+
+TEST(FlKeyboardLayoutTest, MaxValues) {
+ g_autoptr(FlKeyboardLayout) layout = fl_keyboard_layout_new();
+
+ EXPECT_EQ(fl_keyboard_layout_get_logical_key(layout, 255, 127),
+ static_cast<uint64_t>(0));
+
+ fl_keyboard_layout_set_logical_key(layout, 255, 127, 12345678);
+
+ EXPECT_EQ(fl_keyboard_layout_get_logical_key(layout, 255, 127),
+ static_cast<uint64_t>(12345678));
+}
+
+TEST(FlKeyboardLayoutTest, HasGroup) {
+ g_autoptr(FlKeyboardLayout) layout = fl_keyboard_layout_new();
+
+ EXPECT_FALSE(fl_keyboard_layout_has_group(layout, 42));
+
+ fl_keyboard_layout_set_logical_key(layout, 42, 11, 22);
+
+ EXPECT_TRUE(fl_keyboard_layout_has_group(layout, 42));
+}