-
-
Notifications
You must be signed in to change notification settings - Fork 39.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tap-dance: Restructure code and document in more detail #16394
Conversation
9de57cb
to
465b172
Compare
I added this as well. This would be the most functional change in this PR, so I gave it a separate commit to ease reviewing. |
4564b2e
to
1132b43
Compare
1132b43
to
9eaff57
Compare
@algernon, @jackhumbert, what do you think? The code was in pretty good shape already, but I could shave some bytes and cycles without sacrificing readability (I think). I also isolated the action responsibilities more thoroughly, pulling them out of the The added documentation is something I would have liked to read myself. |
qk_tap_dance_state_t state; | ||
uint16_t custom_tapping_term; | ||
void * user_data; | ||
uint16_t custom_tapping_term; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could probably be nuked to, in favor of the get_tapping_term support.
uint16_t custom_tapping_term; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that would clean up the tap-dance implementation nicely. I added a commit where I do so. I looked into usage of ACTION_TAP_DANCE_FN_ADVANCED_TIME
and provided an alternative for all instances. It looks like some could just as well set TAPPING_TERM
to the desired value, but I didn't want to be too invasive.
While this achieves a unified way of setting per-key tapping terms, it is somewhat more verbose for the keymaps.
If we want this, I assume we need a changelog entry, which I did not write.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACTION_TAP_DANCE_FN_ADVANCED_TIME()
, which uses that field, was deprecated back in #6259 (merged on 25 Jul 2020) in favor of the generic custom tapping term support; that's probably enough time to get it removed completely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit unsure what to do about the TAPPING_TERM_PER_KEY
macro. It doesn't seem to be orthogonal to the other config settings such as DYNAMIC_TAPPING_TERM
and NO_ACTION_TAPPING
. Especially the latter I don't fully understand the intent of. If tap-dance is supposed to work even with NO_ACTION_TAPPING
, we might want to have #define get_tapping_term(keycode, event) (TAPPING_TERM)
somewhere on a NO_ACTION_TAPPING
code path (this does mean that all the get_tapping_term
definitions need to be guarded). Part of my confusion is that I don't really see how the setting of TAPPING_TERM_PER_KEY
would have a big impact on the firmware size/speed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rebased this MR on top of #16681. The churn is high, but that is mostly due to the last commit that drops ACTION_TAP_DANCE_FN_ADVANCED_TIME
(as requested in this thread) and updates all the places in the repo where it was used.
As far as I can tell, this is ready to go in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This MR has been in development for 3 months now and the churn is only getting higher. @drashna, can you confirm we are still progressing toward an eventual merge? We now have a restructuring of the code, including more efficient action/state structs and simplification of the overall implementation, fixes to corner cases and initial test coverage, and deprecation of ACTION_TAP_DANCE_FN_ADVANCED_TIME
.
64ba399
to
f04d0d5
Compare
f04d0d5
to
2a4de66
Compare
2a4de66
to
09b1d75
Compare
28c27bf
to
381c0e3
Compare
845a610
to
5854ec3
Compare
To accommodate the changes it would be very good to have unit-test which at least cover the examples which are in the documentation. So we can be sure that any future refactoring of QMKs core implementations won't break the tap dance implementation. |
I agree that would be nice, but since this PR doesn't really change the existing interfaces, maybe the addition of tests can be left to a follow-up PR in order to not pile any more on this one? How extensive do we want the tests to be? I guess at least the presence of the 'public' interface: struct qk_tap_dance_state_t;
qk_tap_dance_user_fn_t;
struct qk_tap_dance_action_t;
ACTION_TAP_DANCE_DOUBLE(kc1, kc2)
ACTION_TAP_DANCE_DUAL_ROLE(kc, layer)
ACTION_TAP_DANCE_LAYER_TOGGLE(kc, layer)
ACTION_TAP_DANCE_LAYER_MOVE(kc, layer)
ACTION_TAP_DANCE_FN(user_fn)
ACTION_TAP_DANCE_FN_ADVANCED(user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset)
TD(n)
TD_INDEX(code)
TAP_DANCE_KEYCODE(state)
void reset_tap_dance(qk_tap_dance_state_t *state); but maybe also some state correctness including mod handling? |
Cool! I don't mind if the test get added in a different PR if they add considerable amounts of code.
There is no established testing rule for QMK e.g. necessary branch or line coverage. So I would go with your recommendation of the public interface. From a brief look over the examples in the documentation it seemed that these would be a good baseline (hence the suggestion). Tricky edge case interactions which were problematic before should be covered as well. As I'm not that familiar with the code or feature (yet) I'll rely on your judgement on which parts to cover. Thanks for improving QMK. |
5854ec3
to
6edc028
Compare
I added the simple tap dance example and the first four complex ones as test cases. I did not include tests for all the possible interrupts and not even for all elements of the public interface, but to be honest I currently don't feel like writing even more test code. There is already twice as many lines for testing than for implementing tap dance. While writing tests, I found some erroneous corner cases in the tracking of the last tap dance, as anticipated by @sigprof. These are now fixed. |
b5cddcf
to
25dd0f1
Compare
729edde
to
0595c4b
Compare
Sorry for leaving this a bit in a limbo. I'm busy with the rp2040 support right now, but I intend to push your PR for the Q3 cycle. |
Crucially, let code and documentation reflect the following. on_each_tap_fn: Called on each key-down of a dance. on_dance_finished_fn: Called on timeout and interrupt, unless suppressed. on_dance_reset_fn: Called on (or after) final key-up.
At any time, there is at most one active tap dance. Therefore, we don't need timing information per tap dance key and we don't need to loop through all tap dance keys to check whether they are being interrupted or timing out.
get_tapping_term offers the same functionality.
0595c4b
to
dfb27ae
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for all your effort that went into this refactor and also sticking with this PR though the churn was high. I read your changes and also tested all the examples from the test harness on a keyboard and everything works as expected. (I cheated on the egg dance and made it trigger at 5 repeats though.) There are some minor nits but you have my approval.
dfb27ae
to
73477c9
Compare
It is renamed to ACTION_TAP_DANCE_LAYER_MOVE.
73477c9
to
61a05b6
Compare
Given that this MR is already taking its sweet time, I added one more commit to it, where I remove the @drashna @sigprof If this extra commit is controversial in any way, I'll remove it from this MR. |
...and it is in! 🎉 |
Description
These changes were developed when studying the tap dance code.
Crucially, let code and documentation reflect the following.
on_each_tap_fn
: Called on each key-down of a dance.on_dance_finished_fn
: Called on timeout and interrupt, unless suppressed.on_dance_reset_fn
: Called on (or after) final key-up.The structure packing on the action struct and per-action state struct is most likely a pointless optimization, but I don't think it hurts readability too much.
There was one
bug
inreset_tap_dance
, where it would be a no-op in case a key was still pressed. This was not what appeared to be assumed by external usage. I decided to put all the responsibility in theprocess_tap_dance_action_…
functions and admit thatreset_tap_dance
was not only used internally (i.e. I moved it around inprocess_tap_dance.h
and documented it.I think the added example shows a lot of tap-dance internals at work that were not (or less clearly) shown by other examples.
Types of Changes
Checklist
It isn't clear to me what constitutes a 'Core PR'. This PR targets
develop
just to be sure. I expect this PR may feel a bit all over the place. If so, let me know how I can restructure it to make reviewing it easier.One thing I left out in this PR is changing
tap_dance_task
to use&tap_dance_actions[last_td]
. As far as I can tell, there is only ever one tap dance 'active', so there is no reason to iterate through all of them.I was hesitant to touch other peoples keymaps, but thought the change to using
user_data
inkeyboards/thevankeyboards/minivan/keymaps/belak/keymap.c
and the replacements inkeyboards/jones/v03*/keymaps/default_*/keymap.c
were concise enough.