Flutter Engine
The Flutter Engine
keyboard_key_embedder_handler.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/windows/keyboard_key_embedder_handler.h"
6
7#include <windows.h>
8
9#include <chrono>
10#include <string>
11
12#include "flutter/fml/logging.h"
13#include "flutter/shell/platform/windows/keyboard_utils.h"
14
15namespace flutter {
16
17namespace {
18// An arbitrary size for the character cache in bytes.
19//
20// It should hold a UTF-32 character encoded in UTF-8 as well as the trailing
21// '\0'.
22constexpr size_t kCharacterCacheSize = 8;
23
24constexpr SHORT kStateMaskToggled = 0x01;
25constexpr SHORT kStateMaskPressed = 0x80;
26
27const char* empty_character = "";
28
29// Get some bits of the char, from the start'th bit from the right (excluded)
30// to the end'th bit from the right (included).
31//
32// For example, _GetBit(0x1234, 8, 4) => 0x3.
33char _GetBit(char32_t ch, size_t start, size_t end) {
34 return (ch >> end) & ((1 << (start - end)) - 1);
35}
36} // namespace
37
38std::string ConvertChar32ToUtf8(char32_t ch) {
39 std::string result;
40 FML_DCHECK(0 <= ch && ch <= 0x10FFFF) << "Character out of range";
41 if (ch <= 0x007F) {
42 result.push_back(ch);
43 } else if (ch <= 0x07FF) {
44 result.push_back(0b11000000 + _GetBit(ch, 11, 6));
45 result.push_back(0b10000000 + _GetBit(ch, 6, 0));
46 } else if (ch <= 0xFFFF) {
47 result.push_back(0b11100000 + _GetBit(ch, 16, 12));
48 result.push_back(0b10000000 + _GetBit(ch, 12, 6));
49 result.push_back(0b10000000 + _GetBit(ch, 6, 0));
50 } else {
51 result.push_back(0b11110000 + _GetBit(ch, 21, 18));
52 result.push_back(0b10000000 + _GetBit(ch, 18, 12));
53 result.push_back(0b10000000 + _GetBit(ch, 12, 6));
54 result.push_back(0b10000000 + _GetBit(ch, 6, 0));
55 }
56 return result;
57}
58
60 SendEventHandler send_event,
61 GetKeyStateHandler get_key_state,
62 MapVirtualKeyToScanCode map_virtual_key_to_scan_code)
63 : perform_send_event_(send_event),
64 get_key_state_(get_key_state),
65 response_id_(1) {
66 InitCriticalKeys(map_virtual_key_to_scan_code);
67}
68
70
71static bool isEasciiPrintable(int codeUnit) {
72 return (codeUnit <= 0x7f && codeUnit >= 0x20) ||
73 (codeUnit <= 0xff && codeUnit >= 0x80);
74}
75
76// Converts upper letters to lower letters in ASCII and extended ASCII, and
77// returns as-is otherwise.
78//
79// Independent of locale.
80static uint64_t toLower(uint64_t n) {
81 constexpr uint64_t lower_a = 0x61;
82 constexpr uint64_t upper_a = 0x41;
83 constexpr uint64_t upper_z = 0x5a;
84
85 constexpr uint64_t lower_a_grave = 0xe0;
86 constexpr uint64_t upper_a_grave = 0xc0;
87 constexpr uint64_t upper_thorn = 0xde;
88 constexpr uint64_t division = 0xf7;
89
90 // ASCII range.
91 if (n >= upper_a && n <= upper_z) {
92 return n - upper_a + lower_a;
93 }
94
95 // EASCII range.
96 if (n >= upper_a_grave && n <= upper_thorn && n != division) {
97 return n - upper_a_grave + lower_a_grave;
98 }
99
100 return n;
101}
102
103// Transform scancodes sent by windows to scancodes written in Chromium spec.
104static uint16_t normalizeScancode(int windowsScanCode, bool extended) {
105 // In Chromium spec the extended bit is shown as 0xe000 bit,
106 // e.g. PageUp is represented as 0xe049.
107 return (windowsScanCode & 0xff) | (extended ? 0xe000 : 0);
108}
109
110uint64_t KeyboardKeyEmbedderHandler::ApplyPlaneToId(uint64_t id,
111 uint64_t plane) {
112 return (id & valueMask) | plane;
113}
114
115uint64_t KeyboardKeyEmbedderHandler::GetPhysicalKey(int scancode,
116 bool extended) {
117 int chromiumScancode = normalizeScancode(scancode, extended);
118 auto resultIt = windowsToPhysicalMap_.find(chromiumScancode);
119 if (resultIt != windowsToPhysicalMap_.end())
120 return resultIt->second;
121 return ApplyPlaneToId(scancode, windowsPlane);
122}
123
124uint64_t KeyboardKeyEmbedderHandler::GetLogicalKey(int key,
125 bool extended,
126 int scancode) {
127 if (key == VK_PROCESSKEY) {
128 return VK_PROCESSKEY;
129 }
130
131 // Normally logical keys should only be derived from key codes, but since some
132 // key codes are either 0 or ambiguous (multiple keys using the same key
133 // code), these keys are resolved by scan codes.
134 auto numpadIter =
135 scanCodeToLogicalMap_.find(normalizeScancode(scancode, extended));
136 if (numpadIter != scanCodeToLogicalMap_.cend())
137 return numpadIter->second;
138
139 // Check if the keyCode is one we know about and have a mapping for.
140 auto logicalIt = windowsToLogicalMap_.find(key);
141 if (logicalIt != windowsToLogicalMap_.cend())
142 return logicalIt->second;
143
144 // Upper case letters should be normalized into lower case letters.
145 if (isEasciiPrintable(key)) {
146 return ApplyPlaneToId(toLower(key), unicodePlane);
147 }
148
149 return ApplyPlaneToId(toLower(key), windowsPlane);
150}
151
152void KeyboardKeyEmbedderHandler::KeyboardHookImpl(
153 int key,
154 int scancode,
155 int action,
156 char32_t character,
157 bool extended,
158 bool was_down,
159 std::function<void(bool)> callback) {
160 const uint64_t physical_key = GetPhysicalKey(scancode, extended);
161 const uint64_t logical_key = GetLogicalKey(key, extended, scancode);
162 FML_DCHECK(action == WM_KEYDOWN || action == WM_KEYUP ||
163 action == WM_SYSKEYDOWN || action == WM_SYSKEYUP);
164
165 auto last_logical_record_iter = pressingRecords_.find(physical_key);
166 bool had_record = last_logical_record_iter != pressingRecords_.end();
167 uint64_t last_logical_record =
168 had_record ? last_logical_record_iter->second : 0;
169
170 // The logical key for the current "tap sequence".
171 //
172 // Key events are formed in tap sequences: down, repeats, up. The logical key
173 // stays consistent throughout a tap sequence, which is this value.
174 uint64_t sequence_logical_key =
175 had_record ? last_logical_record : logical_key;
176
177 if (sequence_logical_key == VK_PROCESSKEY) {
178 // VK_PROCESSKEY means that the key press is used by an IME. These key
179 // presses are considered handled and not sent to Flutter. These events must
180 // be filtered by result_logical_key because the key up event of such
181 // presses uses the "original" logical key.
182 callback(true);
183 return;
184 }
185
186 const bool is_event_down = action == WM_KEYDOWN || action == WM_SYSKEYDOWN;
187
188 bool event_key_can_be_repeat = true;
189 UpdateLastSeenCriticalKey(key, physical_key, sequence_logical_key);
190 // Synchronize the toggled states of critical keys (such as whether CapsLocks
191 // is enabled). Toggled states can only be changed upon a down event, so if
192 // the recorded toggled state does not match the true state, this function
193 // will synthesize (an up event if the key is recorded pressed, then) a down
194 // event.
195 //
196 // After this function, all critical keys will have their toggled state
197 // updated to the true state, while the critical keys whose toggled state have
198 // been changed will be pressed regardless of their true pressed state.
199 // Updating the pressed state will be done by
200 // SynchronizeCriticalPressedStates.
201 SynchronizeCriticalToggledStates(key, is_event_down,
202 &event_key_can_be_repeat);
203 // Synchronize the pressed states of critical keys (such as whether CapsLocks
204 // is pressed).
205 //
206 // After this function, all critical keys except for the target key will have
207 // their toggled state and pressed state matched with their true states. The
208 // target key's pressed state will be updated immediately after this.
209 SynchronizeCriticalPressedStates(key, physical_key, is_event_down,
210 event_key_can_be_repeat);
211
212 // Reassess the last logical record in case pressingRecords_ was modified
213 // by the above synchronization methods.
214 last_logical_record_iter = pressingRecords_.find(physical_key);
215 had_record = last_logical_record_iter != pressingRecords_.end();
216 last_logical_record = had_record ? last_logical_record_iter->second : 0;
217
218 // The resulting event's `type`.
221 char character_bytes[kCharacterCacheSize];
222 // What pressingRecords_[physical_key] should be after the KeyboardHookImpl
223 // returns (0 if the entry should be removed).
224 uint64_t eventual_logical_record;
225 uint64_t result_logical_key;
226
227 if (is_event_down) {
228 if (had_record) {
229 if (was_down) {
230 // A normal repeated key.
232 FML_DCHECK(had_record);
233 ConvertUtf32ToUtf8_(character_bytes, character);
234 eventual_logical_record = last_logical_record;
235 result_logical_key = last_logical_record;
236 } else {
237 // A non-repeated key has been pressed that has the exact physical key
238 // as a currently pressed one, usually indicating multiple keyboards are
239 // pressing keys with the same physical key, or the up event was lost
240 // during a loss of focus. The down event is ignored.
241 callback(true);
242 return;
243 }
244 } else {
245 // A normal down event (whether the system event is a repeat or not).
247 FML_DCHECK(!had_record);
248 ConvertUtf32ToUtf8_(character_bytes, character);
249 eventual_logical_record = logical_key;
250 result_logical_key = logical_key;
251 }
252 } else { // isPhysicalDown is false
253 if (last_logical_record == 0) {
254 // The physical key has been released before. It might indicate a missed
255 // event due to loss of focus, or multiple keyboards pressed keys with the
256 // same physical key. Ignore the up event.
257 callback(true);
258 return;
259 } else {
260 // A normal up event.
262 FML_DCHECK(had_record);
263 // Up events never have character.
264 character_bytes[0] = '\0';
265 eventual_logical_record = 0;
266 result_logical_key = last_logical_record;
267 }
268 }
269
270 if (eventual_logical_record != 0) {
271 pressingRecords_[physical_key] = eventual_logical_record;
272 } else {
273 auto record_iter = pressingRecords_.find(physical_key);
274 // Assert this in debug mode. But in cases that it doesn't satisfy
275 // (such as due to a bug), make sure the `erase` is only called
276 // with a valid value to avoid crashing.
277 if (record_iter != pressingRecords_.end()) {
278 pressingRecords_.erase(record_iter);
279 } else {
280 FML_DCHECK(false);
281 }
282 }
283
284 FlutterKeyEvent key_data{
285 .struct_size = sizeof(FlutterKeyEvent),
286 .timestamp = static_cast<double>(
287 std::chrono::duration_cast<std::chrono::microseconds>(
288 std::chrono::high_resolution_clock::now().time_since_epoch())
289 .count()),
290 .type = type,
291 .physical = physical_key,
292 .logical = result_logical_key,
293 .character = character_bytes,
294 .synthesized = false,
295 };
296
297 response_id_ += 1;
298 uint64_t response_id = response_id_;
299 PendingResponse pending{
300 .callback =
301 [this, callback = std::move(callback)](bool handled,
302 uint64_t response_id) {
303 auto found = pending_responses_.find(response_id);
304 if (found != pending_responses_.end()) {
305 pending_responses_.erase(found);
306 }
307 callback(handled);
308 },
309 .response_id = response_id,
310 };
311 auto pending_ptr = std::make_unique<PendingResponse>(std::move(pending));
312 pending_responses_[response_id] = std::move(pending_ptr);
313 SendEvent(key_data, KeyboardKeyEmbedderHandler::HandleResponse,
314 reinterpret_cast<void*>(pending_responses_[response_id].get()));
315
316 // Post-event synchronization. It is useful in cases where the true pressing
317 // state does not match the event type. For example, a CapsLock down event is
318 // received despite that GetKeyState says that CapsLock is not pressed. In
319 // such case, post-event synchronization will synthesize a CapsLock up event
320 // after the main event.
321 SynchronizeCriticalPressedStates(key, physical_key, is_event_down,
322 event_key_can_be_repeat);
323}
324
326 int key,
327 int scancode,
328 int action,
329 char32_t character,
330 bool extended,
331 bool was_down,
332 std::function<void(bool)> callback) {
333 sent_any_events = false;
334 KeyboardHookImpl(key, scancode, action, character, extended, was_down,
335 std::move(callback));
336 if (!sent_any_events) {
337 FlutterKeyEvent empty_event{
338 .struct_size = sizeof(FlutterKeyEvent),
339 .timestamp = static_cast<double>(
340 std::chrono::duration_cast<std::chrono::microseconds>(
341 std::chrono::high_resolution_clock::now().time_since_epoch())
342 .count()),
344 .physical = 0,
345 .logical = 0,
346 .character = empty_character,
347 .synthesized = false,
348 };
349 SendEvent(empty_event, nullptr, nullptr);
350 }
351}
352
353std::map<uint64_t, uint64_t> KeyboardKeyEmbedderHandler::GetPressedState() {
354 return pressingRecords_;
355}
356
357void KeyboardKeyEmbedderHandler::UpdateLastSeenCriticalKey(
358 int virtual_key,
359 uint64_t physical_key,
360 uint64_t logical_key) {
361 auto found = critical_keys_.find(virtual_key);
362 if (found != critical_keys_.end()) {
363 found->second.physical_key = physical_key;
364 found->second.logical_key = logical_key;
365 }
366}
367
368void KeyboardKeyEmbedderHandler::SynchronizeCriticalToggledStates(
369 int event_virtual_key,
370 bool is_event_down,
371 bool* event_key_can_be_repeat) {
372 // NowState ----------------> PreEventState --------------> TrueState
373 // Synchronization Event
374 for (auto& kv : critical_keys_) {
375 UINT virtual_key = kv.first;
376 CriticalKey& key_info = kv.second;
377 if (key_info.physical_key == 0) {
378 // Never seen this key.
379 continue;
380 }
381 FML_DCHECK(key_info.logical_key != 0);
382
383 // Check toggling state first, because it might alter pressing state.
384 if (key_info.check_toggled) {
385 const bool target_is_pressed =
386 pressingRecords_.find(key_info.physical_key) !=
387 pressingRecords_.end();
388 // The togglable keys observe a 4-phase cycle:
389 //
390 // Phase# 0 1 2 3
391 // Event Down Up Down Up
392 // Pressed 0 1 0 1
393 // Toggled 0 1 1 0
394 const bool true_toggled = get_key_state_(virtual_key) & kStateMaskToggled;
395 bool pre_event_toggled = true_toggled;
396 // Check if the main event's key is the key being checked. If it's the
397 // non-repeat down event, toggle the state.
398 if (virtual_key == event_virtual_key && !target_is_pressed &&
399 is_event_down) {
400 pre_event_toggled = !pre_event_toggled;
401 }
402 if (key_info.toggled_on != pre_event_toggled) {
403 // If the key is pressed, release it first.
404 if (target_is_pressed) {
405 SendEvent(SynthesizeSimpleEvent(
406 kFlutterKeyEventTypeUp, key_info.physical_key,
407 key_info.logical_key, empty_character),
408 nullptr, nullptr);
409 }
410 // Synchronizing toggle state always ends with the key being pressed.
411 pressingRecords_[key_info.physical_key] = key_info.logical_key;
412 SendEvent(SynthesizeSimpleEvent(kFlutterKeyEventTypeDown,
413 key_info.physical_key,
414 key_info.logical_key, empty_character),
415 nullptr, nullptr);
416 *event_key_can_be_repeat = false;
417 }
418 key_info.toggled_on = true_toggled;
419 }
420 }
421}
422
423void KeyboardKeyEmbedderHandler::SynchronizeCriticalPressedStates(
424 int event_virtual_key,
425 int event_physical_key,
426 bool is_event_down,
427 bool event_key_can_be_repeat) {
428 // During an incoming event, there might be a synthesized Flutter event for
429 // each key of each pressing goal, followed by an eventual main Flutter
430 // event.
431 //
432 // NowState ----------------> PreEventState --------------> TrueState
433 // Synchronization Event
434 //
435 // The goal of the synchronization algorithm is to derive a pre-event state
436 // that can satisfy the true state (`true_pressed`) after the event, and that
437 // requires as few synthesized events based on the current state
438 // (`now_pressed`) as possible.
439 for (auto& kv : critical_keys_) {
440 UINT virtual_key = kv.first;
441 CriticalKey& key_info = kv.second;
442 if (key_info.physical_key == 0) {
443 // Never seen this key.
444 continue;
445 }
446 FML_DCHECK(key_info.logical_key != 0);
447 if (key_info.check_pressed) {
448 SHORT true_pressed = get_key_state_(virtual_key) & kStateMaskPressed;
449 auto pressing_record_iter = pressingRecords_.find(key_info.physical_key);
450 bool now_pressed = pressing_record_iter != pressingRecords_.end();
451 bool pre_event_pressed = true_pressed;
452 // Check if the main event is the key being checked to get the correct
453 // target state.
454 if (is_event_down) {
455 // For down events, this key is the event key if they have the same
456 // virtual key, because virtual key represents "functionality."
457 //
458 // In that case, normally Flutter should synthesize nothing since the
459 // resulting event can adapt to the current state by dispatching either
460 // a down or a repeat event. However, in certain cases (when Flutter has
461 // just synchronized the key's toggling state) the event must not be a
462 // repeat event.
463 if (virtual_key == event_virtual_key) {
464 if (event_key_can_be_repeat) {
465 continue;
466 } else {
467 pre_event_pressed = false;
468 }
469 }
470 } else {
471 // For up events, this key is the event key if they have the same
472 // physical key, because it is necessary to ensure that the physical
473 // key is correctly released.
474 //
475 // In that case, although the previous state should be pressed, don't
476 // synthesize a down event even if it's not. The later code will handle
477 // such cases by skipping abrupt up events. Obviously don't synthesize
478 // up events either.
479 if (event_physical_key == key_info.physical_key) {
480 continue;
481 }
482 }
483 if (now_pressed != pre_event_pressed) {
484 if (now_pressed) {
485 pressingRecords_.erase(pressing_record_iter);
486 } else {
487 pressingRecords_[key_info.physical_key] = key_info.logical_key;
488 }
489 const char* empty_character = "";
490 SendEvent(
491 SynthesizeSimpleEvent(
493 key_info.physical_key, key_info.logical_key, empty_character),
494 nullptr, nullptr);
495 }
496 }
497 }
498}
499
501 // TODO(bleroux): consider exposing these constants in flutter_key_map.g.cc?
502 const uint64_t physical_shift_left =
503 windowsToPhysicalMap_.at(kScanCodeShiftLeft);
504 const uint64_t physical_shift_right =
505 windowsToPhysicalMap_.at(kScanCodeShiftRight);
506 const uint64_t logical_shift_left =
507 windowsToLogicalMap_.at(kKeyCodeShiftLeft);
508 const uint64_t physical_control_left =
509 windowsToPhysicalMap_.at(kScanCodeControlLeft);
510 const uint64_t physical_control_right =
511 windowsToPhysicalMap_.at(kScanCodeControlRight);
512 const uint64_t logical_control_left =
513 windowsToLogicalMap_.at(kKeyCodeControlLeft);
514
515 bool shift_pressed = (modifiers_state & kShift) != 0;
516 SynthesizeIfNeeded(physical_shift_left, physical_shift_right,
517 logical_shift_left, shift_pressed);
518 bool control_pressed = (modifiers_state & kControl) != 0;
519 SynthesizeIfNeeded(physical_control_left, physical_control_right,
520 logical_control_left, control_pressed);
521}
522
523void KeyboardKeyEmbedderHandler::SynthesizeIfNeeded(uint64_t physical_left,
524 uint64_t physical_right,
525 uint64_t logical_left,
526 bool is_pressed) {
527 auto pressing_record_iter_left = pressingRecords_.find(physical_left);
528 bool left_pressed = pressing_record_iter_left != pressingRecords_.end();
529 auto pressing_record_iter_right = pressingRecords_.find(physical_right);
530 bool right_pressed = pressing_record_iter_right != pressingRecords_.end();
531 bool already_pressed = left_pressed || right_pressed;
532 bool synthesize_down = is_pressed && !already_pressed;
533 bool synthesize_up = !is_pressed && already_pressed;
534
535 if (synthesize_down) {
536 SendSynthesizeDownEvent(physical_left, logical_left);
537 }
538
539 if (synthesize_up && left_pressed) {
540 uint64_t known_logical = pressing_record_iter_left->second;
541 SendSynthesizeUpEvent(physical_left, known_logical);
542 }
543
544 if (synthesize_up && right_pressed) {
545 uint64_t known_logical = pressing_record_iter_right->second;
546 SendSynthesizeUpEvent(physical_right, known_logical);
547 }
548}
549
550void KeyboardKeyEmbedderHandler::SendSynthesizeDownEvent(uint64_t physical,
551 uint64_t logical) {
552 SendEvent(
553 SynthesizeSimpleEvent(kFlutterKeyEventTypeDown, physical, logical, ""),
554 nullptr, nullptr);
555 pressingRecords_[physical] = logical;
556};
557
558void KeyboardKeyEmbedderHandler::SendSynthesizeUpEvent(uint64_t physical,
559 uint64_t logical) {
560 SendEvent(
561 SynthesizeSimpleEvent(kFlutterKeyEventTypeUp, physical, logical, ""),
562 nullptr, nullptr);
563 pressingRecords_.erase(physical);
564};
565
566void KeyboardKeyEmbedderHandler::HandleResponse(bool handled, void* user_data) {
567 PendingResponse* pending = reinterpret_cast<PendingResponse*>(user_data);
568 auto callback = std::move(pending->callback);
569 callback(handled, pending->response_id);
570}
571
572void KeyboardKeyEmbedderHandler::InitCriticalKeys(
573 MapVirtualKeyToScanCode map_virtual_key_to_scan_code) {
574 auto createCheckedKey = [this, &map_virtual_key_to_scan_code](
575 UINT virtual_key, bool extended,
576 bool check_pressed,
577 bool check_toggled) -> CriticalKey {
578 UINT scan_code = map_virtual_key_to_scan_code(virtual_key, extended);
579 return CriticalKey{
580 .physical_key = GetPhysicalKey(scan_code, extended),
581 .logical_key = GetLogicalKey(virtual_key, extended, scan_code),
582 .check_pressed = check_pressed || check_toggled,
583 .check_toggled = check_toggled,
584 .toggled_on = check_toggled
585 ? !!(get_key_state_(virtual_key) & kStateMaskToggled)
586 : false,
587 };
588 };
589
590 critical_keys_.emplace(VK_LSHIFT,
591 createCheckedKey(VK_LSHIFT, false, true, false));
592 critical_keys_.emplace(VK_RSHIFT,
593 createCheckedKey(VK_RSHIFT, false, true, false));
594 critical_keys_.emplace(VK_LCONTROL,
595 createCheckedKey(VK_LCONTROL, false, true, false));
596 critical_keys_.emplace(VK_RCONTROL,
597 createCheckedKey(VK_RCONTROL, true, true, false));
598 critical_keys_.emplace(VK_LMENU,
599 createCheckedKey(VK_LMENU, false, true, false));
600 critical_keys_.emplace(VK_RMENU,
601 createCheckedKey(VK_RMENU, true, true, false));
602 critical_keys_.emplace(VK_LWIN, createCheckedKey(VK_LWIN, true, true, false));
603 critical_keys_.emplace(VK_RWIN, createCheckedKey(VK_RWIN, true, true, false));
604 critical_keys_.emplace(VK_CAPITAL,
605 createCheckedKey(VK_CAPITAL, false, true, true));
606 critical_keys_.emplace(VK_SCROLL,
607 createCheckedKey(VK_SCROLL, false, true, true));
608 critical_keys_.emplace(VK_NUMLOCK,
609 createCheckedKey(VK_NUMLOCK, true, true, true));
610}
611
612void KeyboardKeyEmbedderHandler::ConvertUtf32ToUtf8_(char* out, char32_t ch) {
613 if (ch == 0) {
614 out[0] = '\0';
615 return;
616 }
617 std::string result = ConvertChar32ToUtf8(ch);
618 strcpy_s(out, kCharacterCacheSize, result.c_str());
619}
620
621FlutterKeyEvent KeyboardKeyEmbedderHandler::SynthesizeSimpleEvent(
623 uint64_t physical,
624 uint64_t logical,
625 const char* character) {
626 return FlutterKeyEvent{
627 .struct_size = sizeof(FlutterKeyEvent),
628 .timestamp = static_cast<double>(
629 std::chrono::duration_cast<std::chrono::microseconds>(
630 std::chrono::high_resolution_clock::now().time_since_epoch())
631 .count()),
632 .type = type,
633 .physical = physical,
634 .logical = logical,
636 .synthesized = true,
637 };
638}
639
640void KeyboardKeyEmbedderHandler::SendEvent(const FlutterKeyEvent& event,
642 void* user_data) {
643 sent_any_events = true;
644 perform_send_event_(event, callback, user_data);
645}
646
647} // namespace flutter
GLenum type
std::function< void(const FlutterKeyEvent &, FlutterKeyEventCallback, void *)> SendEventHandler
void KeyboardHook(int key, int scancode, int action, char32_t character, bool extended, bool was_down, std::function< void(bool)> callback) override
std::function< SHORT(UINT, bool)> MapVirtualKeyToScanCode
std::map< uint64_t, uint64_t > GetPressedState() override
void SyncModifiersIfNeeded(int modifiers_state) override
KeyboardKeyEmbedderHandler(SendEventHandler send_event, GetKeyStateHandler get_key_state, MapVirtualKeyToScanCode map_vk_to_scan)
void(* FlutterKeyEventCallback)(bool, void *)
Definition: embedder.h:1155
FlutterKeyEventType
Definition: embedder.h:1074
@ kFlutterKeyEventTypeDown
Definition: embedder.h:1076
@ kFlutterKeyEventTypeUp
Definition: embedder.h:1075
@ kFlutterKeyEventTypeRepeat
Definition: embedder.h:1077
glong glong end
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlKeyEvent * event
GAsyncResult * result
#define FML_DCHECK(condition)
Definition: logging.h:103
Dart_NativeFunction function
Definition: fuchsia.cc:51
constexpr int kShift
constexpr int kKeyCodeShiftLeft
constexpr int kScanCodeShiftRight
constexpr int kScanCodeShiftLeft
constexpr int kScanCodeControlRight
std::string ConvertChar32ToUtf8(char32_t ch)
static uint16_t normalizeScancode(int windowsScanCode, bool extended)
static uint64_t toLower(uint64_t n)
static bool isEasciiPrintable(int codeUnit)
constexpr int kScanCodeControlLeft
constexpr int kKeyCodeControlLeft
constexpr int kControl
uint32_t UndeadChar(uint32_t ch)
const myers::Point & get(const myers::Segment &)
size_t struct_size
The size of this struct. Must be sizeof(FlutterKeyEvent).
Definition: embedder.h:1112
void * user_data
unsigned int UINT
Definition: windows_types.h:32
short SHORT
Definition: windows_types.h:29