| Flutter Linux Embedder |

fl_key_embedder_responder.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
5 #include "flutter/shell/platform/linux/fl_key_embedder_responder.h"
6
7 #include <gtk/gtk.h>
8 #include <cinttypes>
9
10 #include "flutter/shell/platform/embedder/embedder.h"
11 #include "flutter/shell/platform/linux/fl_key_embedder_responder_private.h"
12 #include "flutter/shell/platform/linux/key_mapping.h"
13
14 constexpr uint64_t kMicrosecondsPerMillisecond = 1000;
15
16 static const FlutterKeyEvent kEmptyEvent{
17 .struct_size = sizeof(FlutterKeyEvent),
18 .timestamp = 0,
19 .type = kFlutterKeyEventTypeDown,
20 .physical = 0,
21 .logical = 0,
22 .character = nullptr,
23 .synthesized = false,
24 };
25
26 // Look up a hash table that maps a uint64_t to a uint64_t.
27 //
28 // Returns 0 if not found.
29 //
30 // Both key and value should be directly hashed.
31 static uint64_t lookup_hash_table(GHashTable* table, uint64_t key) {
32 return gpointer_to_uint64(
33 g_hash_table_lookup(table, uint64_to_gpointer(key)));
34 }
35
36 static gboolean hash_table_find_equal_value(gpointer key,
37 gpointer value,
38 gpointer user_data) {
39 return gpointer_to_uint64(value) == gpointer_to_uint64(user_data);
40 }
41
42 // Look up a hash table that maps a uint64_t to a uint64_t; given its key,
43 // find its value.
44 //
45 // Returns 0 if not found.
46 //
47 // Both key and value should be directly hashed.
48 static uint64_t reverse_lookup_hash_table(GHashTable* table, uint64_t value) {
49 return gpointer_to_uint64(g_hash_table_find(
50 table, hash_table_find_equal_value, uint64_to_gpointer(value)));
51 }
52
53 static uint64_t to_lower(uint64_t n) {
54 constexpr uint64_t lower_a = 0x61;
55 constexpr uint64_t upper_a = 0x41;
56 constexpr uint64_t upper_z = 0x5a;
57
58 constexpr uint64_t lower_a_grave = 0xe0;
59 constexpr uint64_t upper_a_grave = 0xc0;
60 constexpr uint64_t upper_thorn = 0xde;
61 constexpr uint64_t division = 0xf7;
62
63 // ASCII range.
64 if (n >= upper_a && n <= upper_z) {
65 return n - upper_a + lower_a;
66 }
67
68 // EASCII range.
69 if (n >= upper_a_grave && n <= upper_thorn && n != division) {
70 return n - upper_a_grave + lower_a_grave;
71 }
72
73 return n;
74 }
75
76 /**
77 * FlKeyEmbedderUserData:
78 * The user_data used when #FlKeyEmbedderResponder sends message through the
79 * embedder.SendKeyEvent API.
80 */
81 G_DECLARE_FINAL_TYPE(FlKeyEmbedderUserData,
82 fl_key_embedder_user_data,
83 FL,
84 KEY_EMBEDDER_USER_DATA,
85 GObject);
86
87 struct _FlKeyEmbedderUserData {
88 GObject parent_instance;
89
90 FlKeyEmbedderResponderAsyncCallback callback;
91 gpointer user_data;
92 };
93
94 G_DEFINE_TYPE(FlKeyEmbedderUserData, fl_key_embedder_user_data, G_TYPE_OBJECT)
95
96 static void fl_key_embedder_user_data_dispose(GObject* object);
97
98 static void fl_key_embedder_user_data_class_init(
99 FlKeyEmbedderUserDataClass* klass) {
100 G_OBJECT_CLASS(klass)->dispose = fl_key_embedder_user_data_dispose;
101 }
102
103 static void fl_key_embedder_user_data_init(FlKeyEmbedderUserData* self) {}
104
105 static void fl_key_embedder_user_data_dispose(GObject* object) {
106 // The following line suppresses a warning for unused function
107 // FL_IS_KEY_EMBEDDER_USER_DATA.
108 g_return_if_fail(FL_IS_KEY_EMBEDDER_USER_DATA(object));
109 }
110
111 // Creates a new FlKeyChannelUserData private class with all information.
112 //
113 // The callback and the user_data might be nullptr.
114 static FlKeyEmbedderUserData* fl_key_embedder_user_data_new(
115 FlKeyEmbedderResponderAsyncCallback callback,
116 gpointer user_data) {
117 FlKeyEmbedderUserData* self = FL_KEY_EMBEDDER_USER_DATA(
118 g_object_new(fl_key_embedder_user_data_get_type(), nullptr));
119
120 self->callback = callback;
121 self->user_data = user_data;
122 return self;
123 }
124
125 namespace {
126
127 typedef enum {
128 STATE_LOGIC_INFERRENCE_UNDECIDED,
129 STATE_LOGIC_INFERRENCE_NORMAL,
130 STATE_LOGIC_INFERRENCE_REVERSED,
131 } StateLogicInferrence;
132
133 }
134
135 struct _FlKeyEmbedderResponder {
136 GObject parent_instance;
137
138 EmbedderSendKeyEvent send_key_event;
139 void* send_key_event_user_data;
140
141 // Internal record for states of whether a key is pressed.
142 //
143 // It is a map from Flutter physical key to Flutter logical key. Both keys
144 // and values are directly stored uint64s. This table is freed by the
145 // responder.
146 GHashTable* pressing_records;
147
148 // Internal record for states of whether a lock mode is enabled.
149 //
150 // It is a bit mask composed of GTK mode bits.
151 guint lock_records;
152
153 // Internal record for the last observed key mapping.
154 //
155 // It stores the physical key last seen during a key down event for a logical
156 // key. It is used to synthesize modifier keys and lock keys.
157 //
158 // It is a map from Flutter logical key to physical key. Both keys and
159 // values are directly stored uint64s. This table is freed by the responder.
160 GHashTable* mapping_records;
161
162 // The inferred logic type indicating whether the CapsLock state logic is
163 // reversed on this platform.
164 //
165 // For more information, see #update_caps_lock_state_logic_inferrence.
166 StateLogicInferrence caps_lock_state_logic_inferrence;
167
168 // Record if any events has been sent during a
169 // |fl_key_embedder_responder_handle_event| call.
170 bool sent_any_events;
171
172 // A static map from GTK modifier bits to #FlKeyEmbedderCheckedKey to
173 // configure the modifier keys that needs to be tracked and kept synchronous
174 // on.
175 //
176 // The keys are directly stored guints. The values must be freed with g_free.
177 // This table is freed by the responder.
178 GHashTable* modifier_bit_to_checked_keys;
179
180 // A static map from GTK modifier bits to #FlKeyEmbedderCheckedKey to
181 // configure the lock mode bits that needs to be tracked and kept synchronous
182 // on.
183 //
184 // The keys are directly stored guints. The values must be freed with g_free.
185 // This table is freed by the responder.
186 GHashTable* lock_bit_to_checked_keys;
187
188 // A static map generated by reverse mapping lock_bit_to_checked_keys.
189 //
190 // It is a map from primary physical keys to lock bits. Both keys and values
191 // are directly stored uint64s. This table is freed by the responder.
192 GHashTable* logical_key_to_lock_bit;
193 };
194
195 static void fl_key_embedder_responder_dispose(GObject* object);
196
197 G_DEFINE_TYPE(FlKeyEmbedderResponder, fl_key_embedder_responder, G_TYPE_OBJECT)
198
199 // Initializes the FlKeyEmbedderResponder class methods.
200 static void fl_key_embedder_responder_class_init(
201 FlKeyEmbedderResponderClass* klass) {
202 G_OBJECT_CLASS(klass)->dispose = fl_key_embedder_responder_dispose;
203 }
204
205 // Initializes an FlKeyEmbedderResponder instance.
206 static void fl_key_embedder_responder_init(FlKeyEmbedderResponder* self) {}
207
208 // Disposes of an FlKeyEmbedderResponder instance.
209 static void fl_key_embedder_responder_dispose(GObject* object) {
210 FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(object);
211
212 g_clear_pointer(&self->pressing_records, g_hash_table_unref);
213 g_clear_pointer(&self->mapping_records, g_hash_table_unref);
214 g_clear_pointer(&self->modifier_bit_to_checked_keys, g_hash_table_unref);
215 g_clear_pointer(&self->lock_bit_to_checked_keys, g_hash_table_unref);
216 g_clear_pointer(&self->logical_key_to_lock_bit, g_hash_table_unref);
217
218 G_OBJECT_CLASS(fl_key_embedder_responder_parent_class)->dispose(object);
219 }
220
221 // Fill in #logical_key_to_lock_bit by associating a logical key with
222 // its corresponding modifier bit.
223 //
224 // This is used as the body of a loop over #lock_bit_to_checked_keys.
225 static void initialize_logical_key_to_lock_bit_loop_body(gpointer lock_bit,
226 gpointer value,
227 gpointer user_data) {
228 FlKeyEmbedderCheckedKey* checked_key =
229 reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
230 GHashTable* table = reinterpret_cast<GHashTable*>(user_data);
231 g_hash_table_insert(table,
232 uint64_to_gpointer(checked_key->primary_logical_key),
233 GUINT_TO_POINTER(lock_bit));
234 }
235
236 // Creates a new FlKeyEmbedderResponder instance.
237 FlKeyEmbedderResponder* fl_key_embedder_responder_new(
238 EmbedderSendKeyEvent send_key_event,
239 void* send_key_event_user_data) {
240 FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(
241 g_object_new(fl_key_embedder_responder_get_type(), nullptr));
242
243 self->send_key_event = send_key_event;
244 self->send_key_event_user_data = send_key_event_user_data;
245
246 self->pressing_records = g_hash_table_new(g_direct_hash, g_direct_equal);
247 self->mapping_records = g_hash_table_new(g_direct_hash, g_direct_equal);
248 self->lock_records = 0;
249 self->caps_lock_state_logic_inferrence = STATE_LOGIC_INFERRENCE_UNDECIDED;
250
251 self->modifier_bit_to_checked_keys =
252 g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
253 initialize_modifier_bit_to_checked_keys(self->modifier_bit_to_checked_keys);
254
255 self->lock_bit_to_checked_keys =
256 g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
257 initialize_lock_bit_to_checked_keys(self->lock_bit_to_checked_keys);
258
259 self->logical_key_to_lock_bit =
260 g_hash_table_new(g_direct_hash, g_direct_equal);
261 g_hash_table_foreach(self->lock_bit_to_checked_keys,
262 initialize_logical_key_to_lock_bit_loop_body,
263 self->logical_key_to_lock_bit);
264
265 return self;
266 }
267
268 static uint64_t apply_id_plane(uint64_t logical_id, uint64_t plane) {
269 return (logical_id & kValueMask) | plane;
270 }
271
272 static uint64_t event_to_physical_key(FlKeyEvent* event) {
273 auto found = xkb_to_physical_key_map.find(fl_key_event_get_keycode(event));
274 if (found != xkb_to_physical_key_map.end()) {
275 return found->second;
276 }
277 return apply_id_plane(fl_key_event_get_keycode(event), kGtkPlane);
278 }
279
280 static uint64_t event_to_logical_key(FlKeyEvent* event) {
281 guint keyval = fl_key_event_get_keyval(event);
282 auto found = gtk_keyval_to_logical_key_map.find(keyval);
283 if (found != gtk_keyval_to_logical_key_map.end()) {
284 return found->second;
285 }
286 // EASCII range
287 if (keyval < 256) {
288 return apply_id_plane(to_lower(keyval), kUnicodePlane);
289 }
290 // Auto-generate key
291 return apply_id_plane(keyval, kGtkPlane);
292 }
293
294 static uint64_t event_to_timestamp(FlKeyEvent* event) {
295 return kMicrosecondsPerMillisecond *
296 static_cast<double>(fl_key_event_get_time(event));
297 }
298
299 // Returns a newly accocated UTF-8 string from fl_key_event_get_keyval(event)
300 // that must be freed later with g_free().
301 static char* event_to_character(FlKeyEvent* event) {
302 gunichar unicodeChar = gdk_keyval_to_unicode(fl_key_event_get_keyval(event));
303 glong items_written;
304 gchar* result = g_ucs4_to_utf8(&unicodeChar, 1, NULL, &items_written, NULL);
305 if (items_written == 0) {
306 if (result != NULL) {
307 g_free(result);
308 }
309 return nullptr;
310 }
311 return result;
312 }
313
314 // Handles a response from the embedder API to a key event sent to the framework
315 // earlier.
316 static void handle_response(bool handled, gpointer user_data) {
317 g_autoptr(FlKeyEmbedderUserData) data = FL_KEY_EMBEDDER_USER_DATA(user_data);
318
319 g_return_if_fail(data->callback != nullptr);
320
321 data->callback(handled, data->user_data);
322 }
323
324 // Sends a synthesized event to the framework with no demand for callback.
325 static void synthesize_simple_event(FlKeyEmbedderResponder* self,
326 FlutterKeyEventType type,
327 uint64_t physical,
328 uint64_t logical,
329 double timestamp) {
330 FlutterKeyEvent out_event;
331 out_event.struct_size = sizeof(out_event);
332 out_event.timestamp = timestamp;
333 out_event.type = type;
334 out_event.physical = physical;
335 out_event.logical = logical;
336 out_event.character = nullptr;
337 out_event.synthesized = true;
338 self->sent_any_events = true;
339 self->send_key_event(&out_event, nullptr, nullptr,
340 self->send_key_event_user_data);
341 }
342
343 namespace {
344
345 // Context variables for the foreach call used to synchronize pressing states
346 // and lock states.
347 typedef struct {
348 FlKeyEmbedderResponder* self;
349 guint state;
350 uint64_t event_logical_key;
351 bool is_down;
352 double timestamp;
353 } SyncStateLoopContext;
354
355 // Context variables for the foreach call used to find the physical key from
356 // a modifier logical key.
357 typedef struct {
358 bool known_modifier_physical_key;
359 uint64_t logical_key;
360 uint64_t physical_key_from_event;
361 uint64_t corrected_physical_key;
362 } ModifierLogicalToPhysicalContext;
363
364 } // namespace
365
366 // Update the pressing record.
367 //
368 // If `logical_key` is 0, the record will be set as "released". Otherwise, the
369 // record will be set as "pressed" with this logical key. This function asserts
370 // that the key is pressed if the caller asked to release, and vice versa.
371 static void update_pressing_state(FlKeyEmbedderResponder* self,
372 uint64_t physical_key,
373 uint64_t logical_key) {
374 if (logical_key != 0) {
375 g_return_if_fail(lookup_hash_table(self->pressing_records, physical_key) ==
376 0);
377 g_hash_table_insert(self->pressing_records,
378 uint64_to_gpointer(physical_key),
379 uint64_to_gpointer(logical_key));
380 } else {
381 g_return_if_fail(lookup_hash_table(self->pressing_records, physical_key) !=
382 0);
383 g_hash_table_remove(self->pressing_records,
384 uint64_to_gpointer(physical_key));
385 }
386 }
387
388 // Update the lock record.
389 //
390 // If `is_down` is false, this function is a no-op. Otherwise, this function
391 // finds the lock bit corresponding to `physical_key`, and flips its bit.
392 static void possibly_update_lock_bit(FlKeyEmbedderResponder* self,
393 uint64_t logical_key,
394 bool is_down) {
395 if (!is_down) {
396 return;
397 }
398 const guint mode_bit = GPOINTER_TO_UINT(g_hash_table_lookup(
399 self->logical_key_to_lock_bit, uint64_to_gpointer(logical_key)));
400 if (mode_bit != 0) {
401 self->lock_records ^= mode_bit;
402 }
403 }
404
405 static void update_mapping_record(FlKeyEmbedderResponder* self,
406 uint64_t physical_key,
407 uint64_t logical_key) {
408 g_hash_table_insert(self->mapping_records, uint64_to_gpointer(logical_key),
409 uint64_to_gpointer(physical_key));
410 }
411
412 // Synchronizes the pressing state of a key to its state from the event by
413 // synthesizing events.
414 //
415 // This is used as the body of a loop over #modifier_bit_to_checked_keys.
416 static void synchronize_pressed_states_loop_body(gpointer key,
417 gpointer value,
418 gpointer user_data) {
419 SyncStateLoopContext* context =
420 reinterpret_cast<SyncStateLoopContext*>(user_data);
421 FlKeyEmbedderCheckedKey* checked_key =
422 reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
423
424 const guint modifier_bit = GPOINTER_TO_INT(key);
425 FlKeyEmbedderResponder* self = context->self;
426 // Each TestKey contains up to two logical keys, typically the left modifier
427 // and the right modifier, that correspond to the same modifier_bit. We'd
428 // like to infer whether to synthesize a down or up event for each key.
429 //
430 // The hard part is that, if we want to synthesize a down event, we don't know
431 // which physical key to use. Here we assume the keyboard layout do not change
432 // frequently and use the last physical-logical relationship, recorded in
433 // #mapping_records.
434 const uint64_t logical_keys[] = {
435 checked_key->primary_logical_key,
436 checked_key->secondary_logical_key,
437 };
438 const guint length = checked_key->secondary_logical_key == 0 ? 1 : 2;
439
440 const bool any_pressed_by_state = (context->state & modifier_bit) != 0;
441
442 bool any_pressed_by_record = false;
443
444 // Traverse each logical key of this modifier bit for 2 purposes:
445 //
446 // 1. Perform the synthesization of release events: If the modifier bit is 0
447 // and the key is pressed, synthesize a release event.
448 // 2. Prepare for the synthesization of press events: If the modifier bit is
449 // 1, and no keys are pressed (discovered here), synthesize a press event
450 // later.
451 for (guint logical_key_idx = 0; logical_key_idx < length; logical_key_idx++) {
452 const uint64_t logical_key = logical_keys[logical_key_idx];
453 g_return_if_fail(logical_key != 0);
454 const uint64_t pressing_physical_key =
455 reverse_lookup_hash_table(self->pressing_records, logical_key);
456 const bool this_key_pressed_before_event = pressing_physical_key != 0;
457
458 any_pressed_by_record =
459 any_pressed_by_record || this_key_pressed_before_event;
460
461 if (this_key_pressed_before_event && !any_pressed_by_state) {
462 const uint64_t recorded_physical_key =
463 lookup_hash_table(self->mapping_records, logical_key);
464 // Since this key has been pressed before, there must have been a recorded
465 // physical key.
466 g_return_if_fail(recorded_physical_key != 0);
467 // In rare cases #recorded_logical_key is different from #logical_key.
468 const uint64_t recorded_logical_key =
469 lookup_hash_table(self->pressing_records, recorded_physical_key);
470 synthesize_simple_event(self, kFlutterKeyEventTypeUp,
471 recorded_physical_key, recorded_logical_key,
472 context->timestamp);
473 update_pressing_state(self, recorded_physical_key, 0);
474 }
475 }
476 // If the modifier should be pressed, synthesize a down event for its primary
477 // key.
478 if (any_pressed_by_state && !any_pressed_by_record) {
479 const uint64_t logical_key = checked_key->primary_logical_key;
480 const uint64_t recorded_physical_key =
481 lookup_hash_table(self->mapping_records, logical_key);
482 // The physical key is derived from past mapping record if possible.
483 //
484 // The event to be synthesized is a key down event. There might not have
485 // been a mapping record, in which case the hard-coded #primary_physical_key
486 // is used.
487 const uint64_t physical_key = recorded_physical_key != 0
488 ? recorded_physical_key
489 : checked_key->primary_physical_key;
490 if (recorded_physical_key == 0) {
491 update_mapping_record(self, physical_key, logical_key);
492 }
493 synthesize_simple_event(self, kFlutterKeyEventTypeDown, physical_key,
494 logical_key, context->timestamp);
495 update_pressing_state(self, physical_key, logical_key);
496 }
497 }
498
499 // Find the stage # by the current record, which should be the recorded stage
500 // before the event.
501 static int find_stage_by_record(bool is_down, bool is_enabled) {
502 constexpr int stage_by_record_index[] = {
503 0, // is_down: 0, is_enabled: 0
504 2, // 0 1
505 3, // 1 0
506 1 // 1 1
507 };
508 return stage_by_record_index[(is_down << 1) + is_enabled];
509 }
510
511 // Find the stage # by an event for the target key, which should be inferred
512 // stage before the event.
513 static int find_stage_by_self_event(int stage_by_record,
514 bool is_down_event,
515 bool is_state_on,
516 bool reverse_state_logic) {
517 if (!is_state_on) {
518 return reverse_state_logic ? 2 : 0;
519 }
520 if (is_down_event) {
521 return reverse_state_logic ? 0 : 2;
522 }
523 return stage_by_record;
524 }
525
526 // Find the stage # by an event for a non-target key, which should be inferred
527 // stage during the event.
528 static int find_stage_by_others_event(int stage_by_record, bool is_state_on) {
529 g_return_val_if_fail(stage_by_record >= 0 && stage_by_record < 4,
530 stage_by_record);
531 if (!is_state_on) {
532 return 0;
533 }
534 if (stage_by_record == 0) {
535 return 1;
536 }
537 return stage_by_record;
538 }
539
540 // Infer the logic type of CapsLock on the current platform if applicable.
541 //
542 // In most cases, when a lock key is pressed or released, its event has the
543 // key's state as 0-1-1-1 for the 4 stages (as documented in
544 // #synchronize_lock_states_loop_body) respectively. But in very rare cases it
545 // produces 1-1-0-1, which we call "reversed state logic". This is observed
546 // when using Chrome Remote Desktop on macOS (likely a bug).
547 //
548 // To detect whether the current platform behaves normally or reversed, this
549 // function is called on the first down event of CapsLock before calculating
550 // stages. This function then store the inferred mode as
551 // self->caps_lock_state_logic_inferrence.
552 //
553 // This does not help if the same app session is used alternatively between a
554 // reversed platform and a normal platform. But this is the best we can do.
555 static void update_caps_lock_state_logic_inferrence(
556 FlKeyEmbedderResponder* self,
557 bool is_down_event,
558 bool enabled_by_state,
559 int stage_by_record) {
560 if (self->caps_lock_state_logic_inferrence !=
561 STATE_LOGIC_INFERRENCE_UNDECIDED) {
562 return;
563 }
564 if (!is_down_event) {
565 return;
566 }
567 const int stage_by_event = find_stage_by_self_event(
568 stage_by_record, is_down_event, enabled_by_state, false);
569 if ((stage_by_event == 0 && stage_by_record == 2) ||
570 (stage_by_event == 2 && stage_by_record == 0)) {
571 self->caps_lock_state_logic_inferrence = STATE_LOGIC_INFERRENCE_REVERSED;
572 } else {
573 self->caps_lock_state_logic_inferrence = STATE_LOGIC_INFERRENCE_NORMAL;
574 }
575 }
576
577 // Synchronizes the lock state of a key to its state from the event by
578 // synthesizing events.
579 //
580 // This is used as the body of a loop over #lock_bit_to_checked_keys.
581 //
582 // This function might modify #caps_lock_state_logic_inferrence.
583 static void synchronize_lock_states_loop_body(gpointer key,
584 gpointer value,
585 gpointer user_data) {
586 SyncStateLoopContext* context =
587 reinterpret_cast<SyncStateLoopContext*>(user_data);
588 FlKeyEmbedderCheckedKey* checked_key =
589 reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
590
591 guint modifier_bit = GPOINTER_TO_INT(key);
592 FlKeyEmbedderResponder* self = context->self;
593
594 const uint64_t logical_key = checked_key->primary_logical_key;
595 const uint64_t recorded_physical_key =
596 lookup_hash_table(self->mapping_records, logical_key);
597 // The physical key is derived from past mapping record if possible.
598 //
599 // If the event to be synthesized is a key up event, then there must have
600 // been a key down event before, which has updated the mapping record.
601 // If the event to be synthesized is a key down event, then there might
602 // not have been a mapping record, in which case the hard-coded
603 // #primary_physical_key is used.
604 const uint64_t physical_key = recorded_physical_key != 0
605 ? recorded_physical_key
606 : checked_key->primary_physical_key;
607
608 // A lock mode key can be at any of a 4-stage cycle, depending on whether it's
609 // pressed and enabled. The following table lists the definition of each
610 // stage (TruePressed and TrueEnabled), the event of the lock key between
611 // every 2 stages (SelfType and SelfState), and the event of other keys at
612 // each stage (OthersState). On certain platforms SelfState uses a reversed
613 // rule for certain keys (SelfState(rvsd), as documented in
614 // #update_caps_lock_state_logic_inferrence).
615 //
616 // # [0] [1] [2] [3]
617 // TruePressed: Released Pressed Released Pressed
618 // TrueEnabled: Disabled Enabled Enabled Disabled
619 // SelfType: Down Up Down Up
620 // SelfState: 0 1 1 1
621 // SelfState(rvsd): 1 1 0 1
622 // OthersState: 0 1 1 1
623 //
624 // When the exact stage can't be derived, choose the stage that requires the
625 // minimal synthesization.
626
627 const uint64_t pressed_logical_key =
628 recorded_physical_key == 0
629 ? 0
630 : lookup_hash_table(self->pressing_records, recorded_physical_key);
631
632 g_return_if_fail(pressed_logical_key == 0 ||
633 pressed_logical_key == logical_key);
634 const int stage_by_record = find_stage_by_record(
635 pressed_logical_key != 0, (self->lock_records & modifier_bit) != 0);
636
637 const bool enabled_by_state = (context->state & modifier_bit) != 0;
638 const bool this_key_is_event_key = logical_key == context->event_logical_key;
639 if (this_key_is_event_key && checked_key->is_caps_lock) {
640 update_caps_lock_state_logic_inferrence(self, context->is_down,
641 enabled_by_state, stage_by_record);
642 g_return_if_fail(self->caps_lock_state_logic_inferrence !=
643 STATE_LOGIC_INFERRENCE_UNDECIDED);
644 }
645 const bool reverse_state_logic =
646 checked_key->is_caps_lock &&
647 self->caps_lock_state_logic_inferrence == STATE_LOGIC_INFERRENCE_REVERSED;
648 const int stage_by_event =
649 this_key_is_event_key
650 ? find_stage_by_self_event(stage_by_record, context->is_down,
651 enabled_by_state, reverse_state_logic)
652 : find_stage_by_others_event(stage_by_record, enabled_by_state);
653
654 // The destination stage is equal to stage_by_event but shifted cyclically to
655 // be no less than stage_by_record.
656 constexpr int kNumStages = 4;
657 const int destination_stage = stage_by_event >= stage_by_record
658 ? stage_by_event
659 : stage_by_event + kNumStages;
660
661 g_return_if_fail(stage_by_record <= destination_stage);
662 if (stage_by_record == destination_stage) {
663 return;
664 }
665 for (int current_stage = stage_by_record; current_stage < destination_stage;
666 current_stage += 1) {
667 if (current_stage == 9) {
668 return;
669 }
670
671 const int standard_current_stage = current_stage % kNumStages;
672 const bool is_down_event =
673 standard_current_stage == 0 || standard_current_stage == 2;
674 if (is_down_event && recorded_physical_key == 0) {
675 update_mapping_record(self, physical_key, logical_key);
676 }
677 FlutterKeyEventType type =
678 is_down_event ? kFlutterKeyEventTypeDown : kFlutterKeyEventTypeUp;
679 update_pressing_state(self, physical_key, is_down_event ? logical_key : 0);
680 possibly_update_lock_bit(self, logical_key, is_down_event);
681 synthesize_simple_event(self, type, physical_key, logical_key,
682 context->timestamp);
683 }
684 }
685
686 // Find if a given physical key is the primary physical of one of the known
687 // modifier keys.
688 //
689 // This is used as the body of a loop over #modifier_bit_to_checked_keys.
690 static void is_known_modifier_physical_key_loop_body(gpointer key,
691 gpointer value,
692 gpointer user_data) {
693 ModifierLogicalToPhysicalContext* context =
694 reinterpret_cast<ModifierLogicalToPhysicalContext*>(user_data);
695 FlKeyEmbedderCheckedKey* checked_key =
696 reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
697
698 if (checked_key->primary_physical_key == context->physical_key_from_event) {
699 context->known_modifier_physical_key = true;
700 }
701 }
702
703 // Return the primary physical key of a known modifier key which matches the
704 // given logical key.
705 //
706 // This is used as the body of a loop over #modifier_bit_to_checked_keys.
707 static void find_physical_from_logical_loop_body(gpointer key,
708 gpointer value,
709 gpointer user_data) {
710 ModifierLogicalToPhysicalContext* context =
711 reinterpret_cast<ModifierLogicalToPhysicalContext*>(user_data);
712 FlKeyEmbedderCheckedKey* checked_key =
713 reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
714
715 if (checked_key->primary_logical_key == context->logical_key ||
716 checked_key->secondary_logical_key == context->logical_key) {
717 context->corrected_physical_key = checked_key->primary_physical_key;
718 }
719 }
720
721 static uint64_t corrected_modifier_physical_key(
722 GHashTable* modifier_bit_to_checked_keys,
723 uint64_t physical_key_from_event,
724 uint64_t logical_key) {
725 ModifierLogicalToPhysicalContext logical_to_physical_context;
726 logical_to_physical_context.known_modifier_physical_key = false;
727 logical_to_physical_context.physical_key_from_event = physical_key_from_event;
728 logical_to_physical_context.logical_key = logical_key;
729 // If no match is found, defaults to the physical key retrieved from the
730 // event.
731 logical_to_physical_context.corrected_physical_key = physical_key_from_event;
732
733 // Check if the physical key is one of the known modifier physical key.
734 g_hash_table_foreach(modifier_bit_to_checked_keys,
735 is_known_modifier_physical_key_loop_body,
736 &logical_to_physical_context);
737
738 // If the physical key matches a known modifier key, find the modifier
739 // physical key from the logical key.
740 if (logical_to_physical_context.known_modifier_physical_key) {
741 g_hash_table_foreach(modifier_bit_to_checked_keys,
742 find_physical_from_logical_loop_body,
743 &logical_to_physical_context);
744 }
745
746 return logical_to_physical_context.corrected_physical_key;
747 }
748
749 static void fl_key_embedder_responder_handle_event_impl(
750 FlKeyEmbedderResponder* responder,
751 FlKeyEvent* event,
752 uint64_t specified_logical_key,
753 FlKeyEmbedderResponderAsyncCallback callback,
754 gpointer user_data) {
755 FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(responder);
756
757 g_return_if_fail(event != nullptr);
758 g_return_if_fail(callback != nullptr);
759
760 const uint64_t logical_key = specified_logical_key != 0
761 ? specified_logical_key
762 : event_to_logical_key(event);
763 const uint64_t physical_key_from_event = event_to_physical_key(event);
764 const uint64_t physical_key = corrected_modifier_physical_key(
765 self->modifier_bit_to_checked_keys, physical_key_from_event, logical_key);
766 const double timestamp = event_to_timestamp(event);
767 const bool is_down_event = fl_key_event_get_is_press(event);
768
769 SyncStateLoopContext sync_state_context;
770 sync_state_context.self = self;
771 sync_state_context.state = fl_key_event_get_state(event);
772 sync_state_context.timestamp = timestamp;
773 sync_state_context.is_down = is_down_event;
774 sync_state_context.event_logical_key = logical_key;
775
776 // Update lock mode states
777 g_hash_table_foreach(self->lock_bit_to_checked_keys,
778 synchronize_lock_states_loop_body, &sync_state_context);
779
780 // Update pressing states
781 g_hash_table_foreach(self->modifier_bit_to_checked_keys,
782 synchronize_pressed_states_loop_body,
783 &sync_state_context);
784
785 // Construct the real event
786 const uint64_t last_logical_record =
787 lookup_hash_table(self->pressing_records, physical_key);
788
789 FlutterKeyEvent out_event;
790 out_event.struct_size = sizeof(out_event);
791 out_event.timestamp = timestamp;
792 out_event.physical = physical_key;
793 out_event.logical =
794 last_logical_record != 0 ? last_logical_record : logical_key;
795 out_event.character = nullptr;
796 out_event.synthesized = false;
797
798 g_autofree char* character_to_free = nullptr;
799 if (is_down_event) {
800 if (last_logical_record) {
801 // A key has been pressed that has the exact physical key as a currently
802 // pressed one. This can happen during repeated events.
803 out_event.type = kFlutterKeyEventTypeRepeat;
804 } else {
805 out_event.type = kFlutterKeyEventTypeDown;
806 }
807 character_to_free = event_to_character(event); // Might be null
808 out_event.character = character_to_free;
809 } else { // is_down_event false
810 if (!last_logical_record) {
811 // The physical key has been released before. It might indicate a missed
812 // event due to loss of focus, or multiple keyboards pressed keys with the
813 // same physical key. Ignore the up event.
814 callback(true, user_data);
815 return;
816 } else {
817 out_event.type = kFlutterKeyEventTypeUp;
818 }
819 }
820
821 if (out_event.type != kFlutterKeyEventTypeRepeat) {
822 update_pressing_state(self, physical_key, is_down_event ? logical_key : 0);
823 }
824 possibly_update_lock_bit(self, logical_key, is_down_event);
825 if (is_down_event) {
826 update_mapping_record(self, physical_key, logical_key);
827 }
828 FlKeyEmbedderUserData* response_data =
829 fl_key_embedder_user_data_new(callback, user_data);
830 self->sent_any_events = true;
831 self->send_key_event(&out_event, handle_response, response_data,
832 self->send_key_event_user_data);
833 }
834
835 void fl_key_embedder_responder_handle_event(
836 FlKeyEmbedderResponder* self,
837 FlKeyEvent* event,
838 uint64_t specified_logical_key,
839 FlKeyEmbedderResponderAsyncCallback callback,
840 gpointer user_data) {
841 self->sent_any_events = false;
842 fl_key_embedder_responder_handle_event_impl(
843 self, event, specified_logical_key, callback, user_data);
844 if (!self->sent_any_events) {
845 self->send_key_event(&kEmptyEvent, nullptr, nullptr,
846 self->send_key_event_user_data);
847 }
848 }
849
850 void fl_key_embedder_responder_sync_modifiers_if_needed(
851 FlKeyEmbedderResponder* responder,
852 guint state,
853 double event_time) {
854 const double timestamp = event_time * kMicrosecondsPerMillisecond;
855
856 SyncStateLoopContext sync_state_context;
857 sync_state_context.self = responder;
858 sync_state_context.state = state;
859 sync_state_context.timestamp = timestamp;
860
861 // Update pressing states.
862 g_hash_table_foreach(responder->modifier_bit_to_checked_keys,
863 synchronize_pressed_states_loop_body,
864 &sync_state_context);
865 }
866
867 GHashTable* fl_key_embedder_responder_get_pressed_state(
868 FlKeyEmbedderResponder* self) {
869 return self->pressing_records;
870 }
fl_key_embedder_user_data_init
static void fl_key_embedder_user_data_init(FlKeyEmbedderUserData *self)
Definition: fl_key_embedder_responder.cc:103
gtk_keyval_to_logical_key_map
std::map< uint64_t, uint64_t > gtk_keyval_to_logical_key_map
Definition: key_mapping.g.cc:240
_FlKeyEmbedderUserData::callback
FlKeyEmbedderResponderAsyncCallback callback
Definition: fl_key_embedder_responder.cc:90
_FlKeyEmbedderResponder::pressing_records
GHashTable * pressing_records
Definition: fl_key_embedder_responder.cc:146
apply_id_plane
static uint64_t apply_id_plane(uint64_t logical_id, uint64_t plane)
Definition: fl_key_embedder_responder.cc:268
find_stage_by_self_event
static int find_stage_by_self_event(int stage_by_record, bool is_down_event, bool is_state_on, bool reverse_state_logic)
Definition: fl_key_embedder_responder.cc:513
G_DECLARE_FINAL_TYPE
G_DECLARE_FINAL_TYPE(FlKeyEmbedderUserData, fl_key_embedder_user_data, FL, KEY_EMBEDDER_USER_DATA, GObject)
fl_key_embedder_responder_handle_event
void fl_key_embedder_responder_handle_event(FlKeyEmbedderResponder *self, FlKeyEvent *event, uint64_t specified_logical_key, FlKeyEmbedderResponderAsyncCallback callback, gpointer user_data)
Definition: fl_key_embedder_responder.cc:835
FlKeyEmbedderCheckedKey::is_caps_lock
bool is_caps_lock
Definition: fl_key_embedder_responder_private.h:42
to_lower
static uint64_t to_lower(uint64_t n)
Definition: fl_key_embedder_responder.cc:53
fl_key_embedder_responder_get_pressed_state
GHashTable * fl_key_embedder_responder_get_pressed_state(FlKeyEmbedderResponder *self)
Definition: fl_key_embedder_responder.cc:867
type
uint8_t type
Definition: fl_standard_message_codec_test.cc:1115
_FlKeyEmbedderResponder::sent_any_events
bool sent_any_events
Definition: fl_key_embedder_responder.cc:170
event_to_timestamp
static uint64_t event_to_timestamp(FlKeyEvent *event)
Definition: fl_key_embedder_responder.cc:294
fl_key_embedder_responder_init
static void fl_key_embedder_responder_init(FlKeyEmbedderResponder *self)
Definition: fl_key_embedder_responder.cc:206
_FlKeyEmbedderResponder::parent_instance
GObject parent_instance
Definition: fl_key_embedder_responder.cc:136
fl_key_embedder_responder.h
_FlKeyEmbedderResponder::send_key_event_user_data
void * send_key_event_user_data
Definition: fl_key_embedder_responder.cc:139
synthesize_simple_event
static void synthesize_simple_event(FlKeyEmbedderResponder *self, FlutterKeyEventType type, uint64_t physical, uint64_t logical, double timestamp)
Definition: fl_key_embedder_responder.cc:325
uint64_to_gpointer
gpointer uint64_to_gpointer(uint64_t number)
Definition: key_mapping.h:17
kUnicodePlane
const uint64_t kUnicodePlane
Definition: key_mapping.g.cc:515
synchronize_pressed_states_loop_body
static void synchronize_pressed_states_loop_body(gpointer key, gpointer value, gpointer user_data)
Definition: fl_key_embedder_responder.cc:416
_FlKeyEmbedderResponder
Definition: fl_key_embedder_responder.cc:135
find_stage_by_others_event
static int find_stage_by_others_event(int stage_by_record, bool is_state_on)
Definition: fl_key_embedder_responder.cc:528
state
AtkStateType state
Definition: fl_accessible_node.cc:10
update_pressing_state
static void update_pressing_state(FlKeyEmbedderResponder *self, uint64_t physical_key, uint64_t logical_key)
Definition: fl_key_embedder_responder.cc:371
_FlKeyEmbedderUserData::user_data
gpointer user_data
Definition: fl_key_embedder_responder.cc:91
find_stage_by_record
static int find_stage_by_record(bool is_down, bool is_enabled)
Definition: fl_key_embedder_responder.cc:501
user_data
G_BEGIN_DECLS G_MODULE_EXPORT FlValue gpointer user_data
Definition: fl_event_channel.h:90
_FlKeyEmbedderResponder::modifier_bit_to_checked_keys
GHashTable * modifier_bit_to_checked_keys
Definition: fl_key_embedder_responder.cc:178
fl_key_embedder_responder_new
FlKeyEmbedderResponder * fl_key_embedder_responder_new(EmbedderSendKeyEvent send_key_event, void *send_key_event_user_data)
Definition: fl_key_embedder_responder.cc:237
_FlKeyEmbedderUserData::parent_instance
GObject parent_instance
Definition: fl_key_embedder_responder.cc:88
fl_key_event_get_keyval
guint fl_key_event_get_keyval(FlKeyEvent *self)
Definition: fl_key_event.cc:94
find_physical_from_logical_loop_body
static void find_physical_from_logical_loop_body(gpointer key, gpointer value, gpointer user_data)
Definition: fl_key_embedder_responder.cc:707
initialize_modifier_bit_to_checked_keys
void initialize_modifier_bit_to_checked_keys(GHashTable *table)
Definition: key_mapping.g.cc:414
kGtkPlane
const uint64_t kGtkPlane
Definition: key_mapping.g.cc:516
_FlKeyEmbedderResponder::logical_key_to_lock_bit
GHashTable * logical_key_to_lock_bit
Definition: fl_key_embedder_responder.cc:192
hash_table_find_equal_value
static gboolean hash_table_find_equal_value(gpointer key, gpointer value, gpointer user_data)
Definition: fl_key_embedder_responder.cc:36
kValueMask
const uint64_t kValueMask
Definition: key_mapping.g.cc:514
_FlKeyEmbedderResponder::lock_records
guint lock_records
Definition: fl_key_embedder_responder.cc:151
fl_key_embedder_responder_dispose
static void fl_key_embedder_responder_dispose(GObject *object)
Definition: fl_key_embedder_responder.cc:209
key_mapping.h
G_DEFINE_TYPE
G_DEFINE_TYPE(FlBasicMessageChannelResponseHandle, fl_basic_message_channel_response_handle, G_TYPE_OBJECT) static void fl_basic_message_channel_response_handle_dispose(GObject *object)
Definition: fl_basic_message_channel.cc:37
initialize_lock_bit_to_checked_keys
void initialize_lock_bit_to_checked_keys(GHashTable *table)
Definition: key_mapping.g.cc:446
update_caps_lock_state_logic_inferrence
static void update_caps_lock_state_logic_inferrence(FlKeyEmbedderResponder *self, bool is_down_event, bool enabled_by_state, int stage_by_record)
Definition: fl_key_embedder_responder.cc:555
_FlKeyEmbedderUserData
Definition: fl_key_embedder_responder.cc:87
fl_key_embedder_responder_sync_modifiers_if_needed
void fl_key_embedder_responder_sync_modifiers_if_needed(FlKeyEmbedderResponder *responder, guint state, double event_time)
Definition: fl_key_embedder_responder.cc:850
_FlKeyEmbedderResponder::mapping_records
GHashTable * mapping_records
Definition: fl_key_embedder_responder.cc:160
FL
FL
Definition: fl_binary_messenger.cc:27
event_to_physical_key
static uint64_t event_to_physical_key(FlKeyEvent *event)
Definition: fl_key_embedder_responder.cc:272
lookup_hash_table
static uint64_t lookup_hash_table(GHashTable *table, uint64_t key)
Definition: fl_key_embedder_responder.cc:31
fl_key_event_get_time
guint32 fl_key_event_get_time(FlKeyEvent *self)
Definition: fl_key_event.cc:79
event_to_logical_key
static uint64_t event_to_logical_key(FlKeyEvent *event)
Definition: fl_key_embedder_responder.cc:280
_FlKeyEmbedderResponder::send_key_event
EmbedderSendKeyEvent send_key_event
Definition: fl_key_embedder_responder.cc:138
fl_key_embedder_responder_private.h
FlKeyEmbedderCheckedKey::primary_physical_key
uint64_t primary_physical_key
Definition: fl_key_embedder_responder_private.h:35
reverse_lookup_hash_table
static uint64_t reverse_lookup_hash_table(GHashTable *table, uint64_t value)
Definition: fl_key_embedder_responder.cc:48
fl_key_event_get_state
GdkModifierType fl_key_event_get_state(FlKeyEvent *self)
Definition: fl_key_event.cc:99
FlKeyEmbedderResponderAsyncCallback
void(* FlKeyEmbedderResponderAsyncCallback)(bool handled, gpointer user_data)
Definition: fl_key_embedder_responder.h:36
fl_key_event_get_keycode
guint16 fl_key_event_get_keycode(FlKeyEvent *self)
Definition: fl_key_event.cc:89
kMicrosecondsPerMillisecond
constexpr uint64_t kMicrosecondsPerMillisecond
Definition: fl_key_embedder_responder.cc:14
send_key_event
static void send_key_event(FlTextInputHandler *handler, gint keyval, gint state=0)
Definition: fl_text_input_handler_test.cc:144
fl_key_embedder_responder_handle_event_impl
static void fl_key_embedder_responder_handle_event_impl(FlKeyEmbedderResponder *responder, FlKeyEvent *event, uint64_t specified_logical_key, FlKeyEmbedderResponderAsyncCallback callback, gpointer user_data)
Definition: fl_key_embedder_responder.cc:749
fl_key_embedder_responder_class_init
static void fl_key_embedder_responder_class_init(FlKeyEmbedderResponderClass *klass)
Definition: fl_key_embedder_responder.cc:200
synchronize_lock_states_loop_body
static void synchronize_lock_states_loop_body(gpointer key, gpointer value, gpointer user_data)
Definition: fl_key_embedder_responder.cc:583
fl_key_embedder_user_data_class_init
static void fl_key_embedder_user_data_class_init(FlKeyEmbedderUserDataClass *klass)
Definition: fl_key_embedder_responder.cc:98
fl_key_embedder_user_data_new
static FlKeyEmbedderUserData * fl_key_embedder_user_data_new(FlKeyEmbedderResponderAsyncCallback callback, gpointer user_data)
Definition: fl_key_embedder_responder.cc:114
FlKeyEmbedderCheckedKey::secondary_logical_key
uint64_t secondary_logical_key
Definition: fl_key_embedder_responder_private.h:39
is_known_modifier_physical_key_loop_body
static void is_known_modifier_physical_key_loop_body(gpointer key, gpointer value, gpointer user_data)
Definition: fl_key_embedder_responder.cc:690
initialize_logical_key_to_lock_bit_loop_body
static void initialize_logical_key_to_lock_bit_loop_body(gpointer lock_bit, gpointer value, gpointer user_data)
Definition: fl_key_embedder_responder.cc:225
corrected_modifier_physical_key
static uint64_t corrected_modifier_physical_key(GHashTable *modifier_bit_to_checked_keys, uint64_t physical_key_from_event, uint64_t logical_key)
Definition: fl_key_embedder_responder.cc:721
xkb_to_physical_key_map
std::map< uint64_t, uint64_t > xkb_to_physical_key_map
Definition: key_mapping.g.cc:20
FlKeyEmbedderCheckedKey::primary_logical_key
uint64_t primary_logical_key
Definition: fl_key_embedder_responder_private.h:37
fl_key_event_get_is_press
gboolean fl_key_event_get_is_press(FlKeyEvent *self)
Definition: fl_key_event.cc:84
EmbedderSendKeyEvent
void(* EmbedderSendKeyEvent)(const FlutterKeyEvent *event, FlutterKeyEventCallback callback, void *callback_user_data, void *send_key_event_user_data)
Definition: fl_key_embedder_responder.h:22
handle_response
static void handle_response(bool handled, gpointer user_data)
Definition: fl_key_embedder_responder.cc:316
event_to_character
static char * event_to_character(FlKeyEvent *event)
Definition: fl_key_embedder_responder.cc:301
value
uint8_t value
Definition: fl_standard_message_codec.cc:36
possibly_update_lock_bit
static void possibly_update_lock_bit(FlKeyEmbedderResponder *self, uint64_t logical_key, bool is_down)
Definition: fl_key_embedder_responder.cc:392
FlKeyEmbedderCheckedKey
Definition: fl_key_embedder_responder_private.h:33
length
size_t length
Definition: fl_standard_message_codec_test.cc:1113
update_mapping_record
static void update_mapping_record(FlKeyEmbedderResponder *self, uint64_t physical_key, uint64_t logical_key)
Definition: fl_key_embedder_responder.cc:405
_FlKeyEmbedderResponder::caps_lock_state_logic_inferrence
StateLogicInferrence caps_lock_state_logic_inferrence
Definition: fl_key_embedder_responder.cc:166
gpointer_to_uint64
uint64_t gpointer_to_uint64(gpointer pointer)
Definition: key_mapping.h:13
_FlKeyEmbedderResponder::lock_bit_to_checked_keys
GHashTable * lock_bit_to_checked_keys
Definition: fl_key_embedder_responder.cc:186
kEmptyEvent
static const FlutterKeyEvent kEmptyEvent
Definition: fl_key_embedder_responder.cc:16
fl_key_embedder_user_data_dispose
static void fl_key_embedder_user_data_dispose(GObject *object)
Definition: fl_key_embedder_responder.cc:105