diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index bb272099a67e..757acdead662 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -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)),) diff --git a/builddefs/generic_features.mk b/builddefs/generic_features.mk index f14f44087702..d29a496a1ce0 100644 --- a/builddefs/generic_features.mk +++ b/builddefs/generic_features.mk @@ -40,6 +40,7 @@ GENERIC_FEATURES = \ LEADER \ MAGIC \ MOUSEKEY \ + MOUSEGRID \ MUSIC \ OS_DETECTION \ PROGRAMMABLE_BUTTON \ diff --git a/builddefs/show_options.mk b/builddefs/show_options.mk index 1c1a3ebf8e0a..5b2968970f10 100644 --- a/builddefs/show_options.mk +++ b/builddefs/show_options.mk @@ -1,6 +1,7 @@ BUILD_OPTION_NAMES = \ BOOTMAGIC_ENABLE \ MOUSEKEY_ENABLE \ + MOUSEGRID_ENABLE \ EXTRAKEY_ENABLE \ CONSOLE_ENABLE \ COMMAND_ENABLE \ diff --git a/data/constants/keycodes/keycodes_0.0.6_basic.hjson b/data/constants/keycodes/keycodes_0.0.6_basic.hjson new file mode 100644 index 000000000000..597d013b4a9c --- /dev/null +++ b/data/constants/keycodes/keycodes_0.0.6_basic.hjson @@ -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" + ] + }, + } +} diff --git a/docs/features/mouse_grid.md b/docs/features/mouse_grid.md new file mode 100644 index 000000000000..08c94b6d2830 --- /dev/null +++ b/docs/features/mouse_grid.md @@ -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 | + diff --git a/quantum/keyboard.c b/quantum/keyboard.c index d7836cf36e00..f77a9a5f1fd0 100644 --- a/quantum/keyboard.c +++ b/quantum/keyboard.c @@ -47,6 +47,9 @@ along with this program. If not, see . #ifdef MOUSEKEY_ENABLE # include "mousekey.h" #endif +#ifdef MOUSEGRID_ENABLE +# include "mousegrid.h" +#endif #ifdef PS2_MOUSE_ENABLE # include "ps2_mouse.h" #endif @@ -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; diff --git a/quantum/keycodes.h b/quantum/keycodes.h index 921dc7199f26..2be9889c9426 100644 --- a/quantum/keycodes.h +++ b/quantum/keycodes.h @@ -1,4 +1,4 @@ -// Copyright 2024 QMK +// Copyright 2025 QMK // SPDX-License-Identifier: GPL-2.0-or-later /******************************************************************************* @@ -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, @@ -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, @@ -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) @@ -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 diff --git a/quantum/mousegrid.c b/quantum/mousegrid.c new file mode 100644 index 000000000000..0e93e31218ec --- /dev/null +++ b/quantum/mousegrid.c @@ -0,0 +1,325 @@ +#include + +#include "action.h" +#include "deferred_exec.h" +#include "digitizer.h" +#include "keycodes.h" +#include "keymap_introspection.h" +#include "mousegrid.h" +#include "print.h" +#include "progmem.h" +#include "timer.h" + +#define MG_ANIMATION_SLEEP 5 + +typedef struct { + // Cursor x/y position from 0...1 + float x, y; + // Bounding box width/height for the next cursor movement + float scale_x, scale_y; +} mousegrid_cursor_t; + +// Current active grid cursor. +static mousegrid_cursor_t mousegrid_cursor; +// If non-zero, an animation (visual feedback) of cursor bounds is active. +static uint16_t animation_since; +// If non-zero, an animation (visual feedback) of cursor bounds is active. +static uint16_t alphabet_hook_since; +static bool alphabet_x; +// How many cursors are saved +static uint8_t undo_count; +static mousegrid_cursor_t undo_buffer[MG_UNDO_DEPTH]; + +const uint16_t alphabet[MG_ALPHABET_SIZE + 1] PROGMEM = {MG_ALPHABET, 0}; + +mousegrid_cursor_t init_cursor(void) { + return (mousegrid_cursor_t) { + .x = 0.5, + .y = 0.5, + .scale_x = 1.0, + .scale_y = 1.0, + }; +} + +void adjust_position(float *position, int8_t delta, float step) { + *position = *position + step * (float)delta; + if (*position < 0) { + *position = 0; + } + if (*position > 1) { + *position = 1; + } +} + +void rescale(float *scale, float ratio) { + *scale = *scale * ratio; + if (*scale < MG_MIN_SCALE) { + *scale = MG_MIN_SCALE; + } +} + +mousegrid_cursor_t update(mousegrid_cursor_t cursor, int8_t dx, int8_t dy) { + adjust_position(&(cursor.x), dx, cursor.scale_x / MG_HORIZONTAL_GRID); + adjust_position(&(cursor.y), dy, cursor.scale_y / MG_VERTICAL_GRID); + rescale(&(cursor.scale_x), MG_RESCALE_MULTIPLIER / MG_HORIZONTAL_GRID); + rescale(&(cursor.scale_y), MG_RESCALE_MULTIPLIER / MG_VERTICAL_GRID); + return cursor; +} + +void save_undo(mousegrid_cursor_t cursor) { + if (undo_count == MG_UNDO_DEPTH) { + return; + } + undo_buffer[undo_count++] = cursor; +} + +mousegrid_cursor_t restore_undo(void) { + if (undo_count == 0) { + return init_cursor(); + } + return undo_buffer[--undo_count]; +} + +void move(int8_t dx, int8_t dy) { + save_undo(mousegrid_cursor); + mousegrid_cursor = update(mousegrid_cursor, dx, dy); +} + +uint32_t mousegrid_animate(uint32_t trigger_time, void *cb_arg) { + if (animation_since == 0) { + // Animation has been cancelled. + return 0; + } + + uint8_t step = (timer_read() - animation_since) / MG_ANIMATION_STEP; + int8_t dx, dy; + switch (step) { +#if MG_ANIMATION == full + case 0: + case 8: + dx = 0; + dy = -1; + break; + case 1: + dx = 1; + dy = -1; + break; + case 2: + dx = 1; + dy = 0; + break; + case 3: + dx = 1; + dy = 1; + break; + case 4: + dx = 0; + dy = 1; + break; + case 5: + dx = -1; + dy = 1; + break; + case 6: + dx = -1; + dy = 0; + break; + case 7: + dx = -1; + dy = -1; + break; +#elif MG_ANIMATION == corners + case 0: + dx = 1; + dy = -1; + break; + case 1: + dx = 1; + dy = 1; + break; + case 2: + dx = -1; + dy = 1; + break; + case 3: + dx = -1; + dy = -1; + break; +#endif + default: + dx = 0; + dy = 0; + animation_since = 0; + break; + } + mousegrid_cursor_t animation_cursor = update(mousegrid_cursor, dx, dy); + digitizer_in_range_on(); + digitizer_set_position(animation_cursor.x, animation_cursor.y); + return MG_ANIMATION_SLEEP; +} + +void trigger_animation(void) { + animation_since = timer_read(); + defer_exec(1, &mousegrid_animate, NULL); +} + +void cancel_animation(void) { + animation_since = 0; +} + +void send(void) { + digitizer_in_range_on(); + digitizer_set_position(mousegrid_cursor.x, mousegrid_cursor.y); + uprintf( + "mousegrid to: %ux%u scale=%ux%u\n", + (int) (mousegrid_cursor.x * 100), + (int) (mousegrid_cursor.y * 100), + (int) (mousegrid_cursor.scale_x * 100), + (int) (mousegrid_cursor.scale_y * 100) + ); +} + +bool process_direction(uint16_t keycode) { + switch (keycode) { + case MG_TL: + move(-1, -1); + break; + case MG_T: + move(0, -1); + break; + case MG_TR: + move(1, -1); + break; + case MG_L: + move(-1, 0); + break; + case MG_C: + move(0, 0); + break; + case MG_R: + move(1, 0); + break; + case MG_BL: + move(-1, 1); + break; + case MG_B: + move(0, 1); + break; + case MG_BR: + move(1, 1); + break; + default: + return false; + } + cancel_animation(); + send(); + return true; +} + +void trigger_alphabet(void) { + alphabet_hook_since = timer_read(); + alphabet_x = true; +} + +bool process_alphabet(keyrecord_t *record) { + // Most of the time we don't need to steal keycodes. + if (alphabet_hook_since == 0) { + return false; + } + // Has the hook expired? + if (alphabet_hook_since < timer_read() - MG_ALPHABET_HOOK_TIMEOUT) { + alphabet_hook_since = 0; + return false; + } + // Only key down events. + if (!record->event.pressed) { + return false; + } + + keypos_t pos = record->event.key; + uint16_t keycode = keycode_at_keymap_location(MG_ALPHABET_LAYER, pos.row, pos.col); + int8_t index = 0; + for (;; ++index) { + uint16_t character = pgm_read_word(&alphabet[index]); + if (character == 0) { + // End of array. + return false; + } + if (character == keycode) { + break; + } + } + + mousegrid_cursor_t cursor = mousegrid_cursor; + if (alphabet_x) { + cursor.x = (float)index * 1.0 / (MG_ALPHABET_SIZE - 1); + cursor.scale_x = MG_ALPHABET_RESCALE_MULTIPLIER / MG_ALPHABET_SIZE; + alphabet_x = false; + } else { + cursor.y = (float)index * 1.0 / (MG_ALPHABET_SIZE - 1); + cursor.scale_y = MG_ALPHABET_RESCALE_MULTIPLIER / MG_ALPHABET_SIZE; + alphabet_hook_since = 0; + } + mousegrid_cursor = cursor; + send(); + + return true; +} + +bool process_command(uint16_t keycode) { + switch (keycode) { + case MG_ANIM: + trigger_animation(); + return true; + + case MG_UNDO: + mousegrid_cursor = restore_undo(); + send(); + return true; + + case MG_ABC: + mousegrid_init(); + trigger_alphabet(); + send(); + return true; + + case MG_NEAR: + mousegrid_cursor.scale_x = MG_LOCAL_SCALE; + mousegrid_cursor.scale_y = MG_LOCAL_SCALE; + return true; + + case MG_RST: + mousegrid_init(); + send(); + return true; + } + + return false; +} + +void mousegrid_init(void) { + mousegrid_cursor = init_cursor(); + undo_count = 0; +} + +bool process_mousegrid(uint16_t keycode, keyrecord_t *record) { + if (process_alphabet(record)) { + return false; + } + + if (record->event.pressed) { + if (process_command(keycode)) { + return false; + } + if (process_direction(keycode)) { + return false; + } + } + + if (IS_MOUSEGRID_KEYCODE(keycode)) { + return false; + } + + return true; +} + diff --git a/quantum/mousegrid.h b/quantum/mousegrid.h new file mode 100644 index 000000000000..64f15aedef6a --- /dev/null +++ b/quantum/mousegrid.h @@ -0,0 +1,72 @@ +#pragma once + +#include + +#include "action.h" + +// Directional grid of 3x3 by default. But 2x2 or 3x2 should also work. +#ifndef MG_HORIZONTAL_GRID +# define MG_HORIZONTAL_GRID 3 +#endif +#ifndef MG_VERTICAL_GRID +# define MG_VERTICAL_GRID 3 +#endif + +// Stop zooming in once we reach this scale. +#ifndef MG_MIN_SCALE +# define MG_MIN_SCALE 0.01 +#endif +// Scale to set for MG_NEAR. +#ifndef MG_LOCAL_SCALE +# define MG_LOCAL_SCALE 0.2 +#endif + +// Rather than to zoom in to precisely 1/3 per dimension, leave a little bit of +// overlap to be more forgiving of inaccuracies where the optimal path is +// ambiguous. +#ifndef MG_RESCALE_MULTIPLIER +# define MG_RESCALE_MULTIPLIER (1.0 + (1.0 / 9.0)) +#endif + +// Which layer to use for the alphabet key lookups. +#ifndef MG_ALPHABET_LAYER +# define MG_ALPHABET_LAYER 0 +#endif +// Default to English A-Z. Must exactly match the keycodes (eg: taps, etc). +#ifndef MG_ALPHABET +# define MG_ALPHABET \ + KC_A, KC_B, KC_C, KC_D, KC_E, KC_F, KC_G, KC_H, KC_I, KC_J, KC_K, \ + KC_L, KC_M, KC_N, KC_O, KC_P, KC_Q, KC_R, KC_S, KC_T, KC_U, KC_V, \ + KC_W, KC_X, KC_Y, KC_Z +# define MG_ALPHABET_SIZE 26 +#endif +// Little bit of overlap when using directional grid to further refine cursor +// position. +#ifndef MG_ALPHABET_RESCALE_MULTIPLIER +# define MG_ALPHABET_RESCALE_MULTIPLIER 2.0 +#endif +#ifndef MG_ALPHABET_HOOK_TIMEOUT +# define MG_ALPHABET_HOOK_TIMEOUT 5000 +#endif + +// Either "full" or "corners" +#ifndef MG_ANIMATION +# define MG_ANIMATION full +#endif +// Shorter step appeared to cause reports to be dropped or ignored. +#define MG_ANIMATION_STEP 75 + +#ifndef MG_UNDO_DEPTH +# define MG_UNDO_DEPTH 6 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +void mousegrid_init(void); +bool process_mousegrid(uint16_t keycode, keyrecord_t *record); + +#ifdef __cplusplus +} +#endif diff --git a/quantum/quantum.c b/quantum/quantum.c index d4ebd58e7fca..f34962a6fd30 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -84,6 +84,10 @@ # include "process_layer_lock.h" #endif +#ifdef MOUSEGRID_ENABLE +# include "mousegrid.h" +#endif + #ifdef AUDIO_ENABLE # ifndef GOODBYE_SONG # define GOODBYE_SONG SONG(GOODBYE_SOUND) @@ -416,6 +420,9 @@ bool process_record_quantum(keyrecord_t *record) { #endif #ifdef BLUETOOTH_ENABLE process_connection(keycode, record) && +#endif +#ifdef MOUSEGRID_ENABLE + process_mousegrid(keycode, record) && #endif true)) { return false;