| Flutter Linux Embedder |

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_engine_private.h"
12 #include "flutter/shell/platform/linux/key_mapping.h"
13 #include "flutter/shell/platform/linux/testing/fl_mock_binary_messenger.h"
14 #include "flutter/shell/platform/linux/testing/mock_keymap.h"
15
16 #include "gtest/gtest.h"
17
18 // Define compound `expect` in macros. If they were defined in functions, the
19 // stacktrace wouldn't print where the function is called in the unit tests.
20
21 #define EXPECT_KEY_EVENT(RECORD, TYPE, PHYSICAL, LOGICAL, CHAR, SYNTHESIZED) \
22 EXPECT_EQ((RECORD)->event_type, (TYPE)); \
23 EXPECT_EQ((RECORD)->event_physical, (PHYSICAL)); \
24 EXPECT_EQ((RECORD)->event_logical, (LOGICAL)); \
25 EXPECT_STREQ((RECORD)->event_character, (CHAR)); \
26 EXPECT_EQ((RECORD)->event_synthesized, (SYNTHESIZED));
27
28 #define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR) \
29 EXPECT_EQ(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)) \
30 ->event_type, \
31 kFlutterKeyEventTypeDown); \
32 EXPECT_EQ(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)) \
33 ->event_logical, \
34 (OUT_LOGICAL)); \
35 EXPECT_STREQ(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)) \
36 ->event_character, \
37 (OUT_CHAR)); \
38 EXPECT_EQ(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)) \
39 ->event_synthesized, \
40 false); \
41 g_ptr_array_set_size(call_records, 0)
42
43 typedef struct {
44 FlutterKeyEventType event_type;
45 uint64_t event_physical;
46 uint64_t event_logical;
47 gchar* event_character;
48 bool event_synthesized;
49 FlutterKeyEventCallback callback;
50 void* callback_user_data;
51 } CallRecord;
52
53 static CallRecord* call_record_new(const FlutterKeyEvent* event,
54 FlutterKeyEventCallback callback,
55 void* callback_user_data) {
56 CallRecord* record = g_new0(CallRecord, 1);
57 record->event_type = event->type;
58 record->event_physical = event->physical;
59 record->event_logical = event->logical;
60 record->event_character = g_strdup(event->character);
61 record->event_synthesized = event->synthesized;
62 record->callback = callback;
63 record->callback_user_data = callback_user_data;
64 return record;
65 }
66
67 static void call_record_free(CallRecord* record) {
68 g_free(record->event_character);
69 g_free(record);
70 }
71
72 static void call_record_respond(CallRecord* record, bool handled) {
73 if (record->callback != nullptr) {
74 record->callback(handled, record->callback_user_data);
75 }
76 }
77
78 namespace {
79 using ::flutter::testing::keycodes::kLogicalAltLeft;
80 using ::flutter::testing::keycodes::kLogicalBracketLeft;
81 using ::flutter::testing::keycodes::kLogicalComma;
82 using ::flutter::testing::keycodes::kLogicalControlLeft;
83 using ::flutter::testing::keycodes::kLogicalDigit1;
84 using ::flutter::testing::keycodes::kLogicalKeyA;
85 using ::flutter::testing::keycodes::kLogicalKeyB;
86 using ::flutter::testing::keycodes::kLogicalKeyM;
87 using ::flutter::testing::keycodes::kLogicalKeyQ;
88 using ::flutter::testing::keycodes::kLogicalMetaLeft;
89 using ::flutter::testing::keycodes::kLogicalMinus;
90 using ::flutter::testing::keycodes::kLogicalParenthesisRight;
91 using ::flutter::testing::keycodes::kLogicalSemicolon;
92 using ::flutter::testing::keycodes::kLogicalShiftLeft;
93 using ::flutter::testing::keycodes::kLogicalUnderscore;
94
95 using ::flutter::testing::keycodes::kPhysicalAltLeft;
96 using ::flutter::testing::keycodes::kPhysicalControlLeft;
97 using ::flutter::testing::keycodes::kPhysicalKeyA;
98 using ::flutter::testing::keycodes::kPhysicalKeyB;
99 using ::flutter::testing::keycodes::kPhysicalMetaLeft;
100 using ::flutter::testing::keycodes::kPhysicalShiftLeft;
101
102 constexpr guint16 kKeyCodeKeyA = 0x26u;
103 constexpr guint16 kKeyCodeKeyB = 0x38u;
104 constexpr guint16 kKeyCodeKeyM = 0x3au;
105 constexpr guint16 kKeyCodeDigit1 = 0x0au;
106 constexpr guint16 kKeyCodeMinus = 0x14u;
107 constexpr guint16 kKeyCodeSemicolon = 0x2fu;
108 constexpr guint16 kKeyCodeKeyLeftBracket = 0x22u;
109
110 // All key clues for a keyboard layout.
111 //
112 // The index is (keyCode * 2 + hasShift), where each value is the character for
113 // this key (GTK only supports UTF-16.) Since the maximum keycode of interest
114 // is 128, it has a total of 256 entries..
115 typedef std::array<uint32_t, 256> MockGroupLayoutData;
116 typedef std::vector<const MockGroupLayoutData*> MockLayoutData;
117
118 extern const MockLayoutData kLayoutRussian;
119 extern const MockLayoutData kLayoutFrench;
120
121 G_BEGIN_DECLS
122
123 G_DECLARE_FINAL_TYPE(FlMockViewDelegate,
124 fl_mock_view_delegate,
125 FL,
126 MOCK_VIEW_DELEGATE,
127 GObject);
128
129 G_END_DECLS
130
131 struct _FlMockViewDelegate {
132 GObject parent_instance;
133 bool text_filter_result;
134 };
135
136 static void fl_mock_view_keyboard_delegate_iface_init(
137 FlKeyboardViewDelegateInterface* iface);
138
139 G_DEFINE_TYPE_WITH_CODE(
140 FlMockViewDelegate,
141 fl_mock_view_delegate,
142 G_TYPE_OBJECT,
143 G_IMPLEMENT_INTERFACE(fl_keyboard_view_delegate_get_type(),
144 fl_mock_view_keyboard_delegate_iface_init))
145
146 static void fl_mock_view_delegate_init(FlMockViewDelegate* self) {}
147
148 static void fl_mock_view_delegate_class_init(FlMockViewDelegateClass* klass) {}
149
150 static gboolean fl_mock_view_keyboard_text_filter_key_press(
151 FlKeyboardViewDelegate* view_delegate,
152 FlKeyEvent* event) {
153 FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(view_delegate);
154 return self->text_filter_result;
155 }
156
157 static void fl_mock_view_keyboard_delegate_iface_init(
158 FlKeyboardViewDelegateInterface* iface) {
159 iface->text_filter_key_press = fl_mock_view_keyboard_text_filter_key_press;
160 }
161
162 static FlMockViewDelegate* fl_mock_view_delegate_new() {
163 FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(
164 g_object_new(fl_mock_view_delegate_get_type(), nullptr));
165
166 // Added to stop compiler complaining about an unused function.
167 FL_IS_MOCK_VIEW_DELEGATE(self);
168
169 return self;
170 }
171
172 static void fl_mock_view_set_text_filter_result(FlMockViewDelegate* self,
173 bool result) {
174 self->text_filter_result = result;
175 }
176
177 // Block until all GdkMainLoop messages are processed, which is basically used
178 // only for channel messages.
179 static void flush_channel_messages() {
180 GMainLoop* loop = g_main_loop_new(nullptr, 0);
181 g_idle_add(
182 [](gpointer data) {
183 g_autoptr(GMainLoop) loop = reinterpret_cast<GMainLoop*>(data);
184 g_main_loop_quit(loop);
185 return FALSE;
186 },
187 loop);
188 g_main_loop_run(loop);
189 }
190
191 // Dispatch each of the given events, expect their results to be false
192 // (unhandled), and clear the event array.
193 //
194 // Returns the number of events redispatched. If any result is unexpected
195 // (handled), return a minus number `-x` instead, where `x` is the index of
196 // the first unexpected redispatch.
197 int redispatch_events_and_clear(FlKeyboardManager* manager, GPtrArray* events) {
198 guint event_count = events->len;
199 for (guint event_id = 0; event_id < event_count; event_id += 1) {
200 FlKeyEvent* event = FL_KEY_EVENT(g_ptr_array_index(events, event_id));
201 EXPECT_FALSE(fl_keyboard_manager_handle_event(manager, event));
202 }
203 g_ptr_array_set_size(events, 0);
204 return event_count;
205 }
206
207 // Make sure that the keyboard can be disposed without crashes when there are
208 // unresolved pending events.
209 TEST(FlKeyboardManagerTest, DisposeWithUnresolvedPends) {
210 ::testing::NiceMock<flutter::testing::MockKeymap> mock_keymap;
211
212 g_autoptr(FlDartProject) project = fl_dart_project_new();
213 g_autoptr(FlEngine) engine = fl_engine_new(project);
214 g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new();
215 g_autoptr(FlKeyboardManager) manager =
216 fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view));
217
218 g_autoptr(GError) error = nullptr;
219 EXPECT_TRUE(fl_engine_start(engine, &error));
220 EXPECT_EQ(error, nullptr);
221
222 // Don't handle first event.
223 fl_keyboard_manager_set_send_key_event_handler(
224 manager,
225 [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback,
226 void* callback_user_data, gpointer user_data) {},
227 nullptr);
228 g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
229 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
230 fl_keyboard_manager_handle_event(manager, event1);
231
232 // Handle second event.
233 fl_keyboard_manager_set_send_key_event_handler(
234 manager,
235 [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback,
236 void* callback_user_data,
237 gpointer user_data) { callback(true, callback_user_data); },
238 nullptr);
239 g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
240 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
241 fl_keyboard_manager_handle_event(manager, event2);
242
243 // Passes if the cleanup does not crash.
244 }
245
246 TEST(FlKeyboardManagerTest, SingleDelegateWithAsyncResponds) {
247 ::testing::NiceMock<flutter::testing::MockKeymap> mock_keymap;
248
249 g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
250 fl_mock_binary_messenger_set_json_message_channel(
251 messenger, "flutter/keyevent",
252 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
253 gpointer user_data) {
254 g_autoptr(FlValue) return_value = fl_value_new_map();
255 fl_value_set_string_take(return_value, "handled",
256 fl_value_new_bool(FALSE));
257 return fl_value_ref(return_value);
258 },
259 nullptr);
260
261 g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new();
262 g_autoptr(FlEngine) engine =
263 FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
264 FL_BINARY_MESSENGER(messenger), nullptr));
265 g_autoptr(FlKeyboardManager) manager =
266 fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view));
267 fl_keyboard_manager_set_lookup_key_handler(
268 manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; },
269 nullptr);
270
271 /// Test 1: One event that is handled by the framework
272 g_autoptr(GPtrArray) call_records = g_ptr_array_new_with_free_func(
273 reinterpret_cast<GDestroyNotify>(call_record_free));
274 fl_keyboard_manager_set_send_key_event_handler(
275 manager,
276 [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback,
277 void* callback_user_data, gpointer user_data) {
278 GPtrArray* call_records = static_cast<GPtrArray*>(user_data);
279 g_ptr_array_add(call_records,
280 call_record_new(event, callback, callback_user_data));
281 },
282 call_records);
283 g_autoptr(GPtrArray) redispatched =
284 g_ptr_array_new_with_free_func(g_object_unref);
285 fl_keyboard_manager_set_redispatch_handler(
286 manager,
287 [](FlKeyEvent* event, gpointer user_data) {
288 GPtrArray* redispatched = static_cast<GPtrArray*>(user_data);
289 g_ptr_array_add(redispatched, g_object_ref(event));
290 },
291 redispatched);
292
293 // Dispatch a key event
294 g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
295 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
296 EXPECT_TRUE(fl_keyboard_manager_handle_event(manager, event1));
297 flush_channel_messages();
298 EXPECT_EQ(redispatched->len, 0u);
299 EXPECT_EQ(call_records->len, 1u);
300 EXPECT_KEY_EVENT(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)),
301 kFlutterKeyEventTypeDown, kPhysicalKeyA, kLogicalKeyA, "a",
302 false);
303
304 call_record_respond(
305 static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)), true);
306 flush_channel_messages();
307 EXPECT_EQ(redispatched->len, 0u);
308 EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager));
309 g_ptr_array_set_size(call_records, 0);
310
311 /// Test 2: Two events that are unhandled by the framework
312 g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
313 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
314 EXPECT_TRUE(fl_keyboard_manager_handle_event(manager, event2));
315 flush_channel_messages();
316 EXPECT_EQ(redispatched->len, 0u);
317 EXPECT_EQ(call_records->len, 1u);
318 EXPECT_KEY_EVENT(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)),
319 kFlutterKeyEventTypeUp, kPhysicalKeyA, kLogicalKeyA, nullptr,
320 false);
321
322 // Dispatch another key event
323 g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
324 0, TRUE, kKeyCodeKeyB, GDK_KEY_b, static_cast<GdkModifierType>(0), 0);
325 EXPECT_TRUE(fl_keyboard_manager_handle_event(manager, event3));
326 flush_channel_messages();
327 EXPECT_EQ(redispatched->len, 0u);
328 EXPECT_EQ(call_records->len, 2u);
329 EXPECT_KEY_EVENT(static_cast<CallRecord*>(g_ptr_array_index(call_records, 1)),
330 kFlutterKeyEventTypeDown, kPhysicalKeyB, kLogicalKeyB, "b",
331 false);
332
333 // Resolve the second event first to test out-of-order response
334 call_record_respond(
335 static_cast<CallRecord*>(g_ptr_array_index(call_records, 1)), false);
336 EXPECT_EQ(redispatched->len, 1u);
337 EXPECT_EQ(
338 fl_key_event_get_keyval(FL_KEY_EVENT(g_ptr_array_index(redispatched, 0))),
339 0x62u);
340 call_record_respond(
341 static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)), false);
342 flush_channel_messages();
343 EXPECT_EQ(redispatched->len, 2u);
344 EXPECT_EQ(
345 fl_key_event_get_keyval(FL_KEY_EVENT(g_ptr_array_index(redispatched, 1))),
346 0x61u);
347
348 EXPECT_FALSE(fl_keyboard_manager_is_state_clear(manager));
349 g_ptr_array_set_size(call_records, 0);
350
351 // Resolve redispatches
352 EXPECT_EQ(redispatch_events_and_clear(manager, redispatched), 2);
353 flush_channel_messages();
354 EXPECT_EQ(call_records->len, 0u);
355 EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager));
356
357 /// Test 3: Dispatch the same event again to ensure that prevention from
358 /// redispatching only works once.
359 g_autoptr(FlKeyEvent) event4 = fl_key_event_new(
360 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
361 EXPECT_TRUE(fl_keyboard_manager_handle_event(manager, event4));
362 flush_channel_messages();
363 EXPECT_EQ(redispatched->len, 0u);
364 EXPECT_EQ(call_records->len, 1u);
365
366 call_record_respond(
367 static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)), true);
368 EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager));
369 }
370
371 TEST(FlKeyboardManagerTest, SingleDelegateWithSyncResponds) {
372 ::testing::NiceMock<flutter::testing::MockKeymap> mock_keymap;
373
374 g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
375 fl_mock_binary_messenger_set_json_message_channel(
376 messenger, "flutter/keyevent",
377 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
378 gpointer user_data) {
379 g_autoptr(FlValue) return_value = fl_value_new_map();
380 fl_value_set_string_take(return_value, "handled",
381 fl_value_new_bool(FALSE));
382 return fl_value_ref(return_value);
383 },
384 nullptr);
385
386 g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new();
387 g_autoptr(FlEngine) engine =
388 FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
389 FL_BINARY_MESSENGER(messenger), nullptr));
390 g_autoptr(FlKeyboardManager) manager =
391 fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view));
392 fl_keyboard_manager_set_lookup_key_handler(
393 manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; },
394 nullptr);
395
396 /// Test 1: One event that is handled by the framework
397 g_autoptr(GPtrArray) call_records = g_ptr_array_new_with_free_func(
398 reinterpret_cast<GDestroyNotify>(call_record_free));
399 fl_keyboard_manager_set_send_key_event_handler(
400 manager,
401 [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback,
402 void* callback_user_data, gpointer user_data) {
403 GPtrArray* call_records = static_cast<GPtrArray*>(user_data);
404 g_ptr_array_add(call_records,
405 call_record_new(event, callback, callback_user_data));
406 callback(true, callback_user_data);
407 },
408 call_records);
409 g_autoptr(GPtrArray) redispatched =
410 g_ptr_array_new_with_free_func(g_object_unref);
411 fl_keyboard_manager_set_redispatch_handler(
412 manager,
413 [](FlKeyEvent* event, gpointer user_data) {
414 GPtrArray* redispatched = static_cast<GPtrArray*>(user_data);
415 g_ptr_array_add(redispatched, g_object_ref(event));
416 },
417 redispatched);
418
419 // Dispatch a key event
420 g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
421 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
422 EXPECT_TRUE(fl_keyboard_manager_handle_event(manager, event1));
423 flush_channel_messages();
424 EXPECT_EQ(call_records->len, 1u);
425 EXPECT_KEY_EVENT(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)),
426 kFlutterKeyEventTypeDown, kPhysicalKeyA, kLogicalKeyA, "a",
427 false);
428 EXPECT_EQ(redispatched->len, 0u);
429 g_ptr_array_set_size(call_records, 0);
430
431 EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager));
432 g_ptr_array_set_size(redispatched, 0);
433
434 /// Test 2: An event unhandled by the framework
435 fl_keyboard_manager_set_send_key_event_handler(
436 manager,
437 [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback,
438 void* callback_user_data, gpointer user_data) {
439 GPtrArray* call_records = static_cast<GPtrArray*>(user_data);
440 g_ptr_array_add(call_records,
441 call_record_new(event, callback, callback_user_data));
442 callback(false, callback_user_data);
443 },
444 call_records);
445 g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
446 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
447 EXPECT_TRUE(fl_keyboard_manager_handle_event(manager, event2));
448 flush_channel_messages();
449 EXPECT_EQ(call_records->len, 1u);
450 EXPECT_KEY_EVENT(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)),
451 kFlutterKeyEventTypeUp, kPhysicalKeyA, kLogicalKeyA, nullptr,
452 false);
453 EXPECT_EQ(redispatched->len, 1u);
454 g_ptr_array_set_size(call_records, 0);
455
456 EXPECT_FALSE(fl_keyboard_manager_is_state_clear(manager));
457
458 EXPECT_EQ(redispatch_events_and_clear(manager, redispatched), 1);
459 EXPECT_EQ(call_records->len, 0u);
460
461 EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager));
462 }
463
464 static void channel_respond(FlMockBinaryMessenger* messenger,
465 GTask* task,
466 gboolean handled) {
467 g_autoptr(FlValue) value = fl_value_new_map();
468 fl_value_set_string_take(value, "handled", fl_value_new_bool(handled));
469 fl_mock_binary_messenger_json_message_channel_respond(messenger, task, value);
470 }
471
472 TEST(FlKeyboardManagerTest, WithTwoAsyncDelegates) {
473 ::testing::NiceMock<flutter::testing::MockKeymap> mock_keymap;
474
475 g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
476 g_autoptr(GPtrArray) channel_calls =
477 g_ptr_array_new_with_free_func(g_object_unref);
478 fl_mock_binary_messenger_set_json_message_channel(
479 messenger, "flutter/keyevent",
480 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
481 gpointer user_data) {
482 GPtrArray* call_records = static_cast<GPtrArray*>(user_data);
483 g_ptr_array_add(call_records, g_object_ref(task));
484 // Will respond async
485 return static_cast<FlValue*>(nullptr);
486 },
487 channel_calls);
488
489 g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new();
490 g_autoptr(FlEngine) engine =
491 FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
492 FL_BINARY_MESSENGER(messenger), nullptr));
493 g_autoptr(FlKeyboardManager) manager =
494 fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view));
495 fl_keyboard_manager_set_lookup_key_handler(
496 manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; },
497 nullptr);
498
499 g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
500 reinterpret_cast<GDestroyNotify>(call_record_free));
501 fl_keyboard_manager_set_send_key_event_handler(
502 manager,
503 [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback,
504 void* callback_user_data, gpointer user_data) {
505 GPtrArray* call_records = static_cast<GPtrArray*>(user_data);
506 g_ptr_array_add(call_records,
507 call_record_new(event, callback, callback_user_data));
508 },
509 embedder_call_records);
510 g_autoptr(GPtrArray) redispatched =
511 g_ptr_array_new_with_free_func(g_object_unref);
512 fl_keyboard_manager_set_redispatch_handler(
513 manager,
514 [](FlKeyEvent* event, gpointer user_data) {
515 GPtrArray* redispatched = static_cast<GPtrArray*>(user_data);
516 g_ptr_array_add(redispatched, g_object_ref(event));
517 },
518 redispatched);
519
520 /// Test 1: One delegate responds true, the other false
521
522 g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
523 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
524 EXPECT_TRUE(fl_keyboard_manager_handle_event(manager, event1));
525
526 EXPECT_EQ(redispatched->len, 0u);
527 EXPECT_EQ(embedder_call_records->len, 1u);
528 EXPECT_EQ(channel_calls->len, 1u);
529
530 call_record_respond(
531 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
532 true);
533 channel_respond(messenger,
534 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
535 FALSE);
536 flush_channel_messages();
537 EXPECT_EQ(redispatched->len, 0u);
538
539 EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager));
540 g_ptr_array_set_size(embedder_call_records, 0);
541 g_ptr_array_set_size(channel_calls, 0);
542
543 /// Test 2: All delegates respond false
544 g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
545 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
546 EXPECT_TRUE(fl_keyboard_manager_handle_event(manager, event2));
547
548 EXPECT_EQ(redispatched->len, 0u);
549 EXPECT_EQ(embedder_call_records->len, 1u);
550 EXPECT_EQ(channel_calls->len, 1u);
551
552 call_record_respond(
553 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
554 false);
555 channel_respond(messenger,
556 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
557 FALSE);
558
559 g_ptr_array_set_size(embedder_call_records, 0);
560 g_ptr_array_set_size(channel_calls, 0);
561
562 // Resolve redispatch
563 flush_channel_messages();
564 EXPECT_EQ(redispatched->len, 1u);
565 EXPECT_EQ(redispatch_events_and_clear(manager, redispatched), 1);
566 EXPECT_EQ(embedder_call_records->len, 0u);
567 EXPECT_EQ(channel_calls->len, 0u);
568
569 EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager));
570 }
571
572 TEST(FlKeyboardManagerTest, TextInputHandlerReturnsFalse) {
573 ::testing::NiceMock<flutter::testing::MockKeymap> mock_keymap;
574
575 g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
576 fl_mock_binary_messenger_set_json_message_channel(
577 messenger, "flutter/keyevent",
578 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
579 gpointer user_data) {
580 g_autoptr(FlValue) return_value = fl_value_new_map();
581 fl_value_set_string_take(return_value, "handled",
582 fl_value_new_bool(FALSE));
583 return fl_value_ref(return_value);
584 },
585 nullptr);
586 g_autoptr(FlEngine) engine =
587 FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
588 FL_BINARY_MESSENGER(messenger), nullptr));
589 g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new();
590 g_autoptr(FlKeyboardManager) manager =
591 fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view));
592
593 // Dispatch a key event.
594 fl_mock_view_set_text_filter_result(view, FALSE);
595 gboolean redispatched = FALSE;
596 fl_keyboard_manager_set_redispatch_handler(
597 manager,
598 [](FlKeyEvent* event, gpointer user_data) {
599 gboolean* redispatched = static_cast<gboolean*>(user_data);
600 *redispatched = TRUE;
601 },
602 &redispatched);
603 fl_keyboard_manager_set_send_key_event_handler(
604 manager,
605 [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback,
606 void* callback_user_data,
607 gpointer user_data) { callback(false, callback_user_data); },
608 nullptr);
609 g_autoptr(FlKeyEvent) event = fl_key_event_new(
610 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
611 EXPECT_TRUE(fl_keyboard_manager_handle_event(manager, event));
612 flush_channel_messages();
613 // The event was redispatched because no one handles it.
614 EXPECT_TRUE(redispatched);
615 }
616
617 TEST(FlKeyboardManagerTest, TextInputHandlerReturnsTrue) {
618 ::testing::NiceMock<flutter::testing::MockKeymap> mock_keymap;
619
620 g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
621 fl_mock_binary_messenger_set_json_message_channel(
622 messenger, "flutter/keyevent",
623 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
624 gpointer user_data) {
625 g_autoptr(FlValue) return_value = fl_value_new_map();
626 fl_value_set_string_take(return_value, "handled",
627 fl_value_new_bool(FALSE));
628 return fl_value_ref(return_value);
629 },
630 nullptr);
631 g_autoptr(FlEngine) engine =
632 FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
633 FL_BINARY_MESSENGER(messenger), nullptr));
634 g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new();
635 g_autoptr(FlKeyboardManager) manager =
636 fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view));
637
638 // Dispatch a key event.
639 fl_mock_view_set_text_filter_result(view, TRUE);
640 gboolean redispatched = FALSE;
641 fl_keyboard_manager_set_redispatch_handler(
642 manager,
643 [](FlKeyEvent* event, gpointer user_data) {
644 gboolean* redispatched = static_cast<gboolean*>(user_data);
645 *redispatched = TRUE;
646 },
647 &redispatched);
648 fl_keyboard_manager_set_send_key_event_handler(
649 manager,
650 [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback,
651 void* callback_user_data,
652 gpointer user_data) { callback(false, callback_user_data); },
653 nullptr);
654 g_autoptr(FlKeyEvent) event = fl_key_event_new(
655 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
656 EXPECT_TRUE(fl_keyboard_manager_handle_event(manager, event));
657 flush_channel_messages();
658 // The event was not redispatched because handler handles it.
659 EXPECT_FALSE(redispatched);
660 }
661
662 TEST(FlKeyboardManagerTest, CorrectLogicalKeyForLayouts) {
663 ::testing::NiceMock<flutter::testing::MockKeymap> mock_keymap;
664
665 g_autoptr(FlDartProject) project = fl_dart_project_new();
666 g_autoptr(FlEngine) engine = fl_engine_new(project);
667 g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new();
668 g_autoptr(FlKeyboardManager) manager =
669 fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view));
670
671 g_autoptr(GPtrArray) call_records = g_ptr_array_new_with_free_func(
672 reinterpret_cast<GDestroyNotify>(call_record_free));
673 fl_keyboard_manager_set_send_key_event_handler(
674 manager,
675 [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback,
676 void* callback_user_data, gpointer user_data) {
677 GPtrArray* call_records = static_cast<GPtrArray*>(user_data);
678 g_ptr_array_add(call_records,
679 call_record_new(event, callback, callback_user_data));
680 },
681 call_records);
682 fl_keyboard_manager_set_lookup_key_handler(
683 manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; },
684 nullptr);
685
686 auto sendTap = [&](guint8 keycode, guint keyval, guint8 group) {
687 g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
688 0, TRUE, keycode, keyval, static_cast<GdkModifierType>(0), group);
689 fl_keyboard_manager_handle_event(manager, event1);
690 g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
691 0, FALSE, keycode, keyval, static_cast<GdkModifierType>(0), group);
692 fl_keyboard_manager_handle_event(manager, event2);
693 };
694
695 /* US keyboard layout */
696
697 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
698 VERIFY_DOWN(kLogicalKeyA, "a");
699
700 sendTap(kKeyCodeKeyA, GDK_KEY_A, 0); // Shift-KeyA
701 VERIFY_DOWN(kLogicalKeyA, "A");
702
703 sendTap(kKeyCodeDigit1, GDK_KEY_1, 0); // Digit1
704 VERIFY_DOWN(kLogicalDigit1, "1");
705
706 sendTap(kKeyCodeDigit1, GDK_KEY_exclam, 0); // Shift-Digit1
707 VERIFY_DOWN(kLogicalDigit1, "!");
708
709 sendTap(kKeyCodeMinus, GDK_KEY_minus, 0); // Minus
710 VERIFY_DOWN(kLogicalMinus, "-");
711
712 sendTap(kKeyCodeMinus, GDK_KEY_underscore, 0); // Shift-Minus
713 VERIFY_DOWN(kLogicalUnderscore, "_");
714
715 /* French keyboard layout, group 3, which is when the input method is showing
716 * "Fr" */
717
718 fl_keyboard_manager_set_lookup_key_handler(
719 manager,
720 [](const GdkKeymapKey* key, gpointer user_data) {
721 MockLayoutData* layout_data = static_cast<MockLayoutData*>(user_data);
722 guint8 group = static_cast<guint8>(key->group);
723 EXPECT_LT(group, layout_data->size());
724 const MockGroupLayoutData* group_layout = (*layout_data)[group];
725 EXPECT_NE(group_layout, nullptr);
726 EXPECT_TRUE(key->level == 0 || key->level == 1);
727 bool shift = key->level == 1;
728 return (*group_layout)[key->keycode * 2 + shift];
729 },
730 reinterpret_cast<gpointer>(const_cast<MockLayoutData*>(&kLayoutFrench)));
731
732 sendTap(kKeyCodeKeyA, GDK_KEY_q, 3); // KeyA
733 VERIFY_DOWN(kLogicalKeyQ, "q");
734
735 sendTap(kKeyCodeKeyA, GDK_KEY_Q, 3); // Shift-KeyA
736 VERIFY_DOWN(kLogicalKeyQ, "Q");
737
738 sendTap(kKeyCodeSemicolon, GDK_KEY_m, 3); // ; but prints M
739 VERIFY_DOWN(kLogicalKeyM, "m");
740
741 sendTap(kKeyCodeKeyM, GDK_KEY_comma, 3); // M but prints ,
742 VERIFY_DOWN(kLogicalComma, ",");
743
744 sendTap(kKeyCodeDigit1, GDK_KEY_ampersand, 3); // Digit1
745 VERIFY_DOWN(kLogicalDigit1, "&");
746
747 sendTap(kKeyCodeDigit1, GDK_KEY_1, 3); // Shift-Digit1
748 VERIFY_DOWN(kLogicalDigit1, "1");
749
750 sendTap(kKeyCodeMinus, GDK_KEY_parenright, 3); // Minus
751 VERIFY_DOWN(kLogicalParenthesisRight, ")");
752
753 sendTap(kKeyCodeMinus, GDK_KEY_degree, 3); // Shift-Minus
754 VERIFY_DOWN(static_cast<uint32_t>(L'°'), "°");
755
756 /* French keyboard layout, group 0, which is pressing the "extra key for
757 * triggering input method" key once after switching to French IME. */
758
759 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
760 VERIFY_DOWN(kLogicalKeyA, "a");
761
762 sendTap(kKeyCodeDigit1, GDK_KEY_1, 0); // Digit1
763 VERIFY_DOWN(kLogicalDigit1, "1");
764
765 /* Russian keyboard layout, group 2 */
766 fl_keyboard_manager_set_lookup_key_handler(
767 manager,
768 [](const GdkKeymapKey* key, gpointer user_data) {
769 MockLayoutData* layout_data = static_cast<MockLayoutData*>(user_data);
770 guint8 group = static_cast<guint8>(key->group);
771 EXPECT_LT(group, layout_data->size());
772 const MockGroupLayoutData* group_layout = (*layout_data)[group];
773 EXPECT_NE(group_layout, nullptr);
774 EXPECT_TRUE(key->level == 0 || key->level == 1);
775 bool shift = key->level == 1;
776 return (*group_layout)[key->keycode * 2 + shift];
777 },
778 reinterpret_cast<gpointer>(const_cast<MockLayoutData*>(&kLayoutRussian)));
779
780 sendTap(kKeyCodeKeyA, GDK_KEY_Cyrillic_ef, 2); // KeyA
781 VERIFY_DOWN(kLogicalKeyA, "ф");
782
783 sendTap(kKeyCodeDigit1, GDK_KEY_1, 2); // Shift-Digit1
784 VERIFY_DOWN(kLogicalDigit1, "1");
785
786 sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_Cyrillic_ha, 2);
787 VERIFY_DOWN(kLogicalBracketLeft, "х");
788
789 /* Russian keyboard layout, group 0 */
790 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
791 VERIFY_DOWN(kLogicalKeyA, "a");
792
793 sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_bracketleft, 0);
794 VERIFY_DOWN(kLogicalBracketLeft, "[");
795 }
796
797 TEST(FlKeyboardManagerTest, SynthesizeModifiersIfNeeded) {
798 ::testing::NiceMock<flutter::testing::MockKeymap> mock_keymap;
799
800 g_autoptr(FlDartProject) project = fl_dart_project_new();
801 g_autoptr(FlEngine) engine = fl_engine_new(project);
802 g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new();
803 g_autoptr(FlKeyboardManager) manager =
804 fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view));
805
806 g_autoptr(GPtrArray) call_records = g_ptr_array_new_with_free_func(
807 reinterpret_cast<GDestroyNotify>(call_record_free));
808 fl_keyboard_manager_set_send_key_event_handler(
809 manager,
810 [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback,
811 void* callback_user_data, gpointer user_data) {
812 GPtrArray* call_records = static_cast<GPtrArray*>(user_data);
813 g_ptr_array_add(call_records,
814 call_record_new(event, callback, callback_user_data));
815 },
816 call_records);
817
818 auto verifyModifierIsSynthesized = [&](GdkModifierType mask,
819 uint64_t physical, uint64_t logical) {
820 // Modifier is pressed.
821 guint state = mask;
822 fl_keyboard_manager_sync_modifier_if_needed(manager, state, 1000);
823 EXPECT_EQ(call_records->len, 1u);
824 EXPECT_KEY_EVENT(
825 static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)),
826 kFlutterKeyEventTypeDown, physical, logical, NULL, true);
827 // Modifier is released.
828 state = state ^ mask;
829 fl_keyboard_manager_sync_modifier_if_needed(manager, state, 1001);
830 EXPECT_EQ(call_records->len, 2u);
831 EXPECT_KEY_EVENT(
832 static_cast<CallRecord*>(g_ptr_array_index(call_records, 1)),
833 kFlutterKeyEventTypeUp, physical, logical, NULL, true);
834 g_ptr_array_set_size(call_records, 0);
835 };
836
837 // No modifiers pressed.
838 guint state = 0;
839 fl_keyboard_manager_sync_modifier_if_needed(manager, state, 1000);
840 EXPECT_EQ(call_records->len, 0u);
841 g_ptr_array_set_size(call_records, 0);
842
843 // Press and release each modifier once.
844 verifyModifierIsSynthesized(GDK_CONTROL_MASK, kPhysicalControlLeft,
845 kLogicalControlLeft);
846 verifyModifierIsSynthesized(GDK_META_MASK, kPhysicalMetaLeft,
847 kLogicalMetaLeft);
848 verifyModifierIsSynthesized(GDK_MOD1_MASK, kPhysicalAltLeft, kLogicalAltLeft);
849 verifyModifierIsSynthesized(GDK_SHIFT_MASK, kPhysicalShiftLeft,
850 kLogicalShiftLeft);
851 }
852
853 TEST(FlKeyboardManagerTest, GetPressedState) {
854 ::testing::NiceMock<flutter::testing::MockKeymap> mock_keymap;
855
856 g_autoptr(FlDartProject) project = fl_dart_project_new();
857 g_autoptr(FlEngine) engine = fl_engine_new(project);
858 g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new();
859 g_autoptr(FlKeyboardManager) manager =
860 fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view));
861
862 // Dispatch a key event.
863 g_autoptr(FlKeyEvent) event = fl_key_event_new(
864 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
865 fl_keyboard_manager_handle_event(manager, event);
866
867 GHashTable* pressedState = fl_keyboard_manager_get_pressed_state(manager);
868 EXPECT_EQ(g_hash_table_size(pressedState), 1u);
869
870 gpointer physical_key =
871 g_hash_table_lookup(pressedState, uint64_to_gpointer(kPhysicalKeyA));
872 EXPECT_EQ(gpointer_to_uint64(physical_key), kLogicalKeyA);
873 }
874
875 // The following layout data is generated using DEBUG_PRINT_LAYOUT.
876
877 const MockGroupLayoutData kLayoutRussian0{
878 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
879 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
880 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
881 0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
882 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
883 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
884 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
885 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
886 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
887 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
888 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
889 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
890 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
891 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
892 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
893 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
894 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
895 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
896 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
897 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
898 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
899 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
900 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
901 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
902 0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
903 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
904 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
905 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
906 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
907 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
908 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
909 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
910 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
911 };
912
913 const MockGroupLayoutData kLayoutRussian2{{
914 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
915 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
916 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
917 0xffff, 0x0031, 0x0021, 0x0000, 0x0031, 0x0021, 0x0032, 0x0022, // 0x08
918 0x0033, 0x06b0, 0x0034, 0x003b, 0x0035, 0x0025, 0x0036, 0x003a, // 0x0c
919 0x0037, 0x003f, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
920 0x002d, 0x005f, 0x003d, 0x002b, 0x0071, 0x0051, 0x0000, 0x0000, // 0x14
921 0x06ca, 0x06ea, 0x06c3, 0x06e3, 0x06d5, 0x06f5, 0x06cb, 0x06eb, // 0x18
922 0x06c5, 0x06e5, 0x06ce, 0x06ee, 0x06c7, 0x06e7, 0x06db, 0x06fb, // 0x1c
923 0x06dd, 0x06fd, 0x06da, 0x06fa, 0x06c8, 0x06e8, 0x06df, 0x06ff, // 0x20
924 0x0061, 0x0041, 0x0041, 0x0000, 0x06c6, 0x06e6, 0x06d9, 0x06f9, // 0x24
925 0x06d7, 0x06f7, 0x06c1, 0x06e1, 0x06d0, 0x06f0, 0x06d2, 0x06f2, // 0x28
926 0x06cf, 0x06ef, 0x06cc, 0x06ec, 0x06c4, 0x06e4, 0x06d6, 0x06f6, // 0x2c
927 0x06dc, 0x06fc, 0x06a3, 0x06b3, 0x007c, 0x0000, 0x005c, 0x002f, // 0x30
928 0x06d1, 0x06f1, 0x06de, 0x06fe, 0x06d3, 0x06f3, 0x06cd, 0x06ed, // 0x34
929 0x06c9, 0x06e9, 0x06d4, 0x06f4, 0x06d8, 0x06f8, 0x06c2, 0x06e2, // 0x38
930 0x06c0, 0x06e0, 0x002e, 0x002c, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
931 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
932 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
933 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
934 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
935 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
936 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
937 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
938 0xffff, 0xffff, 0x003c, 0x003e, 0x002f, 0x007c, 0xffff, 0xffff, // 0x5c
939 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
940 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x64
941 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0x0000, // 0x68
942 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
943 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
944 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
945 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1, // 0x78
946 0x00b1, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x7c
947 }};
948
949 const MockGroupLayoutData kLayoutFrench0 = {
950 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
951 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
952 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
953 0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
954 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
955 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
956 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
957 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
958 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
959 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
960 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
961 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
962 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
963 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
964 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
965 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
966 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
967 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
968 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
969 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
970 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
971 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
972 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
973 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
974 0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
975 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
976 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
977 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
978 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
979 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
980 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
981 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
982 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
983 };
984
985 const MockGroupLayoutData kLayoutFrench3 = {
986 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
987 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
988 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
989 0x0000, 0xffff, 0x0000, 0x0000, 0x0026, 0x0031, 0x00e9, 0x0032, // 0x08
990 0x0022, 0x0033, 0x0027, 0x0034, 0x0028, 0x0035, 0x002d, 0x0036, // 0x0c
991 0x00e8, 0x0037, 0x005f, 0x0038, 0x00e7, 0x0039, 0x00e0, 0x0030, // 0x10
992 0x0029, 0x00b0, 0x003d, 0x002b, 0x0000, 0x0000, 0x0061, 0x0041, // 0x14
993 0x0061, 0x0041, 0x007a, 0x005a, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
994 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
995 0x006f, 0x004f, 0x0070, 0x0050, 0xffff, 0xffff, 0x0024, 0x00a3, // 0x20
996 0x0041, 0x0000, 0x0000, 0x0000, 0x0071, 0x0051, 0x0073, 0x0053, // 0x24
997 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
998 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x006d, 0x004d, // 0x2c
999 0x00f9, 0x0025, 0x00b2, 0x007e, 0x0000, 0x0000, 0x002a, 0x00b5, // 0x30
1000 0x0077, 0x0057, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
1001 0x0062, 0x0042, 0x006e, 0x004e, 0x002c, 0x003f, 0x003b, 0x002e, // 0x38
1002 0x003a, 0x002f, 0x0021, 0x00a7, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
1003 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
1004 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
1005 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
1006 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
1007 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
1008 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
1009 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
1010 0xffff, 0x003c, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
1011 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
1012 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
1013 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
1014 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
1015 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
1016 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
1017 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, // 0x78
1018 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x7c
1019 };
1020
1021 const MockLayoutData kLayoutRussian{&kLayoutRussian0, nullptr,
1022 &kLayoutRussian2};
1023 const MockLayoutData kLayoutFrench{&kLayoutFrench0, nullptr, nullptr,
1024 &kLayoutFrench3};
1025
1026 } // namespace
G_DEFINE_TYPE_WITH_CODE
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
state
AtkStateType state
Definition: fl_accessible_node.cc:10
TEST
TEST(FlAccessibleNodeTest, BuildTree)
Definition: fl_accessible_node_test.cc:11
G_DECLARE_FINAL_TYPE
G_BEGIN_DECLS G_DECLARE_FINAL_TYPE(FlAccessibleTextField, fl_accessible_text_field, FL, ACCESSIBLE_TEXT_FIELD, FlAccessibleNode)
view
FlView * view
Definition: fl_application.cc:35
FL
FL
Definition: fl_binary_messenger.cc:27
fl_dart_project_new
G_MODULE_EXPORT FlDartProject * fl_dart_project_new()
Definition: fl_dart_project.cc:50
fl_engine_start
gboolean fl_engine_start(FlEngine *self, GError **error)
Definition: fl_engine.cc:544
fl_engine_new
G_MODULE_EXPORT FlEngine * fl_engine_new(FlDartProject *project)
Definition: fl_engine.cc:524
fl_engine_private.h
user_data
G_BEGIN_DECLS G_MODULE_EXPORT FlValue gpointer user_data
Definition: fl_event_channel.h:90
fl_key_event_new
FlKeyEvent * fl_key_event_new(guint32 time, gboolean is_press, guint16 keycode, guint keyval, GdkModifierType state, guint8 group)
Definition: fl_key_event.cc:34
fl_key_event_get_keyval
guint fl_key_event_get_keyval(FlKeyEvent *self)
Definition: fl_key_event.cc:94
fl_keyboard_manager_is_state_clear
gboolean fl_keyboard_manager_is_state_clear(FlKeyboardManager *self)
Definition: fl_keyboard_manager.cc:552
fl_keyboard_manager_handle_event
gboolean fl_keyboard_manager_handle_event(FlKeyboardManager *self, FlKeyEvent *event)
Definition: fl_keyboard_manager.cc:522
fl_keyboard_manager_set_redispatch_handler
void fl_keyboard_manager_set_redispatch_handler(FlKeyboardManager *self, FlKeyboardManagerRedispatchEventHandler redispatch_handler, gpointer user_data)
Definition: fl_keyboard_manager.cc:595
fl_keyboard_manager_set_lookup_key_handler
void fl_keyboard_manager_set_lookup_key_handler(FlKeyboardManager *self, FlKeyboardManagerLookupKeyHandler lookup_key_handler, gpointer user_data)
Definition: fl_keyboard_manager.cc:586
fl_keyboard_manager_set_send_key_event_handler
void fl_keyboard_manager_set_send_key_event_handler(FlKeyboardManager *self, FlKeyboardManagerSendKeyEventHandler send_key_event_handler, gpointer user_data)
Definition: fl_keyboard_manager.cc:577
fl_keyboard_manager_new
FlKeyboardManager * fl_keyboard_manager_new(FlEngine *engine, FlKeyboardViewDelegate *view_delegate)
Definition: fl_keyboard_manager.cc:462
fl_keyboard_manager_get_pressed_state
GHashTable * fl_keyboard_manager_get_pressed_state(FlKeyboardManager *self)
Definition: fl_keyboard_manager.cc:566
fl_keyboard_manager_sync_modifier_if_needed
void fl_keyboard_manager_sync_modifier_if_needed(FlKeyboardManager *self, guint state, double event_time)
Definition: fl_keyboard_manager.cc:558
fl_keyboard_manager.h
EXPECT_KEY_EVENT
#define EXPECT_KEY_EVENT(RECORD, TYPE, PHYSICAL, LOGICAL, CHAR, SYNTHESIZED)
Definition: fl_keyboard_manager_test.cc:21
call_record_respond
static void call_record_respond(CallRecord *record, bool handled)
Definition: fl_keyboard_manager_test.cc:72
call_record_free
static void call_record_free(CallRecord *record)
Definition: fl_keyboard_manager_test.cc:67
VERIFY_DOWN
#define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR)
Definition: fl_keyboard_manager_test.cc:28
call_record_new
static CallRecord * call_record_new(const FlutterKeyEvent *event, FlutterKeyEventCallback callback, void *callback_user_data)
Definition: fl_keyboard_manager_test.cc:53
TRUE
return TRUE
Definition: fl_pixel_buffer_texture_test.cc:53
error
const uint8_t uint32_t uint32_t GError ** error
Definition: fl_pixel_buffer_texture_test.cc:40
value
uint8_t value
Definition: fl_standard_message_codec.cc:36
fl_value_set_string_take
G_MODULE_EXPORT void fl_value_set_string_take(FlValue *self, const gchar *key, FlValue *value)
Definition: fl_value.cc:650
fl_value_new_bool
G_MODULE_EXPORT FlValue * fl_value_new_bool(bool value)
Definition: fl_value.cc:255
fl_value_ref
G_MODULE_EXPORT FlValue * fl_value_ref(FlValue *self)
Definition: fl_value.cc:394
fl_value_new_map
G_MODULE_EXPORT FlValue * fl_value_new_map()
Definition: fl_value.cc:366
FlValue
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition: fl_value.h:42
key_mapping.h
uint64_to_gpointer
gpointer uint64_to_gpointer(uint64_t number)
Definition: key_mapping.h:17
gpointer_to_uint64
uint64_t gpointer_to_uint64(gpointer pointer)
Definition: key_mapping.h:13
CallRecord
Definition: fl_keyboard_manager_test.cc:43
CallRecord::event_physical
uint64_t event_physical
Definition: fl_keyboard_manager_test.cc:45
CallRecord::event_logical
uint64_t event_logical
Definition: fl_keyboard_manager_test.cc:46
CallRecord::callback
FlutterKeyEventCallback callback
Definition: fl_keyboard_manager_test.cc:49
CallRecord::event_synthesized
bool event_synthesized
Definition: fl_keyboard_manager_test.cc:48
CallRecord::event_character
gchar * event_character
Definition: fl_keyboard_manager_test.cc:47
CallRecord::event_type
FlutterKeyEventType event_type
Definition: fl_keyboard_manager_test.cc:44
CallRecord::callback_user_data
void * callback_user_data
Definition: fl_keyboard_manager_test.cc:50