Flutter Engine
 
Loading...
Searching...
No Matches
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
6
7#include <gtk/gtk.h>
8#include <cinttypes>
9
13
14constexpr uint64_t kMicrosecondsPerMillisecond = 1000;
15
18 .timestamp = 0,
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.
31static 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
36static gboolean hash_table_find_equal_value(gpointer key,
37 gpointer value,
38 gpointer 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.
48static uint64_t reverse_lookup_hash_table(GHashTable* table, uint64_t value) {
49 return gpointer_to_uint64(g_hash_table_find(
51}
52
53static 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
76namespace {
77
78typedef enum {
79 STATE_LOGIC_INFERRENCE_UNDECIDED,
80 STATE_LOGIC_INFERRENCE_NORMAL,
81 STATE_LOGIC_INFERRENCE_REVERSED,
82} StateLogicInferrence;
83
84}
85
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.
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.
118
119 // Record if any events has been sent during a
120 // |fl_key_embedder_responder_handle_event| call.
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.
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.
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.
144
145 GCancellable* cancellable;
146};
147
148static void fl_key_embedder_responder_dispose(GObject* object);
149
150G_DEFINE_TYPE(FlKeyEmbedderResponder, fl_key_embedder_responder, G_TYPE_OBJECT)
151
152// Initializes the FlKeyEmbedderResponder class methods.
154 FlKeyEmbedderResponderClass* klass) {
155 G_OBJECT_CLASS(klass)->dispose = fl_key_embedder_responder_dispose;
156}
157
158// Initializes an FlKeyEmbedderResponder instance.
159static void fl_key_embedder_responder_init(FlKeyEmbedderResponder* self) {
160 self->cancellable = g_cancellable_new();
161}
162
163// Disposes of an FlKeyEmbedderResponder instance.
164static 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.
181FlKeyEmbedderResponder* 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,
212 GUINT_TO_POINTER(lock_bit));
213 }
214
215 return self;
216}
217
218static uint64_t apply_id_plane(uint64_t logical_id, uint64_t plane) {
219 return (logical_id & kValueMask) | plane;
220}
221
222static 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 }
228}
229
230static 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
244static uint64_t event_to_timestamp(FlKeyEvent* event) {
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().
251static 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.
265static void synthesize_simple_event(FlKeyEmbedderResponder* self,
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.
291static 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.
312static 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
325static 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.
334static 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);
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 }
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.
424static 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.
436static 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.
451static 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.
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.
504static 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 }
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
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
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.
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) {
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
737void 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;
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
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
775 FlKeyEmbedderResponder* self,
776 guint state,
777 double event_time) {
778 g_return_if_fail(FL_IS_KEY_EMBEDDER_RESPONDER(self));
780 event_time * kMicrosecondsPerMillisecond);
781}
782
784 FlKeyEmbedderResponder* self) {
785 return self->pressing_records;
786}
GLenum type
int32_t value
FlutterKeyEventType
Definition embedder.h:1346
@ kFlutterKeyEventTypeDown
Definition embedder.h:1348
@ kFlutterKeyEventTypeUp
Definition embedder.h:1347
@ kFlutterKeyEventTypeRepeat
Definition embedder.h:1349
FlutterEngine engine
Definition main.cc:84
static gboolean is_enabled(FlutterSemanticsFlags flags)
G_DEFINE_TYPE(FlBasicMessageChannelResponseHandle, fl_basic_message_channel_response_handle, G_TYPE_OBJECT) static void fl_basic_message_channel_response_handle_dispose(GObject *object)
g_autoptr(GMutexLocker) locker
return TRUE
void fl_engine_send_key_event(FlEngine *self, const FlutterKeyEvent *event, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
static const FlutterKeyEvent kEmptyEvent
static uint64_t event_to_logical_key(FlKeyEvent *event)
static void synchronize_pressed_states(FlKeyEmbedderResponder *self, guint state, double timestamp)
static gboolean hash_table_find_equal_value(gpointer key, gpointer value, gpointer user_data)
FlKeyEmbedderResponder * fl_key_embedder_responder_new(FlEngine *engine)
GHashTable * fl_key_embedder_responder_get_pressed_state(FlKeyEmbedderResponder *self)
static void fl_key_embedder_responder_dispose(GObject *object)
static void update_caps_lock_state_logic_inferrence(FlKeyEmbedderResponder *self, bool is_down_event, bool enabled_by_state, int stage_by_record)
static uint64_t apply_id_plane(uint64_t logical_id, uint64_t plane)
static uint64_t event_to_physical_key(FlKeyEvent *event)
static uint64_t corrected_modifier_physical_key(GHashTable *modifier_bit_to_checked_keys, uint64_t physical_key_from_event, uint64_t logical_key)
static void fl_key_embedder_responder_init(FlKeyEmbedderResponder *self)
static void synthesize_simple_event(FlKeyEmbedderResponder *self, FlutterKeyEventType type, uint64_t physical, uint64_t logical, double timestamp)
void fl_key_embedder_responder_sync_modifiers_if_needed(FlKeyEmbedderResponder *self, guint state, double event_time)
static void update_mapping_record(FlKeyEmbedderResponder *self, uint64_t physical_key, uint64_t logical_key)
static void fl_key_embedder_responder_handle_event_impl(FlKeyEmbedderResponder *responder, FlKeyEvent *event, uint64_t specified_logical_key, GTask *task)
static uint64_t lookup_hash_table(GHashTable *table, uint64_t key)
static void possibly_update_lock_bit(FlKeyEmbedderResponder *self, uint64_t logical_key, bool is_down)
static uint64_t event_to_timestamp(FlKeyEvent *event)
static void update_pressing_state(FlKeyEmbedderResponder *self, uint64_t physical_key, uint64_t logical_key)
void fl_key_embedder_responder_handle_event(FlKeyEmbedderResponder *self, FlKeyEvent *event, uint64_t specified_logical_key, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
static int find_stage_by_others_event(int stage_by_record, bool is_state_on)
gboolean fl_key_embedder_responder_handle_event_finish(FlKeyEmbedderResponder *self, GAsyncResult *result, gboolean *handled, GError **error)
static uint64_t to_lower(uint64_t n)
static int find_stage_by_self_event(int stage_by_record, bool is_down_event, bool is_state_on, bool reverse_state_logic)
constexpr uint64_t kMicrosecondsPerMillisecond
static char * event_to_character(FlKeyEvent *event)
static void fl_key_embedder_responder_class_init(FlKeyEmbedderResponderClass *klass)
static uint64_t reverse_lookup_hash_table(GHashTable *table, uint64_t value)
static int find_stage_by_record(bool is_down, bool is_enabled)
static void synchronize_lock_states(FlKeyEmbedderResponder *self, guint state, double timestamp, bool is_down, uint64_t event_logical_key)
guint16 fl_key_event_get_keycode(FlKeyEvent *self)
gboolean fl_key_event_get_is_press(FlKeyEvent *self)
GdkModifierType fl_key_event_get_state(FlKeyEvent *self)
guint fl_key_event_get_keyval(FlKeyEvent *self)
guint32 fl_key_event_get_time(FlKeyEvent *self)
g_hash_table_insert(self->handlers, g_strdup(channel), handler_new(handler, user_data, destroy_notify))
const uint8_t uint32_t uint32_t GError ** error
FlutterDesktopBinaryReply callback
const uint64_t kValueMask
const uint64_t kUnicodePlane
size_t length
std::map< uint64_t, uint64_t > xkb_to_physical_key_map
void initialize_modifier_bit_to_checked_keys(GHashTable *table)
const uint64_t kGtkPlane
void initialize_lock_bit_to_checked_keys(GHashTable *table)
std::map< uint64_t, uint64_t > gtk_keyval_to_logical_key_map
gpointer uint64_to_gpointer(uint64_t number)
Definition key_mapping.h:17
uint64_t gpointer_to_uint64(gpointer pointer)
Definition key_mapping.h:13
StateLogicInferrence caps_lock_state_logic_inferrence
uint64_t logical
Definition embedder.h:1406
size_t struct_size
The size of this struct. Must be sizeof(FlutterKeyEvent).
Definition embedder.h:1384
uint64_t physical
Definition embedder.h:1398
FlutterKeyEventType type
The event kind.
Definition embedder.h:1390
const char * character
Definition embedder.h:1409