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