Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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
6
7#include <windows.h>
8
9#include <chrono>
10#include <string>
11
12#include "flutter/fml/logging.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 && !was_down) {
229 // Windows delivered a new down event without a matching up event. This
230 // happens if the window lost focus before it could receive the key up.
231 // Synthesize an up event so the framework releases the key before
232 // processing the incoming down.
233 SendSynthesizeUpEvent(physical_key, last_logical_record);
234 last_logical_record_iter = pressingRecords_.find(physical_key);
235 had_record = last_logical_record_iter != pressingRecords_.end();
236 last_logical_record = had_record ? last_logical_record_iter->second : 0;
237 }
238
239 if (had_record) {
240 // A normal repeated key.
242 FML_DCHECK(had_record);
243 ConvertUtf32ToUtf8_(character_bytes, character);
244 eventual_logical_record = last_logical_record;
245 result_logical_key = last_logical_record;
246 } else {
247 // A normal down event (whether the system event is a repeat or not).
249 FML_DCHECK(!had_record);
250 ConvertUtf32ToUtf8_(character_bytes, character);
251 eventual_logical_record = logical_key;
252 result_logical_key = logical_key;
253 }
254 } else { // isPhysicalDown is false
255 if (last_logical_record == 0) {
256 // The physical key has been released before. It might indicate a missed
257 // event due to loss of focus, or multiple keyboards pressed keys with the
258 // same physical key. Ignore the up event.
259 callback(true);
260 return;
261 } else {
262 // A normal up event.
264 FML_DCHECK(had_record);
265 // Up events never have character.
266 character_bytes[0] = '\0';
267 eventual_logical_record = 0;
268 result_logical_key = last_logical_record;
269 }
270 }
271
272 if (eventual_logical_record != 0) {
273 pressingRecords_[physical_key] = eventual_logical_record;
274 } else {
275 auto record_iter = pressingRecords_.find(physical_key);
276 // Assert this in debug mode. But in cases that it doesn't satisfy
277 // (such as due to a bug), make sure the `erase` is only called
278 // with a valid value to avoid crashing.
279 if (record_iter != pressingRecords_.end()) {
280 pressingRecords_.erase(record_iter);
281 } else {
282 FML_DCHECK(false);
283 }
284 }
285
286 FlutterKeyEvent key_data{
287 .struct_size = sizeof(FlutterKeyEvent),
288 .timestamp = static_cast<double>(
289 std::chrono::duration_cast<std::chrono::microseconds>(
290 std::chrono::high_resolution_clock::now().time_since_epoch())
291 .count()),
292 .type = type,
293 .physical = physical_key,
294 .logical = result_logical_key,
295 .character = character_bytes,
296 .synthesized = false,
297 };
298
299 response_id_ += 1;
300 uint64_t response_id = response_id_;
301 PendingResponse pending{
302 .callback =
303 [this, callback = std::move(callback)](bool handled,
304 uint64_t response_id) {
305 auto found = pending_responses_.find(response_id);
306 if (found != pending_responses_.end()) {
307 pending_responses_.erase(found);
308 }
309 callback(handled);
310 },
311 .response_id = response_id,
312 };
313 auto pending_ptr = std::make_unique<PendingResponse>(std::move(pending));
314 pending_responses_[response_id] = std::move(pending_ptr);
315 SendEvent(key_data, KeyboardKeyEmbedderHandler::HandleResponse,
316 reinterpret_cast<void*>(pending_responses_[response_id].get()));
317
318 // Post-event synchronization. It is useful in cases where the true pressing
319 // state does not match the event type. For example, a CapsLock down event is
320 // received despite that GetKeyState says that CapsLock is not pressed. In
321 // such case, post-event synchronization will synthesize a CapsLock up event
322 // after the main event.
323 SynchronizeCriticalPressedStates(key, physical_key, is_event_down,
324 event_key_can_be_repeat);
325}
326
328 int key,
329 int scancode,
330 int action,
331 char32_t character,
332 bool extended,
333 bool was_down,
334 std::function<void(bool)> callback) {
335 sent_any_events = false;
336 KeyboardHookImpl(key, scancode, action, character, extended, was_down,
337 std::move(callback));
338 if (!sent_any_events) {
339 FlutterKeyEvent empty_event{
340 .struct_size = sizeof(FlutterKeyEvent),
341 .timestamp = static_cast<double>(
342 std::chrono::duration_cast<std::chrono::microseconds>(
343 std::chrono::high_resolution_clock::now().time_since_epoch())
344 .count()),
346 .physical = 0,
347 .logical = 0,
348 .character = empty_character,
349 .synthesized = false,
350 };
351 SendEvent(empty_event, nullptr, nullptr);
352 }
353}
354
355std::map<uint64_t, uint64_t> KeyboardKeyEmbedderHandler::GetPressedState() {
356 return pressingRecords_;
357}
358
359void KeyboardKeyEmbedderHandler::UpdateLastSeenCriticalKey(
360 int virtual_key,
361 uint64_t physical_key,
362 uint64_t logical_key) {
363 auto found = critical_keys_.find(virtual_key);
364 if (found != critical_keys_.end()) {
365 found->second.physical_key = physical_key;
366 found->second.logical_key = logical_key;
367 }
368}
369
370void KeyboardKeyEmbedderHandler::SynchronizeCriticalToggledStates(
371 int event_virtual_key,
372 bool is_event_down,
373 bool* event_key_can_be_repeat) {
374 // NowState ----------------> PreEventState --------------> TrueState
375 // Synchronization Event
376 for (auto& kv : critical_keys_) {
377 UINT virtual_key = kv.first;
378 CriticalKey& key_info = kv.second;
379 if (key_info.physical_key == 0) {
380 // Never seen this key.
381 continue;
382 }
383 FML_DCHECK(key_info.logical_key != 0);
384
385 // Check toggling state first, because it might alter pressing state.
386 if (key_info.check_toggled) {
387 const bool target_is_pressed =
388 pressingRecords_.find(key_info.physical_key) !=
389 pressingRecords_.end();
390 // The togglable keys observe a 4-phase cycle:
391 //
392 // Phase# 0 1 2 3
393 // Event Down Up Down Up
394 // Pressed 0 1 0 1
395 // Toggled 0 1 1 0
396 const bool true_toggled = get_key_state_(virtual_key) & kStateMaskToggled;
397 bool pre_event_toggled = true_toggled;
398 // Check if the main event's key is the key being checked. If it's the
399 // non-repeat down event, toggle the state.
400 if (virtual_key == event_virtual_key && !target_is_pressed &&
401 is_event_down) {
402 pre_event_toggled = !pre_event_toggled;
403 }
404 if (key_info.toggled_on != pre_event_toggled) {
405 // If the key is pressed, release it first.
406 if (target_is_pressed) {
407 SendEvent(SynthesizeSimpleEvent(
408 kFlutterKeyEventTypeUp, key_info.physical_key,
409 key_info.logical_key, empty_character),
410 nullptr, nullptr);
411 }
412 // Synchronizing toggle state always ends with the key being pressed.
413 pressingRecords_[key_info.physical_key] = key_info.logical_key;
414 SendEvent(SynthesizeSimpleEvent(kFlutterKeyEventTypeDown,
415 key_info.physical_key,
416 key_info.logical_key, empty_character),
417 nullptr, nullptr);
418 *event_key_can_be_repeat = false;
419 }
420 key_info.toggled_on = true_toggled;
421 }
422 }
423}
424
425void KeyboardKeyEmbedderHandler::SynchronizeCriticalPressedStates(
426 int event_virtual_key,
427 int event_physical_key,
428 bool is_event_down,
429 bool event_key_can_be_repeat) {
430 // During an incoming event, there might be a synthesized Flutter event for
431 // each key of each pressing goal, followed by an eventual main Flutter
432 // event.
433 //
434 // NowState ----------------> PreEventState --------------> TrueState
435 // Synchronization Event
436 //
437 // The goal of the synchronization algorithm is to derive a pre-event state
438 // that can satisfy the true state (`true_pressed`) after the event, and that
439 // requires as few synthesized events based on the current state
440 // (`now_pressed`) as possible.
441 for (auto& kv : critical_keys_) {
442 UINT virtual_key = kv.first;
443 CriticalKey& key_info = kv.second;
444 if (key_info.physical_key == 0) {
445 // Never seen this key.
446 continue;
447 }
448 FML_DCHECK(key_info.logical_key != 0);
449 if (key_info.check_pressed) {
450 SHORT true_pressed = get_key_state_(virtual_key) & kStateMaskPressed;
451 auto pressing_record_iter = pressingRecords_.find(key_info.physical_key);
452 bool now_pressed = pressing_record_iter != pressingRecords_.end();
453 bool pre_event_pressed = true_pressed;
454 // Check if the main event is the key being checked to get the correct
455 // target state.
456 if (is_event_down) {
457 // For down events, this key is the event key if they have the same
458 // virtual key, because virtual key represents "functionality."
459 //
460 // In that case, normally Flutter should synthesize nothing since the
461 // resulting event can adapt to the current state by dispatching either
462 // a down or a repeat event. However, in certain cases (when Flutter has
463 // just synchronized the key's toggling state) the event must not be a
464 // repeat event.
465 if (virtual_key == event_virtual_key) {
466 if (event_key_can_be_repeat) {
467 continue;
468 } else {
469 pre_event_pressed = false;
470 }
471 }
472 } else {
473 // For up events, this key is the event key if they have the same
474 // physical key, because it is necessary to ensure that the physical
475 // key is correctly released.
476 //
477 // In that case, although the previous state should be pressed, don't
478 // synthesize a down event even if it's not. The later code will handle
479 // such cases by skipping abrupt up events. Obviously don't synthesize
480 // up events either.
481 if (event_physical_key == key_info.physical_key) {
482 continue;
483 }
484 }
485 if (now_pressed != pre_event_pressed) {
486 if (now_pressed) {
487 pressingRecords_.erase(pressing_record_iter);
488 } else {
489 pressingRecords_[key_info.physical_key] = key_info.logical_key;
490 }
491 const char* empty_character = "";
492 SendEvent(
493 SynthesizeSimpleEvent(
495 key_info.physical_key, key_info.logical_key, empty_character),
496 nullptr, nullptr);
497 }
498 }
499 }
500}
501
503 // TODO(bleroux): consider exposing these constants in flutter_key_map.g.cc?
504 const uint64_t physical_shift_left =
505 windowsToPhysicalMap_.at(kScanCodeShiftLeft);
506 const uint64_t physical_shift_right =
507 windowsToPhysicalMap_.at(kScanCodeShiftRight);
508 const uint64_t logical_shift_left =
509 windowsToLogicalMap_.at(kKeyCodeShiftLeft);
510 const uint64_t physical_control_left =
511 windowsToPhysicalMap_.at(kScanCodeControlLeft);
512 const uint64_t physical_control_right =
513 windowsToPhysicalMap_.at(kScanCodeControlRight);
514 const uint64_t logical_control_left =
515 windowsToLogicalMap_.at(kKeyCodeControlLeft);
516
517 bool shift_pressed = (modifiers_state & kShift) != 0;
518 SynthesizeIfNeeded(physical_shift_left, physical_shift_right,
519 logical_shift_left, shift_pressed);
520 bool control_pressed = (modifiers_state & kControl) != 0;
521 SynthesizeIfNeeded(physical_control_left, physical_control_right,
522 logical_control_left, control_pressed);
523}
524
525void KeyboardKeyEmbedderHandler::SynthesizeIfNeeded(uint64_t physical_left,
526 uint64_t physical_right,
527 uint64_t logical_left,
528 bool is_pressed) {
529 auto pressing_record_iter_left = pressingRecords_.find(physical_left);
530 bool left_pressed = pressing_record_iter_left != pressingRecords_.end();
531 auto pressing_record_iter_right = pressingRecords_.find(physical_right);
532 bool right_pressed = pressing_record_iter_right != pressingRecords_.end();
533 bool already_pressed = left_pressed || right_pressed;
534 bool synthesize_down = is_pressed && !already_pressed;
535 bool synthesize_up = !is_pressed && already_pressed;
536
537 if (synthesize_down) {
538 SendSynthesizeDownEvent(physical_left, logical_left);
539 }
540
541 if (synthesize_up && left_pressed) {
542 uint64_t known_logical = pressing_record_iter_left->second;
543 SendSynthesizeUpEvent(physical_left, known_logical);
544 }
545
546 if (synthesize_up && right_pressed) {
547 uint64_t known_logical = pressing_record_iter_right->second;
548 SendSynthesizeUpEvent(physical_right, known_logical);
549 }
550}
551
552void KeyboardKeyEmbedderHandler::SendSynthesizeDownEvent(uint64_t physical,
553 uint64_t logical) {
554 SendEvent(
555 SynthesizeSimpleEvent(kFlutterKeyEventTypeDown, physical, logical, ""),
556 nullptr, nullptr);
557 pressingRecords_[physical] = logical;
558};
559
560void KeyboardKeyEmbedderHandler::SendSynthesizeUpEvent(uint64_t physical,
561 uint64_t logical) {
562 SendEvent(
563 SynthesizeSimpleEvent(kFlutterKeyEventTypeUp, physical, logical, ""),
564 nullptr, nullptr);
565 pressingRecords_.erase(physical);
566};
567
568void KeyboardKeyEmbedderHandler::HandleResponse(bool handled, void* user_data) {
569 PendingResponse* pending = reinterpret_cast<PendingResponse*>(user_data);
570 auto callback = std::move(pending->callback);
571 callback(handled, pending->response_id);
572}
573
574void KeyboardKeyEmbedderHandler::InitCriticalKeys(
575 MapVirtualKeyToScanCode map_virtual_key_to_scan_code) {
576 auto createCheckedKey = [this, &map_virtual_key_to_scan_code](
577 UINT virtual_key, bool extended,
578 bool check_pressed,
579 bool check_toggled) -> CriticalKey {
580 UINT scan_code = map_virtual_key_to_scan_code(virtual_key, extended);
581 return CriticalKey{
582 .physical_key = GetPhysicalKey(scan_code, extended),
583 .logical_key = GetLogicalKey(virtual_key, extended, scan_code),
584 .check_pressed = check_pressed || check_toggled,
585 .check_toggled = check_toggled,
586 .toggled_on = check_toggled
587 ? !!(get_key_state_(virtual_key) & kStateMaskToggled)
588 : false,
589 };
590 };
591
592 critical_keys_.emplace(VK_LSHIFT,
593 createCheckedKey(VK_LSHIFT, false, true, false));
594 critical_keys_.emplace(VK_RSHIFT,
595 createCheckedKey(VK_RSHIFT, false, true, false));
596 critical_keys_.emplace(VK_LCONTROL,
597 createCheckedKey(VK_LCONTROL, false, true, false));
598 critical_keys_.emplace(VK_RCONTROL,
599 createCheckedKey(VK_RCONTROL, true, true, false));
600 critical_keys_.emplace(VK_LMENU,
601 createCheckedKey(VK_LMENU, false, true, false));
602 critical_keys_.emplace(VK_RMENU,
603 createCheckedKey(VK_RMENU, true, true, false));
604 critical_keys_.emplace(VK_LWIN, createCheckedKey(VK_LWIN, true, true, false));
605 critical_keys_.emplace(VK_RWIN, createCheckedKey(VK_RWIN, true, true, false));
606 critical_keys_.emplace(VK_CAPITAL,
607 createCheckedKey(VK_CAPITAL, false, true, true));
608 critical_keys_.emplace(VK_SCROLL,
609 createCheckedKey(VK_SCROLL, false, true, true));
610 critical_keys_.emplace(VK_NUMLOCK,
611 createCheckedKey(VK_NUMLOCK, true, true, true));
612}
613
614void KeyboardKeyEmbedderHandler::ConvertUtf32ToUtf8_(char* out, char32_t ch) {
615 if (ch == 0) {
616 out[0] = '\0';
617 return;
618 }
619 std::string result = ConvertChar32ToUtf8(ch);
620 strcpy_s(out, kCharacterCacheSize, result.c_str());
621}
622
623FlutterKeyEvent KeyboardKeyEmbedderHandler::SynthesizeSimpleEvent(
625 uint64_t physical,
626 uint64_t logical,
627 const char* character) {
628 return FlutterKeyEvent{
629 .struct_size = sizeof(FlutterKeyEvent),
630 .timestamp = static_cast<double>(
631 std::chrono::duration_cast<std::chrono::microseconds>(
632 std::chrono::high_resolution_clock::now().time_since_epoch())
633 .count()),
634 .type = type,
635 .physical = physical,
636 .logical = logical,
638 .synthesized = true,
639 };
640}
641
642void KeyboardKeyEmbedderHandler::SendEvent(const FlutterKeyEvent& event,
644 void* user_data) {
645 sent_any_events = true;
646 perform_send_event_(event, callback, user_data);
647}
648
649} // 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:1427
FlutterKeyEventType
Definition embedder.h:1346
@ kFlutterKeyEventTypeDown
Definition embedder.h:1348
@ kFlutterKeyEventTypeUp
Definition embedder.h:1347
@ kFlutterKeyEventTypeRepeat
Definition embedder.h:1349
FlutterDesktopBinaryReply callback
#define FML_DCHECK(condition)
Definition logging.h:122
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)
size_t struct_size
The size of this struct. Must be sizeof(FlutterKeyEvent).
Definition embedder.h:1384
const size_t start
const size_t end
unsigned int UINT
short SHORT