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