Flutter Engine
 
Loading...
Searching...
No Matches
fl_key_embedder_responder.cc File Reference

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}
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 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}
int32_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 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}
guint fl_key_event_get_keyval(FlKeyEvent *self)

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}
static uint64_t apply_id_plane(uint64_t logical_id, uint64_t plane)
static uint64_t to_lower(uint64_t n)
const uint64_t kUnicodePlane
const uint64_t kGtkPlane
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 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}
guint16 fl_key_event_get_keycode(FlKeyEvent *self)
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 244 of file fl_key_embedder_responder.cc.

244 {
246 static_cast<double>(fl_key_event_get_time(event));
247}
constexpr uint64_t kMicrosecondsPerMillisecond
guint32 fl_key_event_get_time(FlKeyEvent *self)

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

References self.

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}

References self.

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}
FlutterEngine engine
Definition main.cc:84
g_autoptr(GMutexLocker) locker
void fl_engine_send_key_event(FlEngine *self, const FlutterKeyEvent *event, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
static const FlutterKeyEvent kEmptyEvent
static void fl_key_embedder_responder_handle_event_impl(FlKeyEmbedderResponder *responder, FlKeyEvent *event, uint64_t specified_logical_key, GTask *task)
FlutterDesktopBinaryReply callback

References callback, engine, fl_engine_send_key_event(), fl_key_embedder_responder_handle_event_impl(), g_autoptr(), kEmptyEvent, self, and user_data.

Referenced by fl_keyboard_manager_handle_event(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), 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}
return TRUE
const uint8_t uint32_t uint32_t GError ** error

References error, self, and TRUE.

Referenced by responder_handle_embedder_event_cb(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), 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.
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}
@ kFlutterKeyEventTypeDown
Definition embedder.h:1348
@ kFlutterKeyEventTypeUp
Definition embedder.h:1347
@ kFlutterKeyEventTypeRepeat
Definition embedder.h:1349
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)
GdkModifierType fl_key_event_get_state(FlKeyEvent *self)
uint64_t logical
Definition embedder.h:1406
size_t struct_size
The size of this struct. Must be sizeof(FlutterKeyEvent).
Definition embedder.h:1384
uint64_t physical
Definition embedder.h:1398
FlutterKeyEventType type
The event kind.
Definition embedder.h:1390
const char * character
Definition embedder.h:1409

References FlutterKeyEvent::character, corrected_modifier_physical_key(), engine, 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(), kFlutterKeyEventTypeDown, kFlutterKeyEventTypeRepeat, kFlutterKeyEventTypeUp, FlutterKeyEvent::logical, lookup_hash_table(), FlutterKeyEvent::physical, possibly_update_lock_bit(), self, FlutterKeyEvent::struct_size, synchronize_lock_states(), synchronize_pressed_states(), FlutterKeyEvent::synthesized, FlutterKeyEvent::timestamp, TRUE, FlutterKeyEvent::type, 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}

References self.

◆ 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}
g_hash_table_insert(self->handlers, g_strdup(channel), handler_new(handler, user_data, destroy_notify))
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)
Definition key_mapping.h:17

References engine, g_hash_table_insert(), initialize_lock_bit_to_checked_keys(), initialize_modifier_bit_to_checked_keys(), key, FlKeyEmbedderCheckedKey::primary_logical_key, self, uint64_to_gpointer(), and value.

Referenced by fl_keyboard_manager_new(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), TEST(), 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, self, 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}
uint64_t gpointer_to_uint64(gpointer pointer)
Definition key_mapping.h:13

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 self, and 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}
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 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 }
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}
GLenum type
FlutterKeyEventType
Definition embedder.h:1346
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, key, kFlutterKeyEventTypeDown, kFlutterKeyEventTypeUp, lookup_hash_table(), possibly_update_lock_bit(), FlKeyEmbedderCheckedKey::primary_logical_key, FlKeyEmbedderCheckedKey::primary_physical_key, self, 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);
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 }
416 logical_key, timestamp);
417 update_pressing_state(self, physical_key, logical_key);
418 }
419 }
420}
static uint64_t reverse_lookup_hash_table(GHashTable *table, uint64_t value)
size_t length

References key, kFlutterKeyEventTypeDown, kFlutterKeyEventTypeUp, length, lookup_hash_table(), FlKeyEmbedderCheckedKey::primary_logical_key, FlKeyEmbedderCheckedKey::primary_physical_key, reverse_lookup_hash_table(), FlKeyEmbedderCheckedKey::secondary_logical_key, self, 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 FlutterKeyEvent::character, engine, fl_engine_send_key_event(), g_autoptr(), FlutterKeyEvent::logical, FlutterKeyEvent::physical, self, FlutterKeyEvent::struct_size, FlutterKeyEvent::synthesized, FlutterKeyEvent::timestamp, type, and FlutterKeyEvent::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(), and self.

Referenced by synchronize_lock_states().

◆ update_mapping_record()

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

◆ 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 g_hash_table_insert(), lookup_hash_table(), self, 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,
.physical = 0,
.logical = 0,
.character = nullptr,
.synthesized = false,
}

Definition at line 16 of file fl_key_embedder_responder.cc.

16 {
17 .struct_size = sizeof(FlutterKeyEvent),
18 .timestamp = 0,
20 .physical = 0,
21 .logical = 0,
22 .character = nullptr,
23 .synthesized = false,
24};

Referenced by fl_key_embedder_responder_handle_event().

◆ kMicrosecondsPerMillisecond

constexpr uint64_t kMicrosecondsPerMillisecond = 1000
constexpr