Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
fl_keyboard_manager.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <array>
8#include <cinttypes>
9#include <memory>
10#include <string>
11
16
17// Turn on this flag to print complete layout data when switching IMEs. The data
18// is used in unit tests.
19#define DEBUG_PRINT_LAYOUT
20
21namespace {
22
23static bool is_eascii(uint16_t character) {
24 return character < 256;
25}
26
27#ifdef DEBUG_PRINT_LAYOUT
28// Prints layout entries that will be parsed by `MockLayoutData`.
29void debug_format_layout_data(std::string& debug_layout_data,
30 uint16_t keycode,
31 uint16_t clue1,
32 uint16_t clue2) {
33 if (keycode % 4 == 0) {
34 debug_layout_data.append(" ");
35 }
36
37 constexpr int kBufferSize = 30;
38 char buffer[kBufferSize];
39 buffer[0] = 0;
40 buffer[kBufferSize - 1] = 0;
41
42 snprintf(buffer, kBufferSize, "0x%04x, 0x%04x, ", clue1, clue2);
43 debug_layout_data.append(buffer);
44
45 if (keycode % 4 == 3) {
46 snprintf(buffer, kBufferSize, " // 0x%02x", keycode);
47 debug_layout_data.append(buffer);
48 }
49}
50#endif
51
52} // namespace
53
54typedef struct {
55 // Event being handled.
56 FlKeyEvent* event;
57
58 // TRUE if the embedder has responded.
60
61 // TRUE if the channel has responded.
63
64 // TRUE if this event is to be redispatched;
65 gboolean redispatch;
66
67 // TRUE if either the embedder of channel handled this event (or both).
68 gboolean handled;
70
71static HandleEventData* handle_event_data_new(FlKeyEvent* event) {
72 HandleEventData* data =
73 static_cast<HandleEventData*>(g_new0(HandleEventData, 1));
74 data->event = FL_KEY_EVENT(g_object_ref(event));
75 return data;
76}
77
79 g_object_unref(data->event);
80 g_free(data);
81}
82
85
88
89 // Key events that have been redispatched.
91
92 FlKeyEmbedderResponder* key_embedder_responder;
93
94 FlKeyChannelResponder* key_channel_responder;
95
96 // Record the derived layout.
97 //
98 // It is cleared when the platform reports a layout switch. Each entry,
99 // which corresponds to a group, is only initialized on the arrival of the
100 // first event for that group that has a goal keycode.
101 FlKeyboardLayout* derived_layout;
102
103 // A static map from keycodes to all layout goals.
104 //
105 // It is set up when the manager is initialized and is not changed ever after.
106 std::unique_ptr<std::map<uint16_t, const LayoutGoal*>> keycode_to_goals;
107
108 // A static map from logical keys to all mandatory layout goals.
109 //
110 // It is set up when the manager is initialized and is not changed ever after.
111 std::unique_ptr<std::map<uint64_t, const LayoutGoal*>>
113
114 GdkKeymap* keymap;
115
116 GCancellable* cancellable;
117};
118
119G_DEFINE_TYPE(FlKeyboardManager, fl_keyboard_manager, G_TYPE_OBJECT);
120
121static gboolean event_is_redispatched(FlKeyboardManager* self,
122 FlKeyEvent* event) {
123 guint32 time = fl_key_event_get_time(event);
124 gboolean is_press = !!fl_key_event_get_is_press(event);
125 guint16 keycode = fl_key_event_get_keycode(event);
126 for (guint i = 0; i < self->redispatched_key_events->len; i++) {
127 FlKeyEvent* e =
128 FL_KEY_EVENT(g_ptr_array_index(self->redispatched_key_events, i));
129 if (fl_key_event_get_time(e) == time &&
130 !!fl_key_event_get_is_press(e) == is_press &&
131 fl_key_event_get_keycode(e) == keycode) {
132 g_ptr_array_remove_index(self->redispatched_key_events, i);
133 return TRUE;
134 }
135 }
136
137 return FALSE;
138}
139
140static void keymap_keys_changed_cb(FlKeyboardManager* self) {
141 g_clear_object(&self->derived_layout);
142 self->derived_layout = fl_keyboard_layout_new();
143}
144
145static void complete_handle_event(FlKeyboardManager* self, GTask* task) {
146 HandleEventData* data =
147 static_cast<HandleEventData*>(g_task_get_task_data(task));
148
149 // Waiting for responses.
150 if (!data->embedder_responded || !data->channel_responded) {
151 return;
152 }
153
154 data->redispatch = !data->handled;
155 g_task_return_boolean(task, TRUE);
156}
157
158static void responder_handle_embedder_event_cb(GObject* object,
159 GAsyncResult* result,
160 gpointer user_data) {
161 g_autoptr(GTask) task = G_TASK(user_data);
162 FlKeyboardManager* self = FL_KEYBOARD_MANAGER(g_task_get_source_object(task));
163
164 HandleEventData* data =
165 static_cast<HandleEventData*>(g_task_get_task_data(G_TASK(task)));
166 data->embedder_responded = TRUE;
167
168 g_autoptr(GError) error = nullptr;
169 gboolean handled;
171 FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, &error)) {
172 if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
173 g_warning("Failed to handle key event in embedder: %s", error->message);
174 }
175 handled = FALSE;
176 }
177 if (handled) {
178 data->handled = TRUE;
179 }
180
182}
183
184static void responder_handle_channel_event_cb(GObject* object,
185 GAsyncResult* result,
186 gpointer user_data) {
187 g_autoptr(GTask) task = G_TASK(user_data);
188 FlKeyboardManager* self = FL_KEYBOARD_MANAGER(g_task_get_source_object(task));
189
190 HandleEventData* data =
191 static_cast<HandleEventData*>(g_task_get_task_data(G_TASK(task)));
192 data->channel_responded = TRUE;
193
194 g_autoptr(GError) error = nullptr;
195 gboolean handled;
197 FL_KEY_CHANNEL_RESPONDER(object), result, &handled, &error)) {
198 if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
199 g_warning("Failed to handle key event in platform: %s", error->message);
200 }
201 handled = FALSE;
202 }
203 if (handled) {
204 data->handled = TRUE;
205 }
206
208}
209
210static uint16_t convert_key_to_char(FlKeyboardManager* self,
211 guint keycode,
212 gint group,
213 gint level) {
214 GdkKeymapKey key = {keycode, group, level};
215 constexpr int kBmpMax = 0xD7FF;
216 guint origin;
217 if (self->lookup_key_handler != nullptr) {
218 origin = self->lookup_key_handler(&key, self->lookup_key_handler_user_data);
219 } else {
220 origin = gdk_keymap_lookup_key(self->keymap, &key);
221 }
222 return origin < kBmpMax ? origin : 0xFFFF;
223}
224
225// Make sure that Flutter has derived the layout for the group of the event,
226// if the event contains a goal keycode.
227static void guarantee_layout(FlKeyboardManager* self, FlKeyEvent* event) {
228 guint8 group = fl_key_event_get_group(event);
229 if (fl_keyboard_layout_has_group(self->derived_layout, group)) {
230 return;
231 }
232 if (self->keycode_to_goals->find(fl_key_event_get_keycode(event)) ==
233 self->keycode_to_goals->end()) {
234 return;
235 }
236
237 // Clone all mandatory goals. Each goal is removed from this cloned map when
238 // fulfilled, and the remaining ones will be assigned to a default position.
239 std::map<uint64_t, const LayoutGoal*> remaining_mandatory_goals =
240 *self->logical_to_mandatory_goals;
241
242#ifdef DEBUG_PRINT_LAYOUT
243 std::string debug_layout_data;
244 for (uint16_t keycode = 0; keycode < 128; keycode += 1) {
245 std::vector<uint16_t> this_key_clues = {
246 convert_key_to_char(self, keycode, group, 0),
247 convert_key_to_char(self, keycode, group, 1), // Shift
248 };
249 debug_format_layout_data(debug_layout_data, keycode, this_key_clues[0],
250 this_key_clues[1]);
251 }
252#endif
253
254 // It's important to only traverse layout goals instead of all keycodes.
255 // Some key codes outside of the standard keyboard also gives alpha-numeric
256 // letters, and will therefore take over mandatory goals from standard
257 // keyboard keys if they come first. Example: French keyboard digit 1.
258 for (const LayoutGoal& keycode_goal : layout_goals) {
259 uint16_t keycode = keycode_goal.keycode;
260 std::vector<uint16_t> this_key_clues = {
261 convert_key_to_char(self, keycode, group, 0),
262 convert_key_to_char(self, keycode, group, 1), // Shift
263 };
264
265 // The logical key should be the first available clue from below:
266 //
267 // - Mandatory goal, if it matches any clue. This ensures that all alnum
268 // keys can be found somewhere.
269 // - US layout, if neither clue of the key is EASCII. This ensures that
270 // there are no non-latin logical keys.
271 // - A value derived on the fly from keycode & keyval.
272 for (uint16_t clue : this_key_clues) {
273 auto matching_goal = remaining_mandatory_goals.find(clue);
274 if (matching_goal != remaining_mandatory_goals.end()) {
275 // Found a key that produces a mandatory char. Use it.
276 g_return_if_fail(fl_keyboard_layout_get_logical_key(
277 self->derived_layout, group, keycode) == 0);
278 fl_keyboard_layout_set_logical_key(self->derived_layout, group, keycode,
279 clue);
280 remaining_mandatory_goals.erase(matching_goal);
281 break;
282 }
283 }
284 bool has_any_eascii =
285 is_eascii(this_key_clues[0]) || is_eascii(this_key_clues[1]);
286 // See if any produced char meets the requirement as a logical key.
287 if (fl_keyboard_layout_get_logical_key(self->derived_layout, group,
288 keycode) == 0 &&
289 !has_any_eascii) {
290 auto found_us_layout = self->keycode_to_goals->find(keycode);
291 if (found_us_layout != self->keycode_to_goals->end()) {
293 self->derived_layout, group, keycode,
294 found_us_layout->second->logical_key);
295 }
296 }
297 }
298
299 // Ensure all mandatory goals are assigned.
300 for (const auto mandatory_goal_iter : remaining_mandatory_goals) {
301 const LayoutGoal* goal = mandatory_goal_iter.second;
302 fl_keyboard_layout_set_logical_key(self->derived_layout, group,
303 goal->keycode, goal->logical_key);
304 }
305}
306
307static void fl_keyboard_manager_dispose(GObject* object) {
308 FlKeyboardManager* self = FL_KEYBOARD_MANAGER(object);
309
310 g_cancellable_cancel(self->cancellable);
311
312 self->keycode_to_goals.reset();
313 self->logical_to_mandatory_goals.reset();
314
315 g_clear_pointer(&self->redispatched_key_events, g_ptr_array_unref);
316 g_clear_object(&self->key_embedder_responder);
317 g_clear_object(&self->key_channel_responder);
318 g_clear_object(&self->derived_layout);
319 g_clear_object(&self->cancellable);
320
321 G_OBJECT_CLASS(fl_keyboard_manager_parent_class)->dispose(object);
322}
323
324static void fl_keyboard_manager_class_init(FlKeyboardManagerClass* klass) {
325 G_OBJECT_CLASS(klass)->dispose = fl_keyboard_manager_dispose;
326}
327
328static void fl_keyboard_manager_init(FlKeyboardManager* self) {
329 self->redispatched_key_events =
330 g_ptr_array_new_with_free_func(g_object_unref);
331 self->derived_layout = fl_keyboard_layout_new();
332
333 self->keycode_to_goals =
334 std::make_unique<std::map<uint16_t, const LayoutGoal*>>();
335 self->logical_to_mandatory_goals =
336 std::make_unique<std::map<uint64_t, const LayoutGoal*>>();
337 for (const LayoutGoal& goal : layout_goals) {
338 (*self->keycode_to_goals)[goal.keycode] = &goal;
339 if (goal.mandatory) {
340 (*self->logical_to_mandatory_goals)[goal.logical_key] = &goal;
341 }
342 }
343
345 g_signal_connect_object(self->keymap, "keys-changed",
346 G_CALLBACK(keymap_keys_changed_cb), self,
347 G_CONNECT_SWAPPED);
348 self->cancellable = g_cancellable_new();
349}
350
351FlKeyboardManager* fl_keyboard_manager_new(FlEngine* engine) {
352 FlKeyboardManager* self = FL_KEYBOARD_MANAGER(
353 g_object_new(fl_keyboard_manager_get_type(), nullptr));
354
355 self->key_embedder_responder = fl_key_embedder_responder_new(engine);
356 self->key_channel_responder =
358
359 return self;
360}
361
363 FlKeyEvent* event) {
364 g_return_if_fail(FL_IS_KEYBOARD_MANAGER(self));
365
366 g_ptr_array_add(self->redispatched_key_events, g_object_ref(event));
367}
368
370 FlKeyEvent* event,
371 GCancellable* cancellable,
372 GAsyncReadyCallback callback,
373 gpointer user_data) {
374 g_return_if_fail(FL_IS_KEYBOARD_MANAGER(self));
375 g_return_if_fail(event != nullptr);
376
377 g_autoptr(GTask) task = g_task_new(self, cancellable, callback, user_data);
378
379 guarantee_layout(self, event);
380
381 g_task_set_task_data(
382 task, handle_event_data_new(event),
383 reinterpret_cast<GDestroyNotify>(handle_event_data_free));
384
385 if (event_is_redispatched(self, event)) {
386 HandleEventData* data =
387 static_cast<HandleEventData*>(g_task_get_task_data(task));
388 data->handled = TRUE;
389 g_task_return_boolean(task, TRUE);
390 return;
391 }
392
393 uint64_t specified_logical_key = fl_keyboard_layout_get_logical_key(
394 self->derived_layout, fl_key_event_get_group(event),
397 self->key_embedder_responder, event, specified_logical_key,
399 g_object_ref(task));
401 self->key_channel_responder, event, specified_logical_key,
402 self->cancellable, responder_handle_channel_event_cb, g_object_ref(task));
403}
404
406 FlKeyboardManager* self,
407 GAsyncResult* result,
408 FlKeyEvent** redispatched_event,
409 GError** error) {
410 g_return_val_if_fail(FL_IS_KEYBOARD_MANAGER(self), FALSE);
411 g_return_val_if_fail(g_task_is_valid(result, self), FALSE);
412
413 HandleEventData* data =
414 static_cast<HandleEventData*>(g_task_get_task_data(G_TASK(result)));
415 if (redispatched_event != nullptr && data->redispatch) {
416 *redispatched_event = FL_KEY_EVENT(g_object_ref(data->event));
417 }
418
419 return g_task_propagate_boolean(G_TASK(result), error);
420}
421
423 guint state,
424 double event_time) {
425 g_return_if_fail(FL_IS_KEYBOARD_MANAGER(self));
427 self->key_embedder_responder, state, event_time);
428}
429
430GHashTable* fl_keyboard_manager_get_pressed_state(FlKeyboardManager* self) {
431 g_return_val_if_fail(FL_IS_KEYBOARD_MANAGER(self), nullptr);
433 self->key_embedder_responder);
434}
435
437 FlKeyboardManager* self,
438 FlKeyboardManagerLookupKeyHandler lookup_key_handler,
439 gpointer user_data) {
440 g_return_if_fail(FL_IS_KEYBOARD_MANAGER(self));
441 self->lookup_key_handler = lookup_key_handler;
442 self->lookup_key_handler_user_data = user_data;
443}
FlutterEngine engine
Definition main.cc:84
g_autoptr(FlEngine) engine
return TRUE
G_MODULE_EXPORT FlBinaryMessenger * fl_engine_get_binary_messenger(FlEngine *self)
void fl_key_channel_responder_handle_event(FlKeyChannelResponder *self, FlKeyEvent *event, uint64_t specified_logical_key, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
gboolean fl_key_channel_responder_handle_event_finish(FlKeyChannelResponder *self, GAsyncResult *result, gboolean *handled, GError **error)
FlKeyChannelResponder * fl_key_channel_responder_new(FlBinaryMessenger *messenger)
FlKeyEmbedderResponder * fl_key_embedder_responder_new(FlEngine *engine)
GHashTable * fl_key_embedder_responder_get_pressed_state(FlKeyEmbedderResponder *self)
void fl_key_embedder_responder_sync_modifiers_if_needed(FlKeyEmbedderResponder *self, guint state, double event_time)
void fl_key_embedder_responder_handle_event(FlKeyEmbedderResponder *self, FlKeyEvent *event, uint64_t specified_logical_key, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
gboolean fl_key_embedder_responder_handle_event_finish(FlKeyEmbedderResponder *self, GAsyncResult *result, gboolean *handled, GError **error)
guint16 fl_key_event_get_keycode(FlKeyEvent *self)
gboolean fl_key_event_get_is_press(FlKeyEvent *self)
guint8 fl_key_event_get_group(FlKeyEvent *self)
guint32 fl_key_event_get_time(FlKeyEvent *self)
const std::vector< LayoutGoal > layout_goals
FlKeyboardLayout * fl_keyboard_layout_new()
uint64_t fl_keyboard_layout_get_logical_key(FlKeyboardLayout *self, uint8_t group, uint16_t keycode)
void fl_keyboard_layout_set_logical_key(FlKeyboardLayout *self, uint8_t group, uint16_t keycode, uint64_t logical_key)
gboolean fl_keyboard_layout_has_group(FlKeyboardLayout *self, uint8_t group)
static void fl_keyboard_manager_class_init(FlKeyboardManagerClass *klass)
static void fl_keyboard_manager_dispose(GObject *object)
static void guarantee_layout(FlKeyboardManager *self, FlKeyEvent *event)
static void responder_handle_channel_event_cb(GObject *object, GAsyncResult *result, gpointer user_data)
G_DEFINE_TYPE(FlKeyboardManager, fl_keyboard_manager, G_TYPE_OBJECT)
void fl_keyboard_manager_handle_event(FlKeyboardManager *self, FlKeyEvent *event, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
static void handle_event_data_free(HandleEventData *data)
static uint16_t convert_key_to_char(FlKeyboardManager *self, guint keycode, gint group, gint level)
GHashTable * fl_keyboard_manager_get_pressed_state(FlKeyboardManager *self)
static void fl_keyboard_manager_init(FlKeyboardManager *self)
static void responder_handle_embedder_event_cb(GObject *object, GAsyncResult *result, gpointer user_data)
static gboolean event_is_redispatched(FlKeyboardManager *self, FlKeyEvent *event)
static void keymap_keys_changed_cb(FlKeyboardManager *self)
static HandleEventData * handle_event_data_new(FlKeyEvent *event)
gboolean fl_keyboard_manager_handle_event_finish(FlKeyboardManager *self, GAsyncResult *result, FlKeyEvent **redispatched_event, GError **error)
void fl_keyboard_manager_set_lookup_key_handler(FlKeyboardManager *self, FlKeyboardManagerLookupKeyHandler lookup_key_handler, gpointer user_data)
static void complete_handle_event(FlKeyboardManager *self, GTask *task)
FlKeyboardManager * fl_keyboard_manager_new(FlEngine *engine)
void fl_keyboard_manager_add_redispatched_event(FlKeyboardManager *self, FlKeyEvent *event)
void fl_keyboard_manager_sync_modifier_if_needed(FlKeyboardManager *self, guint state, double event_time)
guint(* FlKeyboardManagerLookupKeyHandler)(const GdkKeymapKey *key, gpointer user_data)
const uint8_t uint32_t uint32_t GError ** error
FlutterDesktopBinaryReply callback
GdkDisplay * gdk_display_get_default()
Definition mock_gtk.cc:70
guint gdk_keymap_lookup_key(GdkKeymap *keymap, const GdkKeymapKey *key)
Definition mock_gtk.cc:65
GdkKeymap * gdk_keymap_get_for_display(GdkDisplay *display)
Definition mock_gtk.cc:57
FlKeyboardManagerLookupKeyHandler lookup_key_handler
FlKeyChannelResponder * key_channel_responder
std::unique_ptr< std::map< uint64_t, const LayoutGoal * > > logical_to_mandatory_goals
FlKeyEmbedderResponder * key_embedder_responder
GPtrArray * redispatched_key_events
std::unique_ptr< std::map< uint16_t, const LayoutGoal * > > keycode_to_goals
FlKeyboardLayout * derived_layout
uint16_t keycode
uint64_t logical_key