@@ -162,25 +162,60 @@ void KeyboardKeyEmbedderHandler::KeyboardHookImpl(
162162 const uint64_t logical_key = GetLogicalKey (key, extended, scancode);
163163 assert (action == WM_KEYDOWN || action == WM_KEYUP ||
164164 action == WM_SYSKEYDOWN || action == WM_SYSKEYUP);
165- const bool is_physical_down = action == WM_KEYDOWN || action == WM_SYSKEYDOWN;
166165
167166 auto last_logical_record_iter = pressingRecords_.find (physical_key);
168167 const bool had_record = last_logical_record_iter != pressingRecords_.end ();
169168 const uint64_t last_logical_record =
170169 had_record ? last_logical_record_iter->second : 0 ;
171170
171+ // The logical key for the current "tap sequence".
172+ //
173+ // Key events are formed in tap sequences: down, repeats, up. The logical key
174+ // stays consistent throughout a tap sequence, which is this value.
175+ uint64_t sequence_logical_key =
176+ had_record ? last_logical_record : logical_key;
177+
178+ if (sequence_logical_key == VK_PROCESSKEY) {
179+ // VK_PROCESSKEY means that the key press is used by an IME. These key
180+ // presses are considered handled and not sent to Flutter. These events must
181+ // be filtered by result_logical_key because the key up event of such
182+ // presses uses the "original" logical key.
183+ callback (true );
184+ return ;
185+ }
186+
187+ const bool is_event_down = action == WM_KEYDOWN || action == WM_SYSKEYDOWN;
188+
189+ UpdateLastSeenCritialKey (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 SynchronizeCritialPressedStates.
200+ SynchronizeCritialToggledStates (key, is_event_down);
201+ // Synchronize the pressed states of critical keys (such as whether CapsLocks
202+ // is pressed).
203+ //
204+ // After this function, all critical keys except for the target key will have
205+ // their toggled state and pressed state matched with their true states. The
206+ // target key's pressed state will be updated immediately after this.
207+ SynchronizeCritialPressedStates (key, physical_key, is_event_down);
208+
172209 // The resulting event's `type`.
173210 FlutterKeyEventType type;
174- // The resulting event's `logical_key`.
175- uint64_t result_logical_key ;
211+ character = UndeadChar (character);
212+ char character_bytes[ kCharacterCacheSize ] ;
176213 // What pressingRecords_[physical_key] should be after the KeyboardHookImpl
177214 // returns (0 if the entry should be removed).
178215 uint64_t eventual_logical_record;
179- char character_bytes[kCharacterCacheSize ];
180-
181- character = UndeadChar (character);
216+ uint64_t result_logical_key;
182217
183- if (is_physical_down ) {
218+ if (is_event_down ) {
184219 if (had_record) {
185220 if (was_down) {
186221 // A normal repeated key.
@@ -223,35 +258,6 @@ void KeyboardKeyEmbedderHandler::KeyboardHookImpl(
223258 }
224259 }
225260
226- if (result_logical_key == VK_PROCESSKEY) {
227- // VK_PROCESSKEY means that the key press is used by an IME. These key
228- // presses are considered handled and not sent to Flutter. These events must
229- // be filtered by result_logical_key because the key up event of such
230- // presses uses the "original" logical key.
231- callback (true );
232- return ;
233- }
234-
235- UpdateLastSeenCritialKey (key, physical_key, result_logical_key);
236- // Synchronize the toggled states of critical keys (such as whether CapsLocks
237- // is enabled). Toggled states can only be changed upon a down event, so if
238- // the recorded toggled state does not match the true state, this function
239- // will synthesize (an up event if the key is recorded pressed, then) a down
240- // event.
241- //
242- // After this function, all critical keys will have their toggled state
243- // updated to the true state, while the critical keys whose toggled state have
244- // been changed will be pressed regardless of their true pressed state.
245- // Updating the pressed state will be done by SynchronizeCritialPressedStates.
246- SynchronizeCritialToggledStates (key, type == kFlutterKeyEventTypeDown );
247- // Synchronize the pressed states of critical keys (such as whether CapsLocks
248- // is pressed).
249- //
250- // After this function, all critical keys except for the target key will have
251- // their toggled state and pressed state matched with their true states. The
252- // target key's pressed state will be updated immediately after this.
253- SynchronizeCritialPressedStates (key, type != kFlutterKeyEventTypeRepeat );
254-
255261 if (eventual_logical_record != 0 ) {
256262 pressingRecords_[physical_key] = eventual_logical_record;
257263 } else {
@@ -333,8 +339,10 @@ void KeyboardKeyEmbedderHandler::UpdateLastSeenCritialKey(
333339}
334340
335341void KeyboardKeyEmbedderHandler::SynchronizeCritialToggledStates (
336- int this_virtual_key,
337- bool is_down_event) {
342+ int event_virtual_key,
343+ bool is_event_down) {
344+ // NowState ----------------> PreEventState --------------> TrueState
345+ // Synchronization Event
338346 for (auto & kv : critical_keys_) {
339347 UINT virtual_key = kv.first ;
340348 CriticalKey& key_info = kv.second ;
@@ -346,21 +354,26 @@ void KeyboardKeyEmbedderHandler::SynchronizeCritialToggledStates(
346354
347355 // Check toggling state first, because it might alter pressing state.
348356 if (key_info.check_toggled ) {
357+ const bool target_is_pressed =
358+ pressingRecords_.find (key_info.physical_key ) !=
359+ pressingRecords_.end ();
349360 // The togglable keys observe a 4-phase cycle:
350361 //
351362 // Phase# 0 1 2 3
352363 // Event Down Up Down Up
353364 // Pressed 0 1 0 1
354365 // Toggled 0 1 1 0
355- SHORT state = get_key_state_ (virtual_key);
356- bool should_toggled = state & kStateMaskToggled ;
357- if (virtual_key == this_virtual_key && is_down_event) {
358- key_info.toggled_on = !key_info.toggled_on ;
366+ const bool true_toggled = get_key_state_ (virtual_key) & kStateMaskToggled ;
367+ bool pre_event_toggled = true_toggled;
368+ // Check if the main event's key is the key being checked. If it's the
369+ // non-repeat down event, toggle the state.
370+ if (virtual_key == event_virtual_key && !target_is_pressed &&
371+ is_event_down) {
372+ pre_event_toggled = !pre_event_toggled;
359373 }
360- if (key_info.toggled_on != should_toggled ) {
374+ if (key_info.toggled_on != pre_event_toggled ) {
361375 // If the key is pressed, release it first.
362- if (pressingRecords_.find (key_info.physical_key ) !=
363- pressingRecords_.end ()) {
376+ if (target_is_pressed) {
364377 SendEvent (SynthesizeSimpleEvent (
365378 kFlutterKeyEventTypeUp , key_info.physical_key ,
366379 key_info.logical_key , empty_character),
@@ -373,14 +386,26 @@ void KeyboardKeyEmbedderHandler::SynchronizeCritialToggledStates(
373386 key_info.logical_key , empty_character),
374387 nullptr , nullptr );
375388 }
376- key_info.toggled_on = should_toggled ;
389+ key_info.toggled_on = true_toggled ;
377390 }
378391 }
379392}
380393
381394void KeyboardKeyEmbedderHandler::SynchronizeCritialPressedStates (
382- int this_virtual_key,
383- bool pressed_state_will_change) {
395+ int event_virtual_key,
396+ int event_physical_key,
397+ bool is_event_down) {
398+ // During an incoming event, there might be a synthesized Flutter event for
399+ // each key of each pressing goal, followed by an eventual main Flutter
400+ // event.
401+ //
402+ // NowState ----------------> PreEventState --------------> TrueState
403+ // Synchronization Event
404+ //
405+ // The goal of the synchronization algorithm is to derive a pre-event state
406+ // that can satisfy the true state (`true_pressed`) after the event, and that
407+ // requires as few synthesized events based on the current state
408+ // (`now_pressed`) as possible.
384409 for (auto & kv : critical_keys_) {
385410 UINT virtual_key = kv.first ;
386411 CriticalKey& key_info = kv.second ;
@@ -390,25 +415,42 @@ void KeyboardKeyEmbedderHandler::SynchronizeCritialPressedStates(
390415 }
391416 assert (key_info.logical_key != 0 );
392417 if (key_info.check_pressed ) {
393- SHORT state = get_key_state_ (virtual_key);
394- auto recorded_pressed_iter = pressingRecords_.find (key_info.physical_key );
395- bool recorded_pressed = recorded_pressed_iter != pressingRecords_.end ();
396- bool should_pressed = state & kStateMaskPressed ;
397- if (virtual_key == this_virtual_key && pressed_state_will_change) {
398- should_pressed = !should_pressed;
418+ SHORT true_pressed = get_key_state_ (virtual_key) & kStateMaskPressed ;
419+ auto pressing_record_iter = pressingRecords_.find (key_info.physical_key );
420+ bool now_pressed = pressing_record_iter != pressingRecords_.end ();
421+ bool pre_event_pressed = true_pressed;
422+ // Check if the main event is the key being checked to get the correct
423+ // target state.
424+ if (is_event_down) {
425+ // For down events, this key is the event key if they have the same
426+ // virtual key, because virtual key represents "functionality."
427+ if (virtual_key == event_virtual_key) {
428+ pre_event_pressed = false ;
429+ }
430+ } else {
431+ // For up events, this key is the event key if they have the same
432+ // physical key, because it is necessary to ensure that the physical
433+ // key is correctly released.
434+ //
435+ // In that case, although the previous state should be pressed, don't
436+ // synthesize a down event even if it's not. The later code will handle
437+ // such cases by skipping abrupt up events. Obviously don't synthesize
438+ // up events either.
439+ if (event_physical_key == key_info.physical_key ) {
440+ continue ;
441+ }
399442 }
400- if (recorded_pressed != should_pressed ) {
401- if (recorded_pressed ) {
402- pressingRecords_.erase (recorded_pressed_iter );
443+ if (now_pressed != pre_event_pressed ) {
444+ if (now_pressed ) {
445+ pressingRecords_.erase (pressing_record_iter );
403446 } else {
404447 pressingRecords_[key_info.physical_key ] = key_info.logical_key ;
405448 }
406449 const char * empty_character = " " ;
407450 SendEvent (
408- SynthesizeSimpleEvent (recorded_pressed ? kFlutterKeyEventTypeUp
409- : kFlutterKeyEventTypeDown ,
410- key_info.physical_key , key_info.logical_key ,
411- empty_character),
451+ SynthesizeSimpleEvent (
452+ now_pressed ? kFlutterKeyEventTypeUp : kFlutterKeyEventTypeDown ,
453+ key_info.physical_key , key_info.logical_key , empty_character),
412454 nullptr , nullptr );
413455 }
414456 }
0 commit comments