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