← More about keyboards

Overview

QMK has a Mouse Keys feature that controls the mouse using keyboard keys. You can then avoid switching hands between keyboard and mouse, in theory a substantial ergonomic advantage. In reality, this interface is pretty awkward.

This post describes an experiment: Orbital Mouse. I find it is easier to control than QMK’s Mouse Keys, or at least it is more fun. To be clear, it still pales in comparison to real mouse or trackball hardware.

What is Orbital Mouse

Orbital Mouse is a userspace library that replaces QMK Mouse Keys. The pointer moves according to a heading direction. Two keys move forward and backward along that direction while another two keys steer.

Orbital Mouse movement.

Figures have been enlarged and annotated in blue for sake of visualizing the control scheme. In real use, sadly, there are no annotations.

Orbital Mouse is essentially tank controls, like the player movement in earlier Resident Evil and Tomb Raiders games, but no monsters chasing your cursor.

When steering, the cursor rotates (orbits!) around a circle to indicate change in heading direction. This rotation effect is additionally useful as a means of fine-scale control, good for hitting small targets like menu items and toolbar buttons.

Through rotation, any of these three menu items are reachable.

Add Orbital Mouse to your keymap

Step 1: Use the Orbital Mouse keycodes in your layout in keymap.c. A description of the keycodes is in the next section. A suggested right-handed layout is:

OM_DBLS, OM_BTNS, OM_U   , OM_BTN2, OM_SEL1,
OM_HLDS, OM_L   , OM_D   , OM_R   , OM_SEL2,
OM_RELS, OM_W_D , OM_W_U , OM_BTN3, OM_SEL3,
A suggested right-handed layout.

Step 2: Include the orbital_mouse header by adding near the top of keymap.c:

#include "features/orbital_mouse.h"`

Step 3: Still in keymap.c, handle Orbital Mouse by defining (or adding to) process_record_user() and matrix_scan_user() as

bool process_record_user(uint16_t keycode, keyrecord_t* record) {
  if (!process_orbital_mouse(keycode, record)) { return false; }

  // Your macros ...
  return true;
}

void matrix_scan_user(void) {
  orbital_mouse_task();

  // Other tasks ...
}

Step 4: In your rules.mk file, add

SRC += features/orbital_mouse.c

MOUSE_ENABLE = yes

Optionally, if Mouse Keys is enabled, you may disable it with “MOUSEKEYS_ENABLE = no” to save some firmware space.

Step 5: In the directory containing keymap.c, create a features subdirectory and copy orbital_mouse.h and orbital_mouse.c there.

Keycodes

Orbital Mouse is controlled with the following keycodes.

Keycode Description
OM_U Move forward.
OM_D Move backward.
OM_L Steer left (counter-clockwise).
OM_R Steer right (clockwise).
OM_BTNS Press the selected mouse button.
OM_HLDS Hold the selected mouse button.
OM_RELS Release the selected mouse button.
OM_DBLS Double click the selected mouse button.
OM_SELn Select mouse button n, for n = 1, …, 8.
OM_BTNn Press mouse button n, for n = 1, …, 8.
OM_W_U Mouse wheel up.
OM_W_D Mouse wheel down.
OM_W_L Mouse wheel left.
OM_W_R Mouse wheel right.

Of course, you don’t have to use all of them in your layout, just use what is useful. There are many ways these keys could be arranged in a layout. I leave it to you to explore.

Selected mouse button

Keycode OM_BTNS and its companions OM_HLDS, OM_RELS, OM_DBLS make use of the “selected” mouse button. Initially, mouse button 1 is selected. Keycodes OM_SEL1OM_SEL8 set the selected mouse button.

OM_HLDS and OM_RELS are useful when a mouse button must be held for an extended duration, like click and drag inputs. Tapping OM_HLDS holds down the selected mouse button. The button is held until OM_RELS is tapped to release it.

Mouse wheel controls

Keycodes OM_W_U, OM_W_D, OM_W_L, OM_W_R scroll the mouse wheel. Unlike cursor movement, mouse wheel navigation follows usual Cartesian up/down/left/right controls.

Configuration

Speed curve

Mouse keys must facilitate both large motions across the screen as well as precise fine motions for selecting menu items and such. An idea from the X Windows System is that holding a mouse key should begin moving the cursor at a slow speed and accelerate to faster speed. A “speed curve” defines how this speed changes as a function of time.

ORBITAL_MOUSE_SPEED_CURVE defines the speed curve as a table of 16 values, representing speed in pixels per 16-ms interval. The nth table entry is the movement speed after holding the key for 0.256n seconds. The entries are piecewise linearly interpolated at times between these points. Table entries are uint8_t values in the range 0–255, larger values meaning faster movement.

The default curve is a bilevel scheme, beginning at a lower speed of 24, for fine-scale motions, then transitions after a second to a higher speed of 66, for large-scale motions:

#define ORBITAL_MOUSE_SPEED_CURVE \
      {24, 24, 24, 32, 58, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66}
//     |               |               |               |           |
// t = 0.000           1.024           2.048           3.072       3.840 s
The default speed curve.

Define ORBITAL_MOUSE_SPEED_CURVE in your config.h to use a different speed curve. You can represent just about any speed curve you like in this manner.

In QMK Mouse Keys, the default Accelerated mode is based on X Window System MouseKeysAccel. Here is a family of curves following that design:

\[ s(t) = s_0 + (s_T - s_0) (t / T)^p. \]





#define ORBITAL_MOUSE_SPEED_CURVE \
      {24, 28, 36, 47, 59, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66}
//     |               |               |               |           |
// t = 0.000           1.024           2.048           3.072       3.840 s

Time (s)

Options

Orbital Mouse can be further customized by tuning the following options in config.h.

#define ORBITAL_MOUSE_RADIUS        36
#define ORBITAL_MOUSE_WHEEL_SPEED   0.2
#define ORBITAL_MOUSE_DBL_DELAY_MS  50

ORBITAL_MOUSE_RADIUS is the radius in pixels of the circle that the cursor rotates or “orbits” around when steering. Must be in [0, 63]. Default is 36.

ORBITAL_MOUSE_WHEEL_SPEED is the mouse wheel speed in units per 16-ms interval, specified as a double value. Must be in [0, 3.99]. Default is 0.2.

ORBITAL_MOUSE_DBL_DELAY_MS is the delay in ms between clicks with OM_DBLS double clicking. If this is too low, the computer might debounce the double click. Default is 50.

Functions

Explanation

If you are interested in the technical details, here are some notes on how Orbital Mouse is implemented.

While Orbital Mouse controls are actively being used, it runs a task function once every 16 ms. This function updates the state, moving and turning according to which keys are currently held, then sends a mouse report to the host.

Details:

← More about keyboards