PLOOPY TRACKBALL NANO ON-THE-FLY DPI ADJUSTMENTS Ploopy Trackball Nano is a tiny pointing device comprising of zero buttons and one big red ball. It is powered by QMK firmware, which means there is a rather friendly API available for customizing the device's behavior. Out the gates, the trackball's DPI settings are rather ripped. I wanted to be able to arbitrarily control the trackball's DPI without having to reflash firmware or adjust tracking speed via X. As the story goes, the shoulders beneath me had already made this possible. Aidan Gauland et. al made a keymap that makes the device listen for 2-bit commands that are sent by quickly pulsing either the caps or num lock keys of some other connected device. It's rather clever, yes? Step-by-step ---------------------------------------------------------------------- Flashing firmware ...................................................................... First, I had to flash the firmware to my Ploopy. That was easy. Before moving on too quickly, let's take a gander over some implementation details of the 2-bit command feature. The device's `keymap.c' file overrides the QMK library's `led_update_user' function, which is a callback invoked whenever a 'lock' key LED is updated (i.e.: caps lock, num lock). (This function is defined in `quantum/led.c' on line 76 and invoked on line 80.) Lock key LED states are the same across devices. So these states can be used to communicate between devices. In order to differentiate between important and arbitrary lock key LED states, the program manages a command window, which is an interval of 25ms during which a sequence of two toggled LED states will be interpreted as a command. The interval opens when a LED states changes (and no other interval is open). The interval closes by way of `defer_exec', a QMK library function that executes a given command after an amount of time. Here, that means waiting 25ms and then closing the interval and processing any received commands. Adding udev rule ...................................................................... Next I wanted to get console events setup for the device so I could troubleshoot possible issues. That was a bit more difficult. I needed to add a `udev' rule. The rule is located at `/etc/udev/rules.d/69-usb-ploopy.rules', containing: ,---- | SUBSYSTEMS=="usb", ATTRS{idVendor}=="5043", ATTRS{idProduct}=="54a3", MODE="0660" `---- `udevadm control --reload-rules' will make this rule take immediate effect. The values contained in the rule were found using `lsusb' and/or `udevadm info'. In my notes, I wrote that somehow I used `udevadm monitor' to get the device node, which I then use with `udevadm info --attribute-walk --name=<device-node>' . I can't reproduce these directions, but I found the same information with `udevadm info --attribute-walk --name=/dev/input/mouse4'. Presumably, `mouse4' would vary on another system. Anyways, through either method I extracted the vendor ID (5043) and the product ID (54a3). The other values in the `udev' rule are defaults (`0660' is `udev' default user/group read/write permissions). Adding macros to send commands ...................................................................... Sending commands to the trackball is done via macros run on another device. I use my Reviung41. First, custom keycodes are added: ,---- | enum custom_keycodes { | PLPY_1, | PLPY_2 | }; `---- These keycodes are used somewhere in the keymap: ,---- | const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { | ... | [4] = LAYOUT(... PLPY_1, PLPY_2 ...), | ... | } `---- And the macros are defined and sent using `SEND_STRING': ,---- | bool process_record_user(uint16_t keycode, keyrecord_t *record) { | switch (keycode) { | case PLPY_1: | SEND_STRING(SS_TAP(X_CAPS_LOCK) SS_DELAY(25) SS_TAP(X_CAPS_LOCK)); | return false; | break; | case PLPY_2: | SEND_STRING(SS_TAP(X_NUM_LOCK) SS_DELAY(25) SS_TAP(X_NUM_LOCK)); | return false; | break; | } | return false; | } `---- End notes ...................................................................... That's all, folks.