@@ 20,6 20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#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();