Flutter Linux Embedder
fl_key_embedder_responder.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
7 #include <gtk/gtk.h>
8 #include <cinttypes>
9 
10 #include "flutter/shell/platform/embedder/embedder.h"
12 
13 constexpr uint64_t kMicrosecondsPerMillisecond = 1000;
14 
15 static const FlutterKeyEvent kEmptyEvent{
16  .struct_size = sizeof(FlutterKeyEvent),
17  .timestamp = 0,
18  .type = kFlutterKeyEventTypeDown,
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.
30 static 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 
35 static 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.
47 static uint64_t reverse_lookup_hash_table(GHashTable* table, uint64_t value) {
48  return gpointer_to_uint64(g_hash_table_find(
50 }
51 
52 static 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 
75 namespace {
76 
77 typedef enum {
78  STATE_LOGIC_INFERRENCE_UNDECIDED,
79  STATE_LOGIC_INFERRENCE_NORMAL,
80  STATE_LOGIC_INFERRENCE_REVERSED,
81 } StateLogicInferrence;
82 
83 }
84 
86  GObject parent_instance;
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.
116  StateLogicInferrence 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 
147 static void fl_key_embedder_responder_dispose(GObject* object);
148 
149 G_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.
158 static void fl_key_embedder_responder_init(FlKeyEmbedderResponder* self) {
159  self->cancellable = g_cancellable_new();
160 }
161 
162 // Disposes of an FlKeyEmbedderResponder instance.
163 static 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.
180 FlKeyEmbedderResponder* 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 
217 static uint64_t apply_id_plane(uint64_t logical_id, uint64_t plane) {
218  return (logical_id & kValueMask) | plane;
219 }
220 
221 static 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 
229 static 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 
243 static 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().
250 static 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.
264 static void synthesize_simple_event(FlKeyEmbedderResponder* self,
265  FlutterKeyEventType type,
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.
290 static 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.
311 static 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 
324 static 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.
333 static 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);
391  synthesize_simple_event(self, kFlutterKeyEventTypeUp,
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  }
414  synthesize_simple_event(self, kFlutterKeyEventTypeDown, physical_key,
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.
423 static 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.
435 static 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.
450 static 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.
503 static 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  }
593  FlutterKeyEventType type =
594  is_down_event ? kFlutterKeyEventTypeDown : kFlutterKeyEventTypeUp;
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.
682  out_event.type = kFlutterKeyEventTypeRepeat;
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 
736 void 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));
778  synchronize_pressed_states(self, state,
779  event_time * kMicrosecondsPerMillisecond);
780 }
781 
783  FlKeyEmbedderResponder* self) {
784  return self->pressing_records;
785 }
g_autoptr(FlEngine) engine
const char FlTextDirection FlAssertiveness gpointer user_data
static gboolean is_enabled(FlutterSemanticsFlags flags)
glong length
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)
Definition: fl_engine.cc:1451
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)
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)
FlKeyEmbedderResponder * fl_key_embedder_responder_new(FlEngine *engine)
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)
static char * event_to_character(FlKeyEvent *event)
constexpr uint64_t kMicrosecondsPerMillisecond
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)
Definition: fl_key_event.cc:89
gboolean fl_key_event_get_is_press(FlKeyEvent *self)
Definition: fl_key_event.cc:84
GdkModifierType fl_key_event_get_state(FlKeyEvent *self)
Definition: fl_key_event.cc:99
guint fl_key_event_get_keyval(FlKeyEvent *self)
Definition: fl_key_event.cc:94
guint32 fl_key_event_get_time(FlKeyEvent *self)
Definition: fl_key_event.cc:79
std::map< uint64_t, uint64_t > xkb_to_physical_key_map
void initialize_modifier_bit_to_checked_keys(GHashTable *table)
const uint64_t kValueMask
const uint64_t kGtkPlane
void initialize_lock_bit_to_checked_keys(GHashTable *table)
const uint64_t kUnicodePlane
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)
const uint8_t uint32_t uint32_t GError ** error
uint8_t value
StateLogicInferrence caps_lock_state_logic_inferrence