From 1d4e8cb52af67afffa8eef768079948aeebed0d5 Mon Sep 17 00:00:00 2001 From: Andrew Reisner Date: Wed, 11 Nov 2026 15:46:04 -0500 Subject: [PATCH] Add raw hid for waybar state --- .../keyball/keyball39/keymaps/init/keymap.c | 57 ++++++++++++++++++- .../keyball/keyball39/keymaps/init/rules.mk | 1 + 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/keyboards/keyball/keyball39/keymaps/init/keymap.c b/keyboards/keyball/keyball39/keymaps/init/keymap.c index bde27bff25cb03a440a4d97848c1cdffad1c4a09..ea0b249cf087a974024b60b4fa3eb3eb4466b014 100644 --- a/keyboards/keyball/keyball39/keymaps/init/keymap.c +++ b/keyboards/keyball/keyball39/keymaps/init/keymap.c @@ -20,6 +20,7 @@ along with this program. If not, see . #include "quantum.h" #include "os_detection.h" +#include "raw_hid.h" // Home row mods #define HR_A KC_A @@ -139,26 +140,80 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { ), // Nav/Settings layer + // F13/F14/F15 (below left/down/right arrows) are otherwise-unused + // keysyms bound in dwl to office shade open/stop/close -- picked so + // they don't need a modifier chord, since there's no home row mod + // available while this layer is held. [5] = LAYOUT_universal( RGB_TOG , AML_TO , AML_I50 , AML_D50 , TG(1) , _______ , SSNP_VRT , KC_UP , SSNP_FRE , QK_BOOT , WAYHALF , WAYFULL , KC_CAD , KC_AENTER, SCRL_DVI, CPI_I100, KC_LEFT , KC_DOWN , KC_RGHT , _______ , - RGB_MOD , RGB_RMOD , RGB_VAI , RGB_VAD , SCRL_DVD, CPI_D100, _______ , _______ , _______ , _______ , + RGB_MOD , RGB_RMOD , RGB_VAI , RGB_VAD , SCRL_DVD, CPI_D100, KC_F13 , KC_F14 , KC_F15 , _______ , QK_BOOT , KBC_RST , _______ , _______ , _______ , _______ , _______ , SSNP_VRT, SSNP_HOR , SSNP_FRE , KC_PGDN , KBC_SAVE ), }; // clang-format on +// waybar qmk/qmk-layers/qmk-trackball modules: raw HID protocol. Fixed-size +// unnumbered reports (QMK_RAW_REPORT_SIZE bytes); byte 0 is a command. 0x01 +// (host->keyboard) asks for the current state; 0x02 (keyboard->host) carries +// it: bytes 1-4 are layer_state as a little-endian uint32_t, bytes 5-6 are +// the trackball's CPI as a little-endian uint16_t, byte 7 is the scroll +// divisor, byte 8 is the scroll-mode flag (0/1). See man/waybar-qmk.5.scd +// and man/waybar-qmk-trackball.5.scd in the waybar repo for the host-side +// half of this. +enum { + QMK_RAW_CMD_GET_LAYER = 0x01, + QMK_RAW_CMD_LAYER_STATE = 0x02, +}; + +// Fixed at 32 (QMK's RAW_EPSIZE for USB LUFA/ChibiOS targets) rather than +// referencing RAW_EPSIZE directly: that macro lives in usb_descriptor.h, +// which isn't visible from this translation unit (e.g. when this file is +// pulled into quantum/keymap_introspection.c for keymap introspection). +#define QMK_RAW_REPORT_SIZE 32 + +static void send_layer_state(void) { + uint8_t response[QMK_RAW_REPORT_SIZE] = {0}; + response[0] = QMK_RAW_CMD_LAYER_STATE; + uint32_t state = layer_state; + memcpy(&response[1], &state, sizeof(state)); + uint16_t cpi = keyball_get_cpi(); + memcpy(&response[5], &cpi, sizeof(cpi)); + response[7] = keyball_get_scroll_div(); + response[8] = keyball_get_scroll_mode() ? 1 : 0; + raw_hid_send(response, sizeof(response)); +} + +void raw_hid_receive(uint8_t *data, uint8_t length) { + if (length > 0 && data[0] == QMK_RAW_CMD_GET_LAYER) { + send_layer_state(); + } +} + layer_state_t layer_state_set_user(layer_state_t state) { // Auto enable scroll mode on layer 5 (nav layer) // Only force scroll ON for layer 5; let SCRL_TO handle toggling otherwise if (get_highest_layer(state) == 5) { keyball_set_scroll_mode(true); } + send_layer_state(); return state; } bool process_record_user(uint16_t keycode, keyrecord_t *record) { switch (keycode) { + case TD(TD_NAV): + // Backstop: QMK's tap-dance reset() isn't reliably tied to physical + // key release (it can be deferred past the next tap-dance timeout + // if other keys are pressed/released while this key is still held, + // leaving layer 5 stuck on with nothing visibly held). Force it off + // the instant this key physically releases, regardless of what the + // tap-dance state machine thinks it's doing. + if (!record->event.pressed && nav_tap_state == TD_NAV_SINGLE_HOLD) { + layer_off(5); + nav_tap_state = TD_NAV_NONE; + } + break; case HR_L: if (record->event.pressed && record->tap.count && layer_state_is(1)) { uint8_t mods = get_mods(); diff --git a/keyboards/keyball/keyball39/keymaps/init/rules.mk b/keyboards/keyball/keyball39/keymaps/init/rules.mk index c1ed8037a4640a544c70e08ffb6e3e355b5a1ee3..4edb4e0ac0fbac93e69978e89f6615ae1f2a94fd 100644 --- a/keyboards/keyball/keyball39/keymaps/init/rules.mk +++ b/keyboards/keyball/keyball39/keymaps/init/rules.mk @@ -5,4 +5,5 @@ TAP_DANCE_ENABLE = yes COMBO_ENABLE = yes POINTING_DEVICE_ENABLE = yes OS_DETECTION_ENABLE = yes +RAW_ENABLE = yes # waybar qmk module: push layer state over raw HID