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