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"
13 
14 constexpr uint64_t kMicrosecondsPerMillisecond = 1000;
15 
16 static const FlutterKeyEvent kEmptyEvent{
17  .struct_size = sizeof(FlutterKeyEvent),
18  .timestamp = 0,
19  .type = kFlutterKeyEventTypeDown,
20  .physical = 0,
21  .logical = 0,
22  .character = nullptr,
23  .synthesized = false,
24 };
25 
26 // Look up a hash table that maps a uint64_t to a uint64_t.
27 //
28 // Returns 0 if not found.
29 //
30 // Both key and value should be directly hashed.
31 static uint64_t lookup_hash_table(GHashTable* table, uint64_t key) {
32  return gpointer_to_uint64(
33  g_hash_table_lookup(table, uint64_to_gpointer(key)));
34 }
35 
36 static gboolean hash_table_find_equal_value(gpointer key,
37  gpointer value,
38  gpointer user_data) {
40 }
41 
42 // Look up a hash table that maps a uint64_t to a uint64_t; given its key,
43 // find its value.
44 //
45 // Returns 0 if not found.
46 //
47 // Both key and value should be directly hashed.
48 static uint64_t reverse_lookup_hash_table(GHashTable* table, uint64_t value) {
49  return gpointer_to_uint64(g_hash_table_find(
51 }
52 
53 static uint64_t to_lower(uint64_t n) {
54  constexpr uint64_t lower_a = 0x61;
55  constexpr uint64_t upper_a = 0x41;
56  constexpr uint64_t upper_z = 0x5a;
57 
58  constexpr uint64_t lower_a_grave = 0xe0;
59  constexpr uint64_t upper_a_grave = 0xc0;
60  constexpr uint64_t upper_thorn = 0xde;
61  constexpr uint64_t division = 0xf7;
62 
63  // ASCII range.
64  if (n >= upper_a && n <= upper_z) {
65  return n - upper_a + lower_a;
66  }
67 
68  // EASCII range.
69  if (n >= upper_a_grave && n <= upper_thorn && n != division) {
70  return n - upper_a_grave + lower_a_grave;
71  }
72 
73  return n;
74 }
75 
76 /* 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()
84 G_DECLARE_FINAL_TYPE(FlKeyEmbedderUserData,
85  fl_key_embedder_user_data,
86  FL,
87  KEY_EMBEDDER_USER_DATA,
88  GObject);
89 
91  GObject parent_instance;
92 
94  gpointer user_data;
95 };
96 
97 G_DEFINE_TYPE(FlKeyEmbedderUserData, fl_key_embedder_user_data, G_TYPE_OBJECT)
98 
99 static 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 
106 static void fl_key_embedder_user_data_init(FlKeyEmbedderUserData* self) {}
107 
108 static 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.
117 static 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 
130 namespace {
131 
132 typedef 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.
171  StateLogicInferrence 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);
202 static 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.
232 static void fl_key_embedder_responder_init(FlKeyEmbedderResponder* self) {}
233 
234 // Disposes of an FlKeyEmbedderResponder instance.
235 static 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.
251 static void initialize_logical_key_to_lock_bit_loop_body(gpointer lock_bit,
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.
263 FlKeyEmbedderResponder* 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 
296 static uint64_t apply_id_plane(uint64_t logical_id, uint64_t plane) {
297  return (logical_id & kValueMask) | plane;
298 }
299 
300 static 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 
308 static 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 
322 static 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().
328 static 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.
343 static 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.
352 static void synthesize_simple_event(FlKeyEmbedderResponder* self,
353  FlutterKeyEventType type,
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 
370 namespace {
371 
372 // Context variables for the foreach call used to synchronize pressing states
373 // and lock states.
374 typedef 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.
384 typedef 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.
398 static 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.
419 static 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 
432 static 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.
443 static void synchronize_pressed_states_loop_body(gpointer key,
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);
497  synthesize_simple_event(self, kFlutterKeyEventTypeUp,
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  }
520  synthesize_simple_event(self, kFlutterKeyEventTypeDown, physical_key,
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.
528 static 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.
540 static 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.
555 static 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.
609 static void synchronize_lock_states_loop_body(gpointer key,
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) {
666  update_caps_lock_state_logic_inferrence(self, context->is_down,
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  }
703  FlutterKeyEventType type =
704  is_down_event ? kFlutterKeyEventTypeDown : kFlutterKeyEventTypeUp;
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.
733 static void find_physical_from_logical_loop_body(gpointer key,
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,
777  FlKeyEvent* event,
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.
829  out_event.type = kFlutterKeyEventTypeRepeat;
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,
864  FlKeyEvent* event,
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 }
fl_key_embedder_user_data_init
static void fl_key_embedder_user_data_init(FlKeyEmbedderUserData *self)
Definition: fl_key_embedder_responder.cc:106
gtk_keyval_to_logical_key_map
std::map< uint64_t, uint64_t > gtk_keyval_to_logical_key_map
Definition: key_mapping.g.cc:240
fl_key_embedder_user_data_new
static FlKeyEmbedderUserData * fl_key_embedder_user_data_new(FlKeyResponderAsyncCallback callback, gpointer user_data)
Definition: fl_key_embedder_responder.cc:117
_FlKeyEmbedderResponder::pressing_records
GHashTable * pressing_records
Definition: fl_key_embedder_responder.cc:151
apply_id_plane
static uint64_t apply_id_plane(uint64_t logical_id, uint64_t plane)
Definition: fl_key_embedder_responder.cc:296
find_stage_by_self_event
static int find_stage_by_self_event(int stage_by_record, bool is_down_event, bool is_state_on, bool reverse_state_logic)
Definition: fl_key_embedder_responder.cc:540
event_to_timestamp
static uint64_t event_to_timestamp(const FlKeyEvent *event)
Definition: fl_key_embedder_responder.cc:322
G_DECLARE_FINAL_TYPE
G_DECLARE_FINAL_TYPE(FlKeyEmbedderUserData, fl_key_embedder_user_data, FL, KEY_EMBEDDER_USER_DATA, GObject)
user_data
FlKeyEvent uint64_t FlKeyResponderAsyncCallback gpointer user_data
Definition: fl_key_embedder_responder.cc:218
FlKeyEmbedderCheckedKey::is_caps_lock
bool is_caps_lock
Definition: fl_key_embedder_responder_private.h:43
to_lower
static uint64_t to_lower(uint64_t n)
Definition: fl_key_embedder_responder.cc:53
fl_key_embedder_responder_get_pressed_state
GHashTable * fl_key_embedder_responder_get_pressed_state(FlKeyEmbedderResponder *self)
Definition: fl_key_embedder_responder.cc:895
type
uint8_t type
Definition: fl_standard_message_codec_test.cc:1115
_FlKeyEmbedderResponder::sent_any_events
bool sent_any_events
Definition: fl_key_embedder_responder.cc:175
FL_TYPE_EMBEDDER_RESPONDER_USER_DATA
#define FL_TYPE_EMBEDDER_RESPONDER_USER_DATA
Definition: fl_key_embedder_responder.cc:204
fl_key_embedder_responder_init
static void fl_key_embedder_responder_init(FlKeyEmbedderResponder *self)
Definition: fl_key_embedder_responder.cc:232
_FlKeyEmbedderResponder::parent_instance
GObject parent_instance
Definition: fl_key_embedder_responder.cc:141
fl_key_embedder_responder.h
_FlKeyEmbedderResponder::send_key_event_user_data
void * send_key_event_user_data
Definition: fl_key_embedder_responder.cc:144
synthesize_simple_event
static void synthesize_simple_event(FlKeyEmbedderResponder *self, FlutterKeyEventType type, uint64_t physical, uint64_t logical, double timestamp)
Definition: fl_key_embedder_responder.cc:352
uint64_to_gpointer
gpointer uint64_to_gpointer(uint64_t number)
Definition: key_mapping.h:17
kUnicodePlane
const uint64_t kUnicodePlane
Definition: key_mapping.g.cc:515
FL_TYPE_EMBEDDER_USER_DATA
#define FL_TYPE_EMBEDDER_USER_DATA
Definition: fl_key_embedder_responder.cc:83
event_to_logical_key
static uint64_t event_to_logical_key(const FlKeyEvent *event)
Definition: fl_key_embedder_responder.cc:308
synchronize_pressed_states_loop_body
static void synchronize_pressed_states_loop_body(gpointer key, gpointer value, gpointer user_data)
Definition: fl_key_embedder_responder.cc:443
event
FlKeyEvent * event
Definition: fl_key_embedder_responder.cc:215
_FlKeyEmbedderResponder
Definition: fl_key_embedder_responder.cc:140
find_stage_by_others_event
static int find_stage_by_others_event(int stage_by_record, bool is_state_on)
Definition: fl_key_embedder_responder.cc:555
_FlKeyEvent
Definition: fl_key_event.h:22
state
AtkStateType state
Definition: fl_accessible_node.cc:10
update_pressing_state
static void update_pressing_state(FlKeyEmbedderResponder *self, uint64_t physical_key, uint64_t logical_key)
Definition: fl_key_embedder_responder.cc:398
fl_key_embedder_responder_handle_event
static void fl_key_embedder_responder_handle_event(FlKeyResponder *responder, FlKeyEvent *event, uint64_t specified_logical_key, FlKeyResponderAsyncCallback callback, gpointer user_data)
Definition: fl_key_embedder_responder.cc:862
_FlKeyEmbedderUserData::user_data
gpointer user_data
Definition: fl_key_embedder_responder.cc:94
find_stage_by_record
static int find_stage_by_record(bool is_down, bool is_enabled)
Definition: fl_key_embedder_responder.cc:528
fl_key_embedder_responder_handle_event_impl
static void fl_key_embedder_responder_handle_event_impl(FlKeyResponder *responder, FlKeyEvent *event, uint64_t specified_logical_key, FlKeyResponderAsyncCallback callback, gpointer user_data)
Definition: fl_key_embedder_responder.cc:775
event_to_physical_key
static uint64_t event_to_physical_key(const FlKeyEvent *event)
Definition: fl_key_embedder_responder.cc:300
_FlKeyEmbedderResponder::modifier_bit_to_checked_keys
GHashTable * modifier_bit_to_checked_keys
Definition: fl_key_embedder_responder.cc:183
fl_key_embedder_responder_new
FlKeyEmbedderResponder * fl_key_embedder_responder_new(EmbedderSendKeyEvent send_key_event, void *send_key_event_user_data)
Definition: fl_key_embedder_responder.cc:263
_FlKeyEmbedderUserData::parent_instance
GObject parent_instance
Definition: fl_key_embedder_responder.cc:91
specified_logical_key
FlKeyEvent uint64_t specified_logical_key
Definition: fl_key_embedder_responder.cc:216
find_physical_from_logical_loop_body
static void find_physical_from_logical_loop_body(gpointer key, gpointer value, gpointer user_data)
Definition: fl_key_embedder_responder.cc:733
initialize_modifier_bit_to_checked_keys
void initialize_modifier_bit_to_checked_keys(GHashTable *table)
Definition: key_mapping.g.cc:414
kGtkPlane
const uint64_t kGtkPlane
Definition: key_mapping.g.cc:516
_FlKeyEmbedderResponder::logical_key_to_lock_bit
GHashTable * logical_key_to_lock_bit
Definition: fl_key_embedder_responder.cc:197
_FlKeyEvent::keycode
guint16 keycode
Definition: fl_key_event.h:28
FL_TYPE_KEY_RESPONDER
#define FL_TYPE_KEY_RESPONDER
Definition: fl_key_responder.h:30
callback
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
Definition: fl_key_embedder_responder.cc:217
event_to_character
static char * event_to_character(const FlKeyEvent *event)
Definition: fl_key_embedder_responder.cc:328
hash_table_find_equal_value
static gboolean hash_table_find_equal_value(gpointer key, gpointer value, gpointer user_data)
Definition: fl_key_embedder_responder.cc:36
kValueMask
const uint64_t kValueMask
Definition: key_mapping.g.cc:514
_FlKeyEmbedderResponder::lock_records
guint lock_records
Definition: fl_key_embedder_responder.cc:156
fl_key_embedder_responder_dispose
static void fl_key_embedder_responder_dispose(GObject *object)
Definition: fl_key_embedder_responder.cc:235
_FlKeyEmbedderUserData::callback
FlKeyResponderAsyncCallback callback
Definition: fl_key_embedder_responder.cc:93
FlKeyResponderAsyncCallback
void(* FlKeyResponderAsyncCallback)(bool handled, gpointer user_data)
Definition: fl_key_responder.h:28
key_mapping.h
G_DEFINE_TYPE
G_DEFINE_TYPE(FlBasicMessageChannelResponseHandle, fl_basic_message_channel_response_handle, G_TYPE_OBJECT) static void fl_basic_message_channel_response_handle_dispose(GObject *object)
Definition: fl_basic_message_channel.cc:37
initialize_lock_bit_to_checked_keys
void initialize_lock_bit_to_checked_keys(GHashTable *table)
Definition: key_mapping.g.cc:446
_FlKeyEvent::keyval
guint keyval
Definition: fl_key_event.h:30
update_caps_lock_state_logic_inferrence
static void update_caps_lock_state_logic_inferrence(FlKeyEmbedderResponder *self, bool is_down_event, bool enabled_by_state, int stage_by_record)
Definition: fl_key_embedder_responder.cc:582
G_DEFINE_TYPE_WITH_CODE
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
_FlKeyEmbedderUserData
Definition: fl_key_embedder_responder.cc:90
fl_key_embedder_responder_sync_modifiers_if_needed
void fl_key_embedder_responder_sync_modifiers_if_needed(FlKeyEmbedderResponder *responder, guint state, double event_time)
Definition: fl_key_embedder_responder.cc:878
_FlKeyEmbedderResponder::mapping_records
GHashTable * mapping_records
Definition: fl_key_embedder_responder.cc:165
FL
FL
Definition: fl_binary_messenger.cc:27
lookup_hash_table
static uint64_t lookup_hash_table(GHashTable *table, uint64_t key)
Definition: fl_key_embedder_responder.cc:31
_FlKeyEmbedderResponder::send_key_event
EmbedderSendKeyEvent send_key_event
Definition: fl_key_embedder_responder.cc:143
fl_key_embedder_responder_private.h
result
GAsyncResult * result
Definition: fl_text_input_plugin.cc:106
FlKeyEmbedderCheckedKey::primary_physical_key
uint64_t primary_physical_key
Definition: fl_key_embedder_responder_private.h:36
reverse_lookup_hash_table
static uint64_t reverse_lookup_hash_table(GHashTable *table, uint64_t value)
Definition: fl_key_embedder_responder.cc:48
kMicrosecondsPerMillisecond
constexpr uint64_t kMicrosecondsPerMillisecond
Definition: fl_key_embedder_responder.cc:14
fl_key_embedder_responder_class_init
static void fl_key_embedder_responder_class_init(FlKeyEmbedderResponderClass *klass)
Definition: fl_key_embedder_responder.cc:226
synchronize_lock_states_loop_body
static void synchronize_lock_states_loop_body(gpointer key, gpointer value, gpointer user_data)
Definition: fl_key_embedder_responder.cc:609
fl_key_embedder_user_data_class_init
static void fl_key_embedder_user_data_class_init(FlKeyEmbedderUserDataClass *klass)
Definition: fl_key_embedder_responder.cc:101
FlKeyEmbedderCheckedKey::secondary_logical_key
uint64_t secondary_logical_key
Definition: fl_key_embedder_responder_private.h:40
send_key_event
static void send_key_event(FlTextInputPlugin *plugin, gint keyval, gint state=0)
Definition: fl_text_input_plugin_test.cc:181
is_known_modifier_physical_key_loop_body
static void is_known_modifier_physical_key_loop_body(gpointer key, gpointer value, gpointer user_data)
Definition: fl_key_embedder_responder.cc:716
initialize_logical_key_to_lock_bit_loop_body
static void initialize_logical_key_to_lock_bit_loop_body(gpointer lock_bit, gpointer value, gpointer user_data)
Definition: fl_key_embedder_responder.cc:251
corrected_modifier_physical_key
static uint64_t corrected_modifier_physical_key(GHashTable *modifier_bit_to_checked_keys, uint64_t physical_key_from_event, uint64_t logical_key)
Definition: fl_key_embedder_responder.cc:747
xkb_to_physical_key_map
std::map< uint64_t, uint64_t > xkb_to_physical_key_map
Definition: key_mapping.g.cc:20
FlKeyEmbedderCheckedKey::primary_logical_key
uint64_t primary_logical_key
Definition: fl_key_embedder_responder_private.h:38
EmbedderSendKeyEvent
void(* EmbedderSendKeyEvent)(const FlutterKeyEvent *event, FlutterKeyEventCallback callback, void *callback_user_data, void *send_key_event_user_data)
Definition: fl_key_embedder_responder.h:29
handle_response
static void handle_response(bool handled, gpointer user_data)
Definition: fl_key_embedder_responder.cc:343
fl_key_embedder_responder_iface_init
static void fl_key_embedder_responder_iface_init(FlKeyResponderInterface *iface)
Definition: fl_key_embedder_responder.cc:220
value
uint8_t value
Definition: fl_standard_message_codec.cc:36
possibly_update_lock_bit
static void possibly_update_lock_bit(FlKeyEmbedderResponder *self, uint64_t logical_key, bool is_down)
Definition: fl_key_embedder_responder.cc:419
FlKeyEmbedderCheckedKey
Definition: fl_key_embedder_responder_private.h:34
length
size_t length
Definition: fl_standard_message_codec_test.cc:1113
update_mapping_record
static void update_mapping_record(FlKeyEmbedderResponder *self, uint64_t physical_key, uint64_t logical_key)
Definition: fl_key_embedder_responder.cc:432
_FlKeyEmbedderResponder::caps_lock_state_logic_inferrence
StateLogicInferrence caps_lock_state_logic_inferrence
Definition: fl_key_embedder_responder.cc:171
gpointer_to_uint64
uint64_t gpointer_to_uint64(gpointer pointer)
Definition: key_mapping.h:13
_FlKeyEmbedderResponder::lock_bit_to_checked_keys
GHashTable * lock_bit_to_checked_keys
Definition: fl_key_embedder_responder.cc:191
kEmptyEvent
static const FlutterKeyEvent kEmptyEvent
Definition: fl_key_embedder_responder.cc:16
fl_key_embedder_user_data_dispose
static void fl_key_embedder_user_data_dispose(GObject *object)
Definition: fl_key_embedder_responder.cc:108