URI: 
       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.