Flutter Windows Embedder
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 
15 namespace flutter {
16 
17 namespace {
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'.
22 constexpr size_t kCharacterCacheSize = 8;
23 
24 constexpr SHORT kStateMaskToggled = 0x01;
25 constexpr SHORT kStateMaskPressed = 0x80;
26 
27 const 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.
33 char _GetBit(char32_t ch, size_t start, size_t end) {
34  return (ch >> end) & ((1 << (start - end)) - 1);
35 }
36 } // namespace
37 
38 std::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 
71 static 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.
80 static 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.
104 static 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 
110 uint64_t KeyboardKeyEmbedderHandler::ApplyPlaneToId(uint64_t id,
111  uint64_t plane) {
112  return (id & valueMask) | plane;
113 }
114 
115 uint64_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 
124 uint64_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 
152 void 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`.
219  FlutterKeyEventType 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.
231  type = kFlutterKeyEventTypeRepeat;
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).
246  type = kFlutterKeyEventTypeDown;
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.
261  type = kFlutterKeyEventTypeUp;
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()),
343  .type = kFlutterKeyEventTypeDown,
344  .physical = 0,
345  .logical = 0,
346  .character = empty_character,
347  .synthesized = false,
348  };
349  SendEvent(empty_event, nullptr, nullptr);
350  }
351 }
352 
353 std::map<uint64_t, uint64_t> KeyboardKeyEmbedderHandler::GetPressedState() {
354  return pressingRecords_;
355 }
356 
357 void 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 
368 void 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 
423 void 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(
492  now_pressed ? kFlutterKeyEventTypeUp : kFlutterKeyEventTypeDown,
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 
523 void 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 
550 void 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 
558 void 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 
566 void 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 
572 void 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 
612 void 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 
621 FlutterKeyEvent KeyboardKeyEmbedderHandler::SynthesizeSimpleEvent(
622  FlutterKeyEventType type,
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,
635  .character = character,
636  .synthesized = true,
637  };
638 }
639 
640 void KeyboardKeyEmbedderHandler::SendEvent(const FlutterKeyEvent& event,
641  FlutterKeyEventCallback callback,
642  void* user_data) {
643  sent_any_events = true;
644  perform_send_event_(event, callback, user_data);
645 }
646 
647 } // namespace flutter
flutter::kKeyCodeShiftLeft
constexpr int kKeyCodeShiftLeft
Definition: keyboard_utils.h:18
scancode
int scancode
Definition: keyboard_key_handler_unittests.cc:115
was_down
bool was_down
Definition: keyboard_key_handler_unittests.cc:119
flutter::KeyboardKeyEmbedderHandler::KeyboardHook
void KeyboardHook(int key, int scancode, int action, char32_t character, bool extended, bool was_down, std::function< void(bool)> callback) override
Definition: keyboard_key_embedder_handler.cc:325
extended
bool extended
Definition: keyboard_key_handler_unittests.cc:118
user_data
void * user_data
Definition: flutter_windows_view_unittests.cc:52
flutter::isEasciiPrintable
static bool isEasciiPrintable(int codeUnit)
Definition: keyboard_key_embedder_handler.cc:71
flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler
std::function< SHORT(int)> GetKeyStateHandler
Definition: keyboard_key_embedder_handler.h:41
character
char32_t character
Definition: keyboard_key_handler_unittests.cc:117
flutter::KeyboardKeyEmbedderHandler::~KeyboardKeyEmbedderHandler
virtual ~KeyboardKeyEmbedderHandler()
flutter::KeyboardKeyEmbedderHandler::SendEventHandler
std::function< void(const FlutterKeyEvent &, FlutterKeyEventCallback, void *)> SendEventHandler
Definition: keyboard_key_embedder_handler.h:40
flutter::kScanCodeShiftLeft
constexpr int kScanCodeShiftLeft
Definition: keyboard_utils.h:16
type
enum flutter::testing::@87::KeyboardChange::Type type
keyboard_key_embedder_handler.h
flutter::toLower
static uint64_t toLower(uint64_t n)
Definition: keyboard_key_embedder_handler.cc:80
flutter::KeyboardKeyEmbedderHandler::MapVirtualKeyToScanCode
std::function< SHORT(UINT, bool)> MapVirtualKeyToScanCode
Definition: keyboard_key_embedder_handler.h:43
flutter::UndeadChar
uint32_t UndeadChar(uint32_t ch)
Definition: keyboard_utils.h:34
keyboard_utils.h
flutter::ConvertChar32ToUtf8
std::string ConvertChar32ToUtf8(char32_t ch)
Definition: keyboard_key_embedder_handler.cc:38
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::normalizeScancode
static uint16_t normalizeScancode(int windowsScanCode, bool extended)
Definition: keyboard_key_embedder_handler.cc:104
flutter::kShift
constexpr int kShift
Definition: keyboard_utils.h:14
flutter::KeyboardKeyEmbedderHandler::KeyboardKeyEmbedderHandler
KeyboardKeyEmbedderHandler(SendEventHandler send_event, GetKeyStateHandler get_key_state, MapVirtualKeyToScanCode map_vk_to_scan)
Definition: keyboard_key_embedder_handler.cc:59
flutter::kScanCodeControlLeft
constexpr int kScanCodeControlLeft
Definition: keyboard_utils.h:19
flutter::kKeyCodeControlLeft
constexpr int kKeyCodeControlLeft
Definition: keyboard_utils.h:21
action
int action
Definition: keyboard_key_handler_unittests.cc:116
flutter::kScanCodeControlRight
constexpr int kScanCodeControlRight
Definition: keyboard_utils.h:20
flutter::KeyboardKeyEmbedderHandler::GetPressedState
std::map< uint64_t, uint64_t > GetPressedState() override
Definition: keyboard_key_embedder_handler.cc:353
flutter::kControl
constexpr int kControl
Definition: keyboard_utils.h:15
flutter::kScanCodeShiftRight
constexpr int kScanCodeShiftRight
Definition: keyboard_utils.h:17
key
int key
Definition: keyboard_key_handler_unittests.cc:114
flutter::KeyboardKeyEmbedderHandler::SyncModifiersIfNeeded
void SyncModifiersIfNeeded(int modifiers_state) override
Definition: keyboard_key_embedder_handler.cc:500
callback
FlutterDesktopBinaryReply callback
Definition: flutter_windows_view_unittests.cc:51