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