Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Classes | Macros | Functions | Variables
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  _FlKeyEmbedderUserData
 
struct  _FlKeyEmbedderResponder
 

Macros

#define FL_TYPE_EMBEDDER_USER_DATA   fl_key_embedder_user_data_get_type()
 
#define FL_TYPE_EMBEDDER_RESPONDER_USER_DATA    fl_key_embedder_responder_get_type()
 

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)
 
 G_DECLARE_FINAL_TYPE (FlKeyEmbedderUserData, fl_key_embedder_user_data, FL, KEY_EMBEDDER_USER_DATA, GObject)
 
static void fl_key_embedder_user_data_dispose (GObject *object)
 
static void fl_key_embedder_user_data_class_init (FlKeyEmbedderUserDataClass *klass)
 
static void fl_key_embedder_user_data_init (FlKeyEmbedderUserData *self)
 
static FlKeyEmbedderUserData * fl_key_embedder_user_data_new (FlKeyResponderAsyncCallback callback, gpointer user_data)
 
static void fl_key_embedder_responder_iface_init (FlKeyResponderInterface *iface)
 
static void fl_key_embedder_responder_dispose (GObject *object)
 
 G_DEFINE_TYPE_WITH_CODE (FlKeyEmbedderResponder, fl_key_embedder_responder, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(FL_TYPE_KEY_RESPONDER, fl_key_embedder_responder_iface_init)) static void fl_key_embedder_responder_handle_event(FlKeyResponder *responder
 
static void fl_key_embedder_responder_class_init (FlKeyEmbedderResponderClass *klass)
 
static void fl_key_embedder_responder_init (FlKeyEmbedderResponder *self)
 
static void initialize_logical_key_to_lock_bit_loop_body (gpointer lock_bit, gpointer value, gpointer user_data)
 
FlKeyEmbedderResponder * fl_key_embedder_responder_new (EmbedderSendKeyEvent send_key_event, void *send_key_event_user_data)
 
static uint64_t apply_id_plane (uint64_t logical_id, uint64_t plane)
 
static uint64_t event_to_physical_key (const FlKeyEvent *event)
 
static uint64_t event_to_logical_key (const FlKeyEvent *event)
 
static uint64_t event_to_timestamp (const FlKeyEvent *event)
 
static char * event_to_character (const FlKeyEvent *event)
 
static void handle_response (bool handled, gpointer user_data)
 
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_loop_body (gpointer key, gpointer value, gpointer user_data)
 
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_loop_body (gpointer key, gpointer value, gpointer user_data)
 
static void is_known_modifier_physical_key_loop_body (gpointer key, gpointer value, gpointer user_data)
 
static void find_physical_from_logical_loop_body (gpointer key, gpointer value, gpointer user_data)
 
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 (FlKeyResponder *responder, FlKeyEvent *event, uint64_t specified_logical_key, FlKeyResponderAsyncCallback callback, gpointer user_data)
 
static void fl_key_embedder_responder_handle_event (FlKeyResponder *responder, FlKeyEvent *event, uint64_t specified_logical_key, FlKeyResponderAsyncCallback callback, gpointer user_data)
 
void fl_key_embedder_responder_sync_modifiers_if_needed (FlKeyEmbedderResponder *responder, 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
 
FlKeyEventevent
 
FlKeyEvent uint64_t specified_logical_key
 
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
 
FlKeyEvent uint64_t FlKeyResponderAsyncCallback gpointer user_data
 

Macro Definition Documentation

◆ FL_TYPE_EMBEDDER_RESPONDER_USER_DATA

#define FL_TYPE_EMBEDDER_RESPONDER_USER_DATA    fl_key_embedder_responder_get_type()

Definition at line 204 of file fl_key_embedder_responder.cc.

221 {
222 iface->handle_event = fl_key_embedder_responder_handle_event;
223}
224
225// Initializes the FlKeyEmbedderResponder class methods.
227 FlKeyEmbedderResponderClass* klass) {
228 G_OBJECT_CLASS(klass)->dispose = fl_key_embedder_responder_dispose;
229}
230
231// Initializes an FlKeyEmbedderResponder instance.
232static void fl_key_embedder_responder_init(FlKeyEmbedderResponder* self) {}
233
234// Disposes of an FlKeyEmbedderResponder instance.
235static void fl_key_embedder_responder_dispose(GObject* object) {
236 FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(object);
237
238 g_clear_pointer(&self->pressing_records, g_hash_table_unref);
239 g_clear_pointer(&self->mapping_records, g_hash_table_unref);
240 g_clear_pointer(&self->modifier_bit_to_checked_keys, g_hash_table_unref);
241 g_clear_pointer(&self->lock_bit_to_checked_keys, g_hash_table_unref);
242 g_clear_pointer(&self->logical_key_to_lock_bit, g_hash_table_unref);
243
244 G_OBJECT_CLASS(fl_key_embedder_responder_parent_class)->dispose(object);
245}
246
247// Fill in #logical_key_to_lock_bit by associating a logical key with
248// its corresponding modifier bit.
249//
250// This is used as the body of a loop over #lock_bit_to_checked_keys.
251static void initialize_logical_key_to_lock_bit_loop_body(gpointer lock_bit,
252 gpointer value,
253 gpointer user_data) {
254 FlKeyEmbedderCheckedKey* checked_key =
255 reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
256 GHashTable* table = reinterpret_cast<GHashTable*>(user_data);
257 g_hash_table_insert(table,
259 GUINT_TO_POINTER(lock_bit));
260}
261
262// Creates a new FlKeyEmbedderResponder instance.
263FlKeyEmbedderResponder* fl_key_embedder_responder_new(
265 void* send_key_event_user_data) {
266 FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(
267 g_object_new(FL_TYPE_EMBEDDER_RESPONDER_USER_DATA, nullptr));
268
269 self->send_key_event = send_key_event;
270 self->send_key_event_user_data = send_key_event_user_data;
271
272 self->pressing_records = g_hash_table_new(g_direct_hash, g_direct_equal);
273 self->mapping_records = g_hash_table_new(g_direct_hash, g_direct_equal);
274 self->lock_records = 0;
275 self->caps_lock_state_logic_inferrence = kStateLogicUndecided;
276
277 self->modifier_bit_to_checked_keys =
278 g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
279 initialize_modifier_bit_to_checked_keys(self->modifier_bit_to_checked_keys);
280
281 self->lock_bit_to_checked_keys =
282 g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
283 initialize_lock_bit_to_checked_keys(self->lock_bit_to_checked_keys);
284
285 self->logical_key_to_lock_bit =
286 g_hash_table_new(g_direct_hash, g_direct_equal);
287 g_hash_table_foreach(self->lock_bit_to_checked_keys,
289 self->logical_key_to_lock_bit);
290
291 return self;
292}
293
294/* Implement FlKeyEmbedderUserData */
295
296static uint64_t apply_id_plane(uint64_t logical_id, uint64_t plane) {
297 return (logical_id & kValueMask) | plane;
298}
299
300static uint64_t event_to_physical_key(const FlKeyEvent* event) {
301 auto found = xkb_to_physical_key_map.find(event->keycode);
302 if (found != xkb_to_physical_key_map.end()) {
303 return found->second;
304 }
306}
307
308static uint64_t event_to_logical_key(const FlKeyEvent* event) {
309 guint keyval = event->keyval;
310 auto found = gtk_keyval_to_logical_key_map.find(keyval);
311 if (found != gtk_keyval_to_logical_key_map.end()) {
312 return found->second;
313 }
314 // EASCII range
315 if (keyval < 256) {
316 return apply_id_plane(to_lower(keyval), kUnicodePlane);
317 }
318 // Auto-generate key
319 return apply_id_plane(keyval, kGtkPlane);
320}
321
322static uint64_t event_to_timestamp(const FlKeyEvent* event) {
323 return kMicrosecondsPerMillisecond * static_cast<double>(event->time);
324}
325
326// Returns a newly accocated UTF-8 string from event->keyval that must be
327// freed later with g_free().
328static char* event_to_character(const FlKeyEvent* event) {
329 gunichar unicodeChar = gdk_keyval_to_unicode(event->keyval);
330 glong items_written;
331 gchar* result = g_ucs4_to_utf8(&unicodeChar, 1, NULL, &items_written, NULL);
332 if (items_written == 0) {
333 if (result != NULL) {
334 g_free(result);
335 }
336 return nullptr;
337 }
338 return result;
339}
340
341// Handles a response from the embedder API to a key event sent to the framework
342// earlier.
343static void handle_response(bool handled, gpointer user_data) {
344 g_autoptr(FlKeyEmbedderUserData) data = FL_KEY_EMBEDDER_USER_DATA(user_data);
345
346 g_return_if_fail(data->callback != nullptr);
347
348 data->callback(handled, data->user_data);
349}
350
351// Sends a synthesized event to the framework with no demand for callback.
352static void synthesize_simple_event(FlKeyEmbedderResponder* self,
354 uint64_t physical,
355 uint64_t logical,
356 double timestamp) {
357 FlutterKeyEvent out_event;
358 out_event.struct_size = sizeof(out_event);
359 out_event.timestamp = timestamp;
360 out_event.type = type;
361 out_event.physical = physical;
362 out_event.logical = logical;
363 out_event.character = nullptr;
364 out_event.synthesized = true;
365 self->sent_any_events = true;
366 self->send_key_event(&out_event, nullptr, nullptr,
367 self->send_key_event_user_data);
368}
369
370namespace {
371
372// Context variables for the foreach call used to synchronize pressing states
373// and lock states.
374typedef struct {
375 FlKeyEmbedderResponder* self;
376 guint state;
377 uint64_t event_logical_key;
378 bool is_down;
379 double timestamp;
380} SyncStateLoopContext;
381
382// Context variables for the foreach call used to find the physical key from
383// a modifier logical key.
384typedef struct {
385 bool known_modifier_physical_key;
386 uint64_t logical_key;
387 uint64_t physical_key_from_event;
388 uint64_t corrected_physical_key;
389} ModifierLogicalToPhysicalContext;
390
391} // namespace
392
393// Update the pressing record.
394//
395// If `logical_key` is 0, the record will be set as "released". Otherwise, the
396// record will be set as "pressed" with this logical key. This function asserts
397// that the key is pressed if the caller asked to release, and vice versa.
398static void update_pressing_state(FlKeyEmbedderResponder* self,
399 uint64_t physical_key,
400 uint64_t logical_key) {
401 if (logical_key != 0) {
402 g_return_if_fail(lookup_hash_table(self->pressing_records, physical_key) ==
403 0);
404 g_hash_table_insert(self->pressing_records,
405 uint64_to_gpointer(physical_key),
406 uint64_to_gpointer(logical_key));
407 } else {
408 g_return_if_fail(lookup_hash_table(self->pressing_records, physical_key) !=
409 0);
410 g_hash_table_remove(self->pressing_records,
411 uint64_to_gpointer(physical_key));
412 }
413}
414
415// Update the lock record.
416//
417// If `is_down` is false, this function is a no-op. Otherwise, this function
418// finds the lock bit corresponding to `physical_key`, and flips its bit.
419static void possibly_update_lock_bit(FlKeyEmbedderResponder* self,
420 uint64_t logical_key,
421 bool is_down) {
422 if (!is_down) {
423 return;
424 }
425 const guint mode_bit = GPOINTER_TO_UINT(g_hash_table_lookup(
426 self->logical_key_to_lock_bit, uint64_to_gpointer(logical_key)));
427 if (mode_bit != 0) {
428 self->lock_records ^= mode_bit;
429 }
430}
431
432static void update_mapping_record(FlKeyEmbedderResponder* self,
433 uint64_t physical_key,
434 uint64_t logical_key) {
435 g_hash_table_insert(self->mapping_records, uint64_to_gpointer(logical_key),
436 uint64_to_gpointer(physical_key));
437}
438
439// Synchronizes the pressing state of a key to its state from the event by
440// synthesizing events.
441//
442// This is used as the body of a loop over #modifier_bit_to_checked_keys.
443static void synchronize_pressed_states_loop_body(gpointer key,
444 gpointer value,
445 gpointer user_data) {
446 SyncStateLoopContext* context =
447 reinterpret_cast<SyncStateLoopContext*>(user_data);
448 FlKeyEmbedderCheckedKey* checked_key =
449 reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
450
451 const guint modifier_bit = GPOINTER_TO_INT(key);
452 FlKeyEmbedderResponder* self = context->self;
453 // Each TestKey contains up to two logical keys, typically the left modifier
454 // and the right modifier, that correspond to the same modifier_bit. We'd
455 // like to infer whether to synthesize a down or up event for each key.
456 //
457 // The hard part is that, if we want to synthesize a down event, we don't know
458 // which physical key to use. Here we assume the keyboard layout do not change
459 // frequently and use the last physical-logical relationship, recorded in
460 // #mapping_records.
461 const uint64_t logical_keys[] = {
462 checked_key->primary_logical_key,
463 checked_key->secondary_logical_key,
464 };
465 const guint length = checked_key->secondary_logical_key == 0 ? 1 : 2;
466
467 const bool any_pressed_by_state = (context->state & modifier_bit) != 0;
468
469 bool any_pressed_by_record = false;
470
471 // Traverse each logical key of this modifier bit for 2 purposes:
472 //
473 // 1. Perform the synthesization of release events: If the modifier bit is 0
474 // and the key is pressed, synthesize a release event.
475 // 2. Prepare for the synthesization of press events: If the modifier bit is
476 // 1, and no keys are pressed (discovered here), synthesize a press event
477 // later.
478 for (guint logical_key_idx = 0; logical_key_idx < length; logical_key_idx++) {
479 const uint64_t logical_key = logical_keys[logical_key_idx];
480 g_return_if_fail(logical_key != 0);
481 const uint64_t pressing_physical_key =
482 reverse_lookup_hash_table(self->pressing_records, logical_key);
483 const bool this_key_pressed_before_event = pressing_physical_key != 0;
484
485 any_pressed_by_record =
486 any_pressed_by_record || this_key_pressed_before_event;
487
488 if (this_key_pressed_before_event && !any_pressed_by_state) {
489 const uint64_t recorded_physical_key =
490 lookup_hash_table(self->mapping_records, logical_key);
491 // Since this key has been pressed before, there must have been a recorded
492 // physical key.
493 g_return_if_fail(recorded_physical_key != 0);
494 // In rare cases #recorded_logical_key is different from #logical_key.
495 const uint64_t recorded_logical_key =
496 lookup_hash_table(self->pressing_records, recorded_physical_key);
498 recorded_physical_key, recorded_logical_key,
499 context->timestamp);
500 update_pressing_state(self, recorded_physical_key, 0);
501 }
502 }
503 // If the modifier should be pressed, synthesize a down event for its primary
504 // key.
505 if (any_pressed_by_state && !any_pressed_by_record) {
506 const uint64_t logical_key = checked_key->primary_logical_key;
507 const uint64_t recorded_physical_key =
508 lookup_hash_table(self->mapping_records, logical_key);
509 // The physical key is derived from past mapping record if possible.
510 //
511 // The event to be synthesized is a key down event. There might not have
512 // been a mapping record, in which case the hard-coded #primary_physical_key
513 // is used.
514 const uint64_t physical_key = recorded_physical_key != 0
515 ? recorded_physical_key
516 : checked_key->primary_physical_key;
517 if (recorded_physical_key == 0) {
518 update_mapping_record(self, physical_key, logical_key);
519 }
521 logical_key, context->timestamp);
522 update_pressing_state(self, physical_key, logical_key);
523 }
524}
525
526// Find the stage # by the current record, which should be the recorded stage
527// before the event.
528static int find_stage_by_record(bool is_down, bool is_enabled) {
529 constexpr int stage_by_record_index[] = {
530 0, // is_down: 0, is_enabled: 0
531 2, // 0 1
532 3, // 1 0
533 1 // 1 1
534 };
535 return stage_by_record_index[(is_down << 1) + is_enabled];
536}
537
538// Find the stage # by an event for the target key, which should be inferred
539// stage before the event.
540static int find_stage_by_self_event(int stage_by_record,
541 bool is_down_event,
542 bool is_state_on,
543 bool reverse_state_logic) {
544 if (!is_state_on) {
545 return reverse_state_logic ? 2 : 0;
546 }
547 if (is_down_event) {
548 return reverse_state_logic ? 0 : 2;
549 }
550 return stage_by_record;
551}
552
553// Find the stage # by an event for a non-target key, which should be inferred
554// stage during the event.
555static int find_stage_by_others_event(int stage_by_record, bool is_state_on) {
556 g_return_val_if_fail(stage_by_record >= 0 && stage_by_record < 4,
557 stage_by_record);
558 if (!is_state_on) {
559 return 0;
560 }
561 if (stage_by_record == 0) {
562 return 1;
563 }
564 return stage_by_record;
565}
566
567// Infer the logic type of CapsLock on the current platform if applicable.
568//
569// In most cases, when a lock key is pressed or released, its event has the
570// key's state as 0-1-1-1 for the 4 stages (as documented in
571// #synchronize_lock_states_loop_body) respectively. But in very rare cases it
572// produces 1-1-0-1, which we call "reversed state logic". This is observed
573// when using Chrome Remote Desktop on macOS (likely a bug).
574//
575// To detect whether the current platform behaves normally or reversed, this
576// function is called on the first down event of CapsLock before calculating
577// stages. This function then store the inferred mode as
578// self->caps_lock_state_logic_inferrence.
579//
580// This does not help if the same app session is used alternatively between a
581// reversed platform and a normal platform. But this is the best we can do.
583 FlKeyEmbedderResponder* self,
584 bool is_down_event,
585 bool enabled_by_state,
586 int stage_by_record) {
587 if (self->caps_lock_state_logic_inferrence != kStateLogicUndecided) {
588 return;
589 }
590 if (!is_down_event) {
591 return;
592 }
593 const int stage_by_event = find_stage_by_self_event(
594 stage_by_record, is_down_event, enabled_by_state, false);
595 if ((stage_by_event == 0 && stage_by_record == 2) ||
596 (stage_by_event == 2 && stage_by_record == 0)) {
597 self->caps_lock_state_logic_inferrence = kStateLogicReversed;
598 } else {
599 self->caps_lock_state_logic_inferrence = kStateLogicNormal;
600 }
601}
602
603// Synchronizes the lock state of a key to its state from the event by
604// synthesizing events.
605//
606// This is used as the body of a loop over #lock_bit_to_checked_keys.
607//
608// This function might modify #caps_lock_state_logic_inferrence.
609static void synchronize_lock_states_loop_body(gpointer key,
610 gpointer value,
611 gpointer user_data) {
612 SyncStateLoopContext* context =
613 reinterpret_cast<SyncStateLoopContext*>(user_data);
614 FlKeyEmbedderCheckedKey* checked_key =
615 reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
616
617 guint modifier_bit = GPOINTER_TO_INT(key);
618 FlKeyEmbedderResponder* self = context->self;
619
620 const uint64_t logical_key = checked_key->primary_logical_key;
621 const uint64_t recorded_physical_key =
622 lookup_hash_table(self->mapping_records, logical_key);
623 // The physical key is derived from past mapping record if possible.
624 //
625 // If the event to be synthesized is a key up event, then there must have
626 // been a key down event before, which has updated the mapping record.
627 // If the event to be synthesized is a key down event, then there might
628 // not have been a mapping record, in which case the hard-coded
629 // #primary_physical_key is used.
630 const uint64_t physical_key = recorded_physical_key != 0
631 ? recorded_physical_key
632 : checked_key->primary_physical_key;
633
634 // A lock mode key can be at any of a 4-stage cycle, depending on whether it's
635 // pressed and enabled. The following table lists the definition of each
636 // stage (TruePressed and TrueEnabled), the event of the lock key between
637 // every 2 stages (SelfType and SelfState), and the event of other keys at
638 // each stage (OthersState). On certain platforms SelfState uses a reversed
639 // rule for certain keys (SelfState(rvsd), as documented in
640 // #update_caps_lock_state_logic_inferrence).
641 //
642 // # [0] [1] [2] [3]
643 // TruePressed: Released Pressed Released Pressed
644 // TrueEnabled: Disabled Enabled Enabled Disabled
645 // SelfType: Down Up Down Up
646 // SelfState: 0 1 1 1
647 // SelfState(rvsd): 1 1 0 1
648 // OthersState: 0 1 1 1
649 //
650 // When the exact stage can't be derived, choose the stage that requires the
651 // minimal synthesization.
652
653 const uint64_t pressed_logical_key =
654 recorded_physical_key == 0
655 ? 0
656 : lookup_hash_table(self->pressing_records, recorded_physical_key);
657
658 g_return_if_fail(pressed_logical_key == 0 ||
659 pressed_logical_key == logical_key);
660 const int stage_by_record = find_stage_by_record(
661 pressed_logical_key != 0, (self->lock_records & modifier_bit) != 0);
662
663 const bool enabled_by_state = (context->state & modifier_bit) != 0;
664 const bool this_key_is_event_key = logical_key == context->event_logical_key;
665 if (this_key_is_event_key && checked_key->is_caps_lock) {
666 update_caps_lock_state_logic_inferrence(self, context->is_down,
667 enabled_by_state, stage_by_record);
668 g_return_if_fail(self->caps_lock_state_logic_inferrence !=
669 kStateLogicUndecided);
670 }
671 const bool reverse_state_logic =
672 checked_key->is_caps_lock &&
673 self->caps_lock_state_logic_inferrence == kStateLogicReversed;
674 const int stage_by_event =
675 this_key_is_event_key
676 ? find_stage_by_self_event(stage_by_record, context->is_down,
677 enabled_by_state, reverse_state_logic)
678 : find_stage_by_others_event(stage_by_record, enabled_by_state);
679
680 // The destination stage is equal to stage_by_event but shifted cyclically to
681 // be no less than stage_by_record.
682 constexpr int kNumStages = 4;
683 const int destination_stage = stage_by_event >= stage_by_record
684 ? stage_by_event
685 : stage_by_event + kNumStages;
686
687 g_return_if_fail(stage_by_record <= destination_stage);
688 if (stage_by_record == destination_stage) {
689 return;
690 }
691 for (int current_stage = stage_by_record; current_stage < destination_stage;
692 current_stage += 1) {
693 if (current_stage == 9) {
694 return;
695 }
696
697 const int standard_current_stage = current_stage % kNumStages;
698 const bool is_down_event =
699 standard_current_stage == 0 || standard_current_stage == 2;
700 if (is_down_event && recorded_physical_key == 0) {
701 update_mapping_record(self, physical_key, logical_key);
702 }
705 update_pressing_state(self, physical_key, is_down_event ? logical_key : 0);
706 possibly_update_lock_bit(self, logical_key, is_down_event);
707 synthesize_simple_event(self, type, physical_key, logical_key,
708 context->timestamp);
709 }
710}
711
712// Find if a given physical key is the primary physical of one of the known
713// modifier keys.
714//
715// This is used as the body of a loop over #modifier_bit_to_checked_keys.
717 gpointer value,
718 gpointer user_data) {
719 ModifierLogicalToPhysicalContext* context =
720 reinterpret_cast<ModifierLogicalToPhysicalContext*>(user_data);
721 FlKeyEmbedderCheckedKey* checked_key =
722 reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
723
724 if (checked_key->primary_physical_key == context->physical_key_from_event) {
725 context->known_modifier_physical_key = true;
726 }
727}
728
729// Return the primary physical key of a known modifier key which matches the
730// given logical key.
731//
732// This is used as the body of a loop over #modifier_bit_to_checked_keys.
733static void find_physical_from_logical_loop_body(gpointer key,
734 gpointer value,
735 gpointer user_data) {
736 ModifierLogicalToPhysicalContext* context =
737 reinterpret_cast<ModifierLogicalToPhysicalContext*>(user_data);
738 FlKeyEmbedderCheckedKey* checked_key =
739 reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
740
741 if (checked_key->primary_logical_key == context->logical_key ||
742 checked_key->secondary_logical_key == context->logical_key) {
743 context->corrected_physical_key = checked_key->primary_physical_key;
744 }
745}
746
747static uint64_t corrected_modifier_physical_key(
748 GHashTable* modifier_bit_to_checked_keys,
749 uint64_t physical_key_from_event,
750 uint64_t logical_key) {
751 ModifierLogicalToPhysicalContext logical_to_physical_context;
752 logical_to_physical_context.known_modifier_physical_key = false;
753 logical_to_physical_context.physical_key_from_event = physical_key_from_event;
754 logical_to_physical_context.logical_key = logical_key;
755 // If no match is found, defaults to the physical key retrieved from the
756 // event.
757 logical_to_physical_context.corrected_physical_key = physical_key_from_event;
758
759 // Check if the physical key is one of the known modifier physical key.
760 g_hash_table_foreach(modifier_bit_to_checked_keys,
762 &logical_to_physical_context);
763
764 // If the physical key matches a known modifier key, find the modifier
765 // physical key from the logical key.
766 if (logical_to_physical_context.known_modifier_physical_key) {
767 g_hash_table_foreach(modifier_bit_to_checked_keys,
769 &logical_to_physical_context);
770 }
771
772 return logical_to_physical_context.corrected_physical_key;
773}
774
776 FlKeyResponder* responder,
778 uint64_t specified_logical_key,
780 gpointer user_data) {
781 FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(responder);
782
783 g_return_if_fail(event != nullptr);
784 g_return_if_fail(callback != nullptr);
785
786 const uint64_t logical_key = specified_logical_key != 0
789 const uint64_t physical_key_from_event = event_to_physical_key(event);
790 const uint64_t physical_key = corrected_modifier_physical_key(
791 self->modifier_bit_to_checked_keys, physical_key_from_event, logical_key);
792 const double timestamp = event_to_timestamp(event);
793 const bool is_down_event = event->is_press;
794
795 SyncStateLoopContext sync_state_context;
796 sync_state_context.self = self;
797 sync_state_context.state = event->state;
798 sync_state_context.timestamp = timestamp;
799 sync_state_context.is_down = is_down_event;
800 sync_state_context.event_logical_key = logical_key;
801
802 // Update lock mode states
803 g_hash_table_foreach(self->lock_bit_to_checked_keys,
804 synchronize_lock_states_loop_body, &sync_state_context);
805
806 // Update pressing states
807 g_hash_table_foreach(self->modifier_bit_to_checked_keys,
809 &sync_state_context);
810
811 // Construct the real event
812 const uint64_t last_logical_record =
813 lookup_hash_table(self->pressing_records, physical_key);
814
815 FlutterKeyEvent out_event;
816 out_event.struct_size = sizeof(out_event);
817 out_event.timestamp = timestamp;
818 out_event.physical = physical_key;
819 out_event.logical =
820 last_logical_record != 0 ? last_logical_record : logical_key;
821 out_event.character = nullptr;
822 out_event.synthesized = false;
823
824 g_autofree char* character_to_free = nullptr;
825 if (is_down_event) {
826 if (last_logical_record) {
827 // A key has been pressed that has the exact physical key as a currently
828 // pressed one. This can happen during repeated events.
830 } else {
831 out_event.type = kFlutterKeyEventTypeDown;
832 }
833 character_to_free = event_to_character(event); // Might be null
834 out_event.character = character_to_free;
835 } else { // is_down_event false
836 if (!last_logical_record) {
837 // The physical key has been released before. It might indicate a missed
838 // event due to loss of focus, or multiple keyboards pressed keys with the
839 // same physical key. Ignore the up event.
840 callback(true, user_data);
841 return;
842 } else {
843 out_event.type = kFlutterKeyEventTypeUp;
844 }
845 }
846
847 if (out_event.type != kFlutterKeyEventTypeRepeat) {
848 update_pressing_state(self, physical_key, is_down_event ? logical_key : 0);
849 }
850 possibly_update_lock_bit(self, logical_key, is_down_event);
851 if (is_down_event) {
852 update_mapping_record(self, physical_key, logical_key);
853 }
854 FlKeyEmbedderUserData* response_data =
856 self->sent_any_events = true;
857 self->send_key_event(&out_event, handle_response, response_data,
858 self->send_key_event_user_data);
859}
860
861// Sends a key event to the framework.
863 FlKeyResponder* responder,
865 uint64_t specified_logical_key,
867 gpointer user_data) {
868 FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(responder);
869 self->sent_any_events = false;
872 if (!self->sent_any_events) {
873 self->send_key_event(&kEmptyEvent, nullptr, nullptr,
874 self->send_key_event_user_data);
875 }
876}
877
879 FlKeyEmbedderResponder* responder,
880 guint state,
881 double event_time) {
882 const double timestamp = event_time * kMicrosecondsPerMillisecond;
883
884 SyncStateLoopContext sync_state_context;
885 sync_state_context.self = responder;
886 sync_state_context.state = state;
887 sync_state_context.timestamp = timestamp;
888
889 // Update pressing states.
890 g_hash_table_foreach(responder->modifier_bit_to_checked_keys,
892 &sync_state_context);
893}
894
896 FlKeyEmbedderResponder* self) {
897 return self->pressing_records;
898}
SI F table(const skcms_Curve *curve, F v)
FlutterKeyEventType
Definition embedder.h:1072
@ kFlutterKeyEventTypeDown
Definition embedder.h:1074
@ kFlutterKeyEventTypeUp
Definition embedder.h:1073
@ kFlutterKeyEventTypeRepeat
Definition embedder.h:1075
AtkStateType state
static void handle_response(bool handled, gpointer user_data)
static void find_physical_from_logical_loop_body(gpointer key, gpointer value, gpointer user_data)
static const FlutterKeyEvent kEmptyEvent
FlKeyEvent uint64_t specified_logical_key
#define FL_TYPE_EMBEDDER_RESPONDER_USER_DATA
GHashTable * fl_key_embedder_responder_get_pressed_state(FlKeyEmbedderResponder *self)
static uint64_t event_to_timestamp(const FlKeyEvent *event)
FlKeyEmbedderResponder * fl_key_embedder_responder_new(EmbedderSendKeyEvent send_key_event, void *send_key_event_user_data)
static void fl_key_embedder_responder_handle_event(FlKeyResponder *responder, FlKeyEvent *event, uint64_t specified_logical_key, FlKeyResponderAsyncCallback callback, gpointer user_data)
static void synchronize_pressed_states_loop_body(gpointer key, gpointer value, gpointer user_data)
static void fl_key_embedder_responder_dispose(GObject *object)
static void update_caps_lock_state_logic_inferrence(FlKeyEmbedderResponder *self, bool is_down_event, bool enabled_by_state, int stage_by_record)
static uint64_t apply_id_plane(uint64_t logical_id, uint64_t plane)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
static void fl_key_embedder_responder_handle_event_impl(FlKeyResponder *responder, FlKeyEvent *event, uint64_t specified_logical_key, FlKeyResponderAsyncCallback callback, gpointer user_data)
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_init(FlKeyEmbedderResponder *self)
static uint64_t event_to_logical_key(const FlKeyEvent *event)
static void synthesize_simple_event(FlKeyEmbedderResponder *self, FlutterKeyEventType type, uint64_t physical, uint64_t logical, double timestamp)
static void update_mapping_record(FlKeyEmbedderResponder *self, uint64_t physical_key, uint64_t logical_key)
static char * event_to_character(const FlKeyEvent *event)
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 void update_pressing_state(FlKeyEmbedderResponder *self, uint64_t physical_key, uint64_t logical_key)
static void synchronize_lock_states_loop_body(gpointer key, gpointer value, gpointer user_data)
void fl_key_embedder_responder_sync_modifiers_if_needed(FlKeyEmbedderResponder *responder, guint state, double event_time)
static int find_stage_by_others_event(int stage_by_record, bool is_state_on)
static uint64_t to_lower(uint64_t n)
static int find_stage_by_self_event(int stage_by_record, bool is_down_event, bool is_state_on, bool reverse_state_logic)
static FlKeyEmbedderUserData * fl_key_embedder_user_data_new(FlKeyResponderAsyncCallback callback, gpointer user_data)
constexpr uint64_t kMicrosecondsPerMillisecond
FlKeyEvent * event
static void fl_key_embedder_responder_class_init(FlKeyEmbedderResponderClass *klass)
static uint64_t reverse_lookup_hash_table(GHashTable *table, uint64_t value)
static void is_known_modifier_physical_key_loop_body(gpointer key, gpointer value, gpointer user_data)
static int find_stage_by_record(bool is_down, bool is_enabled)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback gpointer user_data
static uint64_t event_to_physical_key(const FlKeyEvent *event)
static void initialize_logical_key_to_lock_bit_loop_body(gpointer lock_bit, gpointer value, gpointer user_data)
void(* EmbedderSendKeyEvent)(const FlutterKeyEvent *event, FlutterKeyEventCallback callback, void *callback_user_data, void *send_key_event_user_data)
void(* FlKeyResponderAsyncCallback)(bool handled, gpointer user_data)
uint8_t value
GAsyncResult * result
static void send_key_event(FlTextInputPlugin *plugin, gint keyval, gint state=0)
const uint64_t kValueMask
const uint64_t kUnicodePlane
size_t length
std::map< uint64_t, uint64_t > xkb_to_physical_key_map
void initialize_modifier_bit_to_checked_keys(GHashTable *table)
const uint64_t kGtkPlane
void initialize_lock_bit_to_checked_keys(GHashTable *table)
std::map< uint64_t, uint64_t > gtk_keyval_to_logical_key_map
gpointer uint64_to_gpointer(uint64_t number)
Definition key_mapping.h:17
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
static Target * is_enabled(Benchmark *bench, const Config &config)
uint64_t logical
Definition embedder.h:1132
size_t struct_size
The size of this struct. Must be sizeof(FlutterKeyEvent).
Definition embedder.h:1110
uint64_t physical
Definition embedder.h:1124
FlutterKeyEventType type
The event kind.
Definition embedder.h:1116
const char * character
Definition embedder.h:1135
guint16 keycode

◆ FL_TYPE_EMBEDDER_USER_DATA

#define FL_TYPE_EMBEDDER_USER_DATA   fl_key_embedder_user_data_get_type()

FlKeyEmbedderUserData: The user_data used when #FlKeyEmbedderResponder sends message through the embedder.SendKeyEvent API.

Definition at line 83 of file fl_key_embedder_responder.cc.

Function Documentation

◆ apply_id_plane()

static uint64_t apply_id_plane ( uint64_t  logical_id,
uint64_t  plane 
)
static

Definition at line 296 of file fl_key_embedder_responder.cc.

296 {
297 return (logical_id & kValueMask) | plane;
298}

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

750 {
751 ModifierLogicalToPhysicalContext logical_to_physical_context;
752 logical_to_physical_context.known_modifier_physical_key = false;
753 logical_to_physical_context.physical_key_from_event = physical_key_from_event;
754 logical_to_physical_context.logical_key = logical_key;
755 // If no match is found, defaults to the physical key retrieved from the
756 // event.
757 logical_to_physical_context.corrected_physical_key = physical_key_from_event;
758
759 // Check if the physical key is one of the known modifier physical key.
760 g_hash_table_foreach(modifier_bit_to_checked_keys,
762 &logical_to_physical_context);
763
764 // If the physical key matches a known modifier key, find the modifier
765 // physical key from the logical key.
766 if (logical_to_physical_context.known_modifier_physical_key) {
767 g_hash_table_foreach(modifier_bit_to_checked_keys,
769 &logical_to_physical_context);
770 }
771
772 return logical_to_physical_context.corrected_physical_key;
773}

◆ event_to_character()

static char * event_to_character ( const FlKeyEvent event)
static

Definition at line 328 of file fl_key_embedder_responder.cc.

328 {
329 gunichar unicodeChar = gdk_keyval_to_unicode(event->keyval);
330 glong items_written;
331 gchar* result = g_ucs4_to_utf8(&unicodeChar, 1, NULL, &items_written, NULL);
332 if (items_written == 0) {
333 if (result != NULL) {
334 g_free(result);
335 }
336 return nullptr;
337 }
338 return result;
339}

◆ event_to_logical_key()

static uint64_t event_to_logical_key ( const FlKeyEvent event)
static

Definition at line 308 of file fl_key_embedder_responder.cc.

308 {
309 guint keyval = event->keyval;
310 auto found = gtk_keyval_to_logical_key_map.find(keyval);
311 if (found != gtk_keyval_to_logical_key_map.end()) {
312 return found->second;
313 }
314 // EASCII range
315 if (keyval < 256) {
316 return apply_id_plane(to_lower(keyval), kUnicodePlane);
317 }
318 // Auto-generate key
319 return apply_id_plane(keyval, kGtkPlane);
320}

◆ event_to_physical_key()

static uint64_t event_to_physical_key ( const FlKeyEvent event)
static

Definition at line 300 of file fl_key_embedder_responder.cc.

300 {
301 auto found = xkb_to_physical_key_map.find(event->keycode);
302 if (found != xkb_to_physical_key_map.end()) {
303 return found->second;
304 }
306}

◆ event_to_timestamp()

static uint64_t event_to_timestamp ( const FlKeyEvent event)
static

Definition at line 322 of file fl_key_embedder_responder.cc.

322 {
323 return kMicrosecondsPerMillisecond * static_cast<double>(event->time);
324}

◆ find_physical_from_logical_loop_body()

static void find_physical_from_logical_loop_body ( gpointer  key,
gpointer  value,
gpointer  user_data 
)
static

Definition at line 733 of file fl_key_embedder_responder.cc.

735 {
736 ModifierLogicalToPhysicalContext* context =
737 reinterpret_cast<ModifierLogicalToPhysicalContext*>(user_data);
738 FlKeyEmbedderCheckedKey* checked_key =
739 reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
740
741 if (checked_key->primary_logical_key == context->logical_key ||
742 checked_key->secondary_logical_key == context->logical_key) {
743 context->corrected_physical_key = checked_key->primary_physical_key;
744 }
745}

◆ find_stage_by_others_event()

static int find_stage_by_others_event ( int  stage_by_record,
bool  is_state_on 
)
static

Definition at line 555 of file fl_key_embedder_responder.cc.

555 {
556 g_return_val_if_fail(stage_by_record >= 0 && stage_by_record < 4,
557 stage_by_record);
558 if (!is_state_on) {
559 return 0;
560 }
561 if (stage_by_record == 0) {
562 return 1;
563 }
564 return stage_by_record;
565}

◆ find_stage_by_record()

static int find_stage_by_record ( bool  is_down,
bool  is_enabled 
)
static

Definition at line 528 of file fl_key_embedder_responder.cc.

528 {
529 constexpr int stage_by_record_index[] = {
530 0, // is_down: 0, is_enabled: 0
531 2, // 0 1
532 3, // 1 0
533 1 // 1 1
534 };
535 return stage_by_record_index[(is_down << 1) + is_enabled];
536}

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

543 {
544 if (!is_state_on) {
545 return reverse_state_logic ? 2 : 0;
546 }
547 if (is_down_event) {
548 return reverse_state_logic ? 0 : 2;
549 }
550 return stage_by_record;
551}

◆ fl_key_embedder_responder_class_init()

static void fl_key_embedder_responder_class_init ( FlKeyEmbedderResponderClass *  klass)
static

Definition at line 226 of file fl_key_embedder_responder.cc.

227 {
228 G_OBJECT_CLASS(klass)->dispose = fl_key_embedder_responder_dispose;
229}

◆ fl_key_embedder_responder_dispose()

static void fl_key_embedder_responder_dispose ( GObject *  object)
static

Definition at line 235 of file fl_key_embedder_responder.cc.

235 {
236 FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(object);
237
238 g_clear_pointer(&self->pressing_records, g_hash_table_unref);
239 g_clear_pointer(&self->mapping_records, g_hash_table_unref);
240 g_clear_pointer(&self->modifier_bit_to_checked_keys, g_hash_table_unref);
241 g_clear_pointer(&self->lock_bit_to_checked_keys, g_hash_table_unref);
242 g_clear_pointer(&self->logical_key_to_lock_bit, g_hash_table_unref);
243
244 G_OBJECT_CLASS(fl_key_embedder_responder_parent_class)->dispose(object);
245}

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

896 {
897 return self->pressing_records;
898}

◆ fl_key_embedder_responder_handle_event()

static void fl_key_embedder_responder_handle_event ( FlKeyResponder *  responder,
FlKeyEvent event,
uint64_t  specified_logical_key,
FlKeyResponderAsyncCallback  callback,
gpointer  user_data 
)
static

Definition at line 862 of file fl_key_embedder_responder.cc.

867 {
868 FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(responder);
869 self->sent_any_events = false;
872 if (!self->sent_any_events) {
873 self->send_key_event(&kEmptyEvent, nullptr, nullptr,
874 self->send_key_event_user_data);
875 }
876}

◆ fl_key_embedder_responder_handle_event_impl()

static void fl_key_embedder_responder_handle_event_impl ( FlKeyResponder *  responder,
FlKeyEvent event,
uint64_t  specified_logical_key,
FlKeyResponderAsyncCallback  callback,
gpointer  user_data 
)
static

Definition at line 775 of file fl_key_embedder_responder.cc.

780 {
781 FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(responder);
782
783 g_return_if_fail(event != nullptr);
784 g_return_if_fail(callback != nullptr);
785
786 const uint64_t logical_key = specified_logical_key != 0
789 const uint64_t physical_key_from_event = event_to_physical_key(event);
790 const uint64_t physical_key = corrected_modifier_physical_key(
791 self->modifier_bit_to_checked_keys, physical_key_from_event, logical_key);
792 const double timestamp = event_to_timestamp(event);
793 const bool is_down_event = event->is_press;
794
795 SyncStateLoopContext sync_state_context;
796 sync_state_context.self = self;
797 sync_state_context.state = event->state;
798 sync_state_context.timestamp = timestamp;
799 sync_state_context.is_down = is_down_event;
800 sync_state_context.event_logical_key = logical_key;
801
802 // Update lock mode states
803 g_hash_table_foreach(self->lock_bit_to_checked_keys,
804 synchronize_lock_states_loop_body, &sync_state_context);
805
806 // Update pressing states
807 g_hash_table_foreach(self->modifier_bit_to_checked_keys,
809 &sync_state_context);
810
811 // Construct the real event
812 const uint64_t last_logical_record =
813 lookup_hash_table(self->pressing_records, physical_key);
814
815 FlutterKeyEvent out_event;
816 out_event.struct_size = sizeof(out_event);
817 out_event.timestamp = timestamp;
818 out_event.physical = physical_key;
819 out_event.logical =
820 last_logical_record != 0 ? last_logical_record : logical_key;
821 out_event.character = nullptr;
822 out_event.synthesized = false;
823
824 g_autofree char* character_to_free = nullptr;
825 if (is_down_event) {
826 if (last_logical_record) {
827 // A key has been pressed that has the exact physical key as a currently
828 // pressed one. This can happen during repeated events.
830 } else {
831 out_event.type = kFlutterKeyEventTypeDown;
832 }
833 character_to_free = event_to_character(event); // Might be null
834 out_event.character = character_to_free;
835 } else { // is_down_event false
836 if (!last_logical_record) {
837 // The physical key has been released before. It might indicate a missed
838 // event due to loss of focus, or multiple keyboards pressed keys with the
839 // same physical key. Ignore the up event.
840 callback(true, user_data);
841 return;
842 } else {
843 out_event.type = kFlutterKeyEventTypeUp;
844 }
845 }
846
847 if (out_event.type != kFlutterKeyEventTypeRepeat) {
848 update_pressing_state(self, physical_key, is_down_event ? logical_key : 0);
849 }
850 possibly_update_lock_bit(self, logical_key, is_down_event);
851 if (is_down_event) {
852 update_mapping_record(self, physical_key, logical_key);
853 }
854 FlKeyEmbedderUserData* response_data =
856 self->sent_any_events = true;
857 self->send_key_event(&out_event, handle_response, response_data,
858 self->send_key_event_user_data);
859}

◆ fl_key_embedder_responder_iface_init()

static void fl_key_embedder_responder_iface_init ( FlKeyResponderInterface *  iface)
static

Definition at line 220 of file fl_key_embedder_responder.cc.

221 {
222 iface->handle_event = fl_key_embedder_responder_handle_event;
223}

◆ fl_key_embedder_responder_init()

static void fl_key_embedder_responder_init ( FlKeyEmbedderResponder *  self)
static

Definition at line 232 of file fl_key_embedder_responder.cc.

232{}

◆ fl_key_embedder_responder_new()

FlKeyEmbedderResponder * fl_key_embedder_responder_new ( EmbedderSendKeyEvent  send_key_event,
void *  send_key_event_user_data 
)

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. @send_key_event: a function that is called on every key event. @send_key_event_user_data: an opaque pointer that will be sent back as the last argument of send_key_event, created and managed by the object that holds FlKeyEmbedderResponder.

Returns: a new #FlKeyEmbedderResponder.

Definition at line 263 of file fl_key_embedder_responder.cc.

265 {
266 FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(
267 g_object_new(FL_TYPE_EMBEDDER_RESPONDER_USER_DATA, nullptr));
268
269 self->send_key_event = send_key_event;
270 self->send_key_event_user_data = send_key_event_user_data;
271
272 self->pressing_records = g_hash_table_new(g_direct_hash, g_direct_equal);
273 self->mapping_records = g_hash_table_new(g_direct_hash, g_direct_equal);
274 self->lock_records = 0;
275 self->caps_lock_state_logic_inferrence = kStateLogicUndecided;
276
277 self->modifier_bit_to_checked_keys =
278 g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
279 initialize_modifier_bit_to_checked_keys(self->modifier_bit_to_checked_keys);
280
281 self->lock_bit_to_checked_keys =
282 g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
283 initialize_lock_bit_to_checked_keys(self->lock_bit_to_checked_keys);
284
285 self->logical_key_to_lock_bit =
286 g_hash_table_new(g_direct_hash, g_direct_equal);
287 g_hash_table_foreach(self->lock_bit_to_checked_keys,
289 self->logical_key_to_lock_bit);
290
291 return self;
292}

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

881 {
882 const double timestamp = event_time * kMicrosecondsPerMillisecond;
883
884 SyncStateLoopContext sync_state_context;
885 sync_state_context.self = responder;
886 sync_state_context.state = state;
887 sync_state_context.timestamp = timestamp;
888
889 // Update pressing states.
890 g_hash_table_foreach(responder->modifier_bit_to_checked_keys,
892 &sync_state_context);
893}

◆ fl_key_embedder_user_data_class_init()

static void fl_key_embedder_user_data_class_init ( FlKeyEmbedderUserDataClass *  klass)
static

Definition at line 101 of file fl_key_embedder_responder.cc.

102 {
103 G_OBJECT_CLASS(klass)->dispose = fl_key_embedder_user_data_dispose;
104}
static void fl_key_embedder_user_data_dispose(GObject *object)

◆ fl_key_embedder_user_data_dispose()

static void fl_key_embedder_user_data_dispose ( GObject *  object)
static

Definition at line 108 of file fl_key_embedder_responder.cc.

108 {
109 // The following line suppresses a warning for unused function
110 // FL_IS_KEY_EMBEDDER_USER_DATA.
111 g_return_if_fail(FL_IS_KEY_EMBEDDER_USER_DATA(object));
112}

◆ fl_key_embedder_user_data_init()

static void fl_key_embedder_user_data_init ( FlKeyEmbedderUserData *  self)
static

Definition at line 106 of file fl_key_embedder_responder.cc.

106{}

◆ fl_key_embedder_user_data_new()

static FlKeyEmbedderUserData * fl_key_embedder_user_data_new ( FlKeyResponderAsyncCallback  callback,
gpointer  user_data 
)
static

Definition at line 117 of file fl_key_embedder_responder.cc.

119 {
120 FlKeyEmbedderUserData* self = FL_KEY_EMBEDDER_USER_DATA(
121 g_object_new(FL_TYPE_EMBEDDER_USER_DATA, nullptr));
122
123 self->callback = callback;
124 self->user_data = user_data;
125 return self;
126}
#define FL_TYPE_EMBEDDER_USER_DATA

◆ G_DECLARE_FINAL_TYPE()

G_DECLARE_FINAL_TYPE ( FlKeyEmbedderUserData  ,
fl_key_embedder_user_data  ,
FL  ,
KEY_EMBEDDER_USER_DATA  ,
GObject   
)

◆ G_DEFINE_TYPE_WITH_CODE()

G_DEFINE_TYPE_WITH_CODE ( FlKeyEmbedderResponder  ,
fl_key_embedder_responder  ,
G_TYPE_OBJECT  ,
G_IMPLEMENT_INTERFACE(FL_TYPE_KEY_RESPONDER, fl_key_embedder_responder_iface_init  
)

◆ handle_response()

static void handle_response ( bool  handled,
gpointer  user_data 
)
static

Definition at line 343 of file fl_key_embedder_responder.cc.

343 {
344 g_autoptr(FlKeyEmbedderUserData) data = FL_KEY_EMBEDDER_USER_DATA(user_data);
345
346 g_return_if_fail(data->callback != nullptr);
347
348 data->callback(handled, data->user_data);
349}

◆ 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

◆ initialize_logical_key_to_lock_bit_loop_body()

static void initialize_logical_key_to_lock_bit_loop_body ( gpointer  lock_bit,
gpointer  value,
gpointer  user_data 
)
static

Definition at line 251 of file fl_key_embedder_responder.cc.

253 {
254 FlKeyEmbedderCheckedKey* checked_key =
255 reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
256 GHashTable* table = reinterpret_cast<GHashTable*>(user_data);
257 g_hash_table_insert(table,
259 GUINT_TO_POINTER(lock_bit));
260}

◆ is_known_modifier_physical_key_loop_body()

static void is_known_modifier_physical_key_loop_body ( gpointer  key,
gpointer  value,
gpointer  user_data 
)
static

Definition at line 716 of file fl_key_embedder_responder.cc.

718 {
719 ModifierLogicalToPhysicalContext* context =
720 reinterpret_cast<ModifierLogicalToPhysicalContext*>(user_data);
721 FlKeyEmbedderCheckedKey* checked_key =
722 reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
723
724 if (checked_key->primary_physical_key == context->physical_key_from_event) {
725 context->known_modifier_physical_key = true;
726 }
727}

◆ lookup_hash_table()

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

Definition at line 31 of file fl_key_embedder_responder.cc.

31 {
32 return gpointer_to_uint64(
33 g_hash_table_lookup(table, uint64_to_gpointer(key)));
34}

◆ possibly_update_lock_bit()

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

Definition at line 419 of file fl_key_embedder_responder.cc.

421 {
422 if (!is_down) {
423 return;
424 }
425 const guint mode_bit = GPOINTER_TO_UINT(g_hash_table_lookup(
426 self->logical_key_to_lock_bit, uint64_to_gpointer(logical_key)));
427 if (mode_bit != 0) {
428 self->lock_records ^= mode_bit;
429 }
430}

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

◆ synchronize_lock_states_loop_body()

static void synchronize_lock_states_loop_body ( gpointer  key,
gpointer  value,
gpointer  user_data 
)
static

Definition at line 609 of file fl_key_embedder_responder.cc.

611 {
612 SyncStateLoopContext* context =
613 reinterpret_cast<SyncStateLoopContext*>(user_data);
614 FlKeyEmbedderCheckedKey* checked_key =
615 reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
616
617 guint modifier_bit = GPOINTER_TO_INT(key);
618 FlKeyEmbedderResponder* self = context->self;
619
620 const uint64_t logical_key = checked_key->primary_logical_key;
621 const uint64_t recorded_physical_key =
622 lookup_hash_table(self->mapping_records, logical_key);
623 // The physical key is derived from past mapping record if possible.
624 //
625 // If the event to be synthesized is a key up event, then there must have
626 // been a key down event before, which has updated the mapping record.
627 // If the event to be synthesized is a key down event, then there might
628 // not have been a mapping record, in which case the hard-coded
629 // #primary_physical_key is used.
630 const uint64_t physical_key = recorded_physical_key != 0
631 ? recorded_physical_key
632 : checked_key->primary_physical_key;
633
634 // A lock mode key can be at any of a 4-stage cycle, depending on whether it's
635 // pressed and enabled. The following table lists the definition of each
636 // stage (TruePressed and TrueEnabled), the event of the lock key between
637 // every 2 stages (SelfType and SelfState), and the event of other keys at
638 // each stage (OthersState). On certain platforms SelfState uses a reversed
639 // rule for certain keys (SelfState(rvsd), as documented in
640 // #update_caps_lock_state_logic_inferrence).
641 //
642 // # [0] [1] [2] [3]
643 // TruePressed: Released Pressed Released Pressed
644 // TrueEnabled: Disabled Enabled Enabled Disabled
645 // SelfType: Down Up Down Up
646 // SelfState: 0 1 1 1
647 // SelfState(rvsd): 1 1 0 1
648 // OthersState: 0 1 1 1
649 //
650 // When the exact stage can't be derived, choose the stage that requires the
651 // minimal synthesization.
652
653 const uint64_t pressed_logical_key =
654 recorded_physical_key == 0
655 ? 0
656 : lookup_hash_table(self->pressing_records, recorded_physical_key);
657
658 g_return_if_fail(pressed_logical_key == 0 ||
659 pressed_logical_key == logical_key);
660 const int stage_by_record = find_stage_by_record(
661 pressed_logical_key != 0, (self->lock_records & modifier_bit) != 0);
662
663 const bool enabled_by_state = (context->state & modifier_bit) != 0;
664 const bool this_key_is_event_key = logical_key == context->event_logical_key;
665 if (this_key_is_event_key && checked_key->is_caps_lock) {
667 enabled_by_state, stage_by_record);
668 g_return_if_fail(self->caps_lock_state_logic_inferrence !=
669 kStateLogicUndecided);
670 }
671 const bool reverse_state_logic =
672 checked_key->is_caps_lock &&
673 self->caps_lock_state_logic_inferrence == kStateLogicReversed;
674 const int stage_by_event =
675 this_key_is_event_key
676 ? find_stage_by_self_event(stage_by_record, context->is_down,
677 enabled_by_state, reverse_state_logic)
678 : find_stage_by_others_event(stage_by_record, enabled_by_state);
679
680 // The destination stage is equal to stage_by_event but shifted cyclically to
681 // be no less than stage_by_record.
682 constexpr int kNumStages = 4;
683 const int destination_stage = stage_by_event >= stage_by_record
684 ? stage_by_event
685 : stage_by_event + kNumStages;
686
687 g_return_if_fail(stage_by_record <= destination_stage);
688 if (stage_by_record == destination_stage) {
689 return;
690 }
691 for (int current_stage = stage_by_record; current_stage < destination_stage;
692 current_stage += 1) {
693 if (current_stage == 9) {
694 return;
695 }
696
697 const int standard_current_stage = current_stage % kNumStages;
698 const bool is_down_event =
699 standard_current_stage == 0 || standard_current_stage == 2;
700 if (is_down_event && recorded_physical_key == 0) {
701 update_mapping_record(self, physical_key, logical_key);
702 }
705 update_pressing_state(self, physical_key, is_down_event ? logical_key : 0);
706 possibly_update_lock_bit(self, logical_key, is_down_event);
707 synthesize_simple_event(self, type, physical_key, logical_key,
708 context->timestamp);
709 }
710}

◆ synchronize_pressed_states_loop_body()

static void synchronize_pressed_states_loop_body ( gpointer  key,
gpointer  value,
gpointer  user_data 
)
static

Definition at line 443 of file fl_key_embedder_responder.cc.

445 {
446 SyncStateLoopContext* context =
447 reinterpret_cast<SyncStateLoopContext*>(user_data);
448 FlKeyEmbedderCheckedKey* checked_key =
449 reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
450
451 const guint modifier_bit = GPOINTER_TO_INT(key);
452 FlKeyEmbedderResponder* self = context->self;
453 // Each TestKey contains up to two logical keys, typically the left modifier
454 // and the right modifier, that correspond to the same modifier_bit. We'd
455 // like to infer whether to synthesize a down or up event for each key.
456 //
457 // The hard part is that, if we want to synthesize a down event, we don't know
458 // which physical key to use. Here we assume the keyboard layout do not change
459 // frequently and use the last physical-logical relationship, recorded in
460 // #mapping_records.
461 const uint64_t logical_keys[] = {
462 checked_key->primary_logical_key,
463 checked_key->secondary_logical_key,
464 };
465 const guint length = checked_key->secondary_logical_key == 0 ? 1 : 2;
466
467 const bool any_pressed_by_state = (context->state & modifier_bit) != 0;
468
469 bool any_pressed_by_record = false;
470
471 // Traverse each logical key of this modifier bit for 2 purposes:
472 //
473 // 1. Perform the synthesization of release events: If the modifier bit is 0
474 // and the key is pressed, synthesize a release event.
475 // 2. Prepare for the synthesization of press events: If the modifier bit is
476 // 1, and no keys are pressed (discovered here), synthesize a press event
477 // later.
478 for (guint logical_key_idx = 0; logical_key_idx < length; logical_key_idx++) {
479 const uint64_t logical_key = logical_keys[logical_key_idx];
480 g_return_if_fail(logical_key != 0);
481 const uint64_t pressing_physical_key =
482 reverse_lookup_hash_table(self->pressing_records, logical_key);
483 const bool this_key_pressed_before_event = pressing_physical_key != 0;
484
485 any_pressed_by_record =
486 any_pressed_by_record || this_key_pressed_before_event;
487
488 if (this_key_pressed_before_event && !any_pressed_by_state) {
489 const uint64_t recorded_physical_key =
490 lookup_hash_table(self->mapping_records, logical_key);
491 // Since this key has been pressed before, there must have been a recorded
492 // physical key.
493 g_return_if_fail(recorded_physical_key != 0);
494 // In rare cases #recorded_logical_key is different from #logical_key.
495 const uint64_t recorded_logical_key =
496 lookup_hash_table(self->pressing_records, recorded_physical_key);
498 recorded_physical_key, recorded_logical_key,
499 context->timestamp);
500 update_pressing_state(self, recorded_physical_key, 0);
501 }
502 }
503 // If the modifier should be pressed, synthesize a down event for its primary
504 // key.
505 if (any_pressed_by_state && !any_pressed_by_record) {
506 const uint64_t logical_key = checked_key->primary_logical_key;
507 const uint64_t recorded_physical_key =
508 lookup_hash_table(self->mapping_records, logical_key);
509 // The physical key is derived from past mapping record if possible.
510 //
511 // The event to be synthesized is a key down event. There might not have
512 // been a mapping record, in which case the hard-coded #primary_physical_key
513 // is used.
514 const uint64_t physical_key = recorded_physical_key != 0
515 ? recorded_physical_key
516 : checked_key->primary_physical_key;
517 if (recorded_physical_key == 0) {
518 update_mapping_record(self, physical_key, logical_key);
519 }
521 logical_key, context->timestamp);
522 update_pressing_state(self, physical_key, logical_key);
523 }
524}

◆ synthesize_simple_event()

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

Definition at line 352 of file fl_key_embedder_responder.cc.

356 {
357 FlutterKeyEvent out_event;
358 out_event.struct_size = sizeof(out_event);
359 out_event.timestamp = timestamp;
360 out_event.type = type;
361 out_event.physical = physical;
362 out_event.logical = logical;
363 out_event.character = nullptr;
364 out_event.synthesized = true;
365 self->sent_any_events = true;
366 self->send_key_event(&out_event, nullptr, nullptr,
367 self->send_key_event_user_data);
368}

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

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

586 {
587 if (self->caps_lock_state_logic_inferrence != kStateLogicUndecided) {
588 return;
589 }
590 if (!is_down_event) {
591 return;
592 }
593 const int stage_by_event = find_stage_by_self_event(
594 stage_by_record, is_down_event, enabled_by_state, false);
595 if ((stage_by_event == 0 && stage_by_record == 2) ||
596 (stage_by_event == 2 && stage_by_record == 0)) {
597 self->caps_lock_state_logic_inferrence = kStateLogicReversed;
598 } else {
599 self->caps_lock_state_logic_inferrence = kStateLogicNormal;
600 }
601}

◆ update_mapping_record()

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

Definition at line 432 of file fl_key_embedder_responder.cc.

434 {
435 g_hash_table_insert(self->mapping_records, uint64_to_gpointer(logical_key),
436 uint64_to_gpointer(physical_key));
437}

◆ update_pressing_state()

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

Definition at line 398 of file fl_key_embedder_responder.cc.

400 {
401 if (logical_key != 0) {
402 g_return_if_fail(lookup_hash_table(self->pressing_records, physical_key) ==
403 0);
404 g_hash_table_insert(self->pressing_records,
405 uint64_to_gpointer(physical_key),
406 uint64_to_gpointer(logical_key));
407 } else {
408 g_return_if_fail(lookup_hash_table(self->pressing_records, physical_key) !=
409 0);
410 g_hash_table_remove(self->pressing_records,
411 uint64_to_gpointer(physical_key));
412 }
413}

Variable Documentation

◆ callback

Definition at line 217 of file fl_key_embedder_responder.cc.

◆ event

FlKeyEvent* event

Definition at line 215 of file fl_key_embedder_responder.cc.

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

◆ kMicrosecondsPerMillisecond

constexpr uint64_t kMicrosecondsPerMillisecond = 1000
constexpr

Definition at line 14 of file fl_key_embedder_responder.cc.

◆ specified_logical_key

FlKeyEvent uint64_t specified_logical_key

Definition at line 216 of file fl_key_embedder_responder.cc.

◆ user_data

FlKeyEvent uint64_t FlKeyResponderAsyncCallback gpointer user_data

Definition at line 218 of file fl_key_embedder_responder.cc.