Skip to content

dynamic_tapping_term: prevent DT_DOWN underflowing the tapping term#26337

Draft
arcaartem wants to merge 4 commits into
qmk:developfrom
arcaartem:fix-dynamic-tapping-term-underflow
Draft

dynamic_tapping_term: prevent DT_DOWN underflowing the tapping term#26337
arcaartem wants to merge 4 commits into
qmk:developfrom
arcaartem:fix-dynamic-tapping-term-underflow

Conversation

@arcaartem

@arcaartem arcaartem commented Jul 16, 2026

Copy link
Copy Markdown

Description

g_tapping_term is a uint16_t. QK_DYNAMIC_TAPPING_TERM_DOWN unconditionally subtracted DYNAMIC_TAPPING_TERM_INCREMENT from it, so once the term dropped below the increment the next press would wrap it past zero to roughly 65531 ms. At that point every tap-hold key resolves as a tap and the keyboard is effectively unusable until the term is raised back step by step or the board is reset.

Guard the subtraction so it only applies while it would not underflow. DT_UP behavior is unchanged.

Includes a regression test in the tests/dynamic_tapping_term suite: tapping DT_DOWN 100 times from a starting term derived as an exact multiple of DYNAMIC_TAPPING_TERM_INCREMENT asserts g_tapping_term never exceeds its starting value and settles at the increment floor instead of wrapping, valid for any increment configuration. The test was verified to fail against the unguarded code (term reaches 0, then wraps to 65531) and passes with the guard.

Also documents the clamp: the DT_DOWN entries in docs/keycodes.md and docs/tap_hold.md now note that the tapping term is never lowered below the increment.

Stacked on #26338, which introduces the test suite; the first commit here belongs to that PR and will drop out of this diff once it merges.

Types of Changes

  • Core
  • Bug fix
  • New feature
  • Enhancement/optimization
  • Keyboard (addition or update)
  • Keymap/layout/userspace (addition or update)
  • Documentation

Checklist

  • My code follows the code style of this project: qmk format-c applied, diff is noise-free.
  • I have read the CONTRIBUTING document.
  • My change targets the develop branch.
  • My change is restricted to one logical change (core only, no keyboard/keymap mixed in).
  • I have added tests to cover my changes (make test:dynamic_tapping_term, 4/4 passing; regression test fails without the fix).
  • Compile-verified against crkbd/rev1:default with DYNAMIC_TAPPING_TERM_ENABLE=yes, confirming process_dynamic_tapping_term.c is built and linked.

@github-actions github-actions Bot added the core label Jul 16, 2026
@arcaartem
arcaartem force-pushed the fix-dynamic-tapping-term-underflow branch from 8bd779b to 7502a31 Compare July 16, 2026 12:04
@arcaartem

Copy link
Copy Markdown
Author

Rebased to stack on #26338 (new tests/dynamic_tapping_term suite) and added a regression test for the underflow: 100 DT_DOWN presses from the default term, asserting the term never exceeds its starting value and floors at the increment. Verified the test fails against the unguarded code (wraps to 65531) and passes with the guard.

Add a test suite under tests/dynamic_tapping_term covering the
DYNAMIC_TAPPING_TERM_ENABLE feature: the initial value of
g_tapping_term matches the configured TAPPING_TERM, DT_UP raises it
by DYNAMIC_TAPPING_TERM_INCREMENT per press, and DT_DOWN lowers it by
the same amount per press. No prior test coverage existed for this
feature.

Give the fixture a destructor that restores g_tapping_term to
TAPPING_TERM after every test, so InitialTappingTermMatchesConfiguredDefault
does not depend on running before the other tests mutate the global
tapping term. Verified order-independence by running the suite with
--gtest_shuffle across many seeds.
@arcaartem
arcaartem force-pushed the fix-dynamic-tapping-term-underflow branch from 7502a31 to e3817a1 Compare July 16, 2026 17:38
@arcaartem

Copy link
Copy Markdown
Author

Rebased on the updated #26338. The regression test now derives its starting term as an exact multiple of DYNAMIC_TAPPING_TERM_INCREMENT so the floor assertions hold for any increment configuration, and the clamp is now documented in docs/keycodes.md and docs/tap_hold.md (DT_DOWN never lowers the tapping term below the increment).

arcaartem and others added 3 commits July 17, 2026 09:19
Co-authored-by: Joel Challis <git@zvecr.com>
…c term

Exercise the feature end to end: the same hold duration resolves as a
tap under the default tapping term, then as a hold after DT_DOWN lowers
the term below it. This pins the GET_TAPPING_TERM -> g_tapping_term
wiring that the arithmetic-only tests do not cover.
g_tapping_term is uint16_t. QK_DYNAMIC_TAPPING_TERM_DOWN unconditionally
subtracted DYNAMIC_TAPPING_TERM_INCREMENT from it, so repeated presses
once the term dropped below the increment would wrap it past 0 to
~65531, making tap-hold effectively unusable. Guard the subtraction so
it only applies while it would not underflow.

Adds a regression test that taps DT_DOWN 100 times from a derived
starting term (an exact multiple of DYNAMIC_TAPPING_TERM_INCREMENT) and
asserts g_tapping_term never exceeds that starting value and settles at
the increment floor instead of wrapping, regardless of how the increment
is configured.

Also document the clamp: DT_DOWN never lowers the tapping term below
DYNAMIC_TAPPING_TERM_INCREMENT, noted in docs/keycodes.md and
docs/tap_hold.md next to the existing DT_DOWN description.
@arcaartem
arcaartem force-pushed the fix-dynamic-tapping-term-underflow branch from e3817a1 to db37d92 Compare July 17, 2026 09:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant