Skip to content

Commit

Permalink
mousegrid: implement grid-based mouse navigation
Browse files Browse the repository at this point in the history
Add 2 modes of "grid-based" mouse cursor movement schemes:

1. Specifying an absolute x-y coordinate using 26-character alphabet
2. Iteratively picking a 3x3 direction to zoom in towards

Both of these pose as an absolute positiong digitizer.
  • Loading branch information
windo committed Jan 12, 2025
1 parent 5832301 commit e39ad3a
Show file tree
Hide file tree
Showing 10 changed files with 649 additions and 1 deletion.
5 changes: 5 additions & 0 deletions builddefs/common_features.mk
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ ifeq ($(strip $(MOUSEKEY_ENABLE)), yes)
MOUSE_ENABLE := yes
endif

ifeq ($(strip $(MOUSEGRID_ENABLE)), yes)
DIGITIZER_ENABLE := yes
DEFERRED_EXEC_ENABLE := yes
endif

VALID_POINTING_DEVICE_DRIVER_TYPES := adns5050 adns9800 analog_joystick azoteq_iqs5xx cirque_pinnacle_i2c cirque_pinnacle_spi paw3204 pmw3320 pmw3360 pmw3389 pimoroni_trackball custom
ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes)
ifeq ($(filter $(POINTING_DEVICE_DRIVER),$(VALID_POINTING_DEVICE_DRIVER_TYPES)),)
Expand Down
1 change: 1 addition & 0 deletions builddefs/generic_features.mk
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ GENERIC_FEATURES = \
LEADER \
MAGIC \
MOUSEKEY \
MOUSEGRID \
MUSIC \
OS_DETECTION \
PROGRAMMABLE_BUTTON \
Expand Down
1 change: 1 addition & 0 deletions builddefs/show_options.mk
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
BUILD_OPTION_NAMES = \
BOOTMAGIC_ENABLE \
MOUSEKEY_ENABLE \
MOUSEGRID_ENABLE \
EXTRAKEY_ENABLE \
CONSOLE_ENABLE \
COMMAND_ENABLE \
Expand Down
116 changes: 116 additions & 0 deletions data/constants/keycodes/keycodes_0.0.6_basic.hjson
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
{
"keycodes": {
"0x00E8": {
"group": "mousegrid",
"key": "QK_MOUSE_GRID_TOP_LEFT",
"label": "Select top left mouse grid",
"aliases": [
"MG_TL"
]
},
"0x00E9": {
"group": "mousegrid",
"key": "QK_MOUSE_GRID_TOP",
"label": "Select top mouse grid",
"aliases": [
"MG_T"
]
},
"0x00F0": {
"group": "mousegrid",
"key": "QK_MOUSE_GRID_TOP_RIGHT",
"label": "Select top right mouse grid",
"aliases": [
"MG_TR"
]
},
"0x00F1": {
"group": "mousegrid",
"key": "QK_MOUSE_GRID_LEFT",
"label": "Select left mouse grid",
"aliases": [
"MG_L"
]
},
"0x00F2": {
"group": "mousegrid",
"key": "QK_MOUSE_GRID_CENTER",
"label": "Select center mouse grid",
"aliases": [
"MG_C"
]
},
"0x00F3": {
"group": "mousegrid",
"key": "QK_MOUSE_GRID_RIGHT",
"label": "Select right mouse grid",
"aliases": [
"MG_R"
]
},
"0x00F4": {
"group": "mousegrid",
"key": "QK_MOUSE_GRID_BOTTOM_LEFT",
"label": "Select bottom left mouse grid",
"aliases": [
"MG_BL"
]
},
"0x00F5": {
"group": "mousegrid",
"key": "QK_MOUSE_GRID_BOTTOM",
"label": "Select bottom mouse grid",
"aliases": [
"MG_B"
]
},
"0x00F6": {
"group": "mousegrid",
"key": "QK_MOUSE_GRID_BOTTOM_RIGHT",
"label": "Select bottom right mouse grid",
"aliases": [
"MG_BR"
]
},
"0x00F7": {
"group": "mousegrid",
"key": "QK_MOUSE_GRID_RESET",
"label": "Reset mouse grid cursor",
"aliases": [
"MG_RST"
]
},
"0x00F8": {
"group": "mousegrid",
"key": "QK_MOUSE_GRID_ANIMATE",
"label": "Visualize mouse grid options",
"aliases": [
"MG_ANIM"
]
},
"0x00F9": {
"group": "mousegrid",
"key": "QK_MOUSE_GRID_UNDO",
"label": "Undo a mousegrid move",
"aliases": [
"MG_UNDO"
]
},
"0x00FA": {
"group": "mousegrid",
"key": "QK_MOUSE_GRID_ALPHABET",
"label": "Initiate an alphabet mousegrid move",
"aliases": [
"MG_ABC"
]
},
"0x00FB": {
"group": "mousegrid",
"key": "QK_MOUSE_GRID_NEAR",
"label": "Reset cursor scale for local movement",
"aliases": [
"MG_NEAR"
]
},
}
}
85 changes: 85 additions & 0 deletions docs/features/mouse_grid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Mouse grid

Mouse grid is a feature for quickly positioning the mouse cursor using coordinate or grid based methods.

### Enabling mouse grid

To enable mouse grid, add the following line to your keymap’s `rules.mk`:

```c
MOUSEGRID_ENABLE = yes
```

## Alphabet-based coordinates

One of the methods is to divide the screen into a 26x26 grid and position the mouse by inputting 2 characters for coordinates.

This is triggered by `QK_MOUSE_GRID_ALPHABET` (or `MG_ABC`) key that will wait for a few seconds for 2 keypresses that will be interpreted as characters specifying the coordinates where to place the mouse.

That is, when pressing `MG_ABC`, `KC_M`, `KC_P`, the mouse would be positioned:

| |A|B|C||L|M|N||X|Y|Z|
|-|-|-|-|-|-|-|-|-|-|-|-|
|A| | | | | | | | | | | |
|B| | | | | | | | | | | |
|C| | | | | | | | | | | |
|| | | | | | | | | | | |
|O| | | | | | | | | | | |
|P| | | | | |X| | | | | |
|R| | | | | | | | | | | |
|| | | | | | | | | | | |
|X| | | | | | | | | | | |
|Y| | | | | | | | | | | |
|Z| | | | | | | | | | | |

## Repeated directional grid

The other method works by repeatedly picking from a 3x3 grid to repeatedly zoom in towards the desired cursor position.

This is triggered by `QK_MOUSE_GRID_RESET` (or `MG_RST`) to center the cursor and then repeatedly picking one of the 9 positions in the grid:

| |-1 |0 |+1 |
|--|-------|------|-------|
|-1|`MG_TL`|`MG_T`|`MG_TR`|
|0 |`MG_L` |`MG_C`|`MG_R` |
|+1|`MG_BL`|`MG_B`|`MG_BR`|

In first interation, the whole screen is divided into a 3x3 grid to pick from, while second iteration picks within the 3x3 subgrid of the "quadrant" picked in the first iteration.

Since the "quadrants" are not visualized on screen while navigating, if a movement target is kind of between two quadrants, it can be difficult to tell which one is the "correct" coordinate.

In order to be forgiving of some inaccuracy, the default configuration leaves a little bit of buffer by zooming in slightly less than would be required for the 1/9th of the "quadrant". This means that for slightly inaccurate path can still reach the target but may require an extra keystroke.

While practicing, `QK_MOUSE_GRID_ANIMATE` (or `MG_ANIM`) can be pressed to cause the cursor to quickly jump through all the 9 positions of each quadrant choice. Additionally, `QK_MOUSE_GRID_UNDO` (or `MG_UNDO`) can be used to undo the last quadrant choice.

## Precision

On a 1920x1080 screen an alphabet of 26 characters divides the screen into grid of ~75x50 rectangles. This can be enough for larger UI elements (eg: browser tabs) but to accurately point at dense UI elements (eg: menu items).

On a 1920x1080 screen it takes 4 iterations of 3x3 grid to point at a grid of ~35x20 rectangles, which allows pointing at event relatively small UI elements, like menu items.

Both grid methods can combined, but for fine adjustments it may be better to use the mouse keys feature.

However, the `QK_MOUSE_GRID_NEAR` (or `MG_NEAR`) key can be used to to zoom out the cursor tot he local area, so that next keystrokes would allow move to a nearby UI element faster, without resetting to the center of the screen.

## Mouse grid keycodes

Full list of mouse grid keycodes:

|Key |Alias |Description |
|----------------------------|---------|------------------------------------------------|
|`QK_MOUSE_GRID_ALPHABET` |`MG_ABC` |Position cursor according to alphabet-based grid|
|`QK_MOUSE_GRID_RESET` |`MG_RST` |Reset cursor to the center of the screen |
|`QK_MOUSE_GRID_TOP_LEFT` |`MG_TL` |Pick top-left quadrant |
|`QK_MOUSE_GRID_TOP` |`MG_T` |Pick top quadrant |
|`QK_MOUSE_GRID_TOP_RIGHT` |`MG_TR` |Pick top-right quadrant |
|`QK_MOUSE_GRID_LEFT` |`MG_L` |Pick left quadrant |
|`QK_MOUSE_GRID_CENTER` |`MG_C` |Pick center quadrant |
|`QK_MOUSE_GRID_RIGHT` |`MG_R` |Pick right quadrant |
|`QK_MOUSE_GRID_BOTTOM_LEFT` |`MG_BL` |Pick bottom-left quadrant |
|`QK_MOUSE_GRID_BOTTOM` |`MG_B` |Pick bottom quadrant |
|`QK_MOUSE_GRID_BOTTOM_RIGHT`|`MG_BR` |Pick bottom-right quadrant |
|`QK_MOUSE_GRID_ANIMATE` |`MG_ANIM`|Quickly move the cursor through all quadrants |
|`QK_MOUSE_GRID_UNDO` |`MG_UNDO`|Undo last quadrant pick |
|`QK_MOUSE_GRID_NEAR` |`MG_NEAR`|Zoom cursor to local/nearby quadrant navigation |

6 changes: 6 additions & 0 deletions quantum/keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifdef MOUSEKEY_ENABLE
# include "mousekey.h"
#endif
#ifdef MOUSEGRID_ENABLE
# include "mousegrid.h"
#endif
#ifdef PS2_MOUSE_ENABLE
# include "ps2_mouse.h"
#endif
Expand Down Expand Up @@ -488,6 +491,9 @@ void keyboard_init(void) {
#ifdef HAPTIC_ENABLE
haptic_init();
#endif
#ifdef MOUSEKEY_ENABLE
mousegrid_init();
#endif

#if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE)
debug_enable = true;
Expand Down
32 changes: 31 additions & 1 deletion quantum/keycodes.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 QMK
// Copyright 2025 QMK
// SPDX-License-Identifier: GPL-2.0-or-later

/*******************************************************************************
Expand Down Expand Up @@ -314,6 +314,20 @@ enum qk_keycode_defines {
KC_RIGHT_SHIFT = 0x00E5,
KC_RIGHT_ALT = 0x00E6,
KC_RIGHT_GUI = 0x00E7,
QK_MOUSE_GRID_TOP_LEFT = 0x00E8,
QK_MOUSE_GRID_TOP = 0x00E9,
QK_MOUSE_GRID_TOP_RIGHT = 0x00F0,
QK_MOUSE_GRID_LEFT = 0x00F1,
QK_MOUSE_GRID_CENTER = 0x00F2,
QK_MOUSE_GRID_RIGHT = 0x00F3,
QK_MOUSE_GRID_BOTTOM_LEFT = 0x00F4,
QK_MOUSE_GRID_BOTTOM = 0x00F5,
QK_MOUSE_GRID_BOTTOM_RIGHT = 0x00F6,
QK_MOUSE_GRID_RESET = 0x00F7,
QK_MOUSE_GRID_ANIMATE = 0x00F8,
QK_MOUSE_GRID_UNDO = 0x00F9,
QK_MOUSE_GRID_ALPHABET = 0x00FA,
QK_MOUSE_GRID_NEAR = 0x00FB,
QK_SWAP_HANDS_TOGGLE = 0x56F0,
QK_SWAP_HANDS_TAP_TOGGLE = 0x56F1,
QK_SWAP_HANDS_MOMENTARY_ON = 0x56F2,
Expand Down Expand Up @@ -977,6 +991,20 @@ enum qk_keycode_defines {
KC_RGUI = KC_RIGHT_GUI,
KC_RCMD = KC_RIGHT_GUI,
KC_RWIN = KC_RIGHT_GUI,
MG_TL = QK_MOUSE_GRID_TOP_LEFT,
MG_T = QK_MOUSE_GRID_TOP,
MG_TR = QK_MOUSE_GRID_TOP_RIGHT,
MG_L = QK_MOUSE_GRID_LEFT,
MG_C = QK_MOUSE_GRID_CENTER,
MG_R = QK_MOUSE_GRID_RIGHT,
MG_BL = QK_MOUSE_GRID_BOTTOM_LEFT,
MG_B = QK_MOUSE_GRID_BOTTOM,
MG_BR = QK_MOUSE_GRID_BOTTOM_RIGHT,
MG_RST = QK_MOUSE_GRID_RESET,
MG_ANIM = QK_MOUSE_GRID_ANIMATE,
MG_UNDO = QK_MOUSE_GRID_UNDO,
MG_ABC = QK_MOUSE_GRID_ALPHABET,
MG_NEAR = QK_MOUSE_GRID_NEAR,
SH_TOGG = QK_SWAP_HANDS_TOGGLE,
SH_TT = QK_SWAP_HANDS_TAP_TOGGLE,
SH_MON = QK_SWAP_HANDS_MOMENTARY_ON,
Expand Down Expand Up @@ -1491,6 +1519,7 @@ enum qk_keycode_defines {
#define IS_CONSUMER_KEYCODE(code) ((code) >= KC_AUDIO_MUTE && (code) <= KC_LAUNCHPAD)
#define IS_MOUSE_KEYCODE(code) ((code) >= QK_MOUSE_CURSOR_UP && (code) <= QK_MOUSE_ACCELERATION_2)
#define IS_MODIFIER_KEYCODE(code) ((code) >= KC_LEFT_CTRL && (code) <= KC_RIGHT_GUI)
#define IS_MOUSEGRID_KEYCODE(code) ((code) >= QK_MOUSE_GRID_TOP_LEFT && (code) <= QK_MOUSE_GRID_NEAR)
#define IS_SWAP_HANDS_KEYCODE(code) ((code) >= QK_SWAP_HANDS_TOGGLE && (code) <= QK_SWAP_HANDS_ONE_SHOT)
#define IS_MAGIC_KEYCODE(code) ((code) >= QK_MAGIC_SWAP_CONTROL_CAPS_LOCK && (code) <= QK_MAGIC_TOGGLE_ESCAPE_CAPS_LOCK)
#define IS_MIDI_KEYCODE(code) ((code) >= QK_MIDI_ON && (code) <= QK_MIDI_PITCH_BEND_UP)
Expand All @@ -1517,6 +1546,7 @@ enum qk_keycode_defines {
#define CONSUMER_KEYCODE_RANGE KC_AUDIO_MUTE ... KC_LAUNCHPAD
#define MOUSE_KEYCODE_RANGE QK_MOUSE_CURSOR_UP ... QK_MOUSE_ACCELERATION_2
#define MODIFIER_KEYCODE_RANGE KC_LEFT_CTRL ... KC_RIGHT_GUI
#define MOUSEGRID_KEYCODE_RANGE QK_MOUSE_GRID_TOP_LEFT ... QK_MOUSE_GRID_NEAR
#define SWAP_HANDS_KEYCODE_RANGE QK_SWAP_HANDS_TOGGLE ... QK_SWAP_HANDS_ONE_SHOT
#define MAGIC_KEYCODE_RANGE QK_MAGIC_SWAP_CONTROL_CAPS_LOCK ... QK_MAGIC_TOGGLE_ESCAPE_CAPS_LOCK
#define MIDI_KEYCODE_RANGE QK_MIDI_ON ... QK_MIDI_PITCH_BEND_UP
Expand Down
Loading

0 comments on commit e39ad3a

Please sign in to comment.