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_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 217 of file fl_key_embedder_responder.cc.

217  {
218  return (logical_id & kValueMask) | plane;
219 }
const uint64_t kValueMask

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 603 of file fl_key_embedder_responder.cc.

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

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 250 of file fl_key_embedder_responder.cc.

250  {
251  gunichar unicodeChar = gdk_keyval_to_unicode(fl_key_event_get_keyval(event));
252  glong items_written;
253  gchar* result = g_ucs4_to_utf8(&unicodeChar, 1, NULL, &items_written, NULL);
254  if (items_written == 0) {
255  if (result != NULL) {
256  g_free(result);
257  }
258  return nullptr;
259  }
260  return result;
261 }
guint fl_key_event_get_keyval(FlKeyEvent *self)
Definition: fl_key_event.cc:94

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 229 of file fl_key_embedder_responder.cc.

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

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 221 of file fl_key_embedder_responder.cc.

221  {
222  auto found = xkb_to_physical_key_map.find(fl_key_event_get_keycode(event));
223  if (found != xkb_to_physical_key_map.end()) {
224  return found->second;
225  }
227 }
guint16 fl_key_event_get_keycode(FlKeyEvent *self)
Definition: fl_key_event.cc:89
std::map< uint64_t, uint64_t > xkb_to_physical_key_map

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 243 of file fl_key_embedder_responder.cc.

243  {
245  static_cast<double>(fl_key_event_get_time(event));
246 }
constexpr uint64_t kMicrosecondsPerMillisecond
guint32 fl_key_event_get_time(FlKeyEvent *self)
Definition: fl_key_event.cc:79

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 450 of file fl_key_embedder_responder.cc.

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

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 423 of file fl_key_embedder_responder.cc.

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

References is_enabled().

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 435 of file fl_key_embedder_responder.cc.

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

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 152 of file fl_key_embedder_responder.cc.

153  {
154  G_OBJECT_CLASS(klass)->dispose = fl_key_embedder_responder_dispose;
155 }
static void fl_key_embedder_responder_dispose(GObject *object)

References fl_key_embedder_responder_dispose().

◆ fl_key_embedder_responder_dispose()

static void fl_key_embedder_responder_dispose ( GObject *  object)
static

Definition at line 163 of file fl_key_embedder_responder.cc.

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

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 782 of file fl_key_embedder_responder.cc.

783  {
784  return self->pressing_records;
785 }

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 736 of file fl_key_embedder_responder.cc.

741  {
742  g_autoptr(GTask) task = g_task_new(self, cancellable, callback, user_data);
743 
744  self->sent_any_events = false;
746  specified_logical_key, task);
747  if (!self->sent_any_events) {
748  g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
749  if (engine != nullptr) {
750  fl_engine_send_key_event(engine, &kEmptyEvent, self->cancellable, nullptr,
751  nullptr);
752  }
753  }
754 }
g_autoptr(FlEngine) engine
const char FlTextDirection FlAssertiveness gpointer user_data
void fl_engine_send_key_event(FlEngine *self, const FlutterKeyEvent *event, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
Definition: fl_engine.cc:1451
static const FlutterKeyEvent kEmptyEvent
static void fl_key_embedder_responder_handle_event_impl(FlKeyEmbedderResponder *responder, FlKeyEvent *event, uint64_t specified_logical_key, GTask *task)

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

Referenced by fl_keyboard_manager_handle_event(), and TEST_F().

◆ 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. If error is not NULL, *error must be initialized (typically NULL, but an error from a previous call using GLib error handling is explicitly valid).

Completes request started with fl_key_embedder_responder_handle_event().

Returns TRUE on success.

Definition at line 756 of file fl_key_embedder_responder.cc.

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

References error, and TRUE.

Referenced by responder_handle_embedder_event_cb(), and TEST_F().

◆ 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 641 of file fl_key_embedder_responder.cc.

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

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(), g_autoptr(), lookup_hash_table(), possibly_update_lock_bit(), 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 158 of file fl_key_embedder_responder.cc.

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

◆ 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 180 of file fl_key_embedder_responder.cc.

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

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_F().

◆ 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 773 of file fl_key_embedder_responder.cc.

776  {
777  g_return_if_fail(FL_IS_KEY_EMBEDDER_RESPONDER(self));
778  synchronize_pressed_states(self, state,
779  event_time * kMicrosecondsPerMillisecond);
780 }

References kMicrosecondsPerMillisecond, 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 35 of file fl_key_embedder_responder.cc.

37  {
39 }
uint64_t gpointer_to_uint64(gpointer pointer)

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 311 of file fl_key_embedder_responder.cc.

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

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 47 of file fl_key_embedder_responder.cc.

47  {
48  return gpointer_to_uint64(g_hash_table_find(
50 }
static gboolean hash_table_find_equal_value(gpointer key, gpointer value, gpointer user_data)

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 503 of file fl_key_embedder_responder.cc.

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

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, 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 333 of file fl_key_embedder_responder.cc.

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

References length, lookup_hash_table(), FlKeyEmbedderCheckedKey::primary_logical_key, FlKeyEmbedderCheckedKey::primary_physical_key, reverse_lookup_hash_table(), FlKeyEmbedderCheckedKey::secondary_logical_key, 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 264 of file fl_key_embedder_responder.cc.

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

References fl_engine_send_key_event(), g_autoptr(), 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 52 of file fl_key_embedder_responder.cc.

52  {
53  constexpr uint64_t lower_a = 0x61;
54  constexpr uint64_t upper_a = 0x41;
55  constexpr uint64_t upper_z = 0x5a;
56 
57  constexpr uint64_t lower_a_grave = 0xe0;
58  constexpr uint64_t upper_a_grave = 0xc0;
59  constexpr uint64_t upper_thorn = 0xde;
60  constexpr uint64_t division = 0xf7;
61 
62  // ASCII range.
63  if (n >= upper_a && n <= upper_z) {
64  return n - upper_a + lower_a;
65  }
66 
67  // EASCII range.
68  if (n >= upper_a_grave && n <= upper_thorn && n != division) {
69  return n - upper_a_grave + lower_a_grave;
70  }
71 
72  return n;
73 }

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 477 of file fl_key_embedder_responder.cc.

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

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 324 of file fl_key_embedder_responder.cc.

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

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 290 of file fl_key_embedder_responder.cc.

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

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 15 of file fl_key_embedder_responder.cc.

Referenced by fl_key_embedder_responder_handle_event().

◆ kMicrosecondsPerMillisecond

constexpr uint64_t kMicrosecondsPerMillisecond = 1000
constexpr