Indicating Success on QMK Keyboards
In the previous post, I discussed ways of using QMK leader key sequences in QMK keymaps.
One of the downsides with this is that QMK leader key sequences are handled only after a timeout (i.e. enter the key sequence, then wait a moment).
One way of improving the UX of this is having the keyboard provide some feedback when a leader sequence is activated.
The QMK docs page for the Leader key
sequences gives an example using the
AUDIO
feature, with PLAY_SONG
, and will play song indicating whether a
leader key sequences was activated successfully or not.
My keyboards don’t support the AUDIO
feature.
But, I do have keyboards with per-key RGB or with haptic feedback.
The leader_end_user
function (which handles leader key sequences after
timeout) can be updated with:
void leader_end_user(void) {
bool did_leader_succeed = true;
if (leader_sequence_two_keys(KC_Q, KC_B)) { // mnemonic: QMK Boot
();
reset_keyboard} else if (/* ... */) {
/* .... */
} else {
= false;
did_leader_succeed }
(did_leader_succeed);
leader_end_notify}
i.e. call some leader_end_notify
function, unless the leader key sequence
wasn’t recognised. (It’s conceptually neater to initialize did_leader_succeed
with
false
, and only set it to true
when handling a key sequence.. but, that
would take more lines of code for the same effect).
The leader_end_notify
can be written like this:
void leader_end_notify(bool succeeded) {
# ifdef HAPTIC_ENABLE
if (succeeded && haptic_get_enable()) {
();
haptic_play}
# endif
# ifdef RGB_MATRIX_ENABLE
(succeeded);
rgb_matrix_blink_start# endif
}
The rgb_matrix_blink_start
is a function can is a bit trickier.
On a split keyboard, it’s a bit difficult to communicate state between the
master half and the slave half. – My first attempt, I tried using the
rgb_matrix_set_color_all
function; but, this didn’t update the RGB LEDs
on the slave half.
I work with this limitation by using the RGB matrix effects. The QMK framework’s split keyboard transport protocol already handles syncing state for RGB effects.
So, we “blink” the keyboard by:
- setting the effect to “solid color” (white).
- waiting a bit.
- restoring the effect/color to what it was before.
Hence, the implmentation, we have some extra state to store:
#ifdef RGB_MATRIX_ENABLE
uint8_t old_mode = 0;
= {HSV_OFF};
HSV old_hsv uint32_t rgb_matrix_blink_timer = 0;
bool blinking_active = false;
#endif
In rgb_matrix_blink_start
, we store the current RGB effect and colour, and keep track of the time:
void rgb_matrix_blink_start(bool succeeded) {
/* impl. note: in order to get the 'blink' to work across split keyboard,
* save the old mode & hsv, then set to solid white.
* The old mode & hsv are then restored after some time,
* in the rgb_matrix_indicators_user callback.
*/
= rgb_matrix_get_mode();
old_mode = rgb_matrix_get_hsv();
old_hsv
(RGB_MATRIX_SOLID_COLOR);
rgb_matrix_mode_noeepromif (succeeded) {
(HSV_WHITE);
rgb_matrix_sethsv_noeeprom} else {
(HSV_RED);
rgb_matrix_sethsv_noeeprom}
= timer_read32();
rgb_matrix_blink_timer = true;
blinking_active }
void rgb_matrix_blink_end(void) {
(old_mode);
rgb_matrix_mode_noeeprom(old_hsv.h, old_hsv.s, old_hsv.v);
rgb_matrix_sethsv_noeeprom
= false;
blinking_active }
and in rgb_matrix_blink_end
, we restore the RGB effect and colour.
This rgb_matrix_blink_end
is called by a “housekeeping task”, which is called by QMK at the end of each scan loop.
It simply checks the blink timer, and calls the blink end function if enough time has passed.
void housekeeping_task_user(void) {
if (blinking_active && timer_elapsed32(rgb_matrix_blink_timer) >= RGB_MATRIX_BLINK_INTERVAL) {
();
rgb_matrix_blink_end}
}
With this, I can now confidently be notified that a leader sequence succeeded, using either haptic feedback, or RGB matrix functionality.
I expect that code for the RGBLIGHT
QMK feature would be similar to the RGB matrix implementation above.
So, e.g., if I use a leader key sequence to enable caps word, I can be confident caps word has activated. Or if I invoke a sequence and don’t see the keyboard ‘blink’ (or don’t receive haptic feedback), then I can understand that I failed to enter a leader key sequence.