Flutter Linux Embedder
fl_key_embedder_responder.cc File Reference
#include "flutter/shell/platform/linux/fl_key_embedder_responder.h"
#include <gtk/gtk.h>
#include <cinttypes>
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/linux/fl_key_embedder_responder_private.h"
#include "flutter/shell/platform/linux/key_mapping.h"

Go to the source code of this file.

Classes

struct  _FlKeyEmbedderResponder
 

Functions

static uint64_t lookup_hash_table (GHashTable *table, uint64_t key)
 
static gboolean hash_table_find_equal_value (gpointer key, gpointer value, gpointer user_data)
 
static uint64_t reverse_lookup_hash_table (GHashTable *table, uint64_t value)
 
static uint64_t to_lower (uint64_t n)
 
static void fl_key_embedder_responder_dispose (GObject *object)
 
static void fl_key_embedder_responder_class_init (FlKeyEmbedderResponderClass *klass)
 
static void fl_key_embedder_responder_init (FlKeyEmbedderResponder *self)
 
FlKeyEmbedderResponder * fl_key_embedder_responder_new (FlEngine *engine)
 
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 event_to_logical_key (FlKeyEvent *event)
 
static uint64_t event_to_timestamp (FlKeyEvent *event)
 
static char * event_to_character (FlKeyEvent *event)
 
static void synthesize_simple_event (FlKeyEmbedderResponder *self, FlutterKeyEventType type, uint64_t physical, uint64_t logical, double timestamp)
 
static void update_pressing_state (FlKeyEmbedderResponder *self, uint64_t physical_key, uint64_t logical_key)
 
static void possibly_update_lock_bit (FlKeyEmbedderResponder *self, uint64_t logical_key, bool is_down)
 
static void update_mapping_record (FlKeyEmbedderResponder *self, uint64_t physical_key, uint64_t logical_key)
 
static void synchronize_pressed_states (FlKeyEmbedderResponder *self, guint state, double timestamp)
 
static int find_stage_by_record (bool is_down, bool is_enabled)
 
static int find_stage_by_self_event (int stage_by_record, bool is_down_event, bool is_state_on, bool reverse_state_logic)
 
static int find_stage_by_others_event (int stage_by_record, bool is_state_on)
 
static void update_caps_lock_state_logic_inferrence (FlKeyEmbedderResponder *self, bool is_down_event, bool enabled_by_state, int stage_by_record)
 
static void synchronize_lock_states (FlKeyEmbedderResponder *self, guint state, double timestamp, bool is_down, uint64_t event_logical_key)
 
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_handle_event_impl (FlKeyEmbedderResponder *responder, FlKeyEvent *event, uint64_t specified_logical_key, GTask *task)
 
void fl_key_embedder_responder_handle_event (FlKeyEmbedderResponder *self, FlKeyEvent *event, uint64_t specified_logical_key, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
 
gboolean fl_key_embedder_responder_handle_event_finish (FlKeyEmbedderResponder *self, GAsyncResult *result, gboolean *handled, GError **error)
 
void fl_key_embedder_responder_sync_modifiers_if_needed (FlKeyEmbedderResponder *self, guint state, double event_time)
 
GHashTable * fl_key_embedder_responder_get_pressed_state (FlKeyEmbedderResponder *self)
 

Variables

constexpr uint64_t kMicrosecondsPerMillisecond = 1000
 
static const FlutterKeyEvent kEmptyEvent
 

Function Documentation

◆ apply_id_plane()

static uint64_t apply_id_plane ( uint64_t  logical_id,
uint64_t  plane 
)
static

Definition at line 218 of file fl_key_embedder_responder.cc.

218  {
219  return (logical_id & kValueMask) | plane;
220 }

References kValueMask.

Referenced by event_to_logical_key(), and event_to_physical_key().

◆ 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 
)
static

Definition at line 604 of file fl_key_embedder_responder.cc.

607  {
608  // If no match is found, defaults to the physical key retrieved from the
609  // event.
610  uint64_t corrected_physical_key = physical_key_from_event;
611 
612  // Check if the physical key is one of the known modifier physical key.
613  bool known_modifier_physical_key = false;
614  GHashTableIter iter;
615  g_hash_table_iter_init(&iter, modifier_bit_to_checked_keys);
616  gpointer value;
617  while (g_hash_table_iter_next(&iter, nullptr, &value)) {
618  FlKeyEmbedderCheckedKey* checked_key =
619  reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
620  if (checked_key->primary_physical_key == physical_key_from_event) {
621  known_modifier_physical_key = true;
622  }
623  }
624 
625  // If the physical key matches a known modifier key, find the modifier
626  // physical key from the logical key.
627  if (known_modifier_physical_key) {
628  g_hash_table_iter_init(&iter, modifier_bit_to_checked_keys);
629  while (g_hash_table_iter_next(&iter, nullptr, &value)) {
630  FlKeyEmbedderCheckedKey* checked_key =
631  reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
632  if (checked_key->primary_logical_key == logical_key ||
633  checked_key->secondary_logical_key == logical_key) {
634  corrected_physical_key = checked_key->primary_physical_key;
635  }
636  }
637  }
638 
639  return corrected_physical_key;
640 }

References FlKeyEmbedderCheckedKey::primary_logical_key, FlKeyEmbedderCheckedKey::primary_physical_key, FlKeyEmbedderCheckedKey::secondary_logical_key, and value.

Referenced by fl_key_embedder_responder_handle_event_impl().

◆ event_to_character()

static char* event_to_character ( FlKeyEvent *  event)
static

Definition at line 251 of file fl_key_embedder_responder.cc.

251  {
252  gunichar unicodeChar = gdk_keyval_to_unicode(fl_key_event_get_keyval(event));
253  glong items_written;
254  gchar* result = g_ucs4_to_utf8(&unicodeChar, 1, NULL, &items_written, NULL);
255  if (items_written == 0) {
256  if (result != NULL) {
257  g_free(result);
258  }
259  return nullptr;
260  }
261  return result;
262 }

References fl_key_event_get_keyval().

Referenced by fl_key_embedder_responder_handle_event_impl().

◆ event_to_logical_key()

static uint64_t event_to_logical_key ( FlKeyEvent *  event)
static

Definition at line 230 of file fl_key_embedder_responder.cc.

230  {
231  guint keyval = fl_key_event_get_keyval(event);
232  auto found = gtk_keyval_to_logical_key_map.find(keyval);
233  if (found != gtk_keyval_to_logical_key_map.end()) {
234  return found->second;
235  }
236  // EASCII range
237  if (keyval < 256) {
238  return apply_id_plane(to_lower(keyval), kUnicodePlane);
239  }
240  // Auto-generate key
241  return apply_id_plane(keyval, kGtkPlane);
242 }

References apply_id_plane(), fl_key_event_get_keyval(), gtk_keyval_to_logical_key_map, kGtkPlane, kUnicodePlane, and to_lower().

Referenced by fl_key_embedder_responder_handle_event_impl().

◆ event_to_physical_key()

static uint64_t event_to_physical_key ( FlKeyEvent *  event)
static

Definition at line 222 of file fl_key_embedder_responder.cc.

222  {
223  auto found = xkb_to_physical_key_map.find(fl_key_event_get_keycode(event));
224  if (found != xkb_to_physical_key_map.end()) {
225  return found->second;
226  }
228 }

References apply_id_plane(), fl_key_event_get_keycode(), kGtkPlane, and xkb_to_physical_key_map.

Referenced by fl_key_embedder_responder_handle_event_impl().

◆ event_to_timestamp()

static uint64_t event_to_timestamp ( FlKeyEvent *  event)
static

Definition at line 244 of file fl_key_embedder_responder.cc.

244  {
246  static_cast<double>(fl_key_event_get_time(event));
247 }

References fl_key_event_get_time(), and kMicrosecondsPerMillisecond.

Referenced by fl_key_embedder_responder_handle_event_impl().

◆ find_stage_by_others_event()

static int find_stage_by_others_event ( int  stage_by_record,
bool  is_state_on 
)
static

Definition at line 451 of file fl_key_embedder_responder.cc.

451  {
452  g_return_val_if_fail(stage_by_record >= 0 && stage_by_record < 4,
453  stage_by_record);
454  if (!is_state_on) {
455  return 0;
456  }
457  if (stage_by_record == 0) {
458  return 1;
459  }
460  return stage_by_record;
461 }

Referenced by synchronize_lock_states().

◆ find_stage_by_record()

static int find_stage_by_record ( bool  is_down,
bool  is_enabled 
)
static

Definition at line 424 of file fl_key_embedder_responder.cc.

424  {
425  constexpr int stage_by_record_index[] = {
426  0, // is_down: 0, is_enabled: 0
427  2, // 0 1
428  3, // 1 0
429  1 // 1 1
430  };
431  return stage_by_record_index[(is_down << 1) + is_enabled];
432 }

Referenced by synchronize_lock_states().

◆ 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 
)
static

Definition at line 436 of file fl_key_embedder_responder.cc.

439  {
440  if (!is_state_on) {
441  return reverse_state_logic ? 2 : 0;
442  }
443  if (is_down_event) {
444  return reverse_state_logic ? 0 : 2;
445  }
446  return stage_by_record;
447 }

Referenced by synchronize_lock_states(), and update_caps_lock_state_logic_inferrence().

◆ fl_key_embedder_responder_class_init()

static void fl_key_embedder_responder_class_init ( FlKeyEmbedderResponderClass *  klass)
static

Definition at line 153 of file fl_key_embedder_responder.cc.

154  {
155  G_OBJECT_CLASS(klass)->dispose = fl_key_embedder_responder_dispose;
156 }

References fl_key_embedder_responder_dispose().

◆ fl_key_embedder_responder_dispose()

static void fl_key_embedder_responder_dispose ( GObject *  object)
static

Definition at line 164 of file fl_key_embedder_responder.cc.

164  {
165  FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(object);
166 
167  g_cancellable_cancel(self->cancellable);
168 
169  g_weak_ref_clear(&self->engine);
170  g_clear_pointer(&self->pressing_records, g_hash_table_unref);
171  g_clear_pointer(&self->mapping_records, g_hash_table_unref);
172  g_clear_pointer(&self->modifier_bit_to_checked_keys, g_hash_table_unref);
173  g_clear_pointer(&self->lock_bit_to_checked_keys, g_hash_table_unref);
174  g_clear_pointer(&self->logical_key_to_lock_bit, g_hash_table_unref);
175  g_clear_object(&self->cancellable);
176 
177  G_OBJECT_CLASS(fl_key_embedder_responder_parent_class)->dispose(object);
178 }

Referenced by fl_key_embedder_responder_class_init().

◆ fl_key_embedder_responder_get_pressed_state()

GHashTable* fl_key_embedder_responder_get_pressed_state ( FlKeyEmbedderResponder *  responder)

fl_key_embedder_responder_get_pressed_state: @responder: the #FlKeyEmbedderResponder self.

Returns the keyboard pressed state. The hash table contains one entry per pressed keys, mapping from the logical key to the physical key.

Definition at line 783 of file fl_key_embedder_responder.cc.

784  {
785  return self->pressing_records;
786 }

Referenced by fl_keyboard_manager_get_pressed_state().

◆ fl_key_embedder_responder_handle_event()

void fl_key_embedder_responder_handle_event ( FlKeyEmbedderResponder *  responder,
FlKeyEvent *  event,
uint64_t  specified_logical_key,
GCancellable *  cancellable,
GAsyncReadyCallback  callback,
gpointer  user_data 
)

fl_key_embedder_responder_handle_event: @responder: the #FlKeyEmbedderResponder self. @event: the event to be handled. Must not be null. The object is managed by callee and must not be assumed available after this function. @specified_logical_key: @cancellable: (allow-none): a #GCancellable or NULL. @callback: (scope async): a #GAsyncReadyCallback to call when the view is added. @user_data: (closure): user data to pass to @callback.

Let the responder handle an event, expecting the responder to report whether to handle the event.

Definition at line 737 of file fl_key_embedder_responder.cc.

742  {
743  g_autoptr(GTask) task = g_task_new(self, cancellable, callback, user_data);
744 
745  self->sent_any_events = false;
747  specified_logical_key, task);
748  if (!self->sent_any_events) {
749  g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
750  if (engine != nullptr) {
751  fl_engine_send_key_event(engine, &kEmptyEvent, self->cancellable, nullptr,
752  nullptr);
753  }
754  }
755 }

References fl_engine_send_key_event(), fl_key_embedder_responder_handle_event_impl(), kEmptyEvent, and user_data.

Referenced by fl_keyboard_manager_handle_event(), and TEST().

◆ fl_key_embedder_responder_handle_event_finish()

gboolean fl_key_embedder_responder_handle_event_finish ( FlKeyEmbedderResponder *  responder,
GAsyncResult *  result,
gboolean *  handled,
GError **  error 
)

fl_key_embedder_responder_handle_event_finish: @responder: an #FlKeyEmbedderResponder.

Returns
: a #GAsyncResult. @handled: location to write if this event was handled by the embedder. @error: (allow-none): #GError location to store the error occurring, or NULL to ignore.

Completes request started with fl_key_embedder_responder_handle_event().

Returns TRUE on success.

Definition at line 757 of file fl_key_embedder_responder.cc.

761  {
762  g_return_val_if_fail(g_task_is_valid(result, self), FALSE);
763 
764  g_autofree gboolean* return_value =
765  static_cast<gboolean*>(g_task_propagate_pointer(G_TASK(result), error));
766  if (return_value == nullptr) {
767  return FALSE;
768  }
769 
770  *handled = *return_value;
771  return TRUE;
772 }

References error, and TRUE.

Referenced by responder_handle_embedder_event_cb(), and TEST().

◆ 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,
GTask *  task 
)
static

Definition at line 642 of file fl_key_embedder_responder.cc.

646  {
647  FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(responder);
648 
649  const uint64_t logical_key = specified_logical_key != 0
650  ? specified_logical_key
651  : event_to_logical_key(event);
652  const uint64_t physical_key_from_event = event_to_physical_key(event);
653  const uint64_t physical_key = corrected_modifier_physical_key(
654  self->modifier_bit_to_checked_keys, physical_key_from_event, logical_key);
655  guint state = fl_key_event_get_state(event);
656  const double timestamp = event_to_timestamp(event);
657  const bool is_down_event = fl_key_event_get_is_press(event);
658 
659  // Update lock mode states
660  synchronize_lock_states(self, state, timestamp, is_down_event, logical_key);
661 
662  // Update pressing states
663  synchronize_pressed_states(self, state, timestamp);
664 
665  // Construct the real event
666  const uint64_t last_logical_record =
667  lookup_hash_table(self->pressing_records, physical_key);
668 
669  FlutterKeyEvent out_event;
670  out_event.struct_size = sizeof(out_event);
671  out_event.timestamp = timestamp;
672  out_event.physical = physical_key;
673  out_event.logical =
674  last_logical_record != 0 ? last_logical_record : logical_key;
675  out_event.character = nullptr;
676  out_event.synthesized = false;
677 
678  g_autofree char* character_to_free = nullptr;
679  if (is_down_event) {
680  if (last_logical_record) {
681  // A key has been pressed that has the exact physical key as a currently
682  // pressed one. This can happen during repeated events.
683  out_event.type = kFlutterKeyEventTypeRepeat;
684  } else {
685  out_event.type = kFlutterKeyEventTypeDown;
686  }
687  character_to_free = event_to_character(event); // Might be null
688  out_event.character = character_to_free;
689  } else { // is_down_event false
690  if (!last_logical_record) {
691  // The physical key has been released before. It might indicate a missed
692  // event due to loss of focus, or multiple keyboards pressed keys with the
693  // same physical key. Ignore the up event.
694  gboolean* return_value = g_new0(gboolean, 1);
695  *return_value = TRUE;
696  g_task_return_pointer(task, return_value, g_free);
697  return;
698  } else {
699  out_event.type = kFlutterKeyEventTypeUp;
700  }
701  }
702 
703  if (out_event.type != kFlutterKeyEventTypeRepeat) {
704  update_pressing_state(self, physical_key, is_down_event ? logical_key : 0);
705  }
706  possibly_update_lock_bit(self, logical_key, is_down_event);
707  if (is_down_event) {
708  update_mapping_record(self, physical_key, logical_key);
709  }
710  self->sent_any_events = true;
711  g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
712  if (engine != nullptr) {
714  engine, &out_event, self->cancellable,
715  [](GObject* object, GAsyncResult* result, gpointer user_data) {
716  g_autoptr(GTask) task = G_TASK(user_data);
717 
718  gboolean handled;
719  g_autoptr(GError) error = nullptr;
720  if (!fl_engine_send_key_event_finish(FL_ENGINE(object), result,
721  &handled, &error)) {
722  if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
723  return;
724  }
725  g_warning("Failed to handle key event: %s", error->message);
726  handled = FALSE;
727  }
728 
729  gboolean* return_value = g_new0(gboolean, 1);
730  *return_value = handled;
731  g_task_return_pointer(task, return_value, g_free);
732  },
733  g_object_ref(task));
734  }
735 }

References corrected_modifier_physical_key(), event_to_character(), event_to_logical_key(), event_to_physical_key(), event_to_timestamp(), fl_engine_send_key_event(), fl_key_event_get_is_press(), fl_key_event_get_state(), lookup_hash_table(), possibly_update_lock_bit(), state, synchronize_lock_states(), synchronize_pressed_states(), TRUE, update_mapping_record(), update_pressing_state(), and user_data.

Referenced by fl_key_embedder_responder_handle_event().

◆ fl_key_embedder_responder_init()

static void fl_key_embedder_responder_init ( FlKeyEmbedderResponder *  self)
static

Definition at line 159 of file fl_key_embedder_responder.cc.

159  {
160  self->cancellable = g_cancellable_new();
161 }

◆ fl_key_embedder_responder_new()

FlKeyEmbedderResponder* fl_key_embedder_responder_new ( FlEngine *  engine)

FlKeyEmbedderResponder:

A #FlKeyResponder that handles events by sending the converted events through the embedder API.

This class communicates with the HardwareKeyboard API in the framework. fl_key_embedder_responder_new: @engine: The #FlEngine, whose the embedder API will be used to send the event.

Creates a new #FlKeyEmbedderResponder.

Returns: a new #FlKeyEmbedderResponder.

Definition at line 181 of file fl_key_embedder_responder.cc.

181  {
182  FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(
183  g_object_new(fl_key_embedder_responder_get_type(), nullptr));
184 
185  g_weak_ref_init(&self->engine, engine);
186 
187  self->pressing_records = g_hash_table_new(g_direct_hash, g_direct_equal);
188  self->mapping_records = g_hash_table_new(g_direct_hash, g_direct_equal);
189  self->lock_records = 0;
190  self->caps_lock_state_logic_inferrence = STATE_LOGIC_INFERRENCE_UNDECIDED;
191 
192  self->modifier_bit_to_checked_keys =
193  g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
194  initialize_modifier_bit_to_checked_keys(self->modifier_bit_to_checked_keys);
195 
196  self->lock_bit_to_checked_keys =
197  g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
198  initialize_lock_bit_to_checked_keys(self->lock_bit_to_checked_keys);
199 
200  // Associate a logical key with its corresponding modifier bit.
201  self->logical_key_to_lock_bit =
202  g_hash_table_new(g_direct_hash, g_direct_equal);
203  GHashTableIter iter;
204  g_hash_table_iter_init(&iter, self->lock_bit_to_checked_keys);
205  gpointer key, value;
206  while (g_hash_table_iter_next(&iter, &key, &value)) {
207  guint lock_bit = GPOINTER_TO_UINT(key);
208  FlKeyEmbedderCheckedKey* checked_key =
209  reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
210  g_hash_table_insert(self->logical_key_to_lock_bit,
212  GUINT_TO_POINTER(lock_bit));
213  }
214 
215  return self;
216 }

References initialize_lock_bit_to_checked_keys(), initialize_modifier_bit_to_checked_keys(), FlKeyEmbedderCheckedKey::primary_logical_key, uint64_to_gpointer(), and value.

Referenced by fl_keyboard_manager_new(), and TEST().

◆ fl_key_embedder_responder_sync_modifiers_if_needed()

void fl_key_embedder_responder_sync_modifiers_if_needed ( FlKeyEmbedderResponder *  responder,
guint  state,
double  event_time 
)

fl_key_embedder_responder_sync_modifiers_if_needed: @responder: the #FlKeyEmbedderResponder self. @state: the state of the modifiers mask. @event_time: the time attribute of the incoming GDK event.

If needed, synthesize modifier keys up and down event by comparing their current pressing states with the given modifiers mask.

Definition at line 774 of file fl_key_embedder_responder.cc.

777  {
778  g_return_if_fail(FL_IS_KEY_EMBEDDER_RESPONDER(self));
780  event_time * kMicrosecondsPerMillisecond);
781 }

References kMicrosecondsPerMillisecond, state, and synchronize_pressed_states().

Referenced by fl_keyboard_manager_sync_modifier_if_needed().

◆ hash_table_find_equal_value()

static gboolean hash_table_find_equal_value ( gpointer  key,
gpointer  value,
gpointer  user_data 
)
static

Definition at line 36 of file fl_key_embedder_responder.cc.

38  {
40 }

References gpointer_to_uint64(), user_data, and value.

Referenced by reverse_lookup_hash_table().

◆ lookup_hash_table()

static uint64_t lookup_hash_table ( GHashTable *  table,
uint64_t  key 
)
static

◆ possibly_update_lock_bit()

static void possibly_update_lock_bit ( FlKeyEmbedderResponder *  self,
uint64_t  logical_key,
bool  is_down 
)
static

Definition at line 312 of file fl_key_embedder_responder.cc.

314  {
315  if (!is_down) {
316  return;
317  }
318  const guint mode_bit = GPOINTER_TO_UINT(g_hash_table_lookup(
319  self->logical_key_to_lock_bit, uint64_to_gpointer(logical_key)));
320  if (mode_bit != 0) {
321  self->lock_records ^= mode_bit;
322  }
323 }

References uint64_to_gpointer().

Referenced by fl_key_embedder_responder_handle_event_impl(), and synchronize_lock_states().

◆ reverse_lookup_hash_table()

static uint64_t reverse_lookup_hash_table ( GHashTable *  table,
uint64_t  value 
)
static

Definition at line 48 of file fl_key_embedder_responder.cc.

48  {
49  return gpointer_to_uint64(g_hash_table_find(
51 }

References gpointer_to_uint64(), hash_table_find_equal_value(), uint64_to_gpointer(), and value.

Referenced by synchronize_pressed_states().

◆ synchronize_lock_states()

static void synchronize_lock_states ( FlKeyEmbedderResponder *  self,
guint  state,
double  timestamp,
bool  is_down,
uint64_t  event_logical_key 
)
static

Definition at line 504 of file fl_key_embedder_responder.cc.

508  {
509  GHashTableIter iter;
510  g_hash_table_iter_init(&iter, self->lock_bit_to_checked_keys);
511  gpointer key, value;
512  while (g_hash_table_iter_next(&iter, &key, &value)) {
513  guint modifier_bit = GPOINTER_TO_UINT(key);
514  FlKeyEmbedderCheckedKey* checked_key =
515  reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
516 
517  const uint64_t logical_key = checked_key->primary_logical_key;
518  const uint64_t recorded_physical_key =
519  lookup_hash_table(self->mapping_records, logical_key);
520  // The physical key is derived from past mapping record if possible.
521  //
522  // If the event to be synthesized is a key up event, then there must have
523  // been a key down event before, which has updated the mapping record.
524  // If the event to be synthesized is a key down event, then there might
525  // not have been a mapping record, in which case the hard-coded
526  // #primary_physical_key is used.
527  const uint64_t physical_key = recorded_physical_key != 0
528  ? recorded_physical_key
529  : checked_key->primary_physical_key;
530 
531  // A lock mode key can be at any of a 4-stage cycle, depending on whether
532  // it's pressed and enabled. The following table lists the definition of
533  // each stage (TruePressed and TrueEnabled), the event of the lock key
534  // between every 2 stages (SelfType and SelfState), and the event of other
535  // keys at each stage (OthersState). On certain platforms SelfState uses a
536  // reversed rule for certain keys (SelfState(rvsd), as documented in
537  // #update_caps_lock_state_logic_inferrence).
538  //
539  // # [0] [1] [2] [3]
540  // TruePressed: Released Pressed Released Pressed
541  // TrueEnabled: Disabled Enabled Enabled Disabled
542  // SelfType: Down Up Down Up
543  // SelfState: 0 1 1 1
544  // SelfState(rvsd): 1 1 0 1
545  // OthersState: 0 1 1 1
546  //
547  // When the exact stage can't be derived, choose the stage that requires the
548  // minimal synthesization.
549 
550  const uint64_t pressed_logical_key =
551  recorded_physical_key == 0
552  ? 0
553  : lookup_hash_table(self->pressing_records, recorded_physical_key);
554 
555  g_return_if_fail(pressed_logical_key == 0 ||
556  pressed_logical_key == logical_key);
557  const int stage_by_record = find_stage_by_record(
558  pressed_logical_key != 0, (self->lock_records & modifier_bit) != 0);
559 
560  const bool enabled_by_state = (state & modifier_bit) != 0;
561  const bool this_key_is_event_key = logical_key == event_logical_key;
562  if (this_key_is_event_key && checked_key->is_caps_lock) {
563  update_caps_lock_state_logic_inferrence(self, is_down, enabled_by_state,
564  stage_by_record);
565  g_return_if_fail(self->caps_lock_state_logic_inferrence !=
566  STATE_LOGIC_INFERRENCE_UNDECIDED);
567  }
568  const bool reverse_state_logic =
569  checked_key->is_caps_lock && self->caps_lock_state_logic_inferrence ==
570  STATE_LOGIC_INFERRENCE_REVERSED;
571  const int stage_by_event =
572  this_key_is_event_key
573  ? find_stage_by_self_event(stage_by_record, is_down,
574  enabled_by_state, reverse_state_logic)
575  : find_stage_by_others_event(stage_by_record, enabled_by_state);
576 
577  // The destination stage is equal to stage_by_event but shifted cyclically
578  // to be no less than stage_by_record.
579  constexpr int kNumStages = 4;
580  const int destination_stage = stage_by_event >= stage_by_record
581  ? stage_by_event
582  : stage_by_event + kNumStages;
583 
584  g_return_if_fail(stage_by_record <= destination_stage);
585  for (int current_stage = stage_by_record;
586  current_stage < destination_stage && current_stage < 9;
587  current_stage += 1) {
588  const int standard_current_stage = current_stage % kNumStages;
589  const bool is_down_event =
590  standard_current_stage == 0 || standard_current_stage == 2;
591  if (is_down_event && recorded_physical_key == 0) {
592  update_mapping_record(self, physical_key, logical_key);
593  }
594  FlutterKeyEventType type =
595  is_down_event ? kFlutterKeyEventTypeDown : kFlutterKeyEventTypeUp;
596  update_pressing_state(self, physical_key,
597  is_down_event ? logical_key : 0);
598  possibly_update_lock_bit(self, logical_key, is_down_event);
599  synthesize_simple_event(self, type, physical_key, logical_key, timestamp);
600  }
601  }
602 }

References find_stage_by_others_event(), find_stage_by_record(), find_stage_by_self_event(), FlKeyEmbedderCheckedKey::is_caps_lock, lookup_hash_table(), possibly_update_lock_bit(), FlKeyEmbedderCheckedKey::primary_logical_key, FlKeyEmbedderCheckedKey::primary_physical_key, state, synthesize_simple_event(), type, update_caps_lock_state_logic_inferrence(), update_mapping_record(), update_pressing_state(), and value.

Referenced by fl_key_embedder_responder_handle_event_impl().

◆ synchronize_pressed_states()

static void synchronize_pressed_states ( FlKeyEmbedderResponder *  self,
guint  state,
double  timestamp 
)
static

Definition at line 334 of file fl_key_embedder_responder.cc.

336  {
337  GHashTableIter iter;
338  g_hash_table_iter_init(&iter, self->modifier_bit_to_checked_keys);
339  gpointer key, value;
340  while (g_hash_table_iter_next(&iter, &key, &value)) {
341  guint modifier_bit = GPOINTER_TO_UINT(key);
342  FlKeyEmbedderCheckedKey* checked_key =
343  reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
344 
345  // Each TestKey contains up to two logical keys, typically the left modifier
346  // and the right modifier, that correspond to the same modifier_bit. We'd
347  // like to infer whether to synthesize a down or up event for each key.
348  //
349  // The hard part is that, if we want to synthesize a down event, we don't
350  // know which physical key to use. Here we assume the keyboard layout do not
351  // change frequently and use the last physical-logical relationship,
352  // recorded in #mapping_records.
353  const uint64_t logical_keys[] = {
354  checked_key->primary_logical_key,
355  checked_key->secondary_logical_key,
356  };
357  const guint length = checked_key->secondary_logical_key == 0 ? 1 : 2;
358 
359  const bool any_pressed_by_state = (state & modifier_bit) != 0;
360 
361  bool any_pressed_by_record = false;
362 
363  // Traverse each logical key of this modifier bit for 2 purposes:
364  //
365  // 1. Perform the synthesization of release events: If the modifier bit is
366  // 0
367  // and the key is pressed, synthesize a release event.
368  // 2. Prepare for the synthesization of press events: If the modifier bit
369  // is
370  // 1, and no keys are pressed (discovered here), synthesize a press
371  // event later.
372  for (guint logical_key_idx = 0; logical_key_idx < length;
373  logical_key_idx++) {
374  const uint64_t logical_key = logical_keys[logical_key_idx];
375  g_return_if_fail(logical_key != 0);
376  const uint64_t pressing_physical_key =
377  reverse_lookup_hash_table(self->pressing_records, logical_key);
378  const bool this_key_pressed_before_event = pressing_physical_key != 0;
379 
380  any_pressed_by_record =
381  any_pressed_by_record || this_key_pressed_before_event;
382 
383  if (this_key_pressed_before_event && !any_pressed_by_state) {
384  const uint64_t recorded_physical_key =
385  lookup_hash_table(self->mapping_records, logical_key);
386  // Since this key has been pressed before, there must have been a
387  // recorded physical key.
388  g_return_if_fail(recorded_physical_key != 0);
389  // In rare cases #recorded_logical_key is different from #logical_key.
390  const uint64_t recorded_logical_key =
391  lookup_hash_table(self->pressing_records, recorded_physical_key);
392  synthesize_simple_event(self, kFlutterKeyEventTypeUp,
393  recorded_physical_key, recorded_logical_key,
394  timestamp);
395  update_pressing_state(self, recorded_physical_key, 0);
396  }
397  }
398  // If the modifier should be pressed, synthesize a down event for its
399  // primary key.
400  if (any_pressed_by_state && !any_pressed_by_record) {
401  const uint64_t logical_key = checked_key->primary_logical_key;
402  const uint64_t recorded_physical_key =
403  lookup_hash_table(self->mapping_records, logical_key);
404  // The physical key is derived from past mapping record if possible.
405  //
406  // The event to be synthesized is a key down event. There might not have
407  // been a mapping record, in which case the hard-coded
408  // #primary_physical_key is used.
409  const uint64_t physical_key = recorded_physical_key != 0
410  ? recorded_physical_key
411  : checked_key->primary_physical_key;
412  if (recorded_physical_key == 0) {
413  update_mapping_record(self, physical_key, logical_key);
414  }
415  synthesize_simple_event(self, kFlutterKeyEventTypeDown, physical_key,
416  logical_key, timestamp);
417  update_pressing_state(self, physical_key, logical_key);
418  }
419  }
420 }

References length, lookup_hash_table(), FlKeyEmbedderCheckedKey::primary_logical_key, FlKeyEmbedderCheckedKey::primary_physical_key, reverse_lookup_hash_table(), FlKeyEmbedderCheckedKey::secondary_logical_key, state, synthesize_simple_event(), update_mapping_record(), update_pressing_state(), and value.

Referenced by fl_key_embedder_responder_handle_event_impl(), and fl_key_embedder_responder_sync_modifiers_if_needed().

◆ synthesize_simple_event()

static void synthesize_simple_event ( FlKeyEmbedderResponder *  self,
FlutterKeyEventType  type,
uint64_t  physical,
uint64_t  logical,
double  timestamp 
)
static

Definition at line 265 of file fl_key_embedder_responder.cc.

269  {
270  FlutterKeyEvent out_event;
271  out_event.struct_size = sizeof(out_event);
272  out_event.timestamp = timestamp;
273  out_event.type = type;
274  out_event.physical = physical;
275  out_event.logical = logical;
276  out_event.character = nullptr;
277  out_event.synthesized = true;
278  self->sent_any_events = true;
279  g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
280  if (engine != nullptr) {
281  fl_engine_send_key_event(engine, &out_event, self->cancellable, nullptr,
282  nullptr);
283  }
284 }

References fl_engine_send_key_event(), and type.

Referenced by synchronize_lock_states(), and synchronize_pressed_states().

◆ to_lower()

static uint64_t to_lower ( uint64_t  n)
static

Definition at line 53 of file fl_key_embedder_responder.cc.

53  {
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 }

Referenced by event_to_logical_key().

◆ 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 
)
static

Definition at line 478 of file fl_key_embedder_responder.cc.

482  {
483  if (self->caps_lock_state_logic_inferrence !=
484  STATE_LOGIC_INFERRENCE_UNDECIDED) {
485  return;
486  }
487  if (!is_down_event) {
488  return;
489  }
490  const int stage_by_event = find_stage_by_self_event(
491  stage_by_record, is_down_event, enabled_by_state, false);
492  if ((stage_by_event == 0 && stage_by_record == 2) ||
493  (stage_by_event == 2 && stage_by_record == 0)) {
494  self->caps_lock_state_logic_inferrence = STATE_LOGIC_INFERRENCE_REVERSED;
495  } else {
496  self->caps_lock_state_logic_inferrence = STATE_LOGIC_INFERRENCE_NORMAL;
497  }
498 }

References find_stage_by_self_event().

Referenced by synchronize_lock_states().

◆ update_mapping_record()

static void update_mapping_record ( FlKeyEmbedderResponder *  self,
uint64_t  physical_key,
uint64_t  logical_key 
)
static

Definition at line 325 of file fl_key_embedder_responder.cc.

327  {
328  g_hash_table_insert(self->mapping_records, uint64_to_gpointer(logical_key),
329  uint64_to_gpointer(physical_key));
330 }

References uint64_to_gpointer().

Referenced by fl_key_embedder_responder_handle_event_impl(), synchronize_lock_states(), and synchronize_pressed_states().

◆ update_pressing_state()

static void update_pressing_state ( FlKeyEmbedderResponder *  self,
uint64_t  physical_key,
uint64_t  logical_key 
)
static

Definition at line 291 of file fl_key_embedder_responder.cc.

293  {
294  if (logical_key != 0) {
295  g_return_if_fail(lookup_hash_table(self->pressing_records, physical_key) ==
296  0);
297  g_hash_table_insert(self->pressing_records,
298  uint64_to_gpointer(physical_key),
299  uint64_to_gpointer(logical_key));
300  } else {
301  g_return_if_fail(lookup_hash_table(self->pressing_records, physical_key) !=
302  0);
303  g_hash_table_remove(self->pressing_records,
304  uint64_to_gpointer(physical_key));
305  }
306 }

References lookup_hash_table(), and uint64_to_gpointer().

Referenced by fl_key_embedder_responder_handle_event_impl(), synchronize_lock_states(), and synchronize_pressed_states().

Variable Documentation

◆ kEmptyEvent

const FlutterKeyEvent kEmptyEvent
static
Initial value:
{
.struct_size = sizeof(FlutterKeyEvent),
.timestamp = 0,
.type = kFlutterKeyEventTypeDown,
.physical = 0,
.logical = 0,
.character = nullptr,
.synthesized = false,
}

Definition at line 16 of file fl_key_embedder_responder.cc.

Referenced by fl_key_embedder_responder_handle_event().

◆ kMicrosecondsPerMillisecond

constexpr uint64_t kMicrosecondsPerMillisecond = 1000
constexpr
gtk_keyval_to_logical_key_map
std::map< uint64_t, uint64_t > gtk_keyval_to_logical_key_map
Definition: key_mapping.g.cc:240
apply_id_plane
static uint64_t apply_id_plane(uint64_t logical_id, uint64_t plane)
Definition: fl_key_embedder_responder.cc:218
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:436
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
type
uint8_t type
Definition: fl_standard_message_codec_test.cc:1115
event_to_timestamp
static uint64_t event_to_timestamp(FlKeyEvent *event)
Definition: fl_key_embedder_responder.cc:244
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:265
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_engine_send_key_event
void fl_engine_send_key_event(FlEngine *self, const FlutterKeyEvent *event, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
Definition: fl_engine.cc:1164
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:451
state
AtkStateType state
Definition: fl_accessible_node.cc:10
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, GTask *task)
Definition: fl_key_embedder_responder.cc:642
update_pressing_state
static void update_pressing_state(FlKeyEmbedderResponder *self, uint64_t physical_key, uint64_t logical_key)
Definition: fl_key_embedder_responder.cc:291
find_stage_by_record
static int find_stage_by_record(bool is_down, bool is_enabled)
Definition: fl_key_embedder_responder.cc:424
user_data
G_BEGIN_DECLS G_MODULE_EXPORT FlValue gpointer user_data
Definition: fl_event_channel.h:90
fl_key_event_get_keyval
guint fl_key_event_get_keyval(FlKeyEvent *self)
Definition: fl_key_event.cc:94
synchronize_lock_states
static void synchronize_lock_states(FlKeyEmbedderResponder *self, guint state, double timestamp, bool is_down, uint64_t event_logical_key)
Definition: fl_key_embedder_responder.cc:504
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
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
fl_key_embedder_responder_dispose
static void fl_key_embedder_responder_dispose(GObject *object)
Definition: fl_key_embedder_responder.cc:164
synchronize_pressed_states
static void synchronize_pressed_states(FlKeyEmbedderResponder *self, guint state, double timestamp)
Definition: fl_key_embedder_responder.cc:334
TRUE
return TRUE
Definition: fl_pixel_buffer_texture_test.cc:53
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:478
event_to_physical_key
static uint64_t event_to_physical_key(FlKeyEvent *event)
Definition: fl_key_embedder_responder.cc:222
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:230
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
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
error
const uint8_t uint32_t uint32_t GError ** error
Definition: fl_pixel_buffer_texture_test.cc:40
FlKeyEmbedderCheckedKey::secondary_logical_key
uint64_t secondary_logical_key
Definition: fl_key_embedder_responder_private.h:39
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:604
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
event_to_character
static char * event_to_character(FlKeyEvent *event)
Definition: fl_key_embedder_responder.cc:251
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:312
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:325
gpointer_to_uint64
uint64_t gpointer_to_uint64(gpointer pointer)
Definition: key_mapping.h:13
kEmptyEvent
static const FlutterKeyEvent kEmptyEvent
Definition: fl_key_embedder_responder.cc:16