Fix: modifier keys + first character dropped on wireless after idle sleep#6
Open
jkquickstar wants to merge 2 commits intoEpomaker:mainfrom
Open
Fix: modifier keys + first character dropped on wireless after idle sleep#6jkquickstar wants to merge 2 commits intoEpomaker:mainfrom
jkquickstar wants to merge 2 commits intoEpomaker:mainfrom
Conversation
2a0974d to
9b07674
Compare
Root cause: Two independent bugs causing modifiers (Cmd, Ctrl, etc.) to be
lost when the first keystroke after an idle period is a modifier combo.
1. NKRO dual-message race (wireless.c)
wireless_send_nkro() sends TWO UART messages per keystroke: a 6KRO report
(0xA1) containing modifiers+keys, followed by an NKRO bitmap (0xA2)
containing overflow keys. For normal typing (≤6 keys), the NKRO bitmap
is all zeros. Both messages are forwarded by the dongle as separate USB
HID reports. When they arrive within the same USB poll interval (~1ms),
the host only sees the last state — the empty NKRO — causing modifiers
to be dropped.
Fix: Skip md_send_nkro() when the NKRO bitmap is all zeros.
2. Post-sleep radio cold start (wireless.c + companion changes)
After the keyboard sleeps (1-minute idle timeout), the radio link to the
dongle goes cold. The first keypress wakes the MCU, but the HID report
is sent before the radio re-establishes — the report is lost.
Fix: Three-layer defense:
- Keep-alive: Re-send keyboard state every 1s to prevent radio/USB from
entering power-saving modes during normal operation
- Post-sleep resync: Aggressively re-send keyboard state (30×50ms=1500ms)
after detecting wakeup from sleep, with dedup cache invalidation
- Report-dropped recovery: When reports fail due to module disconnection,
invalidate the dedup cache and force a resend on reconnection
Companion changes needed in keyboard-level code (tide75.c, control.c):
- lpwr_stop_hook_post(): Prime radio by re-issuing device-mode command
(wireless_devs_change) immediately after MCU wakes, before main loop
processes the wake-up keypress (~85ms warm-up window)
- lpwr_wakeup_hook(): Call matrix_init() to reset matrix/debounce state
- action_util.c/h: Add keyboard_report_dedup_invalidate() and
nkro_report_dedup_invalidate() functions (move dedup cache from
function-local statics to file-scoped statics)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9b07674 to
c495c7e
Compare
Root cause: the default lpwr_wakeup_cb() calls suspend_wakeup_init() which invokes clear_mods() ~200ms after MCU wake. By that time the main loop has already scanned the wake-up keys and registered their modifiers. clear_mods() destroys that state, and because held keys are not re-processed by matrix debounce, modifiers are never re-registered — causing e.g. Cmd+Shift+4 to register as '$'. Changes: Keyboard-specific (tide75/): - Override lpwr_wakeup_cb() in control.c to skip suspend_wakeup_init() and call only suspend_wakeup_init_quantum() — preserves modifier state while still restoring LED/RGB matrix suspend state - Override lpwr_presleep_cb() to track RGB state independently (rgb_enable_bak is static in lowpower.c, inaccessible externally) - Add wireless_devs_change() in lpwr_stop_hook_post() to prime the radio module immediately on wake (like Lofree/P75 keyboards) - Remove redundant wireless_devs_change() from suspend_wakeup_init_kb - Set debounce to 8ms (from default) and force_nkro to false Wireless driver (tide75/linker/wireless/): - Add post-sleep resync mechanism (80x15ms = 1200ms) to re-deliver reports after radio warm-up - Add keep-alive (1000ms) to prevent radio link power-saving - Skip NKRO overflow (0xA2) during post-sleep resync to prevent modifier race between 6KRO and NKRO radio packets - Fix transport switching order (enable new before disabling old) - Fix USB suspend/resume tracking in transport.c QMK core (quantum/): - Add dedup cache invalidation API (keyboard_report_dedup_invalidate, nkro_report_dedup_invalidate) in action_util for resync support Tested: Cmd+Shift+4, Cmd+C, first-character typing all work after 1-minute wireless idle sleep on 2.4GHz. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes modifier key drops (e.g. Cmd+Shift+4 registering as
$instead of triggering macOS screenshot) and first-character drops on 2.4GHz wireless after the keyboard wakes from 1-minute idle sleep.Root cause: The default
lpwr_wakeup_cb()inlowpower.ccallssuspend_wakeup_init()→clear_mods()approximately 200ms after the MCU wakes. By that point, the main loop has already scanned the wake-up keys and registered their modifiers.clear_mods()destroys that state, and because held keys aren't re-processed by the matrix debouncer (it sees them as already pressed), the modifiers are never re-registered.Changes
Keyboard-specific (
tide75/control/):lpwr_wakeup_cb()(weak function) to skipsuspend_wakeup_init()and call onlysuspend_wakeup_init_quantum()— preserves modifier state while restoring LED/RGB matrix suspend statelpwr_presleep_cb()to track RGB enabled state independentlywireless_devs_change()inlpwr_stop_hook_post()to prime the radio module on wake (matching Lofree/P75 keyboard behavior)force_nkroto falseWireless driver (
tide75/linker/wireless/):QMK core (
quantum/action_util):keyboard_report_dedup_invalidate()andnkro_report_dedup_invalidate()APIs for resync support — these mark the report dedup cache as stale, forcing the nextsend_keyboard_report()to transmitTest plan
How to apply
The
quantum/action_util.candquantum/action_util.hfiles are QMK core modifications (not keyboard-specific). To use this fix, copy thequantum/files to your QMK tree'squantum/directory alongside the keyboard-specific files.🤖 Generated with Claude Code