Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
fl_keyboard_manager_test.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/shell/platform/linux/fl_keyboard_manager.h"
6
7#include <cstring>
8#include <vector>
9
10#include "flutter/shell/platform/embedder/test_utils/key_codes.g.h"
11#include "flutter/shell/platform/linux/fl_binary_messenger_private.h"
12#include "flutter/shell/platform/linux/fl_method_codec_private.h"
13#include "flutter/shell/platform/linux/key_mapping.h"
14#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"
15#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_codec.h"
16#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
17#include "flutter/shell/platform/linux/testing/fl_test.h"
18#include "flutter/shell/platform/linux/testing/mock_binary_messenger.h"
19#include "flutter/shell/platform/linux/testing/mock_text_input_plugin.h"
20#include "flutter/testing/testing.h"
21
22#include "gmock/gmock.h"
23#include "gtest/gtest.h"
24
25// Define compound `expect` in macros. If they were defined in functions, the
26// stacktrace wouldn't print where the function is called in the unit tests.
27
28#define EXPECT_KEY_EVENT(RECORD, TYPE, PHYSICAL, LOGICAL, CHAR, SYNTHESIZED) \
29 EXPECT_EQ((RECORD).type, CallRecord::kKeyCallEmbedder); \
30 EXPECT_EQ((RECORD).event->type, (TYPE)); \
31 EXPECT_EQ((RECORD).event->physical, (PHYSICAL)); \
32 EXPECT_EQ((RECORD).event->logical, (LOGICAL)); \
33 EXPECT_STREQ((RECORD).event->character, (CHAR)); \
34 EXPECT_EQ((RECORD).event->synthesized, (SYNTHESIZED));
35
36#define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR) \
37 EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder); \
38 EXPECT_EQ(call_records[0].event->type, kFlutterKeyEventTypeDown); \
39 EXPECT_EQ(call_records[0].event->logical, (OUT_LOGICAL)); \
40 EXPECT_STREQ(call_records[0].event->character, (OUT_CHAR)); \
41 EXPECT_EQ(call_records[0].event->synthesized, false); \
42 call_records.clear()
43
44namespace {
45using ::flutter::testing::keycodes::kLogicalAltLeft;
46using ::flutter::testing::keycodes::kLogicalBracketLeft;
47using ::flutter::testing::keycodes::kLogicalComma;
48using ::flutter::testing::keycodes::kLogicalControlLeft;
49using ::flutter::testing::keycodes::kLogicalDigit1;
50using ::flutter::testing::keycodes::kLogicalKeyA;
51using ::flutter::testing::keycodes::kLogicalKeyB;
52using ::flutter::testing::keycodes::kLogicalKeyM;
53using ::flutter::testing::keycodes::kLogicalKeyQ;
54using ::flutter::testing::keycodes::kLogicalMetaLeft;
55using ::flutter::testing::keycodes::kLogicalMinus;
56using ::flutter::testing::keycodes::kLogicalParenthesisRight;
57using ::flutter::testing::keycodes::kLogicalSemicolon;
58using ::flutter::testing::keycodes::kLogicalShiftLeft;
59using ::flutter::testing::keycodes::kLogicalUnderscore;
60
61using ::flutter::testing::keycodes::kPhysicalAltLeft;
62using ::flutter::testing::keycodes::kPhysicalControlLeft;
63using ::flutter::testing::keycodes::kPhysicalKeyA;
64using ::flutter::testing::keycodes::kPhysicalKeyB;
65using ::flutter::testing::keycodes::kPhysicalMetaLeft;
66using ::flutter::testing::keycodes::kPhysicalShiftLeft;
67
68// Hardware key codes.
69typedef std::function<void(bool handled)> AsyncKeyCallback;
70typedef std::function<void(AsyncKeyCallback callback)> ChannelCallHandler;
71typedef std::function<void(const FlutterKeyEvent* event,
72 AsyncKeyCallback callback)>
73 EmbedderCallHandler;
74typedef std::function<void(std::unique_ptr<FlKeyEvent>)> RedispatchHandler;
75
76// A type that can record all kinds of effects that the keyboard manager
77// triggers.
78//
79// An instance of `CallRecord` might not have all the fields filled.
80typedef struct {
81 enum {
82 kKeyCallEmbedder,
83 kKeyCallChannel,
84 } type;
85
86 AsyncKeyCallback callback;
87 std::unique_ptr<FlutterKeyEvent> event;
88 std::unique_ptr<char[]> event_character;
89} CallRecord;
90
91// Clone a C-string.
92//
93// Must be deleted by delete[].
94char* cloneString(const char* source) {
95 if (source == nullptr) {
96 return nullptr;
97 }
98 size_t charLen = strlen(source);
99 char* target = new char[charLen + 1];
100 strncpy(target, source, charLen + 1);
101 return target;
102}
103
104constexpr guint16 kKeyCodeKeyA = 0x26u;
105constexpr guint16 kKeyCodeKeyB = 0x38u;
106constexpr guint16 kKeyCodeKeyM = 0x3au;
107constexpr guint16 kKeyCodeDigit1 = 0x0au;
108constexpr guint16 kKeyCodeMinus = 0x14u;
109constexpr guint16 kKeyCodeSemicolon = 0x2fu;
110constexpr guint16 kKeyCodeKeyLeftBracket = 0x22u;
111
112static constexpr char kKeyEventChannelName[] = "flutter/keyevent";
113static constexpr char kKeyboardChannelName[] = "flutter/keyboard";
114static constexpr char kGetKeyboardStateMethod[] = "getKeyboardState";
115static constexpr uint64_t kMockPhysicalKey = 42;
116static constexpr uint64_t kMockLogicalKey = 42;
117
118// All key clues for a keyboard layout.
119//
120// The index is (keyCode * 2 + hasShift), where each value is the character for
121// this key (GTK only supports UTF-16.) Since the maximum keycode of interest
122// is 128, it has a total of 256 entries..
123typedef std::array<uint32_t, 256> MockGroupLayoutData;
124typedef std::vector<const MockGroupLayoutData*> MockLayoutData;
125
126extern const MockLayoutData kLayoutUs;
127extern const MockLayoutData kLayoutRussian;
128extern const MockLayoutData kLayoutFrench;
129
130G_BEGIN_DECLS
131
132G_DECLARE_FINAL_TYPE(FlMockViewDelegate,
133 fl_mock_view_delegate,
134 FL,
135 MOCK_VIEW_DELEGATE,
136 GObject);
137
138G_DECLARE_FINAL_TYPE(FlMockKeyBinaryMessenger,
139 fl_mock_key_binary_messenger,
140 FL,
141 MOCK_KEY_BINARY_MESSENGER,
142 GObject)
143
144G_END_DECLS
145
146MATCHER_P(MethodSuccessResponse, result, "") {
147 g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
148 g_autoptr(FlMethodResponse) response =
149 fl_method_codec_decode_response(FL_METHOD_CODEC(codec), arg, nullptr);
150 fl_method_response_get_result(response, nullptr);
151 if (fl_value_equal(fl_method_response_get_result(response, nullptr),
152 result)) {
153 return true;
154 }
155 *result_listener << ::testing::PrintToString(response);
156 return false;
157}
158
159/***** FlMockKeyBinaryMessenger *****/
160/* Mock a binary messenger that only processes messages from the embedding on
161 * the key event channel, and does so according to the callback set by
162 * fl_mock_key_binary_messenger_set_callback_handler */
163
164struct _FlMockKeyBinaryMessenger {
165 GObject parent_instance;
166};
167
168struct FlMockKeyBinaryMessengerPrivate {
169 ChannelCallHandler callback_handler;
170};
171
172static void fl_mock_key_binary_messenger_iface_init(
173 FlBinaryMessengerInterface* iface);
174
176 FlMockKeyBinaryMessenger,
177 fl_mock_key_binary_messenger,
178 G_TYPE_OBJECT,
179 G_IMPLEMENT_INTERFACE(fl_binary_messenger_get_type(),
180 fl_mock_key_binary_messenger_iface_init);
181 G_ADD_PRIVATE(FlMockKeyBinaryMessenger))
182
184 static_cast<FlMockKeyBinaryMessengerPrivate*>( \
185 fl_mock_key_binary_messenger_get_instance_private( \
186 FL_MOCK_KEY_BINARY_MESSENGER(obj)))
187
188static void fl_mock_key_binary_messenger_init(FlMockKeyBinaryMessenger* self) {
189 FlMockKeyBinaryMessengerPrivate* priv =
191 new (priv) FlMockKeyBinaryMessengerPrivate();
192}
193
194static void fl_mock_key_binary_messenger_finalize(GObject* object) {
196 ->~FlMockKeyBinaryMessengerPrivate();
197}
198
199static void fl_mock_key_binary_messenger_class_init(
200 FlMockKeyBinaryMessengerClass* klass) {
201 G_OBJECT_CLASS(klass)->finalize = fl_mock_key_binary_messenger_finalize;
202}
203
204static void fl_mock_key_binary_messenger_send_on_channel(
205 FlBinaryMessenger* messenger,
206 const gchar* channel,
207 GBytes* message,
208 GCancellable* cancellable,
209 GAsyncReadyCallback callback,
210 gpointer user_data) {
211 FlMockKeyBinaryMessenger* self = FL_MOCK_KEY_BINARY_MESSENGER(messenger);
212
213 if (callback != nullptr) {
214 EXPECT_STREQ(channel, kKeyEventChannelName);
216 [self, cancellable, callback, user_data](bool handled) {
217 g_autoptr(GTask) task =
218 g_task_new(self, cancellable, callback, user_data);
219 g_autoptr(FlValue) result = fl_value_new_map();
221 fl_value_new_bool(handled));
222 g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new();
223 g_autoptr(GError) error = nullptr;
224 GBytes* data = fl_message_codec_encode_message(
225 FL_MESSAGE_CODEC(codec), result, &error);
226
227 g_task_return_pointer(
228 task, data, reinterpret_cast<GDestroyNotify>(g_bytes_unref));
229 });
230 }
231}
232
233static GBytes* fl_mock_key_binary_messenger_send_on_channel_finish(
234 FlBinaryMessenger* messenger,
235 GAsyncResult* result,
236 GError** error) {
237 return static_cast<GBytes*>(g_task_propagate_pointer(G_TASK(result), error));
238}
239
241 FlBinaryMessenger* messenger,
242 const gchar* channel,
243 int64_t new_size) {
244 // Mock implementation. Do nothing.
245}
246
248 FlBinaryMessenger* messenger,
249 const gchar* channel,
250 bool warns) {
251 // Mock implementation. Do nothing.
252}
253
254static void fl_mock_key_binary_messenger_iface_init(
255 FlBinaryMessengerInterface* iface) {
256 iface->set_message_handler_on_channel =
257 [](FlBinaryMessenger* messenger, const gchar* channel,
259 GDestroyNotify destroy_notify) {
260 EXPECT_STREQ(channel, kKeyEventChannelName);
261 // No need to mock. The key event channel expects no incoming messages
262 // from the framework.
263 };
264 iface->send_response = [](FlBinaryMessenger* messenger,
265 FlBinaryMessengerResponseHandle* response_handle,
266 GBytes* response, GError** error) -> gboolean {
267 // The key event channel expects no incoming messages from the framework,
268 // hence no responses either.
269 g_return_val_if_reached(TRUE);
270 return TRUE;
271 };
272 iface->send_on_channel = fl_mock_key_binary_messenger_send_on_channel;
273 iface->send_on_channel_finish =
274 fl_mock_key_binary_messenger_send_on_channel_finish;
275 iface->resize_channel = fl_mock_binary_messenger_resize_channel;
276 iface->set_warns_on_channel_overflow =
278}
279
280static FlMockKeyBinaryMessenger* fl_mock_key_binary_messenger_new() {
281 FlMockKeyBinaryMessenger* self = FL_MOCK_KEY_BINARY_MESSENGER(
282 g_object_new(fl_mock_key_binary_messenger_get_type(), NULL));
283
284 // Added to stop compiler complaining about an unused function.
285 FL_IS_MOCK_KEY_BINARY_MESSENGER(self);
286
287 return self;
288}
289
290static void fl_mock_key_binary_messenger_set_callback_handler(
291 FlMockKeyBinaryMessenger* self,
292 ChannelCallHandler handler) {
294 std::move(handler);
295}
296
297/***** FlMockViewDelegate *****/
298
299struct _FlMockViewDelegate {
300 GObject parent_instance;
301};
302
303struct FlMockViewDelegatePrivate {
304 FlMockKeyBinaryMessenger* messenger;
305 EmbedderCallHandler embedder_handler;
306 bool text_filter_result;
307 RedispatchHandler redispatch_handler;
308 KeyboardLayoutNotifier layout_notifier;
309 const MockLayoutData* layout_data;
310};
311
312static void fl_mock_view_keyboard_delegate_iface_init(
313 FlKeyboardViewDelegateInterface* iface);
314
316 FlMockViewDelegate,
317 fl_mock_view_delegate,
318 G_TYPE_OBJECT,
319 G_IMPLEMENT_INTERFACE(fl_keyboard_view_delegate_get_type(),
320 fl_mock_view_keyboard_delegate_iface_init);
321 G_ADD_PRIVATE(FlMockViewDelegate))
322
324 static_cast<FlMockViewDelegatePrivate*>( \
325 fl_mock_view_delegate_get_instance_private(FL_MOCK_VIEW_DELEGATE(obj)))
326
327static void fl_mock_view_delegate_init(FlMockViewDelegate* self) {
328 FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(self);
329 new (priv) FlMockViewDelegatePrivate();
330}
331
332static void fl_mock_view_delegate_finalize(GObject* object) {
333 FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(object)->~FlMockViewDelegatePrivate();
334}
335
336static void fl_mock_view_delegate_dispose(GObject* object) {
337 FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(object);
338
339 g_clear_object(&priv->messenger);
340
341 G_OBJECT_CLASS(fl_mock_view_delegate_parent_class)->dispose(object);
342}
343
344static void fl_mock_view_delegate_class_init(FlMockViewDelegateClass* klass) {
345 G_OBJECT_CLASS(klass)->dispose = fl_mock_view_delegate_dispose;
346 G_OBJECT_CLASS(klass)->finalize = fl_mock_view_delegate_finalize;
347}
348
349static void fl_mock_view_keyboard_send_key_event(
350 FlKeyboardViewDelegate* view_delegate,
351 const FlutterKeyEvent* event,
353 void* user_data) {
354 FlMockViewDelegatePrivate* priv =
356 priv->embedder_handler(event, [callback, user_data](bool handled) {
357 if (callback != nullptr) {
358 callback(handled, user_data);
359 }
360 });
361}
362
363static gboolean fl_mock_view_keyboard_text_filter_key_press(
364 FlKeyboardViewDelegate* view_delegate,
365 FlKeyEvent* event) {
366 FlMockViewDelegatePrivate* priv =
368 return priv->text_filter_result;
369}
370
371static FlBinaryMessenger* fl_mock_view_keyboard_get_messenger(
372 FlKeyboardViewDelegate* view_delegate) {
373 FlMockViewDelegatePrivate* priv =
375 return FL_BINARY_MESSENGER(priv->messenger);
376}
377
378static void fl_mock_view_keyboard_redispatch_event(
379 FlKeyboardViewDelegate* view_delegate,
380 std::unique_ptr<FlKeyEvent> event) {
381 FlMockViewDelegatePrivate* priv =
383 if (priv->redispatch_handler) {
384 priv->redispatch_handler(std::move(event));
385 }
386}
387
388static void fl_mock_view_keyboard_subscribe_to_layout_change(
389 FlKeyboardViewDelegate* delegate,
390 KeyboardLayoutNotifier notifier) {
391 FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(delegate);
392 priv->layout_notifier = std::move(notifier);
393}
394
395static guint fl_mock_view_keyboard_lookup_key(FlKeyboardViewDelegate* delegate,
396 const GdkKeymapKey* key) {
397 FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(delegate);
398 guint8 group = static_cast<guint8>(key->group);
399 EXPECT_LT(group, priv->layout_data->size());
400 const MockGroupLayoutData* group_layout = (*priv->layout_data)[group];
401 EXPECT_TRUE(group_layout != nullptr);
402 EXPECT_TRUE(key->level == 0 || key->level == 1);
403 bool shift = key->level == 1;
404 return (*group_layout)[key->keycode * 2 + shift];
405}
406
407static GHashTable* fl_mock_view_keyboard_get_keyboard_state(
408 FlKeyboardViewDelegate* view_delegate) {
409 GHashTable* result = g_hash_table_new(g_direct_hash, g_direct_equal);
410 g_hash_table_insert(result, reinterpret_cast<gpointer>(kMockPhysicalKey),
411 reinterpret_cast<gpointer>(kMockLogicalKey));
412
413 return result;
414}
415
416static void fl_mock_view_keyboard_delegate_iface_init(
417 FlKeyboardViewDelegateInterface* iface) {
418 iface->send_key_event = fl_mock_view_keyboard_send_key_event;
419 iface->text_filter_key_press = fl_mock_view_keyboard_text_filter_key_press;
420 iface->get_messenger = fl_mock_view_keyboard_get_messenger;
421 iface->redispatch_event = fl_mock_view_keyboard_redispatch_event;
422 iface->subscribe_to_layout_change =
423 fl_mock_view_keyboard_subscribe_to_layout_change;
424 iface->lookup_key = fl_mock_view_keyboard_lookup_key;
425 iface->get_keyboard_state = fl_mock_view_keyboard_get_keyboard_state;
426}
427
428static FlMockViewDelegate* fl_mock_view_delegate_new() {
429 FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(
430 g_object_new(fl_mock_view_delegate_get_type(), nullptr));
431
432 // Added to stop compiler complaining about an unused function.
433 FL_IS_MOCK_VIEW_DELEGATE(self);
434
435 FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(self);
436 priv->messenger = fl_mock_key_binary_messenger_new();
437
438 return self;
439}
440
441static void fl_mock_view_set_embedder_handler(FlMockViewDelegate* self,
442 EmbedderCallHandler handler) {
443 FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(self);
444 priv->embedder_handler = std::move(handler);
445}
446
447static void fl_mock_view_set_text_filter_result(FlMockViewDelegate* self,
448 bool result) {
449 FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(self);
450 priv->text_filter_result = result;
451}
452
453static void fl_mock_view_set_redispatch_handler(FlMockViewDelegate* self,
454 RedispatchHandler handler) {
455 FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(self);
456 priv->redispatch_handler = std::move(handler);
457}
458
459static void fl_mock_view_set_layout(FlMockViewDelegate* self,
460 const MockLayoutData* layout) {
461 FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(self);
462 priv->layout_data = layout;
463 if (priv->layout_notifier != nullptr) {
464 priv->layout_notifier();
465 }
466}
467
468/***** End FlMockViewDelegate *****/
469
470// Create a new #FlKeyEvent with the given information.
471static FlKeyEvent* fl_key_event_new_by_mock(bool is_press,
472 guint keyval,
473 guint16 keycode,
474 GdkModifierType state,
475 gboolean is_modifier,
476 guint8 group = 0) {
477 FlKeyEvent* event = g_new0(FlKeyEvent, 1);
478 event->is_press = is_press;
479 event->time = 0;
480 event->state = state;
481 event->keyval = keyval;
482 event->group = group;
483 event->keycode = keycode;
484 return event;
485}
486
487class KeyboardTester {
488 public:
490 ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
491
492 view_ = fl_mock_view_delegate_new();
493 respondToEmbedderCallsWith(false);
494 respondToChannelCallsWith(false);
495 respondToTextInputWith(false);
496 setLayout(kLayoutUs);
497
498 manager_ =
499 fl_keyboard_manager_new(messenger, FL_KEYBOARD_VIEW_DELEGATE(view_));
500 }
501
503 g_clear_object(&view_);
504 g_clear_object(&manager_);
505 }
506
507 FlKeyboardManager* manager() { return manager_; }
508
509 // Block until all GdkMainLoop messages are processed, which is basically
510 // used only for channel messages.
511 void flushChannelMessages() {
512 GMainLoop* loop = g_main_loop_new(nullptr, 0);
513 g_idle_add(_flushChannelMessagesCb, loop);
514 g_main_loop_run(loop);
515 }
516
517 // Dispatch each of the given events, expect their results to be false
518 // (unhandled), and clear the event array.
519 //
520 // Returns the number of events redispatched. If any result is unexpected
521 // (handled), return a minus number `-x` instead, where `x` is the index of
522 // the first unexpected redispatch.
523 int redispatchEventsAndClear(
524 std::vector<std::unique_ptr<FlKeyEvent>>& events) {
525 size_t event_count = events.size();
526 int first_error = -1;
527 during_redispatch_ = true;
528 for (size_t event_id = 0; event_id < event_count; event_id += 1) {
530 manager_, events[event_id].release());
531 EXPECT_FALSE(handled);
532 if (handled) {
533 first_error = first_error == -1 ? event_id : first_error;
534 }
535 }
536 during_redispatch_ = false;
537 events.clear();
538 return first_error < 0 ? event_count : -first_error;
539 }
540
541 void respondToEmbedderCallsWith(bool response) {
542 fl_mock_view_set_embedder_handler(
543 view_, [response, this](const FlutterKeyEvent* event,
544 const AsyncKeyCallback& callback) {
545 EXPECT_FALSE(during_redispatch_);
546 callback(response);
547 });
548 }
549
550 void recordEmbedderCallsTo(std::vector<CallRecord>& storage) {
551 fl_mock_view_set_embedder_handler(
552 view_, [&storage, this](const FlutterKeyEvent* event,
553 AsyncKeyCallback callback) {
554 EXPECT_FALSE(during_redispatch_);
555 auto new_event = std::make_unique<FlutterKeyEvent>(*event);
556 char* new_event_character = cloneString(event->character);
557 new_event->character = new_event_character;
558 storage.push_back(CallRecord{
559 .type = CallRecord::kKeyCallEmbedder,
560 .callback = std::move(callback),
561 .event = std::move(new_event),
562 .event_character = std::unique_ptr<char[]>(new_event_character),
563 });
564 });
565 }
566
567 void respondToEmbedderCallsWithAndRecordsTo(
568 bool response,
569 std::vector<CallRecord>& storage) {
570 fl_mock_view_set_embedder_handler(
571 view_, [&storage, response, this](const FlutterKeyEvent* event,
572 const AsyncKeyCallback& callback) {
573 EXPECT_FALSE(during_redispatch_);
574 auto new_event = std::make_unique<FlutterKeyEvent>(*event);
575 char* new_event_character = cloneString(event->character);
576 new_event->character = new_event_character;
577 storage.push_back(CallRecord{
578 .type = CallRecord::kKeyCallEmbedder,
579 .event = std::move(new_event),
580 .event_character = std::unique_ptr<char[]>(new_event_character),
581 });
582 callback(response);
583 });
584 }
585
586 void respondToChannelCallsWith(bool response) {
587 FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(view_);
588
589 fl_mock_key_binary_messenger_set_callback_handler(
590 priv->messenger, [response, this](const AsyncKeyCallback& callback) {
591 EXPECT_FALSE(during_redispatch_);
592 callback(response);
593 });
594 }
595
596 void recordChannelCallsTo(std::vector<CallRecord>& storage) {
597 FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(view_);
598
599 fl_mock_key_binary_messenger_set_callback_handler(
600 priv->messenger, [&storage, this](AsyncKeyCallback callback) {
601 EXPECT_FALSE(during_redispatch_);
602 storage.push_back(CallRecord{
603 .type = CallRecord::kKeyCallChannel,
604 .callback = std::move(callback),
605 });
606 });
607 }
608
609 void respondToTextInputWith(bool response) {
610 fl_mock_view_set_text_filter_result(view_, response);
611 }
612
613 void recordRedispatchedEventsTo(
614 std::vector<std::unique_ptr<FlKeyEvent>>& storage) {
615 fl_mock_view_set_redispatch_handler(
616 view_, [&storage](std::unique_ptr<FlKeyEvent> key) {
617 storage.push_back(std::move(key));
618 });
619 }
620
621 void setLayout(const MockLayoutData& layout) {
622 fl_mock_view_set_layout(view_, &layout);
623 }
624
625 private:
626 FlMockViewDelegate* view_;
627 FlKeyboardManager* manager_;
628 bool during_redispatch_ = false;
629
630 static gboolean _flushChannelMessagesCb(gpointer data) {
631 g_autoptr(GMainLoop) loop = reinterpret_cast<GMainLoop*>(data);
632 g_main_loop_quit(loop);
633 return FALSE;
634 }
635};
636
637// Make sure that the keyboard can be disposed without crashes when there are
638// unresolved pending events.
639TEST(FlKeyboardManagerTest, DisposeWithUnresolvedPends) {
641 std::vector<CallRecord> call_records;
642
643 // Record calls so that they aren't responded.
644 tester.recordEmbedderCallsTo(call_records);
646 tester.manager(),
647 fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA,
648 static_cast<GdkModifierType>(0), false));
649
650 tester.respondToEmbedderCallsWith(true);
652 tester.manager(),
653 fl_key_event_new_by_mock(false, GDK_KEY_a, kKeyCodeKeyA,
654 static_cast<GdkModifierType>(0), false));
655
656 tester.flushChannelMessages();
657
658 // Passes if the cleanup does not crash.
659}
660
661TEST(FlKeyboardManagerTest, SingleDelegateWithAsyncResponds) {
663 std::vector<CallRecord> call_records;
664 std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
665
666 gboolean manager_handled = false;
667
668 /// Test 1: One event that is handled by the framework
669 tester.recordEmbedderCallsTo(call_records);
670 tester.recordRedispatchedEventsTo(redispatched);
671
672 // Dispatch a key event
673 manager_handled = fl_keyboard_manager_handle_event(
674 tester.manager(),
675 fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA,
676 static_cast<GdkModifierType>(0), false));
677 tester.flushChannelMessages();
678 EXPECT_EQ(manager_handled, true);
679 EXPECT_EQ(redispatched.size(), 0u);
680 EXPECT_EQ(call_records.size(), 1u);
681 EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeDown, kPhysicalKeyA,
682 kLogicalKeyA, "a", false);
683
684 call_records[0].callback(true);
685 tester.flushChannelMessages();
686 EXPECT_EQ(redispatched.size(), 0u);
688 call_records.clear();
689
690 /// Test 2: Two events that are unhandled by the framework
691 manager_handled = fl_keyboard_manager_handle_event(
692 tester.manager(),
693 fl_key_event_new_by_mock(false, GDK_KEY_a, kKeyCodeKeyA,
694 static_cast<GdkModifierType>(0), false));
695 tester.flushChannelMessages();
696 EXPECT_EQ(manager_handled, true);
697 EXPECT_EQ(redispatched.size(), 0u);
698 EXPECT_EQ(call_records.size(), 1u);
699 EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeUp, kPhysicalKeyA,
700 kLogicalKeyA, nullptr, false);
701
702 // Dispatch another key event
703 manager_handled = fl_keyboard_manager_handle_event(
704 tester.manager(),
705 fl_key_event_new_by_mock(true, GDK_KEY_b, kKeyCodeKeyB,
706 static_cast<GdkModifierType>(0), false));
707 tester.flushChannelMessages();
708 EXPECT_EQ(manager_handled, true);
709 EXPECT_EQ(redispatched.size(), 0u);
710 EXPECT_EQ(call_records.size(), 2u);
711 EXPECT_KEY_EVENT(call_records[1], kFlutterKeyEventTypeDown, kPhysicalKeyB,
712 kLogicalKeyB, "b", false);
713
714 // Resolve the second event first to test out-of-order response
715 call_records[1].callback(false);
716 EXPECT_EQ(redispatched.size(), 1u);
717 EXPECT_EQ(redispatched[0]->keyval, 0x62u);
718 call_records[0].callback(false);
719 tester.flushChannelMessages();
720 EXPECT_EQ(redispatched.size(), 2u);
721 EXPECT_EQ(redispatched[1]->keyval, 0x61u);
722
723 EXPECT_FALSE(fl_keyboard_manager_is_state_clear(tester.manager()));
724 call_records.clear();
725
726 // Resolve redispatches
727 EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 2);
728 tester.flushChannelMessages();
729 EXPECT_EQ(call_records.size(), 0u);
731
732 /// Test 3: Dispatch the same event again to ensure that prevention from
733 /// redispatching only works once.
734 manager_handled = fl_keyboard_manager_handle_event(
735 tester.manager(),
736 fl_key_event_new_by_mock(false, GDK_KEY_a, kKeyCodeKeyA,
737 static_cast<GdkModifierType>(0), false));
738 tester.flushChannelMessages();
739 EXPECT_EQ(manager_handled, true);
740 EXPECT_EQ(redispatched.size(), 0u);
741 EXPECT_EQ(call_records.size(), 1u);
742
743 call_records[0].callback(true);
745}
746
747TEST(FlKeyboardManagerTest, SingleDelegateWithSyncResponds) {
749 gboolean manager_handled = false;
750 std::vector<CallRecord> call_records;
751 std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
752
753 /// Test 1: One event that is handled by the framework
754 tester.respondToEmbedderCallsWithAndRecordsTo(true, call_records);
755 tester.recordRedispatchedEventsTo(redispatched);
756
757 // Dispatch a key event
758 manager_handled = fl_keyboard_manager_handle_event(
759 tester.manager(),
760 fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA,
761 static_cast<GdkModifierType>(0), false));
762 tester.flushChannelMessages();
763 EXPECT_EQ(manager_handled, true);
764 EXPECT_EQ(call_records.size(), 1u);
765 EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeDown, kPhysicalKeyA,
766 kLogicalKeyA, "a", false);
767 EXPECT_EQ(redispatched.size(), 0u);
768 call_records.clear();
769
771 redispatched.clear();
772
773 /// Test 2: An event unhandled by the framework
774 tester.respondToEmbedderCallsWithAndRecordsTo(false, call_records);
775 manager_handled = fl_keyboard_manager_handle_event(
776 tester.manager(),
777 fl_key_event_new_by_mock(false, GDK_KEY_a, kKeyCodeKeyA,
778 static_cast<GdkModifierType>(0), false));
779 tester.flushChannelMessages();
780 EXPECT_EQ(manager_handled, true);
781 EXPECT_EQ(call_records.size(), 1u);
782 EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeUp, kPhysicalKeyA,
783 kLogicalKeyA, nullptr, false);
784 EXPECT_EQ(redispatched.size(), 1u);
785 call_records.clear();
786
787 EXPECT_FALSE(fl_keyboard_manager_is_state_clear(tester.manager()));
788
789 EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);
790 EXPECT_EQ(call_records.size(), 0u);
791
793}
794
795TEST(FlKeyboardManagerTest, WithTwoAsyncDelegates) {
797 std::vector<CallRecord> call_records;
798 std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
799
800 gboolean manager_handled = false;
801
802 tester.recordEmbedderCallsTo(call_records);
803 tester.recordChannelCallsTo(call_records);
804 tester.recordRedispatchedEventsTo(redispatched);
805
806 /// Test 1: One delegate responds true, the other false
807
808 manager_handled = fl_keyboard_manager_handle_event(
809 tester.manager(),
810 fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA,
811 static_cast<GdkModifierType>(0), false));
812
813 EXPECT_EQ(manager_handled, true);
814 EXPECT_EQ(redispatched.size(), 0u);
815 EXPECT_EQ(call_records.size(), 2u);
816
817 EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder);
818 EXPECT_EQ(call_records[1].type, CallRecord::kKeyCallChannel);
819
820 call_records[0].callback(true);
821 call_records[1].callback(false);
822 tester.flushChannelMessages();
823 EXPECT_EQ(redispatched.size(), 0u);
824
826 call_records.clear();
827
828 /// Test 2: All delegates respond false
829 manager_handled = fl_keyboard_manager_handle_event(
830 tester.manager(),
831 fl_key_event_new_by_mock(false, GDK_KEY_a, kKeyCodeKeyA,
832 static_cast<GdkModifierType>(0), false));
833
834 EXPECT_EQ(manager_handled, true);
835 EXPECT_EQ(redispatched.size(), 0u);
836 EXPECT_EQ(call_records.size(), 2u);
837
838 EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder);
839 EXPECT_EQ(call_records[1].type, CallRecord::kKeyCallChannel);
840
841 call_records[0].callback(false);
842 call_records[1].callback(false);
843
844 call_records.clear();
845
846 // Resolve redispatch
847 tester.flushChannelMessages();
848 EXPECT_EQ(redispatched.size(), 1u);
849 EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);
850 EXPECT_EQ(call_records.size(), 0u);
851
853}
854
855TEST(FlKeyboardManagerTest, TextInputPluginReturnsFalse) {
857 std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
858 gboolean manager_handled = false;
859 tester.recordRedispatchedEventsTo(redispatched);
860 tester.respondToTextInputWith(false);
861
862 // Dispatch a key event.
863 manager_handled = fl_keyboard_manager_handle_event(
864 tester.manager(),
865 fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA,
866 static_cast<GdkModifierType>(0), false));
867 tester.flushChannelMessages();
868 EXPECT_EQ(manager_handled, true);
869 // The event was redispatched because no one handles it.
870 EXPECT_EQ(redispatched.size(), 1u);
871
872 // Resolve redispatched event.
873 EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);
874
876}
877
878TEST(FlKeyboardManagerTest, TextInputPluginReturnsTrue) {
880 std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
881 gboolean manager_handled = false;
882 tester.recordRedispatchedEventsTo(redispatched);
883 tester.respondToTextInputWith(true);
884
885 // Dispatch a key event.
886 manager_handled = fl_keyboard_manager_handle_event(
887 tester.manager(),
888 fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA,
889 static_cast<GdkModifierType>(0), false));
890 tester.flushChannelMessages();
891 EXPECT_EQ(manager_handled, true);
892 // The event was not redispatched because text input plugin handles it.
893 EXPECT_EQ(redispatched.size(), 0u);
894
896}
897
898TEST(FlKeyboardManagerTest, CorrectLogicalKeyForLayouts) {
900
901 std::vector<CallRecord> call_records;
902 tester.recordEmbedderCallsTo(call_records);
903
904 auto sendTap = [&](guint8 keycode, guint keyval, guint8 group) {
907 true, keyval, keycode,
908 static_cast<GdkModifierType>(0), false, group));
911 false, keyval, keycode,
912 static_cast<GdkModifierType>(0), false, group));
913 };
914
915 /* US keyboard layout */
916
917 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
918 VERIFY_DOWN(kLogicalKeyA, "a");
919
920 sendTap(kKeyCodeKeyA, GDK_KEY_A, 0); // Shift-KeyA
921 VERIFY_DOWN(kLogicalKeyA, "A");
922
923 sendTap(kKeyCodeDigit1, GDK_KEY_1, 0); // Digit1
924 VERIFY_DOWN(kLogicalDigit1, "1");
925
926 sendTap(kKeyCodeDigit1, GDK_KEY_exclam, 0); // Shift-Digit1
927 VERIFY_DOWN(kLogicalDigit1, "!");
928
929 sendTap(kKeyCodeMinus, GDK_KEY_minus, 0); // Minus
930 VERIFY_DOWN(kLogicalMinus, "-");
931
932 sendTap(kKeyCodeMinus, GDK_KEY_underscore, 0); // Shift-Minus
933 VERIFY_DOWN(kLogicalUnderscore, "_");
934
935 /* French keyboard layout, group 3, which is when the input method is showing
936 * "Fr" */
937
938 tester.setLayout(kLayoutFrench);
939
940 sendTap(kKeyCodeKeyA, GDK_KEY_q, 3); // KeyA
941 VERIFY_DOWN(kLogicalKeyQ, "q");
942
943 sendTap(kKeyCodeKeyA, GDK_KEY_Q, 3); // Shift-KeyA
944 VERIFY_DOWN(kLogicalKeyQ, "Q");
945
946 sendTap(kKeyCodeSemicolon, GDK_KEY_m, 3); // ; but prints M
947 VERIFY_DOWN(kLogicalKeyM, "m");
948
949 sendTap(kKeyCodeKeyM, GDK_KEY_comma, 3); // M but prints ,
950 VERIFY_DOWN(kLogicalComma, ",");
951
952 sendTap(kKeyCodeDigit1, GDK_KEY_ampersand, 3); // Digit1
953 VERIFY_DOWN(kLogicalDigit1, "&");
954
955 sendTap(kKeyCodeDigit1, GDK_KEY_1, 3); // Shift-Digit1
956 VERIFY_DOWN(kLogicalDigit1, "1");
957
958 sendTap(kKeyCodeMinus, GDK_KEY_parenright, 3); // Minus
959 VERIFY_DOWN(kLogicalParenthesisRight, ")");
960
961 sendTap(kKeyCodeMinus, GDK_KEY_degree, 3); // Shift-Minus
962 VERIFY_DOWN(static_cast<uint32_t>(L'°'), "°");
963
964 /* French keyboard layout, group 0, which is pressing the "extra key for
965 * triggering input method" key once after switching to French IME. */
966
967 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
968 VERIFY_DOWN(kLogicalKeyA, "a");
969
970 sendTap(kKeyCodeDigit1, GDK_KEY_1, 0); // Digit1
971 VERIFY_DOWN(kLogicalDigit1, "1");
972
973 /* Russian keyboard layout, group 2 */
974 tester.setLayout(kLayoutRussian);
975
976 sendTap(kKeyCodeKeyA, GDK_KEY_Cyrillic_ef, 2); // KeyA
977 VERIFY_DOWN(kLogicalKeyA, "Ñ„");
978
979 sendTap(kKeyCodeDigit1, GDK_KEY_1, 2); // Shift-Digit1
980 VERIFY_DOWN(kLogicalDigit1, "1");
981
982 sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_Cyrillic_ha, 2);
983 VERIFY_DOWN(kLogicalBracketLeft, "Ñ…");
984
985 /* Russian keyboard layout, group 0 */
986 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
987 VERIFY_DOWN(kLogicalKeyA, "a");
988
989 sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_bracketleft, 0);
990 VERIFY_DOWN(kLogicalBracketLeft, "[");
991}
992
993TEST(FlKeyboardManagerTest, SynthesizeModifiersIfNeeded) {
995 std::vector<CallRecord> call_records;
996 tester.recordEmbedderCallsTo(call_records);
997
998 auto verifyModifierIsSynthesized = [&](GdkModifierType mask,
999 uint64_t physical, uint64_t logical) {
1000 // Modifier is pressed.
1001 guint state = mask;
1003 EXPECT_EQ(call_records.size(), 1u);
1004 EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeDown, physical,
1005 logical, NULL, true);
1006 // Modifier is released.
1007 state = state ^ mask;
1009 EXPECT_EQ(call_records.size(), 2u);
1010 EXPECT_KEY_EVENT(call_records[1], kFlutterKeyEventTypeUp, physical, logical,
1011 NULL, true);
1012 call_records.clear();
1013 };
1014
1015 // No modifiers pressed.
1016 guint state = 0;
1018 EXPECT_EQ(call_records.size(), 0u);
1019 call_records.clear();
1020
1021 // Press and release each modifier once.
1022 verifyModifierIsSynthesized(GDK_CONTROL_MASK, kPhysicalControlLeft,
1023 kLogicalControlLeft);
1024 verifyModifierIsSynthesized(GDK_META_MASK, kPhysicalMetaLeft,
1025 kLogicalMetaLeft);
1026 verifyModifierIsSynthesized(GDK_MOD1_MASK, kPhysicalAltLeft, kLogicalAltLeft);
1027 verifyModifierIsSynthesized(GDK_SHIFT_MASK, kPhysicalShiftLeft,
1028 kLogicalShiftLeft);
1029}
1030
1031TEST(FlKeyboardManagerTest, GetPressedState) {
1033 tester.respondToTextInputWith(true);
1034
1035 // Dispatch a key event.
1037 tester.manager(),
1038 fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA,
1039 static_cast<GdkModifierType>(0), false));
1040
1041 GHashTable* pressedState =
1043 EXPECT_EQ(g_hash_table_size(pressedState), 1u);
1044
1045 gpointer physical_key =
1046 g_hash_table_lookup(pressedState, uint64_to_gpointer(kPhysicalKeyA));
1047 EXPECT_EQ(gpointer_to_uint64(physical_key), kLogicalKeyA);
1048}
1049
1050TEST(FlKeyboardPluginTest, KeyboardChannelGetPressedState) {
1051 ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
1052
1054 messenger, FL_KEYBOARD_VIEW_DELEGATE(fl_mock_view_delegate_new()));
1055 EXPECT_NE(manager, nullptr);
1056
1057 g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
1058 g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
1059 FL_METHOD_CODEC(codec), kGetKeyboardStateMethod, nullptr, nullptr);
1060
1061 g_autoptr(FlValue) response = fl_value_new_map();
1062 fl_value_set_take(response, fl_value_new_int(kMockPhysicalKey),
1063 fl_value_new_int(kMockLogicalKey));
1064 EXPECT_CALL(messenger,
1066 ::testing::Eq<FlBinaryMessenger*>(messenger), ::testing::_,
1067 MethodSuccessResponse(response), ::testing::_))
1068 .WillOnce(::testing::Return(true));
1069
1070 messenger.ReceiveMessage(kKeyboardChannelName, message);
1071}
1072
1073// The following layout data is generated using DEBUG_PRINT_LAYOUT.
1074
1075const MockGroupLayoutData kLayoutUs0{{
1076 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
1077 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
1078 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
1079 0xffff, 0x0031, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
1080 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
1081 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
1082 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
1083 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
1084 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
1085 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
1086 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
1087 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
1088 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
1089 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
1090 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
1091 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
1092 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
1093 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
1094 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
1095 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
1096 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
1097 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
1098 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
1099 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
1100 0xffff, 0xffff, 0x003c, 0x003e, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
1101 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
1102 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x64
1103 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
1104 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
1105 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
1106 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
1107 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
1108 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
1109}};
1110
1111const MockGroupLayoutData kLayoutRussian0{
1112 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
1113 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
1114 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
1115 0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
1116 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
1117 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
1118 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
1119 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
1120 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
1121 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
1122 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
1123 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
1124 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
1125 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
1126 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
1127 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
1128 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
1129 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
1130 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
1131 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
1132 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
1133 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
1134 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
1135 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
1136 0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
1137 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
1138 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
1139 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
1140 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
1141 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
1142 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
1143 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
1144 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
1145};
1146
1147const MockGroupLayoutData kLayoutRussian2{{
1148 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
1149 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
1150 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
1151 0xffff, 0x0031, 0x0021, 0x0000, 0x0031, 0x0021, 0x0032, 0x0022, // 0x08
1152 0x0033, 0x06b0, 0x0034, 0x003b, 0x0035, 0x0025, 0x0036, 0x003a, // 0x0c
1153 0x0037, 0x003f, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
1154 0x002d, 0x005f, 0x003d, 0x002b, 0x0071, 0x0051, 0x0000, 0x0000, // 0x14
1155 0x06ca, 0x06ea, 0x06c3, 0x06e3, 0x06d5, 0x06f5, 0x06cb, 0x06eb, // 0x18
1156 0x06c5, 0x06e5, 0x06ce, 0x06ee, 0x06c7, 0x06e7, 0x06db, 0x06fb, // 0x1c
1157 0x06dd, 0x06fd, 0x06da, 0x06fa, 0x06c8, 0x06e8, 0x06df, 0x06ff, // 0x20
1158 0x0061, 0x0041, 0x0041, 0x0000, 0x06c6, 0x06e6, 0x06d9, 0x06f9, // 0x24
1159 0x06d7, 0x06f7, 0x06c1, 0x06e1, 0x06d0, 0x06f0, 0x06d2, 0x06f2, // 0x28
1160 0x06cf, 0x06ef, 0x06cc, 0x06ec, 0x06c4, 0x06e4, 0x06d6, 0x06f6, // 0x2c
1161 0x06dc, 0x06fc, 0x06a3, 0x06b3, 0x007c, 0x0000, 0x005c, 0x002f, // 0x30
1162 0x06d1, 0x06f1, 0x06de, 0x06fe, 0x06d3, 0x06f3, 0x06cd, 0x06ed, // 0x34
1163 0x06c9, 0x06e9, 0x06d4, 0x06f4, 0x06d8, 0x06f8, 0x06c2, 0x06e2, // 0x38
1164 0x06c0, 0x06e0, 0x002e, 0x002c, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
1165 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
1166 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
1167 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
1168 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
1169 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
1170 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
1171 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
1172 0xffff, 0xffff, 0x003c, 0x003e, 0x002f, 0x007c, 0xffff, 0xffff, // 0x5c
1173 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
1174 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x64
1175 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0x0000, // 0x68
1176 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
1177 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
1178 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
1179 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1, // 0x78
1180 0x00b1, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x7c
1181}};
1182
1183const MockGroupLayoutData kLayoutFrench0 = {
1184 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
1185 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
1186 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
1187 0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
1188 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
1189 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
1190 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
1191 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
1192 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
1193 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
1194 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
1195 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
1196 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
1197 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
1198 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
1199 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
1200 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
1201 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
1202 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
1203 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
1204 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
1205 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
1206 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
1207 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
1208 0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
1209 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
1210 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
1211 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
1212 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
1213 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
1214 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
1215 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
1216 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
1217};
1218
1219const MockGroupLayoutData kLayoutFrench3 = {
1220 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
1221 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
1222 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
1223 0x0000, 0xffff, 0x0000, 0x0000, 0x0026, 0x0031, 0x00e9, 0x0032, // 0x08
1224 0x0022, 0x0033, 0x0027, 0x0034, 0x0028, 0x0035, 0x002d, 0x0036, // 0x0c
1225 0x00e8, 0x0037, 0x005f, 0x0038, 0x00e7, 0x0039, 0x00e0, 0x0030, // 0x10
1226 0x0029, 0x00b0, 0x003d, 0x002b, 0x0000, 0x0000, 0x0061, 0x0041, // 0x14
1227 0x0061, 0x0041, 0x007a, 0x005a, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
1228 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
1229 0x006f, 0x004f, 0x0070, 0x0050, 0xffff, 0xffff, 0x0024, 0x00a3, // 0x20
1230 0x0041, 0x0000, 0x0000, 0x0000, 0x0071, 0x0051, 0x0073, 0x0053, // 0x24
1231 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
1232 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x006d, 0x004d, // 0x2c
1233 0x00f9, 0x0025, 0x00b2, 0x007e, 0x0000, 0x0000, 0x002a, 0x00b5, // 0x30
1234 0x0077, 0x0057, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
1235 0x0062, 0x0042, 0x006e, 0x004e, 0x002c, 0x003f, 0x003b, 0x002e, // 0x38
1236 0x003a, 0x002f, 0x0021, 0x00a7, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
1237 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
1238 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
1239 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
1240 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
1241 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
1242 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
1243 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
1244 0xffff, 0x003c, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
1245 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
1246 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
1247 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
1248 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
1249 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
1250 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
1251 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, // 0x78
1252 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x7c
1253};
1254
1255const MockLayoutData kLayoutUs{&kLayoutUs0};
1256const MockLayoutData kLayoutRussian{&kLayoutRussian0, nullptr,
1257 &kLayoutRussian2};
1258const MockLayoutData kLayoutFrench{&kLayoutFrench0, nullptr, nullptr,
1259 &kLayoutFrench3};
1260
1261} // namespace
#define TEST(S, s, D, expected)
void(* FlutterKeyEventCallback)(bool, void *)
Definition embedder.h:1153
@ kFlutterKeyEventTypeDown
Definition embedder.h:1074
@ kFlutterKeyEventTypeUp
Definition embedder.h:1073
SkBitmap source
Definition examples.cpp:28
G_DEFINE_TYPE_WITH_CODE(FlAccessibleNode, fl_accessible_node, ATK_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(ATK_TYPE_COMPONENT, fl_accessible_node_component_interface_init) G_IMPLEMENT_INTERFACE(ATK_TYPE_ACTION, fl_accessible_node_action_interface_init)) static gboolean flag_is_changed(FlutterSemanticsFlag old_flags
AtkStateType state
G_BEGIN_DECLS G_DECLARE_FINAL_TYPE(FlAccessibleTextField, fl_accessible_text_field, FL, ACCESSIBLE_TEXT_FIELD, FlAccessibleNode)
G_MODULE_EXPORT gboolean fl_binary_messenger_send_response(FlBinaryMessenger *self, FlBinaryMessengerResponseHandle *response_handle, GBytes *response, GError **error)
G_MODULE_EXPORT GObject typedef void(* FlBinaryMessengerMessageHandler)(FlBinaryMessenger *messenger, const gchar *channel, GBytes *message, FlBinaryMessengerResponseHandle *response_handle, gpointer user_data)
G_MODULE_EXPORT FlJsonMessageCodec * fl_json_message_codec_new()
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlKeyEvent * event
static FlKeyEvent * fl_key_event_new_by_mock(guint32 time_in_milliseconds, bool is_press, guint keyval, guint16 keycode, GdkModifierType state, gboolean is_modifier)
typedefG_BEGIN_DECLS struct _FlKeyboardManager FlKeyboardManager
gboolean fl_keyboard_manager_is_state_clear(FlKeyboardManager *self)
gboolean fl_keyboard_manager_handle_event(FlKeyboardManager *self, FlKeyEvent *event)
GHashTable * fl_keyboard_manager_get_pressed_state(FlKeyboardManager *self)
static constexpr char kGetKeyboardStateMethod[]
FlKeyboardManager * fl_keyboard_manager_new(FlBinaryMessenger *messenger, FlKeyboardViewDelegate *view_delegate)
void fl_keyboard_manager_sync_modifier_if_needed(FlKeyboardManager *self, guint state, double event_time)
#define EXPECT_KEY_EVENT(RECORD, TYPE, PHYSICAL, LOGICAL, CHAR, SYNTHESIZED)
#define FL_MOCK_KEY_BINARY_MESSENGER_GET_PRIVATE(obj)
#define FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(obj)
#define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR)
std::function< void()> KeyboardLayoutNotifier
G_MODULE_EXPORT GBytes * fl_message_codec_encode_message(FlMessageCodec *self, FlValue *message, GError **error)
FlMethodResponse * fl_method_codec_decode_response(FlMethodCodec *self, GBytes *message, GError **error)
GBytes * fl_method_codec_encode_method_call(FlMethodCodec *self, const gchar *name, FlValue *args, GError **error)
G_MODULE_EXPORT FlValue * fl_method_response_get_result(FlMethodResponse *self, GError **error)
FlPixelBufferTexturePrivate * priv
const uint8_t uint32_t uint32_t GError ** error
MATCHER_P(SuccessResponse, result, "")
G_MODULE_EXPORT FlStandardMethodCodec * fl_standard_method_codec_new()
GAsyncResult * result
uint32_t * target
G_MODULE_EXPORT FlValue * fl_value_new_map()
Definition fl_value.cc:366
G_MODULE_EXPORT void fl_value_set_string_take(FlValue *self, const gchar *key, FlValue *value)
Definition fl_value.cc:650
G_MODULE_EXPORT FlValue * fl_value_new_bool(bool value)
Definition fl_value.cc:255
G_MODULE_EXPORT void fl_value_set_take(FlValue *self, FlValue *key, FlValue *value)
Definition fl_value.cc:618
G_MODULE_EXPORT FlValue * fl_value_new_int(int64_t value)
Definition fl_value.cc:262
G_MODULE_EXPORT bool fl_value_equal(FlValue *a, FlValue *b)
Definition fl_value.cc:471
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition fl_value.h:42
FlutterKeyboardManager * manager
gpointer uint64_to_gpointer(uint64_t number)
Definition key_mapping.h:17
uint64_t gpointer_to_uint64(gpointer pointer)
Definition key_mapping.h:13
CallbackHandler callback_handler
Win32Message message
static void fl_mock_binary_messenger_resize_channel(FlBinaryMessenger *messenger, const gchar *channel, int64_t new_size)
static void fl_mock_binary_messenger_set_warns_on_channel_overflow(FlBinaryMessenger *messenger, const gchar *channel, bool warns)
return FALSE
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font manager
Definition switches.h:218
#define EXPECT_TRUE(handle)
Definition unit_test.h:685