diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index c88ce36011c8..7096fb5e490a 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -131,6 +131,8 @@ ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes) VPATH += $(QUANTUM_DIR)/pointing_device SRC += $(QUANTUM_DIR)/pointing_device/pointing_device.c SRC += $(QUANTUM_DIR)/pointing_device/pointing_device_auto_mouse.c + SRC += $(QUANTUM_DIR)/pointing_device/pointing_device_modes.c + SRC += $(QUANTUM_DIR)/process_keycode/process_pointing_modes.c ifneq ($(strip $(POINTING_DEVICE_DRIVER)), custom) SRC += drivers/sensors/$(strip $(POINTING_DEVICE_DRIVER)).c OPT_DEFS += -DPOINTING_DEVICE_DRIVER_$(strip $(shell echo $(POINTING_DEVICE_DRIVER) | tr '[:lower:]' '[:upper:]')) diff --git a/data/constants/keycodes/keycodes_0.0.1.hjson b/data/constants/keycodes/keycodes_0.0.1.hjson index 7ba1ecf2019e..6ff161a6da47 100644 --- a/data/constants/keycodes/keycodes_0.0.1.hjson +++ b/data/constants/keycodes/keycodes_0.0.1.hjson @@ -36,8 +36,7 @@ "0x52C0/0x001F": { "define": "QK_LAYER_TAP_TOGGLE" }, - // 0x52E0/0x001F - UNUSED - // 0x5300/0x02FF - UNUSED + // 0x5346/0x02B9 - UNUSED "0x5600/0x00FF": { "define": "QK_SWAP_HANDS" }, diff --git a/data/constants/keycodes/keycodes_0.0.7.hjson b/data/constants/keycodes/keycodes_0.0.7.hjson new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/data/constants/keycodes/keycodes_0.0.7.hjson @@ -0,0 +1 @@ + diff --git a/data/constants/keycodes/keycodes_0.0.7_pointing_modes.hjson b/data/constants/keycodes/keycodes_0.0.7_pointing_modes.hjson new file mode 100644 index 000000000000..e81ddc377482 --- /dev/null +++ b/data/constants/keycodes/keycodes_0.0.7_pointing_modes.hjson @@ -0,0 +1,31 @@ +{ + "ranges": { + "0x5302/0x001F": { + "define": "QK_POINTING_MODES_MO" + }, + "0x5322/0x001F": { + "define": "QK_POINTING_MODES_TG" + }, + "0x5342/0x0001": { + "define": "QK_POINTING_MODES_UTIL" + } + }, + "keycodes": { + "0x5342": { + "group": "pointing_modes_util", + "key": "QK_PM_CYCLE_DEVICES", + "label": "Cycle pointing mode device", + "aliases": [ + "PM_CYDEV" + ] + }, + "0x5343":{ + "group": "pointing_modes_util", + "key": "QK_PM_CYCLE_PRECISION", + "label": "Cycle through precision values for active device", + "aliases": [ + "PM_CYCPRE" + ] + } + } +} diff --git a/docs/features/pointing_device.md b/docs/features/pointing_device.md index 0ecf82c8df67..d2c55dfa336c 100644 --- a/docs/features/pointing_device.md +++ b/docs/features/pointing_device.md @@ -520,7 +520,9 @@ Recall that the mouse report is set to zero (except the buttons) whenever it is ### Drag Scroll or Mouse Scroll -A very common implementation is to use the mouse movement to scroll instead of moving the cursor on the system. This uses the `pointing_device_task_user` callback to intercept and modify the mouse report before it's sent to the host system. +A very common implementation is to use the mouse movement to scroll instead of moving the cursor on the system. This uses the `pointing_device_task_user` callback to intercept and modify the mouse report before it's sent to the host system. + +!> Note that the pointing device modes feature implements this along with other popular pointing device features automatically ```c enum custom_keycodes { @@ -754,7 +756,7 @@ While all default mouse keys and layer keys(for current mouse layer) are treated | `bool is_mouse_record_kb(uint16_t keycode, keyrecord_t* record)` | keyboard level callback for adding mouse keys | | `bool is_mouse_record_user(uint16_t keycode, keyrecord_t* record)` | user/keymap level callback for adding mouse keys | -##### To use the callback function to add mouse keys: +##### To use the callback function to add mouse keys: The following code will cause the enter key and all of the arrow keys to be treated as mouse keys (hold target layer while they are pressed and reset active layer timer). ```c @@ -813,7 +815,7 @@ _NOTE: Generally it would be preferable to use the `is_mouse_record_*` functions ### Advanced control examples -#### Disable auto mouse on certain layers: +#### Disable auto mouse on certain layers: The auto mouse feature can be disabled any time and this can be helpful if you want to disable the auto mouse feature under certain circumstances such as when particular layers are active. One issue however is the handling of the target layer, it needs to be removed appropriately **before** disabling auto mouse _(see notes under control functions above)_. The following function would disable the auto_mouse feature whenever the layers `_LAYER5` through `_LAYER7` are active as the top most layer _(ignoring target layer)_. @@ -838,11 +840,9 @@ layer_state_t layer_state_set_user(layer_state_t state) { #### Set different target layer when a particular layer is active: -The below code will change the auto mouse layer target to `_MOUSE_LAYER_2` when `_DEFAULT_LAYER_2` is highest default layer state. - -*NOTE: that `auto_mouse_layer_off` is used here instead of `remove_auto_mouse_layer` as `default_layer_state_set_*` stack is separate from the `layer_state_set_*` stack*, if something similar was to be done in `layer_state_set_user`, `state = remove_auto_mouse_layer(state, false)` should be used instead. - -*ADDITIONAL NOTE: `AUTO_MOUSE_TARGET_LAYER` is checked if already set to avoid deactivating the target layer unless needed*. +The below code will change the auto mouse layer target to `_MOUSE_LAYER_2` when `_DEFAULT_LAYER_2` is highest default layer state. +*NOTE: that `auto_mouse_layer_off` is used here instead of `remove_auto_mouse_layer` as `default_layer_state_set_*` stack is separate from the `layer_state_set_*` stack* if something similar was to be done in `layer_state_set_user_state = remove_auto_mouse_layer(state, false)` should be used instead +*ADDITIONAL NOTE: `AUTO_MOUSE_TARGET_LAYER` is checked if already set to avoid deactivating the target layer unless needed* ```c // in keymap.c @@ -864,7 +864,8 @@ layer_state_t default_layer_state_set_user(layer_state_t state) { } ``` -### Use custom keys to control auto mouse: +### Use custom keys to control auto mouse: + Custom key records could also be created that control the auto mouse feature. The code example below would create a custom key that would toggle the auto mouse feature on and off when pressed while also setting a bool that could be used to disable other code that may turn it on such as the layer code above. @@ -927,3 +928,827 @@ In general the following two functions must be implemented in appropriate locati | -------------------------------------------------------------- | ------------------------------------------------------------ | ---------------------------: | | `pointing_device_task_auto_mouse(report_mouse_t mouse_report)` | handles target layer activation and is_active status updates | `pointing_device_task` stack | | `process_auto_mouse(uint16_t keycode, keyrecord_t* record)` | Keycode processing for auto mouse | `process_record` stack | + +--- + +# Pointing Device Modes :id=pointing-device-modes + +Inspired by the work of previous trackball users that added features such as drag scroll, caret scroll, and sniping modes to their keyboards, this framework allows for easy setup and inclusion of different pointing device modes that when active will change the behaviour of a pointing device by taking it's x/y outputs and changing them into something else such as h/v for drag scrolling, key presses such as arrow keys for caret scrolling, and even just adjusting the x/y values before output. When a pointing device mode is active it accumulates x and y outputs from a pointing device and stores it into internal x & y values, halting normal mouse x and y output (_modes can re-enable and/or modify mouse output_), these internally stored x and y values are then divided by a defined divisor and current device precision value resulting the modified output (_key taps, h/v, modified mouse x/y etc._). The dividing factors can be used to control sensitivity in each mode and precision can be used to control sensitivity on each device as adjusting cpi may not always be desired/possible. + +The framework has keycode support for up to **31** *(32 total modes including `PM_NONE`, 30 not including built in modes)* custom modes natively through the `PM_MO()` and `PM_TG()` keycode macros which act as momentary and toggle keys for `` respectively, similarly to the layer keys of the same type (up to 256). 2 of the 15 modes are already used by built in modes, however these can easily be overwritten if needed. There is an additional Null mode `PM_NONE` (_Default pointing device output_) that cannot be overwritten. More modes beyond this (_mode id's > 31_) can be added but they will require the addition of custom keycodes to activate the modes as the `PM_MO()` and `PM_TG()` macros only support up to mode id 31. New custom modes can be added through either adding keycode maps to the `pointing_device_mode_maps` array or through the through user/kb callbacks functions (_see advanced use below_). + +## How To Enable + +On a keyboard that has a pointing device (_i.e._ `POINTING_DEVICE_ENABLE` _is defined_) pointing modes can be enabled by defining `POINTING_DEVICE_MODES_ENABLE` in `config.h`, defining new modes, and adding mode change keycodes to the keymap. + +```c +// in config.h: +#define POINTING_DEVICE_MODES_ENABLE +``` + +## Activating Pointing Device Modes + +The first 15 pointing device modes can easily be activated by keypress through adding the following keycode macros to a keymap: + +#### Built-in keycodes and Keycode Macros (_for _`PM_NONE`_ and the first 16 modes only_) + +| Keycode Macro | Description | +| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `PM_MO()` | Momentary key for pointing mode `` (i.e active while key pressed deactivate on release) | +| `PM_TG()` | Toggle key for pointing mode `` (toggle on release, remain until pressed and released again) | +| `PM_CYCPRE` | Increase precision on active device by `POINTING_MODES_PRECISION_STEP` will set to `POINTING_MODES_PRECISION_MIN` if `POINTING_MODES_PRECISION_MAX` is exceeded, (_behaviour reverses if shift is held_) | +| `PM_CYCDEV` | Cycle through devices controlled by pointing modes in order of increasing device_id (_Requires more than one device to be controlled by pointing modes see Pointing Modes for Multiple Pointing Devices below_) | + +!> For pointing device modes above mode id 31 a custom keycode would need to be added unless the mode is being activated through some other means (such as on specific layers see (advanced use)[#pointing-modes-advanced-use] below) + + +### Toggled Pointing Device Modes vs Momentary Pointing Modes + +Pointing device modes activated by toggle type functions/macros have their mode id saved until toggled off or a different mode is activated by toggle overwriting the last toggle mode. When a Momentary type function or key is used while another mode is toggled the toggled mode will be reactivated once the momentary mode is released. Toggling a mode on will overwrite both the saved toggled mode id (_if different than current_) as well as the current mode id while using a momentary type key will only overwrite the current mode. + +### Built-in Pointing Device Modes + +| Pointing Device Mode | Alias | Mode Id | Description | +| :------------------------ | --------- | :-----: | --------------------------------------------------------------------------------------------------------------------------- | +| `PM_NONE` | _None_ | 0 | Null pointing mode that will will pass through normal x and y output of pointing device (Cannot be overwritten) | +| `PM_DRAG` | `PM_DRG` | 1 | Change x and y movement of pointing device into h and v axis values `x->h` `y->v` | +| `PM_SAFE_RANGE` | _None_ | 2 | Start of free mode id range supported by the `PM_MO()` and `PM_TG()` key macros (_default start of mode maps_) | +| `PM_ADVANCED_RANGE_START` | _None_ | 32 | Start of mode id range that will require the addition of custom keycode to activate them (_new keycodes, on layers etc._) | + +***Notes:*** +-***These modes can all be overwritten (including `PM_NONE` which would impact default cursor movement so care should be taken).*** +-***Mode ids 3-31 are free to be used for custom modes and will be supported by the built in keycode macros `PM_MO()` and `PM_TG()` (see adding custom modes below)*** + +### Use In A keymap: + +```c +// in keymap.c + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + [_TEST] = LAYOUT( + PM_MO(PM_DRG), PM_TG(PM_DRG) + ) + }; + +``` + +## Settings + +| Define | Description | Range | Units | Default | +| -------------------------------- | ------------------------------------------------------------------------------------------------------- | :----------------: | :----------: | -----------------------: | +| `POINTING_DEVICE_MODES_ENABLE` | (Required) Enables pointing device pointing device modes feature | `NA` | `None` | _Not defined_ | +| `POINTING_MODES_8WAY_MAP_ENABLE` | (Required) Changes expected number of keycodes per map to 8 | `NA` | `None` | _Not defined_ | +| `POINTING_MODES_THRESHOLD` | (optional) Minimum threshold of input in order to determine a direction for mode maps | `1->XY_REPORT_MAX` | `(x\|y)/dot` | `0` | +| `POINTING_MODES_DEFAULT_MODE` | (optional) Default pointing device mode | `0->255` | `None` | `PM_NONE` | +| `POINTING_MODES_TAP_DELAY` | (optional) Delay between key presses in `pointing_tap_codes` in ms | `0->255` | `ms` | `TAP_CODE_DELAY` | +| `POINTING_MODES_MAP_START` | (optional) Starting mode id of `pointing_device_mode_maps` for adding modes without maps | `0->255` | `None` | `PM_SAFE_RANGE` | +| `POINTING_MODES_DEFAULT_DIVISOR` | (optional) Default divisor for all modes that do not have a defined divisor | `1->XY_REPORT_MAX` | `Varies` | `32` | +| `POINTING_MODES_DRAG_DIVISOR` | (optional) Pointing device x/y movement per h/v axis tick in `PM_DRAG` mode | `1->XY_REPORT_MAX` | `(x\|y)/dot` | `4` | +| `POINTING_MODES_PRECISION_MIN` | (optional) Minimum precision setting (_will always at least have this value NOT RECOMMENDED TO CHANGE_) | `1->XY_REPORT_MAX` | `(x\|y)/dot` | `1` | +| `POINTING_MODES_PRECISION_MAX` | (optional) Maximum precision setting (_will never go above this value_) | `1->XY_REPORT_MAX` | `(x\|y)/dot` | `4` | +| `POINTING_MODES_PRECISION_STEP` | (optional) Step size of precision adjustments used in `PM_CYCPRE` keycode | `1->XY_REPORT_MAX` | `(x\|y)/dot` | `2` | +| `POINTING_MODES_HOLD_DECAY_RATE` | (optional) amount residuals are reduced per cycle for `PMO_HOLD` modes (effects held time) | `0->XY_REPORT_MAX` | `(x\|y)/dot` | `2` | + +!> `POINTING_MODES_HOLD_DECAY_RATE` can be set to zero however this will mean last direction key or keys will be held until the direction is changed or mode_id changes + +!> `XY_REPORT_MAX` will depend on if `MOUSE_EXTENDED_REPORT` is defined; `127` if it is not defined and `32,767` if it is + +Speed and sensitivity of any mode will be impacted by the pointing device CPI setting so divisors may need to be adjusted to personal preference and CPI settings typically used (it is possible to have divisors change based on cpi setting if desired). + +!> Drag scroll speed will additionally be effected by OS mouse scroll settings, there are usually separate settings for scroll "wheel" and "wheel tilt" which is Vertical and Horizontal scroll respectively (_e.g. lines per scroll in windows_). The `POINTING_MODES_DRAG_DIVISOR` default value of 4 is based on having mouse settings in the OS set to three lines per tick of "mouse wheel" or "wheel tilt" (_Windows Default_). + +!> `POINTING_MODES_TAP_DELAY` defaults to `TAP_CODE_DELAY` so if `TAP_CODE_DELAY` is set high it can cause some noticeable latency + +## Functions + +There are many functions available to control and modify pointing modes details about these functions and data types unique to pointing modes can be found in the relevant sections below. All the functions below will get operate on the current active device (relevant if there is more than one pointing device that is controlled by ) + +| Function | Description | Data Type | Input Types | +| ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | :--------------------------: | :--------------------------: | +| `pointing_modes_set_mode(mode_id)` | Set active device's pointing mode to `mode_id` | _None_ | `uint8_t` | +| `pointing_modes_get_mode()` | Get active device's current mode id | `uint8_t` | _None_ | +| `pointing_modes_toggle_mode(mode_id)` | Set active device's toggle mode to `mode_id` or `POINTING_MODES_DEFAULT_MODE` if `toggle_id == mode_id` | _None_ | `uint8_t` | +| `pointing_modes_get_toggled_mode()` | Get active device's toggle mode id | _None_ | `uint8_t` | +| `pointing_modes_set_precision(precision)` | Set active device's precision value to `precision` | _None_ | `mouse_xy_report_t` | +| `pointing_modes_get_precision()` | Get active device's precision value | `mouse_xy_report_t` | _None_ | +| `pointing_modes_set_residuals(residuals)` | Set active device's residuals(_accumulated x, y_) to `residuals` | _None_ | `pointing_modes_residuals_t` | +| `pointing_modes_get_residuals()` | Get active device's residuals | `pointing_modes_residuals_t` | _None_ | +| `pointing_modes_reset()` | reset active device to default values and clear residuals | _None_ | _None_ | +| `pointing_modes_get_divisor()` | Get current precision adjusted divisor | `mouse_xy_report_t` | _None_ | +| `pointing_modes_get_direction()` | Get current direction | `uint8_t` | _None_ | +| `pointing_modes_get_type()` | Get current mode type and options | `uint8_t` | _None_ | +| `pointing_modes_update()` | Recalculate direction, precision adjusted final divisor, and mode_type | _None_ | _None_ | +| `pointing_modes_apply_divisor(axis)` | Returns result of applying `divisor` to `residuals` based on `axis` (_[see advanced modes](#Pointing-Modes-Advanced-use)_) | `mouse_xy_report_t` | `uint8_t` | +| `pointing_modes_get_active_device()` | Returns device id of current active device (_see controlling multiple devices_) | `uint8_t` | _None_ | +| `pointing_modes_set_active_device(device_id)` | Set active device to `device_id` (_see controlling multiple devices_) | _None_ | `uint8_t` | + +***Note: `pointing_modes_get_divisor`, `pointing_modes_get_direction`, `pointing_modes_get_mode_type` will not recalculate any of the values to reflect changes in `mode_id` or `residuals` made in the same block of code, `pointing_modes_update` must be used first*** + +## Precision + +Precision is an additional value that is stored for each pointing mode device that will be multiplied against the divisor prior to applying it against pointing device output and residuals, this includes `PM_None` which has a default divisor of `1`. Precision is meant to be a separately controlled value to temporarily reduce sensitivity of both standard mouse output and pointing modes. This can be easily controlled by default using the `PM_CYCPRE` keycode which will cycle through precision values by `POINTING_MODES_PRECISION_STEP` looping back over to the starting value e.g. `1 -> 2 -> 4 -> 1 -> 2` or if shift is held this is reversed `1 -> 4 -> 2 -> 1 -> 4`. + +This is useful if changing sensitivity on the pointing device directly is not possible, unreliable, or could cause additional wear of non-volitile memory on the pointing device. + +***Note: That the maximum value that a divisor can have is 255 even after precision adjustment so it will be clamped to this value if overflow occurs*** +***Note2: The default Maximum precision is `4` so `PM_CYCPRE` will cycle through `1`, `2`, and `4`*** + +## Basic Pointing Modes + +There are a couple of ways to add new pointing device modes, using the pointing device mode maps will be covered here under basic use where the other method of using the `pointing_modes_task_*` callbacks to add or modify modes will be covered under advance use. + +### Pointing Device Mode Maps + +To add pointing device modes that are only using keycode output is accomplished through creating pointing device mode maps, pointing device mode maps support all QMK keycodes similar to encoder maps. + +```c +// Pointing Device Mode Maps Format +const uint16_t PROGMEM pointing_modes_maps[][POINTING_MODE_NUM_DIRECTIONS] = { + [0] = { + , + , , + + }, + //... all other pointing mode maps ... + [] = { + , + , , + + } +} + +``` + +#### Example Mode Maps: + +```c +// in config.h: +#define POINTING_DEVICE_POINTING_MODES_ENABLE // (Required) +#define EXTRAKEY_ENABLE // (optional) +// pointing mode map start is left at default value in this example + +// in keymap.c +// required enum to set mode id's +// Note the use of POINTING_MODE_MAP_START here is essential +enum keymap_pointing_mode_ids { + PM_BROW = POINTING_MODES_MAP_START, // BROWSER TAB Manipulation (POINTING_MODE_MAP_START is required)[mode id 2] + PM_APP, // Open App browsing [mode id 3] + PM_CARET, // Arrow keys/cursor movement [mode id 4] + PM_VOLUME, // Adjust audio volume (requires EXTRAKEY_ENABLE) [mode id 5] + PM_HISTORY, // Undo/Redo scrolling [mode id 6] + PM_RGB_MB, // Change RGB Mode brightness [mode id 7] + PM_RGB_HS, // Change RGB Hue and Saturation [mode id 8] + PM_RGB_MSP, // Change RGB Mode and Speed [mode id 9] + PM_GAME // WASD game movement mode [mode id 10] +}; + +// (optional) enum to make tracking mod maps easier(index numbers can be used directly) +// Must be in the same order as the above mode ids +enum keymap_pointing_modes_map_index { + _PM_BROW, // first mode map [index 0] + _PM_APP, // second mode map [index 1] + _PM_CARET, // Third mode map [index 2] + _PM_VOLUME, // fourth mode map [index 3] + _PM_HISTORY, // fifth mode map [index 4] + _PM_RGB_MB, // sixth mode map [index 5] + _PM_RGB_HS, // seventh mode map [index 6] + _PM_RGB_MSP, // eighth mode map [index 7] + _PM_GAME // ninth mode map [index 8] +}; + +// Note POINTING_MODES_NUM_DIRECTIONS is required +const uint16_t PROGMEM pointing_modes_maps[][POINTING_MODES_NUM_DIRECTIONS] = { + [_PM_BROW] = { + KC_NO, + C(S(KC_TAB)), C(KC_TAB), + KC_NO + }, + [_PM_APP] = { + KC_NO, + A(S(KC_TAB)), A(KC_TAB), + KC_NO + }, + [_PM_CARET] = { + KC_UP, + KC_LEFT, KC_RIGHT, + KC_DOWN + }, + [_PM_VOLUME] = { + KC_VOLU, + KC_NO, KC_NO, + KC_VOLD + }, + [_PM_HISTORY] = { + KC_NO, + C(KC_Z), C(KC_Y), + KC_NO + }, + [_PM_RGB_MB] = { + RGB_VAI, + RGB_RMOD, RGB_MOD, + RGB_VAD + }, + [_PM_RGB_HS] = { + RGB_HUI, + RGB_SAD, RGB_SAI, + RGB_HUD + }, + [_PM_RGB_MSP] = { + RGB_SPI, + RGB_RMOD, RGB_MOD, + RGB_SPD + }, + [_PM_GAME] = { + KC_W, + KC_A, KC_D, + KC_S + } +}; + +``` +!> use `KC_NO` when no keycode is desired, use of `KC_TRNS` or `_______` is unsupported as these maps do not act like layers _(only one can be active per pointing device at a time)_. + +The mode map array starts at index 0 and **must** be in the **same order** as the mode_ids of the maps (i.e ` + POINTING_MODES_MAP_START = `), so use of `POINTING_MODES_MAP_START` is essential. However you can have more modes than you have maps as not all modes will be based on key presses (_such as advanced mode using `pointing_modes_task_*` functions_). + +### 8-Way Mode maps + +If `POINTING_MODES_8WAY_MAP_ENABLE` is defined then mode maps expect 8 keycodes per map but will allow having unique keycodes for diagonal directions as long as the mode type is set to `PMT_8WAY` (`PMT_4WAY` will still be the default). All maps **must** have 8 keycodes even if it is intended to use only the cardinal directions (`PMT_4WAY` or `PMT_DPAD`), it is recommended to fill unused directions with `KC_NO` as this helps with clarity. + +```c +// Pointing Device Mode Maps 8-Way Format: +const uint16_t PROGMEM pointing_modes_maps[][POINTING_MODE_NUM_DIRECTIONS] = { + [0] = { + , , , + , , + , , + }, + //... all other pointing mode maps ... + [] = { + , , , + , , + , , + } +} +``` + +### Divisors + +All newly added modes will use `POINTING_MODE_DEFAULT_DIVISOR` as its divisor unless one is defined for each mode in the `pointing_modes_get_divisor_*` callback functions that have a user and keyboard version. The callback functions for setting divisors have both the` mode_id` and the `direction` _(see table)_ available to them to allow for easier setting of divisors without needing to call functions to get these values. +The default divisor of `32` will work well for most maps, however this would not be ideal for any mode that requires more sensitivity such as a WASD or Caret (arrow key) mode. + +!> Divisors should be set to a value between `1` and `MOUSE_XY_REPORT_MAX` (_`127` by default and `32767` if `MOUSE_EXTENDED_REPORT` is defined_) + +#### Callbacks to set pointing device mode divisors + +The following callbacks can be used to overwrite built in mode divisors or to set divisors for new modes. The `get_pointing_mode_divisor` stacks works by checking the functions until a non zero value is reached in order of `user`->`kb`->`built in`->`default_value`. Returning a divisor of `0` will allow processing to continue on to the next stage, However this means that if any of the get divisor callback functions return a default value other than 0 then that will overwrite all subsequent divisors(such as built in modes and divisors from the keyboard). These functions allows for overriding and modifying built in divisors by users/keymaps and keyboards and overriding keyboard level divisors by users/keymaps so it is possible to give built in modes the same level of divisor customisation as new custom modes. + +| Callback | Description | +| --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | +| `mouse_xy_report_t pointing_modes_get_divisor_kb(uint8_t mode_id, uint8_t direction)` | Keyboard level callback for setting divisor based on `mode_id` and `direction` | +| `mouse_xy_report_t pointing_modes_get_divisor_user(uint8_t mode_id, uint8_t direction)` | Keymap/user level callback for setting divisor based on `mode_id` and `direction` | + + +#### Directions table + +Directions could be used to set the divisor based on the direction (`direction` is updated before the `divisor` are updated). Directions are represented as labels on specific values of a 8-bit unsigned integer(_but only 4 bits are used_). The following table outlines the direction labels and the values that they represent. + + +| Direction code | Value | Value Hex | Value Binary | Description | +| :------------- | :---: | :-------: | :----------: | ----------------------------------------------------------------------------------------------------- | +| `PMD_None` | 0 | 0x00 | 0000 | Both y and x are 0 or below `POINTING_MODES_THRESHOLD` if it is defined | +| `PMD_DOWN` | 1 | 0x01 | 0001 | Stored y axis is positive and 2*\|y\| >= \|x\| | +| `PMD_UP` | 2 | 0x02 | 0010 | Stored y axis is negative and 2*\|y\| >= \|x\| | +| `PMD_LEFT` | 4 | 0x04 | 0100 | Stored x axis is negative and 2*\|x\| >= \|y\| | +| `PMD_RIGHT` | 8 | 0x08 | 1000 | Stored x axis is positive and 2*\|x\| >= \|y\| | +| `PMD_DNLT` | 5 | 0x05 | 0101 | Both `PMD_DOWN` and `PMD_LEFT` conditions are met and mode type is `PMT_DPAD` or `PMT_8WAY` | +| `PMD_UPLT` | 6 | 0x05 | 0110 | Both `PMD_UP` and `PMD_LEFT` conditions are met and mode type is `PMT_DPAD` or `PMT_8WAY` | +| `PMD_DNRT` | 9 | 0x09 | 1001 | Both `PMD_DOWN` and `PMD_RIGHT` conditions are met and mode type is `PMT_DPAD` or `PMT_8WAY` | +| `PMD_UPRT` | 10 | 0x0A | 1010 | Both `PMD_UP` and `PMD_RIGHT` conditions are met and mode type is `PMT_DPAD` or `PMT_8WAY` | +| `PMD_VERT` | 3 | 0x03 | 0011 | For filtering direction to only vertical component (up or down) using `direction & PMD_VERT` | +| `PMD_HORI` | 12 | 0x0C | 1100 | For filtering direction to only horizontal component (left or right) using `direction & PMD_HORI` | + +***Note: that diagonal directions are only relevant for the `PMT_8WAY` and `PMT_DPAD` mode types (see Mode Types and Mode options)*** + +#### Example code of assigning divisors for new modes + +```c +// added to keymap.c +// assuming poinding device enum and maps from example above +mouse_xy_report_t pointing_modes_get_divisor_user(uint8_t mode_id, uint8_t direction) { + switch(mode_id) { + + case PM_HALF_V: + // half speed for vertical axis + return direction & PMD_VERT ? 2 : 1; + + case PM_HALF_H: + // half speed for horizontal axis + return direction & PMD_HORI ? 2 : 1; + + case PM_ALL_DIFF: + // example of unique divisor for each direction in 4WAY mode (not recommended just an example of what can be done) + switch(direction) { + case PMD_DOWN: + return 32; + case PMD_UP: + return 64; + case PMD_LEFT: + return 16; + case PMD_RIGHT: + return 128; + } + + case PM_SLOW: + return 64; + case PM_GAME: + return 16; + } + + return 0; // returning 0 to let processing of divisors continue +} +``` + +### Mode Types and Mode Options + +Pointing modes can be further modified by changing the mode type as well as mode options. Some options will obviously only impact maps as they change how keypresses are handled, but options or types that impact directions affect all modes even modes that are not using a mode map such as `PM_NONE`, `PM_DRG`, and any custom modes that do not have a map associated with them. A mode can have **only one Mode Type** but can have **multiple Mode Options**. These are set for a given mode based on the callback functions for setting both the mode type and options using the bitwise or (`|`) operator to combine them (_e.g. `PMT_DPAD | PMO_XINV | PMO_HOLD`_). + +#### Callback Functions for Setting Mode Type +| Callback | Description | +| --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | +| `uint8_t pointing_modes_get_type_kb(uint8_t mode_id)` | Keyboard level callback for setting mode type and mode options based on `mode_id` | +| `uint8_t pointing_modes_get_type_user(uint8_t mode_id)` | Keymap/user level callback for setting mode type and mode options based on `mode_id` | + +#### Mode Type and options Tables + +Mode Types and options are represented by labels over values of a 8-bit unsigned integer with the bottom 4 bits storing the type and the top 4 bits storing the options. + +| Mode Type | Value Hex | Value Binary | Description | +| :----------- | :-------: | :----------: | :------------------------------------------------------------------------------------------------------------------------------------------ | +| `PMT_4WAY` | 0x00 | 00000000 | (Default) Restricts `direction` to cardinal directions (`PMD_DOWN`, `PMD_UP`, `PMD_LEFT`, or `PMD_RIGHT`) with one keycode per direction | +| `PMT_HORI` | 0x01 | 00000001 | Disables pointing device y axis output | +| `PMT_VERT` | 0x02 | 00000010 | Disables pointing device x axis output | +| `PMT_8WAY` | 0x03 | 00000011 | Allows for `direction` to have both cardinal and diagonal values and will output a single keycode per direction. | +| `PMT_DPAD` | 0x04 | 00000100 | Allows for horizontal and vertical keys to be output simultaneously on diagonal directions (`PMD_DNLT`, `PMD_UPLT`, `PMD_DNRT`, `PMD_UPRT`) | +| `PMT_MODES` | 0x0F | 00001111 | Bit mask to allow extraction of mode types from output of `pointing_mode_get_type()` using bitwise operations e.g. `mode_type & PMT_MODES` | + +***Note: The `PMT_4WAY` mode will additionally favour staying on the current axis i.e. if last input was horizontal will be less sensitive to vertical inputs until a large enough vertical input is given (_this effect increases with larger divisors/precision_)*** + +| Mode Type | Value Hex | Value Binary | Description | +| :----------- | :-------: | :----------: | :------------------------------------------------------------------------------------------------------------------------------------------ | +| `PMO_TAP` | 0x00 | 00000000 | (Default) Will tap direction keycode a number of times equal to the multiple of the divisor in that direction | +| `PMO_HOLD` | 0x10 | 00010000 | Will press and hold pressed key for a length of time depending on size of the input (_can be interrupted/extended by further inputs_) | +| `PMO_XINV` | 0x20 | 00100000 | Inverts the Horizontal (**x** axis of the pointing device for a given mode | +| `PMO_YINV` | 0x40 | 01000000 | Inverts the Vertical (**y**) axis of the pointing device for a given mode | +| `PMO_OPTS` | 0xF0 | 11110000 | Bit mask to allow extraction of mode options from output of `pointing_mode_get_type()` using bitwise operations e.g. `mode_type & PMT_OPTS` | + + +#### Example of Setting Mode types and options + +```c +// in keymap.c +uint8_t pointing_modes_get_type_user(uint8_t mode_id) { + switch(mode_id) { + // Default cursor output + case PM_NONE: + return PMO_YINV; + // Drag scroll + case PM_DRG: + return PMO_YINV; + // Browser Tabs + case PM_BROW: + return PMT_HORI | PMO_HOLD; + // Arrow key output + case PM_CARET: + return PMO_TAP; + // Volume output + case PM_VOL: + return PMT_VERT | PMO_YINV | PMO_HOLD; + // History scrolling (UNDO/REDO) + case PM_HIST: + return PMT_HORI | PMO_TAP; + // Windows APP scrolling + case PM_APP: + return PMT_HORI; + // WASD output + case PM_GAME: + return PMT_DPAD | PMO_HOLD; + } + return PMT_4WAY; // regular default Passing anything else will overwrite all built in modes +} + +``` + +### Creating Custom pointing modes keycodes + +There are built in functions to simplify the creation of custom keycodes and it is generally recommended to use these as they handle some edge cases to ensure consistent behaviour. + +| Function | Description | Return type | +| :---------------------------------------------------------- | :------------------------------------------------------------------------ | :---------: | +| `pointing_modes_key_momentary(mode_id, pressed)` | Momentary change of active device mode id to `mode_id` while key is held | _None_ | +| `pointing_modes_key_toggle(mode_id, pressed)` | Toggle active device pointing mode `mode_id` on key release | _None_ | +| `pointing_modes_key_set_device(mode_id, pressed)` | Set active device to `device_id` on key release | _None_ | +| `pointing_modes_key_set_precision(precision, pressed)` | Set active device precision to `precision` on key release | _None_ | + +***Note: all of these functions must be used in `process_record_*` functions and `record->event.pressed` should be passed for `pressed`_)*** + +These can be used to activate pointing device modes outside of the range of the built in keycodes as well as adding custom features to a mode that activate on key press such as registering a keycode and holding it until key release (_see code examples below_). + +#### Example code for adding an open app navigation mode outside of basic mode range + +```c +// in keymap.c + +enum my_pointing_modes { + // place Pointing device mode id outside of basic range as it will not use PM_MO or PM_TG macros + PM_TEST = PM_SAFE_RANGE // Control alt tabbing through open applications [mode id: 16] +}; + +enum my_custom_keycodes { + KC_MO_TEST = SAFE_RANGE, + KC_TG_TEST +}; + +bool process_record_user(uint16_t keycode, keyrecord_t* record) { + + switch(keycode) { + case KC_MO_TEST: + pointing_mode_key_momentary(PM_TEST, bool pressed); + return false; // stop key record processing + case KC_TG_TEST: + pointing_mode_key_toggle(PM_TEST, bool pressed); + return false; // stop key record processing + } + return true; // allow normal key record processing +} +``` + +The above code will create an Application switching pointing mode that should work on windows and linux allowing for navigation through currently open applications having the alt key held will keep the application/window UI active as long as the `KC_MO_APP` key is held (there is a slightly more advanced version of this in a below example that delays registering the alt key until enough scroll has accumulated). + + +## Pointing Modes Advanced use + +There are a number of functions allowing access and control of different aspects of the pointing modes feature most of these are intended for more advanced control such as custom keycodes to activate pointing modes or pointing modes that are manipulating something other than key presses (pointing device data, internal keyboard variables, etc.). + +#### Axes table for `pointing_modes_apply_divisor` function + +| Divisor method code | Value | Description | +| :------------------ | :---: | ------------------------------------------------------------------------------------------------------------------------------- | +| `PM_X_AXIS` | 0 | Return quotient of `residuals.x` and `divisor` and update `residuals.x` | +| `PM_Y_AXIS` | 1 | Return quotient of `residuals.y` and `divisor` and update `residuals.y` | +| `PM_H_AXIS` | 2 | Return quotient of `residuals.x` and `divisor` and clamp to `mouse_hv_report_t` range and update `residuals.x` | +| `PM_V_AXIS` | 3 | Return quotient of `residuals.y` and `divisor` and clamp to `mouse_hv_report_t` range and update `residuals.y` | +| `PM_X_KEY` | 4 | return `-1`, `0`, or `1` if `residuals.x` is positive, zero or negative and larger than divisor and update `residuals.x` | +| `PM_Y_KEY` | 5 | return `-1`, `0`, or `1` if `residuals.y` is positive, zero or negative and larger than divisor and update `residuals.y` | +| `PM_XY_KEY` | 6 | return sum of magnitude(_absolute value_) of `PM_X_KEY` and `PM_Y_KEY` functions, updating both `residual.x` and `residual.y` | + +This function allows simple application of a divisor to a given axis returning the relevant result and updating the residuals. + +### `pointing_modes_residuals_t` structure + +The current pointing mode on the active device is controlled by tracking several internal variables and the `pointing_mode_residual_t` data structure these residuals contain accumulated pointing device output (_this accumulation ensures smooth movement for any divisor_). A few other variables are tracked outside of the pointing mode structure and there are specialised functions to access/modify them. all of these variables are cleared on mode changes/resets (_mode id will be set to tg_mode_id_). These variables will also be available when using the `pointing_modes_task_*` functions. + +| Variable | Description | Data type | Functions to access/modify outside of mode processing | +| :-------------------- | :---------------------------------------- | :-----------------: | :--------------------------------------------------------------------- | +| `residuals.x` | Stored horizontal axis value | `mouse_xy_report_t` | `pointing_modes_get_residuals`, `pointing_modes_set_residuals` | +| `residuals.y` | Stored vertical axis value | `mouse_xy_report_t` | `pointing_modes_get_residuals`, `pointing_modes_set_residuals` | + +### Other Internally tracked Variables requiring functions to access and Modify + +| Variable | Description | Data type | Access/Control Functions | +| :-------------- | :---------------------------------------------------------------------------- | :-----------------: | :------------------------------------------------------------------------------------------------------------------------ | +| `mode_id` | Mode id of active device | `uint8_t` | `pointing_modes_get_mode`, `pointing_modes_set_mode` | +| `toggle_id` | Toggle id of last active toggle mode on active device | `uint8_t` | `pointing_modes_toggle_mode`, `pointing_modes_get_toggled_mode` | +| `active_device` | Active device index [see here](#Pointing-Modes-for-Multiple-Pointing-Devices) | `uint8_t` | `pointing_modes_get_active_device`, `pointing_modes_set_active_device`, keycode:`PM_CYCDEV` | +| `divisor` | Divisor of current mode id and direction | `mouse_xy_report_t` | `pointing_modes_get_divisor`, `pointing_modes_get_divisor_user`, `pointing_modes_get_divisor_kb`, `pointing_modes_update` | +| `precision` | Active device sensitivity setting | `mouse_xy_report_t` | `pointing_modes_get_precision`, `pointing_modes_set_precision`, keycode:`PM_CYCPRE` | +| `direction` | Direction based on stored x and y values | `uint8_t` | `pointing_modes_get_direction`, `pointing_modes_update` | +| `mode_type` | Mode type and options based on mode id | `uint8_t` | `pointing_modes_get_type`, `pointing_modes_get_type_user`, `pointing_modes_get_type_kb`, `pointing_modes_update` | + +#### Code example for changing modes on layer changes (will keep current toggle mode id) + +```c +// in keymap.c +// assuming enum and Layout for layers are already defined +layer_state_t layer_state_set_user(layer_state_t state) { + // reset mode id to toggle_id on layer change + pointing_modes_reset(); + switch(get_highest_layer(state)) { + case _RAISE: + pointing_modes_set_mode(PM_NONE); + break; + case _LOWER: + pointing_modes_set_mode(PM_DRAG); + break; + } + return state; +} +``` + +The above example will maintain the current toggle mode id and set layer pointing modes as temporary modes on top of it so that when changing to a different layer the current toggle mode will be re asserted. + +!> Note that the above approach will also cause the pointing mode of the active device to be reset on every layer change. + +#### Example that treats toggle modes as a "default" mode that changes depending on layer + +```c +// in keymap.c +// assuming enum and Layout for layers are already defined +layer_state_t layer_state_set_user(layer_state_t state) { + switch(get_highest_layer(state)) { + case _RAISE: + pointing_modes_toggle_mode(PM_NONE); + break; + case _LOWER: + pointing_modes_toggle_mode(PM_DRAG); + break; + default: + // disable toggled pointing mode if + switch(pointing_modes_get_toggled_mode()) { + case PM_DRAG: + pointing_mode_toggle_mode(POINTING_MODE_DEFAULT); + } + } + return state; +} +``` +This approach will overwrite any toggled modes upon a layer switch but will allow for momentary mode switching while on other layers returning to the current toggled mode for that layer once a momentary mode is released. This approach works best when keys primarily are used for momentary modes and layers trigger changes to toggled modes. + + +### Advanced custom modes + +Creating pointing device modes outside of pointing mode maps requires using the mode processing callbacks, Which give easy access to both the `mouse_report` and `residuals`. +***Note: at this point in the process `mouse_report` x, and y have already been transferred to the `residuals` and must be added back if cursor movement is desired*** + +#### Callbacks for adding custom modes + +These callbacks work similar to keycode processing callbacks in that returning false will prevent further processing of the pointing device mode. The processing order of pointing device modes is: `user`->`kb`->`built in`->`maps`. + +| Callback | Description | +| :-------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | +| `bool pointing_modes_task_kb(report_mouse_t* mouse_report, pointing_modes_residuals_t residuals)` | keyboard level callback for adding pointing device modes | +| `bool pointing_modes_task_user(report_mouse_t* mouse_report, pointing_modes_residuals_t residuals)` | user/keymap level callback for adding pointing device modes | + + +#### Creating modes using callback functions: + +```c +// in .h or .c +// add custom pointing device mode +enum my_kb_pointing_modes { + // start at the end of basic range + PM_CUR_NOPRE = PM_ADVANCED_RANGE_START, // [mode id: 34] + PM_CUR_ACCEL, // [mode id: 35] + // good practice to allow users to expand further + KB_PM_SAFE_RANGE +}; + +// add custom keycodes +enum my_kb_keycodes { + // start of keyboard custom keycode range + KB_MO_CUR_NOPRE = QK_KB, + KB_TG_CUR_ACCEL +}; + +#define CONSTRAIN_XY(val) (((val) > (XY_REPORT_MAX)) ? (XY_REPORT_MAX) : ((val) < (XY_REPORT_MIN)) ? (XY_REPORT_MIN) : (val)) + + +// in .c + +// define keybaord level divisors +mouse_xy_report_t pointing_modes_get_divisor_kb(uint8_t mode_id, uint8_t direction) { + switch(mode_id) { + case PM_CUR_NOPRE: + return 1; + case PM_CUR_ACCEL: + return 10; + } + return 0; // continue processing +} + +bool pointing_modes_task_kb(report_mouse_t* mouse_report, pointing_modes_residuals_t* residuals) { + // setting up on needed variable for APP scrolling mode + static bool alt_registered; + switch(pointing_modes_get_mode()){ + // cursor movement unaffected by precision + case PM_CUR_NOPRE: + mouse_report->x += residuals->x; + mouse_report->y += residuals->y; + return false; // stop pointing mode processing + + // Manipulating pointing_mode & mouse_report (cursor speed boost mode example) + case PM_CUR_ACCEL: + // reset mouse_report note that mouse_report is a pointer in this function's context + *mouse_report = pointing_device_get_report(); + // set up temp variable and context + { + // add linear acceleration to x + uint8_t mode_type = pointing_modes_get_type(); + if (mode_type & PMO_XINV) { // note checking for inversion options may not be needed this just makes the function more general + mouse_report->x = CONSTRAIN_XY(mouse_report->x - pointing_modes_apply_divisor(PM_X_AXIS)); + } else { + mouse_report->x = CONSTRAIN_XY(mouse_report->x + pointing_modes_apply_divisor(PM_X_AXIS)); + } + // add linear acceleration to y + if (mode_type & PMO_YINV) { // note checking for inversion options may not be needed this just makes the function more general + mouse_report->x = CONSTRAIN_XY(mouse_report->y - pointing_modes_apply_divisor(PM_Y_AXIS)); + } else { + mouse_report->x = CONSTRAIN_XY(mouse_report->y + pointing_modes_apply_divisor(PM_Y_AXIS)); + } + } + return false; // stop pointing mode processing + } + return true; +} + +bool process_record_kb(uint16_t keycode, keyrecord_t* record) { + switch(keycode) { + case KB_MO_CUR_NOPRE: + pointing_mode_key_momentary(PM_CUR_NOPRE, record); + return true; // continue key record processing + + case KB_TG_CUR_ACCEL: + pointing_mode_key_toggle(PM_CUR_ACCEL, record); + return true; // continue key record processing + } + return true; +} + +``` + +### Example combining mode maps with the `pointing_modes_task_*` functions for smoother app scrolling +```c +// in keymap.c +// add custom pointing device mode +enum my_kb_pointing_modes { + // set up map id for smoother app scrolling + PM_APP = POINTING_MODES_MAP_START // [mode id: 02 (Default can be chenged)] +}; + +// add map index label +enum my_kb_pointing_mode_maps { + _PM_APP // [index: 0] +}; + +mouse_xy_report_t pointing_modes_get_divisor_user(uint8_t mode_id, uint8_t direction) { + switch(mode_id) { + case PM_APP: + return 64; + } + return 0; // continue processing +} + +uint8_t pointing_modes_get_type_user(uint8_t mode_id) { + switch(mode_id) { + case PM_APP: + return PMT_HORI; + } + return PMT_4WAY; // continue processing +} + +bool pointing_modes_task_kb(report_mouse_t* mouse_report, pointing_modes_residuals_t* residuals) { + // setting up on needed variable for APP scrolling mode + static bool alt_registered; + switch(pointing_modes_get_mode()){ + case PM_APP: + // add alt key if not already registed + if (!alt_registered && (abs(residuals->x) >= pointing_modes_get_divisor())) { + add_mods(MOD_BIT(KC_LALT)); + alt_registered = true; + } + return true; // continue processing + default: + // remove alt key as soon as the mode changes + if(alt_registered) { + unregister_mods(MOD_BIT(KC_LALT)); + alt_registered = false; + } + } +} + +// with the alt key added during pointing_modes_task_* this will keep the app switching menu open as long as the modes are on +const uint16_t PROGMEM pointing_modes_maps[][POINTING_MODES_NUM_DIRECTIONS] = { + [_PM_APP] = { + KC_NO, + S(KC_TAB), KC_TAB, + KC_NO + } +}; +``` + +## Pointing Modes for Multiple Pointing Devices + +Pointing modes supports multiple pointing devices allowing for either control of one pointing device at a time or simultaneous control of multiple pointing devices. This supports left and right devices natively when both `SPLIT_POINTING_ENABLE` and `POINTING_DEVICE_COMBINED` as either single or simultaneous control (depending if `POINTING_MODE_SINGLE_CONTROL` is defined), However while controlling 3 or more pointing devices is possible this will need to be handled by using a custom `pointing_device_task` function. + +#### Multiple device Settings + +| Define | Description all (optional) | Data type | Range | Default | +| ------------------------------------- | -------------------------------------------------------------------------------------------------------------- | :-------: | :-----: | ------------: | +| `POINTING_MODES_NUM_DEVICES` | (optional) Number of devices available for control (intended for when using more than 2 pointing devices) | `uint8_t` | `1-255` | `1` or `2` | +| `POINTING_MODES_SINGLE_CONTROL` | (optional) Force control of only one device at a time (but the active device can be switched) | _None_ | _None_ | _Not defined_ | +| `POINTING_MODES_DEFAULT_DEVICE_ID` | (optional) Default device id controlled (`PM_RIGHT_DEVICE`, `PM_LEFT_DEVICE` depending on `MASTER_RIGHT`) | `uint8_t` | `1-255` | `0` | + +!> `POINTING_MODE_NUM_DEVICES` defaults to 2 if both `SPLIT_POINTING_ENABLE` and `POINTING_DEVICE_COMBINED` are defined + +#### Default device id defines + +| Device code | Value | Description | +| :---------------- | ----- | ------------------------------------------------------- | +| `PM_RIGHT_DEVICE` | `0` | Device index of right side pointing device | +| `PM_LEFT_DEVICE` | `1` | Device index of left side pointing device | + +!> Although up to `255` device id's are technically supported (ignoring hardware limitations) only two are given labels by default as that is the most common configuration + +#### Multiple Device keycodes + +| Keycode | Alias | Description | +| :--------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| `QK_PM_CYCLE_DEVICES` | `PM_CYCDEV` | Cycles the active device through available devices on each key release (e.g. toggling between `PM_RIGHT_DEVICE` and `PM_LEFT_DEVICE` with 2 devices) | + +#### Multiple device functions + +These functions control and query the current active device id. + +| Function | Description | Return type | +| :------------------------------------------------- | ------------------------------------------------------------------ | :---------: | +| `pointing_modes_get_active_device(void)` | Return active device index | `uint8_t` | +| `pointing_modes_set_active_device(uint8_t device)` | Set active device index | `void` | + +### Simultaneous device control + +When `POINTING_MODES_SINGLE_CONTROL` is not defined and `POINTING_MODES_NUM_DEVICES > 1` simultaneous control of multiple devices is enabled, which is the default behaviour when `SPLIT_POINTING_ENABLE` and `POINTING_DEVICE_COMBINED` are both defined. This will cause separate tracking of pointing modes data (`mode_id`, `toggle_id`, `residuals`, held keys, and `precision`) for each device allowing all to have different active modes simultaneously. All functions under this scheme require changing the active device to the desired device before calling any pointing modes function and the functions will only target that device (_this also affects built-in keycodes e.g. PM_CYCPRE_). Once set the active device will remain so until changed or the keyboard is powered down so keypresses or functions elsewhere in code will impact the current active device only. Changing the active device is made easier through use of the above keycodes (`PMR_CYCDEV`) so the active device can be set with a keypress and then subsequent `PM_MO` or `PM_TG` keypress will affect the newly active device. + +The default behaviour when using `SPLIT_POINTING_ENABLE` and `POINTING_DEVICE_COMBINED` are both defined will assign `left_mouse_report` to the `PM_LEFT_DEVICE` index and `right_mouse_report` to the `PM_RIGHT_DEVICE` and will process both devices pointing modes every /time `pointing_device_task` is called. Simply setting the active device and changing the mode id should enable using modes on either device. + +The code below is an example of how a hypothetical three device scenario using PMW33XX devices as the driver supports multiple devices per mcu. + + +#### Example of setting up 3 PMW33XX based devices for pointing modes (and just general mouse reports) +```c +// in keyboard config.h +#define POINTING_DEVICE_MODES_ENABLE +#define POINTING_MODES_NUM_DEVICES 3 + +#define PMW33XX_CS_PINS { , , } + +// in .h +enum my_pointing_device_ids{ + PD_LEFT == 0, // index 0 + PD_CENTRE, // index 1 + PD_RIGHT // index 2 +}; + +// in .c +#ifdef POINTING_DEVICE_ENABLE +// constrain to mouse movement +# define constrain_hid_xy(amt) ((amt) < XY_REPORT_MIN ? XY_REPORT_MIN : ((amt) > XY_REPORT_MAX ? XY_REPORT_MAX : (amt))) +void pointing_device_init_kb(void) { + // initialize non-default devices i.e. device id > 0 + pmw33xx_init(PD_CENTRE); + pmw33xx_init(PD_RIGHT); + pmw33xx_set_cpi_all_sensors(800); // set all sensors to 800 cpi + // set default pointing modes for devices + pointing_mode_set_active_device(PD_LEFT); + pointing_mode_set_mode(PM_DRAG); + + pointing_mode_set_active_device(PD_CENTRE); + pointing_mode_set_mode(PM_CARET); + // PD_RIGHT is left as POINTING_MODE_DEFAULT which is PM_NONE if undefined + // reset default device ID + pointing_mode_set_active_device(POINTING_MODE_DEFAULT_DEVICE_ID); + + pointing_device_init_user(); +} + +// Contains report from sensor #0 already, need to apply modes and merge in from other sensors +report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { + // start at highest device id + uint8_t device = PD_RIGHT; + // store active device id + uint8_t stored_device = pointing_modes_get_active_device(); + do { + pmw33xx_report_t device_report = pmw33xx_read_burst(device); + if (!device_report.motion.b.is_lifted && device_report.motion.b.is_motion) { + // create temporary mouse_report + report_mouse_t device_mouse_report = {0}; + device_mouse_report.x = constrain_hid_xy(device_report.delta_x); + device_mouse_report.y = constrain_hid_xy(device_report.delta_y); + // set pointing mode device and process pointing mode for that device + pointing_modes_set_active_device(device); + device_mouse_report = pointing_modes_device_task(device_mouse_report); + // merge modified mouse report + mouse_report.x = constrain_hid_xy(mouse_report.x + device_mouse_report.x); + mouse_report.y = constrain_hid_xy(mouse_report.y + device_mouse_report.y); + } + } while(device--); + // reset device id + pointing_modes_set_active_device(stored_device); + return pointing_device_task_user(mouse_report); +} +#endif +``` + +### Single Device Control of Multiple Devices + +When `POINTING_MODES_NUM_DEVICES > 1` and `POINTING_MODES_SINGLE_CONTROL` is defined, then only a single set of pointing modes data (`mode_id`, `toggle_id`, `residuals`, held keys, and `precision`) is tracked, but it is possible to switch which device is currently has the mode id active through use of `pointing_mode_set_active_device` This handles two pointing devices _(left and right)_ natively when `SPLIT_POINTING_ENABLE` and `POINTING_DEVICE_COMBINED` are both defined but for more than two pointing devices custom `pointing_device_task` code will be needed. + +!> Note that the `*_pointing_mode_device` functions do not change any internal behaviour when pointing modes is in single device mode, instead these functions are intended to simply track the desired controlled pointing device (this is handled automatically for two devices with `SPLIT_POINTING_ENABLE` and `POINTING_DEVICE_COMBINED`) diff --git a/quantum/keyboard.h b/quantum/keyboard.h index 0f39fde6825a..73ca65350cd9 100644 --- a/quantum/keyboard.h +++ b/quantum/keyboard.h @@ -32,7 +32,7 @@ typedef struct { uint8_t row; } keypos_t; -typedef enum keyevent_type_t { TICK_EVENT = 0, KEY_EVENT = 1, ENCODER_CW_EVENT = 2, ENCODER_CCW_EVENT = 3, COMBO_EVENT = 4, DIP_SWITCH_ON_EVENT = 5, DIP_SWITCH_OFF_EVENT = 6 } keyevent_type_t; +typedef enum keyevent_type_t { TICK_EVENT = 0, KEY_EVENT = 1, ENCODER_CW_EVENT = 2, ENCODER_CCW_EVENT = 3, COMBO_EVENT = 4, DIP_SWITCH_ON_EVENT = 5, DIP_SWITCH_OFF_EVENT = 6, POINTING_MODES_EVENT = 7} keyevent_type_t; /* key event */ typedef struct { @@ -50,6 +50,7 @@ typedef struct { #define KEYLOC_ENCODER_CCW 252 #define KEYLOC_DIP_SWITCH_ON 251 #define KEYLOC_DIP_SWITCH_OFF 250 +#define KEYLOC_POINTING_MODES 249 static inline bool IS_NOEVENT(const keyevent_t event) { return event.type == TICK_EVENT; @@ -69,6 +70,9 @@ static inline bool IS_ENCODEREVENT(const keyevent_t event) { static inline bool IS_DIPSWITCHEVENT(const keyevent_t event) { return event.type == DIP_SWITCH_ON_EVENT || event.type == DIP_SWITCH_OFF_EVENT; } +static inline bool IS_POINTINGEVENT(const keyevent_t event) { + return event.type == POINTING_MODES_EVENT; +} /* Common keypos_t object factory */ #define MAKE_KEYPOS(row_num, col_num) ((keypos_t){.row = (row_num), .col = (col_num)}) @@ -103,6 +107,15 @@ static inline bool IS_DIPSWITCHEVENT(const keyevent_t event) { # define MAKE_DIPSWITCH_OFF_EVENT(switch_id, press) MAKE_EVENT(KEYLOC_DIP_SWITCH_OFF, (switch_id), (press), DIP_SWITCH_OFF_EVENT) #endif // DIP_SWITCH_MAP_ENABLE +#ifdef POINTING_DEVICE_MODES_ENABLE +/* Pointing mode events */ +# ifdef POINTING_MODES_8WAY_MAP_ENABLE +# define MAKE_POINTING_MODES_EVENT(map_id, dir, press) MAKE_EVENT(KEYLOC_POINTING_MODES, (uint8_t)(((map_id) << 3) | (dir)), (press), POINTING_MODES_EVENT) +# else +# define MAKE_POINTING_MODES_EVENT(map_id, dir, press) MAKE_EVENT(KEYLOC_POINTING_MODES, (uint8_t)(((map_id) << 2) | (dir)), (press), POINTING_MODES_EVENT) +# endif // POINTING_MODES_8WAY_MAP_ENABLE +#endif // POINTING_DEVICE_MODES_ENABLE + /* it runs once at early stage of startup before keyboard_init. */ void keyboard_setup(void); /* it runs once after initializing host side protocol, debug and MCU peripherals. */ diff --git a/quantum/keycodes.h b/quantum/keycodes.h index 5929e3568739..f66d53a3c6a1 100644 --- a/quantum/keycodes.h +++ b/quantum/keycodes.h @@ -54,6 +54,12 @@ enum qk_keycode_ranges { QK_LAYER_TAP_TOGGLE_MAX = 0x52DF, QK_PERSISTENT_DEF_LAYER = 0x52E0, QK_PERSISTENT_DEF_LAYER_MAX = 0x52FF, + QK_POINTING_MODES_MO = 0x5302, + QK_POINTING_MODES_MO_MAX = 0x5321, + QK_POINTING_MODES_TG = 0x5322, + QK_POINTING_MODES_TG_MAX = 0x5341, + QK_POINTING_MODES_UTIL = 0x5342, + QK_POINTING_MODES_UTIL_MAX = 0x5343, QK_SWAP_HANDS = 0x5600, QK_SWAP_HANDS_MAX = 0x56FF, QK_TAP_DANCE = 0x5700, @@ -314,6 +320,8 @@ enum qk_keycode_defines { KC_RIGHT_SHIFT = 0x00E5, KC_RIGHT_ALT = 0x00E6, KC_RIGHT_GUI = 0x00E7, + QK_PM_CYCLE_DEVICES = 0x5342, + QK_PM_CYCLE_PRECISION = 0x5343, QK_SWAP_HANDS_TOGGLE = 0x56F0, QK_SWAP_HANDS_TAP_TOGGLE = 0x56F1, QK_SWAP_HANDS_MOMENTARY_ON = 0x56F2, @@ -977,6 +985,8 @@ enum qk_keycode_defines { KC_RGUI = KC_RIGHT_GUI, KC_RCMD = KC_RIGHT_GUI, KC_RWIN = KC_RIGHT_GUI, + PM_CYDEV = QK_PM_CYCLE_DEVICES, + PM_CYCPRE = QK_PM_CYCLE_PRECISION, SH_TOGG = QK_SWAP_HANDS_TOGGLE, SH_TT = QK_SWAP_HANDS_TAP_TOGGLE, SH_MON = QK_SWAP_HANDS_MOMENTARY_ON, @@ -1465,6 +1475,9 @@ enum qk_keycode_defines { #define IS_QK_ONE_SHOT_MOD(code) ((code) >= QK_ONE_SHOT_MOD && (code) <= QK_ONE_SHOT_MOD_MAX) #define IS_QK_LAYER_TAP_TOGGLE(code) ((code) >= QK_LAYER_TAP_TOGGLE && (code) <= QK_LAYER_TAP_TOGGLE_MAX) #define IS_QK_PERSISTENT_DEF_LAYER(code) ((code) >= QK_PERSISTENT_DEF_LAYER && (code) <= QK_PERSISTENT_DEF_LAYER_MAX) +#define IS_QK_POINTING_MODES_MO(code) ((code) >= QK_POINTING_MODES_MO && (code) <= QK_POINTING_MODES_MO_MAX) +#define IS_QK_POINTING_MODES_TG(code) ((code) >= QK_POINTING_MODES_TG && (code) <= QK_POINTING_MODES_TG_MAX) +#define IS_QK_POINTING_MODES_UTIL(code) ((code) >= QK_POINTING_MODES_UTIL && (code) <= QK_POINTING_MODES_UTIL_MAX) #define IS_QK_SWAP_HANDS(code) ((code) >= QK_SWAP_HANDS && (code) <= QK_SWAP_HANDS_MAX) #define IS_QK_TAP_DANCE(code) ((code) >= QK_TAP_DANCE && (code) <= QK_TAP_DANCE_MAX) #define IS_QK_MAGIC(code) ((code) >= QK_MAGIC && (code) <= QK_MAGIC_MAX) @@ -1491,6 +1504,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_POINTING_MODES_UTIL_KEYCODE(code) ((code) >= QK_PM_CYCLE_DEVICES && (code) <= QK_PM_CYCLE_PRECISION) #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 +1531,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 POINTING_MODES_UTIL_KEYCODE_RANGE QK_PM_CYCLE_DEVICES ... QK_PM_CYCLE_PRECISION #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/keymap_common.c b/quantum/keymap_common.c index abdcd5c7ba13..311d39d2c5da 100644 --- a/quantum/keymap_common.c +++ b/quantum/keymap_common.c @@ -208,6 +208,7 @@ __attribute__((weak)) uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key return keycode_at_encodermap_location(layer, key.col, false); } #endif // ENCODER_MAP_ENABLE + #ifdef DIP_SWITCH_MAP_ENABLE else if (key.row == KEYLOC_DIP_SWITCH_ON && key.col < NUM_DIP_SWITCHES) { return keycode_at_dip_switch_map_location(key.col, true); @@ -216,5 +217,17 @@ __attribute__((weak)) uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key } #endif // DIP_SWITCH_MAP_ENABLE +#ifdef POINTING_DEVICE_MODES_ENABLE +# ifdef POINTING_MODES_8WAY_MAP_ENABLE + else if (key.row == KEYLOC_POINTING_MODES && key.col < ((pointing_modes_map_count() << 3) | 0x07)) { + return keycode_at_pointing_modes_map_location(key.col); + } +# else + else if (key.row == KEYLOC_POINTING_MODES && key.col < ((pointing_modes_map_count() << 2) | 0x03)) { + return keycode_at_pointing_modes_map_location(key.col); + } +# endif +#endif // POINTING_DEVICE_MODES_ENABLE + return KC_NO; } diff --git a/quantum/keymap_introspection.c b/quantum/keymap_introspection.c index 236b54ce9801..c236bfe94761 100644 --- a/quantum/keymap_introspection.c +++ b/quantum/keymap_introspection.c @@ -171,3 +171,63 @@ __attribute__((weak)) const key_override_t* key_override_get(uint16_t key_overri } #endif // defined(KEY_OVERRIDE_ENABLE) + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Pointing mode mapping + +#ifdef POINTING_DEVICE_MODES_ENABLE + +# define POINTING_MODES_MAP_COUNT_RAW (uint8_t)(sizeof(pointing_modes_maps) / ((POINTING_MODES_NUM_DIRECTIONS) * sizeof(uint16_t))) + +uint8_t pointing_modes_map_count_raw(void) { + return POINTING_MODES_MAP_COUNT_RAW; +} + +__attribute__((weak)) uint8_t pointing_modes_map_count(void) { + return pointing_modes_map_count_raw(); +} + +/* + * @brief Retrieve keycode from pointing mode map + * + * Returns keycode from pointing mode map based on 8 bit index location + * breakdown of location: + * mode id | direction + * XXXX XX | XX + * + * NOTE: silently fails and returns KC_NO if mode map out of range + * + * @param[in] map_loc uint8_t + * + * @return uint16_t keycode at pointing mode map location + */ +uint16_t keycode_at_pointing_modes_map_location_raw(uint8_t map_loc) { +# ifdef POINTING_MODES_8WAY_MAP_ENABLE + uint8_t map_id = map_loc >> 3; + uint8_t dir = map_loc & 0x07; +# else + uint8_t map_id = map_loc >> 2; + uint8_t dir = map_loc & 0x03; +# endif // POINTING_MODES_8WAY_MAP_ENABLE + + if (map_id < pointing_modes_map_count()) { + return pgm_read_word(&pointing_modes_maps[map_id][dir]); + } + return KC_NO; +} + +/* + * @brief Weakly defined function for retreiving keycode from pointing mode map + * + * Defaults to passing map_loc to raw function, would allow interception of + * keycode retrieval process for pointing mode maps + * + * @param[in] map_loc uint8_t + * + * @return uint16_t keycode at pointing mode map location + */ +__attribute__((weak)) uint16_t keycode_at_pointing_modes_map_location(uint8_t map_loc) { + return keycode_at_pointing_modes_map_location_raw(map_loc); +} + +#endif // POINTING_DEVICE_MODES_ENABLE diff --git a/quantum/keymap_introspection.h b/quantum/keymap_introspection.h index 719825c674d4..5f1cdc7d73ba 100644 --- a/quantum/keymap_introspection.h +++ b/quantum/keymap_introspection.h @@ -109,3 +109,19 @@ const key_override_t* key_override_get_raw(uint16_t key_override_idx); const key_override_t* key_override_get(uint16_t key_override_idx); #endif // defined(KEY_OVERRIDE_ENABLE) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Pointing Mode Mapping + +#if defined(POINTING_DEVICE_MODES_ENABLE) + +// Get the number of pointing mode maps, stored in firmware +uint8_t pointing_modes_map_count_raw(void); +// Get the number of pointing mode maps, potentially stored dynamically +uint8_t pointing_modes_map_count(void); + +// Get the keycode for the pointing mode map location, stored in firmware +uint16_t keycode_at_pointing_modes_map_location_raw(uint8_t map_loc); +// Get the keycode for the encoder mapping location, potentially stored dynamically +uint16_t keycode_at_pointing_modes_map_location(uint8_t map_loc); + +#endif // defined(POINTING_DEVICE_MODES_ENABLE) diff --git a/quantum/pointing_device/pointing_device.c b/quantum/pointing_device/pointing_device.c index cac2875fc8d4..98fbb854fa22 100644 --- a/quantum/pointing_device/pointing_device.c +++ b/quantum/pointing_device/pointing_device.c @@ -198,6 +198,7 @@ __attribute__((weak)) bool pointing_device_send(void) { uint8_t buttons = local_mouse_report.buttons; memset(&local_mouse_report, 0, sizeof(local_mouse_report)); local_mouse_report.buttons = buttons; + memcpy(&old_report, &local_mouse_report, sizeof(local_mouse_report)); return should_send_report || buttons; @@ -303,7 +304,7 @@ __attribute__((weak)) bool pointing_device_task(void) { local_mouse_report = pointing_device_adjust_by_defines_right(local_mouse_report); shared_mouse_report = pointing_device_adjust_by_defines(shared_mouse_report); } - local_mouse_report = is_keyboard_left() ? pointing_device_task_combined_kb(local_mouse_report, shared_mouse_report) : pointing_device_task_combined_kb(shared_mouse_report, local_mouse_report); + local_mouse_report = is_keyboard_left() ? pointing_device_task_combined(local_mouse_report, shared_mouse_report) : pointing_device_task_combined(shared_mouse_report, local_mouse_report); #else local_mouse_report = pointing_device_adjust_by_defines(local_mouse_report); local_mouse_report = pointing_device_task_kb(local_mouse_report); @@ -311,6 +312,10 @@ __attribute__((weak)) bool pointing_device_task(void) { // automatic mouse layer function #ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE pointing_device_task_auto_mouse(local_mouse_report); +#endif + // pointing device modes handling for single pointing device +#if defined(POINTING_DEVICE_MODES_ENABLE) && !(defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED)) + local_mouse_report = pointing_modes_device_task(local_mouse_report); #endif // combine with mouse report to ensure that the combined is sent correctly #ifdef MOUSEKEY_ENABLE @@ -486,6 +491,44 @@ report_mouse_t pointing_device_adjust_by_defines_right(report_mouse_t mouse_repo return mouse_report; } +/** + * @brief Handle core combined pointing device tasks + * + * Takes 2 report_mouse_t structs allowing individual modification of either side and then returns pointing_device_task_combined_kb. + * + * NOTE: Only available when using both SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED + * + * @param[in] left_report report_mouse_t + * @param[in] right_report report_mouse_t + * @return pointing_device_task_combined_kb(left_report, right_report) by default + */ +report_mouse_t pointing_device_task_combined(report_mouse_t left_report, report_mouse_t right_report) { +# ifdef POINTING_DEVICE_MODE_ENABLE +# if POINTING_MODES_SINGLE_CONTROL + // only one side controlled at any one time + switch (pointing_modes_get_active_device()) { + case PM_RIGHT_DEVICE: + right_report = pointing_modes_device_task(right_report); + break; + default: + left_report = pointing_modes_device_task(left_report); + } +# else + // both sides controlled independently + // save current device id + uint8_t active_device = pointing_modes_get_active_device(); + pointing_modes_set_active_device(PM_RIGHT_DEVICE); + right_report = pointing_modes_device_task(right_report); + + pointing_modes_set_active_device(PM_LEFT_DEVICE); + left_report = pointing_modes_device_task(left_report); + // set device id back + pointing_modes_set_active_device(active_device); +# endif +# endif + return pointing_device_task_combined_kb(left_report, right_report); +} + /** * @brief Weak function allowing for keyboard level mouse report modification * @@ -515,7 +558,7 @@ __attribute__((weak)) report_mouse_t pointing_device_task_combined_kb(report_mou __attribute__((weak)) report_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, report_mouse_t right_report) { return pointing_device_combine_reports(left_report, right_report); } -#endif +#endif // defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED) __attribute__((weak)) void pointing_device_keycode_handler(uint16_t keycode, bool pressed) { if IS_MOUSEKEY_BUTTON (keycode) { diff --git a/quantum/pointing_device/pointing_device.h b/quantum/pointing_device/pointing_device.h index 72188c977dd1..a1502d37cfe1 100644 --- a/quantum/pointing_device/pointing_device.h +++ b/quantum/pointing_device/pointing_device.h @@ -31,6 +31,9 @@ typedef struct { #ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE # include "pointing_device_auto_mouse.h" #endif +#ifdef POINTING_DEVICE_MODES_ENABLE +# include "pointing_device_modes.h" +#endif #if defined(POINTING_DEVICE_DRIVER_adns5050) # include "drivers/sensors/adns5050.h" @@ -139,6 +142,7 @@ uint16_t pointing_device_get_shared_cpi(void); # if defined(POINTING_DEVICE_COMBINED) void pointing_device_set_cpi_on_side(bool left, uint16_t cpi); report_mouse_t pointing_device_combine_reports(report_mouse_t left_report, report_mouse_t right_report); +report_mouse_t pointing_device_task_combined(report_mouse_t left_report, report_mouse_t right_report); report_mouse_t pointing_device_task_combined_kb(report_mouse_t left_report, report_mouse_t right_report); report_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, report_mouse_t right_report); report_mouse_t pointing_device_adjust_by_defines_right(report_mouse_t mouse_report); diff --git a/quantum/pointing_device/pointing_device_auto_mouse.c b/quantum/pointing_device/pointing_device_auto_mouse.c index 250351f6088d..a594ff547a71 100644 --- a/quantum/pointing_device/pointing_device_auto_mouse.c +++ b/quantum/pointing_device/pointing_device_auto_mouse.c @@ -1,5 +1,5 @@ /* Copyright 2021 Christopher Courtney, aka Drashna Jael're (@drashna) - * Copyright 2022 Alabastard + * Copyright 2022 Alabastard (@Alabastard-64) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -430,7 +430,12 @@ bool process_auto_mouse(uint16_t keycode, keyrecord_t* record) { */ static bool is_mouse_record(uint16_t keycode, keyrecord_t* record) { // allow for keyboard to hook in and override if need be - if (is_mouse_record_kb(keycode, record) || IS_MOUSEKEY(keycode)) return true; + if (is_mouse_record_kb(keycode, record) || IS_MOUSEKEY(keycode) || +# ifdef POINTING_DEVICE_MODES_ENABLE + IS_QK_POINTING_MODES_MO(keycode) || IS_QK_POINTING_MODES_TG(keycode) || +# endif + false) + return true; return false; } diff --git a/quantum/pointing_device/pointing_device_modes.c b/quantum/pointing_device/pointing_device_modes.c new file mode 100644 index 000000000000..1679e2a83800 --- /dev/null +++ b/quantum/pointing_device/pointing_device_modes.c @@ -0,0 +1,732 @@ +/* Copyright 2023 Alabastard (@Alabastard-64) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef POINTING_DEVICE_MODES_ENABLE + +# include "pointing_device_modes.h" + +// initialize local functions +static void pointing_modes_task(report_mouse_t* mouse_report); +static void pointing_modes_axes_conv(report_mouse_t* mouse_report); +static void pointing_modes_update_direction(void); +static void pointing_modes_update_divisor(void); +static void pointing_modes_update_type(void); +static void pointing_modes_release_held_keys(void); +static void pointing_modes_hold_decay(void); +static void pointing_modes_exec_mapping(uint8_t map_id); + +// local variables +static uint8_t current_direction = PMD_NONE; +static mouse_xy_report_t current_divisor = POINTING_MODES_DEFAULT_DIVISOR; +static uint8_t current_type = PMT_4WAY | PMO_TAP; + +// set up devices and active device +static uint8_t pm_mode_ids[] = {[0 ... POINTING_MODES_DEVICE_CONTROL_COUNT - 1] = POINTING_MODES_DEFAULT_MODE}; +static uint8_t pm_base_modes[] = {[0 ... POINTING_MODES_DEVICE_CONTROL_COUNT - 1] = POINTING_MODES_DEFAULT_MODE}; +static mouse_xy_report_t pm_precisions[] = {[0 ... POINTING_MODES_DEVICE_CONTROL_COUNT - 1] = POINTING_MODES_PRECISION_MIN}; +static pointing_modes_residuals_t pm_res[] = {[0 ... POINTING_MODES_DEVICE_CONTROL_COUNT - 1] = {.x = 0, .y = 0}}; +static pointing_modes_held_keys_t pm_held[] = {[0 ... POINTING_MODES_DEVICE_CONTROL_COUNT - 1] = {.h = PMK_NONE, .v = PMK_NONE}}; +# if POINTING_MODES_DEVICE_CONTROL_COUNT > 1 +static uint8_t active_device_id = POINTING_MODES_DEFAULT_DEVICE; +# else +static const uint8_t active_device_id = 0; +# endif + +// set up inline clamping functions ---------------------------------------------------------------------------- +static inline mouse_xy_report_t pointing_device_xy_clamp(xy_clamp_range_t value) { + if (value < XY_REPORT_MIN) { + return XY_REPORT_MIN; + } else if (value > XY_REPORT_MAX) { + return XY_REPORT_MAX; + } else { + return value; + } +} + +static inline mouse_hv_report_t pointing_device_hv_clamp(hv_clamp_range_t value) { + if (value < HV_REPORT_MIN) { + return HV_REPORT_MIN; + } else if (value > HV_REPORT_MAX) { + return HV_REPORT_MAX; + } else { + return value; + } +} + +// Internal functions ------------------------------------------------------------------------------------------ + +/** + * @brief Handle processing of pointing modes + * + * Takes in report_mouse_t and pointing_modes_device_t allowing manipulation of mouse_report + * and pointing_mode via pointing_modes_set_device_settings + * + * @return mouse_report report_mouse_t + */ +static void pointing_modes_task(report_mouse_t* mouse_report) { + // allow overwrite of pointing modes by user, and kb + if ( + !( + pointing_modes_task_user(mouse_report, &pm_res[active_device_id]) && + pointing_modes_task_kb(mouse_report, &pm_res[active_device_id]) + ) + ) { + return; + } + switch (pm_mode_ids[active_device_id]) { + // if no pointing mode selected set back to mouse_report and clear + case PM_NONE: + mouse_report->x += pointing_modes_apply_divisor(PM_X_AXIS); + mouse_report->y += pointing_modes_apply_divisor(PM_Y_AXIS); + return; + // drag scroll mode (sets mouse axes to mouse_report h & v with divisor) + case PM_DRAG: + mouse_report->h += (mouse_hv_report_t)pointing_modes_apply_divisor(PM_H_AXIS); + mouse_report->v += (mouse_hv_report_t)pointing_modes_apply_divisor(PM_V_AXIS); + return; + + default: + // check that the mode id is a valid map + if (pointing_modes_get_mode() < POINTING_MODES_MAP_START) { + pointing_modes_set_mode(PM_NONE); + return; + } + pointing_modes_exec_mapping(pm_mode_ids[active_device_id] - POINTING_MODES_MAP_START); + } + return; +} + +/** + * @brief Converts mouse_report x/y to residuals + * + * The default uses accumulation based on inversion defines and + * halts cursor movement + * + * @params mouse_report[in] report_mouse_t + * + * @return updated mouse_report report_mouse_t + */ +static void pointing_modes_axes_conv(report_mouse_t* mouse_report) { + uint8_t mode_type = pointing_modes_get_type(); + if(mouse_report->x && !(mode_type == PMT_VERT)) { + if (mode_type & PMO_XINV) { + pm_res[active_device_id].x -= mouse_report->x; + } else { + pm_res[active_device_id].x += mouse_report->x; + } + } + mouse_report->x = 0; +// set Y axis of pointing mode for device + if(mouse_report->y && !(mode_type == PMT_HORI)) { + if (mode_type & PMO_YINV) { + pm_res[active_device_id].y -= mouse_report->y; + } else { + pm_res[active_device_id].y += mouse_report->y; + } + } + mouse_report->y = 0; + return; +} + +/** + * @brief local function to get single direction based on h/v + * + * Determines direction based on axis with largest magnitude + * + * NOTE: Defaults to PMD_DOWN + * + */ +static void pointing_modes_update_direction(void) { + current_direction = PMD_NONE; + if((abs(pm_res[active_device_id].x) + abs(pm_res[active_device_id].y)) <= POINTING_MODES_THRESHOLD) { + return; + } + // This will detect all 8 directions: 2|Axis| >= |Other_Axis| allows for diagonal detection + if ((abs(pm_res[active_device_id].x) << 1) >= abs(pm_res[active_device_id].y)) { + if (pm_res[active_device_id].x > 0) { + current_direction |= PMD_RIGHT; + } else { + current_direction |= PMD_LEFT; + } + } + if ((abs(pm_res[active_device_id].y) << 1) >= abs(pm_res[active_device_id].x)) { + if (pm_res[active_device_id].y > 0) { + current_direction |= PMD_DOWN; + } else { + current_direction |= PMD_UP; + } + } + if(pointing_modes_get_type() == PMT_4WAY) { + // remove diagonal directions + if((current_direction & PMD_HORI) && (current_direction & PMD_VERT)) { + if(abs(pm_res[active_device_id].x) > abs(pm_res[active_device_id].y)) { + current_direction &= PMD_HORI; + } else { + current_direction &= PMD_VERT; + } + } + } + return; +} + +/** + * @brief local function to get pointing mode divisor + * + * Will handle default divisors and call weak kb and user functions + * + * NOTE: Expects that pointing mode and direction has been updated + * + */ +static void pointing_modes_update_divisor(void) { + uint8_t mode_id = pointing_modes_get_mode(); + // allow for user and keyboard overrides + current_divisor = pointing_modes_get_divisor_user(mode_id, current_direction); + if (!current_divisor) { + current_divisor = pointing_modes_get_divisor_kb(mode_id, current_direction); + if (!current_divisor) { + // built in divisors + switch (pointing_modes_get_mode()) { + case PM_NONE: + current_divisor = 1; + break; + case PM_DRAG: + current_divisor = POINTING_MODES_DRAG_DIVISOR; + break; + } + } + } + // check for zero divisor and apply precision + current_divisor = current_divisor ? current_divisor : POINTING_MODES_DEFAULT_DIVISOR; + current_divisor = abs(pointing_device_xy_clamp(current_divisor * pointing_modes_get_precision())); + return; +} + +/** + * @brief update mode type based on current status + * + + */ +static void pointing_modes_update_type(void) { + uint8_t mode_id = pointing_modes_get_mode(); + current_type = 0x00; + // allow for keyboard and user overrides + current_type = pointing_modes_get_type_user(mode_id); + if (current_type) {return;} + current_type = pointing_modes_get_type_kb(mode_id); + if (current_type) {return;} + + return; +} + +/** + * @brief Release held keys of active device + * + * releases held keys and sets them to the default value of 8 + */ +static void pointing_modes_release_held_keys(void) { + if(pm_mode_ids[active_device_id] < POINTING_MODES_MAP_START) {return;} + uint8_t map_id = pm_mode_ids[active_device_id] - POINTING_MODES_MAP_START; + if (pm_held[active_device_id].v ^ PMK_NONE) { + action_exec(MAKE_POINTING_MODES_EVENT(map_id, pm_held[active_device_id].v, false)); + pm_held[active_device_id].v = PMK_NONE; + } + if (pm_held[active_device_id].h ^ PMK_NONE) { + action_exec(MAKE_POINTING_MODES_EVENT(map_id, pm_held[active_device_id].h, false)); + pm_held[active_device_id].h = PMK_NONE; + } +} + +/** + * @brief Decrease residuals by the decay rate + * + * Decays residuals towards zero 0 by decay rate of active device + * + */ +static void pointing_modes_hold_decay(void) { + // Decay X + if (pm_res[active_device_id].x > 0) { + pm_res[active_device_id].x -= MIN((mouse_xy_report_t)POINTING_MODES_HOLD_DECAY_RATE, pm_res[active_device_id].x); + } + if (pm_res[active_device_id].x < 0) { + pm_res[active_device_id].x += MIN((mouse_xy_report_t)POINTING_MODES_HOLD_DECAY_RATE, abs(pm_res[active_device_id].x)); + } + // Decay Y + if (pm_res[active_device_id].y > 0) { + pm_res[active_device_id].y -= MIN((mouse_xy_report_t)POINTING_MODES_HOLD_DECAY_RATE, pm_res[active_device_id].y); + } + if (pm_res[active_device_id].y < 0) { + pm_res[active_device_id].y += MIN((mouse_xy_report_t)POINTING_MODES_HOLD_DECAY_RATE, abs(pm_res[active_device_id].y)); + } +} + +/** + * @brief Tap keycodes from pointing mode maps + * + * Will translate internal x & y axes into mode map keycode taps. + * and will update the x and y values.- + * + * NOTE: favours staying on axis and favours the horizontal over the vertical axis + * and will clear the orthogonal axis + * + * @params map_id id of current map + */ +static void pointing_modes_exec_mapping(uint8_t map_id) { + int8_t count[] = {0, 0}; + uint8_t key[] = {PMK_NONE, PMK_NONE}; + uint8_t mode_type = pointing_modes_get_type(); + uint8_t dir = pointing_modes_get_direction(); + switch (mode_type & PMT_MODES) { + case PMT_4WAY ... PMT_8WAY: + switch (dir) { + case PMD_NONE: + break; + case PMD_DOWN: + key[0] = PMK_DOWN; + case PMD_UP: + if(key[0]==PMK_NONE) {key[0] = PMK_UP;} + count[0] = pointing_modes_apply_divisor(PM_Y_KEY); + if (!count[0]) {break;} // exit if accumulated y is too low + pm_res[active_device_id].x = 0; + break; + case PMD_LEFT: + key[0] = PMK_LEFT; + case PMD_RIGHT: + if(key[0]==PMK_NONE) {key[0] = PMK_RIGHT;} + count[0] = pointing_modes_apply_divisor(PM_X_KEY); + if (!count[0]) {break;} // exit if accumulated x is too low + pm_res[active_device_id].y = 0; +# ifdef POINTING_MODES_8WAY_MAP_ENABLE + break; + case PMD_DNLT: + key[0] = PMK_DOWNLEFT; + case PMD_UPLT: + if(key[0]==PMK_NONE) {key[0] = PMK_UPLEFT;} + case PMD_DNRT: + if(key[0]==PMK_NONE) {key[0] = PMK_DOWNRIGHT;} + case PMD_UPRT: + if(key[0]==PMK_NONE) {key[0] = PMK_UPRIGHT;} + count[0] = pointing_modes_apply_divisor(PM_XY_KEY); +# endif // POINTING_MODES_8WAY_MAP_ENABLE + } + break; + case PMT_DPAD: + if(dir & PMD_HORI) { + count[PM_X_AXIS] = pointing_modes_apply_divisor(PM_X_KEY); + key[PM_X_AXIS] = (dir & PMD_RIGHT)? PMK_RIGHT:PMK_LEFT; + } + if(dir & PMD_VERT) { + count[PM_Y_AXIS] = pointing_modes_apply_divisor(PM_Y_KEY); + key[PM_Y_AXIS] = (dir & PMD_DOWN)? PMK_DOWN:PMK_UP; + } + } + switch(mode_type & PMO_OPTS){ + case PMO_TAP: + // Tap Horizontal/4-Way/8-Way + if(count[PM_X_AXIS]) { + action_exec(MAKE_POINTING_MODES_EVENT(map_id, key[PM_X_AXIS], true)); +# if POINTING_MODES_TAP_DELAY > 0 + wait_ms(POINTING_MODES_TAP_DELAY); +# endif // POINTING_MODES_TAP_DELAY > 0 + action_exec(MAKE_POINTING_MODES_EVENT(map_id, key[PM_X_AXIS], false)); + } + // Tap Vertical + if(count[PM_Y_AXIS]) { + action_exec(MAKE_POINTING_MODES_EVENT(map_id, key[PM_Y_AXIS], true)); +# if POINTING_MODES_TAP_DELAY > 0 + wait_ms(POINTING_MODES_TAP_DELAY); +# endif // POINTING_MODES_TAP_DELAY > 0 + action_exec(MAKE_POINTING_MODES_EVENT(map_id, key[PM_Y_AXIS], false)); + } + break; + + case PMO_HOLD: + // Hold: DPAD_HORI/2-Way/4-Way/8-Way + if(count[PM_X_AXIS]) { + if(pm_held[active_device_id].h != key[PM_X_AXIS]) { + if(pm_held[active_device_id].h != PMK_NONE) { + action_exec(MAKE_POINTING_MODES_EVENT(map_id, pm_held[active_device_id].h, false)); + } + action_exec(MAKE_POINTING_MODES_EVENT(map_id, key[PM_X_AXIS], true)); + pm_held[active_device_id].h = key[PM_X_AXIS]; + } + } + // Hold: DPAD_VERT + if(count[PM_Y_AXIS]) { + if(pm_held[active_device_id].v != key[PM_Y_AXIS]) { + if(pm_held[active_device_id].v != PMK_NONE) { + action_exec(MAKE_POINTING_MODES_EVENT(map_id, pm_held[active_device_id].v, false)); + } + action_exec(MAKE_POINTING_MODES_EVENT(map_id, key[PM_Y_AXIS], true)); + pm_held[active_device_id].v = key[PM_Y_AXIS]; + } + } + // Decay hold + pointing_modes_hold_decay(); + // Hold: release on no movement + if(dir==PMD_NONE) { + pointing_modes_release_held_keys(); + } + } +} + +// External functions ------------------------------------------------------------------------------------------ + +/** + * @brief apply divisor to specified device axis and update residuals + * + * Will apply the divisor to the specified axis (PM_X_AXIS, PM_Y_AXIS, PM_H_AXIS, PM_V_AXIS) + * will use appropriate clamping in returned result and update residuals + * + * @params value[in] axis uint8_t + * + * @return quotient + */ +mouse_xy_report_t pointing_modes_apply_divisor(uint8_t axis) { + mouse_xy_report_t result = 0; + uint8_t mode_opts = pointing_modes_get_type() & PMO_OPTS; + switch (axis){ + case PM_X_AXIS: + result = pm_res[active_device_id].x / current_divisor; + pm_res[active_device_id].x -= pointing_device_xy_clamp(result * current_divisor); + break; + case PM_Y_AXIS: + result = pm_res[active_device_id].y / current_divisor; + pm_res[active_device_id].y -= pointing_device_xy_clamp(result * current_divisor); + break; + case PM_H_AXIS: + result = pointing_device_hv_clamp(pm_res[active_device_id].x / current_divisor); + pm_res[active_device_id].x -= pointing_device_xy_clamp(result * current_divisor); + break; + case PM_V_AXIS: + result = pointing_device_hv_clamp(pm_res[active_device_id].y / current_divisor); + pm_res[active_device_id].y -= pointing_device_xy_clamp(result * current_divisor); + break; + case PM_X_KEY: + if (abs(pm_res[active_device_id].x) >= current_divisor) { + if (pm_res[active_device_id].x > 0) { + result = 1; + if (mode_opts ^ PMO_HOLD) { + pm_res[active_device_id].x -= current_divisor; + } + } else { + result = -1; + if (mode_opts ^ PMO_HOLD) { + pm_res[active_device_id].x += current_divisor; + } + } + } + break; + case PM_Y_KEY: + if (abs(pm_res[active_device_id].y) >= current_divisor) { + if (pm_res[active_device_id].y > 0) { + result = 1; + if (mode_opts ^ PMO_HOLD) { + pm_res[active_device_id].y -= current_divisor; + } + } else { + result = -1; + if (mode_opts ^ PMO_HOLD) { + pm_res[active_device_id].y += current_divisor; + } + } + } + break; + case PM_XY_KEY: + result = pointing_modes_apply_divisor(PM_X_KEY); + result += abs(pointing_modes_apply_divisor(PM_Y_KEY)); + break; + } + return result; +} + +/** + * @brief Return device id of current controlled device + * + * @return current device id [uint8_t] + */ +uint8_t pointing_modes_get_active_device(void) { + return active_device_id; +} + +/** + * @brief Change current device + * + * Will change which device (PM_LEFT_DEVICE, PM_RIGHT_DEVICE, etc.) is currently being modified + * + * NOTE: If mode is set to out of range device number device is silently set to zero (this allows cycling) + * + * @params[in] new side uint8_t + */ +void pointing_modes_set_active_device(uint8_t device_id) { +# if (POINTING_MODES_NUM_DEVICES > 1) + if (device_id > (POINTING_MODES_NUM_DEVICES - 1)) device_id = 0; + active_device_id = device_id; +# else + ; +# endif +} + +/** + * @brief Reset active device mode and mode data + * + * Clear poiting device status, and set mode id to toggle mode id + */ +void pointing_modes_reset(void) { + pointing_modes_release_held_keys(); + // must set these directly rather than calling pointing_modes_set_mode to avoid loops + pm_mode_ids[active_device_id] = pm_base_modes[active_device_id]; + pm_res[active_device_id].x = 0; + pm_res[active_device_id].y = 0; +} + +/** + * @brief get precision of active device + * + * @return mouse_xy_report_t active device precision divisor + */ +mouse_xy_report_t pointing_modes_get_precision(void) { + return pm_precisions[active_device_id]; +} + +/** + * @brief set precision of active device + * + * @param[in] mouse_xy_report_t precision + */ +void pointing_modes_set_precision(mouse_xy_report_t precision) { + if (precision > POINTING_MODES_PRECISION_MAX || precision < POINTING_MODES_PRECISION_MIN) { + pm_precisions[active_device_id] = POINTING_MODES_PRECISION_MIN; + } else { + pm_precisions[active_device_id] = precision; + } +} + + +/** + * @brief access current pointing mode id + * + * @return uint8_t current pointing mode id + */ +uint8_t pointing_modes_get_mode(void) { + return pm_mode_ids[active_device_id]; +} + +/** + * @brief set pointing mode id + * + * @param[in] mode_id uint8_t + */ +void pointing_modes_set_mode(uint8_t mode_id) { + if (pointing_modes_get_mode() != mode_id) { + pointing_modes_reset(); + pm_mode_ids[active_device_id] = mode_id; + } +} + +/** + * @brief Access current toggled pointing mode + * + * @return uint8_t toggle pointing mode + */ +uint8_t pointing_modes_get_toggled_mode(void) { + return pm_base_modes[active_device_id]; +} + +/** + * @brief Toggle pointing mode id on/off + * + * Will change tg_mode_id setting to POINTING_MODES_DEFAULT_MODE when toggling "off" + * + * @param[in] mode_id uint8_t + */ +void pointing_modes_toggle_mode(uint8_t mode_id) { + if (pointing_modes_get_toggled_mode() == mode_id) { + pm_base_modes[active_device_id] = POINTING_MODES_DEFAULT_MODE; + } else { + pm_base_modes[active_device_id] = mode_id; + } + if (pointing_modes_get_mode() != pm_base_modes[active_device_id]) pointing_modes_reset(); +} + +/** + * @brief update dependent parameters of pointing_mode + * + * Will update the direction and divisor values based on mode id and h, and v axis values + * + */ +void pointing_modes_update(void) { + pointing_modes_update_type(); + pointing_modes_update_direction(); + pointing_modes_update_divisor(); +} + +/** + * @brief Access current pointing mode direction + * + * @return uint8_t direction + */ +uint8_t pointing_modes_get_direction(void) { + return current_direction; +} + +/** + * @brief Access current pointing mode divisor + * + * @return mouse_xy_report_t divisor + */ +mouse_xy_report_t pointing_modes_get_divisor(void) { + return current_divisor; +} + +/** + * @brief get current mode type + * + * @return uint8_t mode_type + */ +uint8_t pointing_modes_get_type(void) { + return current_type; +} +/** + * @brief Access active device residuals + * + * @return pointing_modes_residuals_t + */ +pointing_modes_residuals_t pointing_modes_get_residuals(void) { + return pm_res[active_device_id]; +} + +/** + * @brief Modify active device residuals + * + * @param[in] residuals pointing_modes_residuals_t + */ +void pointing_modes_set_residuals(pointing_modes_residuals_t residuals) { + pm_res[active_device_id] = residuals; + return; +} + +/** + * @brief Core function to handle pointing mode task + * + * Meant to be implemented in pointing_device_task + * + * @param[in] mouse_report report_mouse_t + * + * @return mouse_report report_mouse_t + */ +report_mouse_t pointing_modes_device_task(report_mouse_t mouse_report) { + pointing_modes_update_type(); + pointing_modes_axes_conv(&mouse_report); + pointing_modes_update_direction(); + pointing_modes_update_divisor(); + pointing_modes_task(&mouse_report); + return mouse_report; +} + +// Callback functions ------------------------------------------------------------------------------------------ + +/** + * @brief User level processing of pointing device modes + * + * Used in pointing mode process allowing modification of mouse_report + * directly and returned bool controls the continued processing of pointing modes. + * + * NOTE: returning false will stop mode processing (for overwriting modes) + * + * @params mouse_report[in] pointer to report_mouse_t + * + * @return bool continue processing flag + */ +__attribute__((weak)) bool pointing_modes_task_user(report_mouse_t* mouse_report, pointing_modes_residuals_t* residuals) { + return true; // continue processing +} + +/** + * @brief Keyboard level processing of pointing device modes + * + * Used in pointing mode process allowing modification of mouse_report + * directly and returned bool controls the continued processing of pointing modes. + * + * NOTE: returning false will stop mode processing (for overwriting modes) + * + * @params mouse_report[in] pointer to report_mouse_t + * @params residuals[in] pointer to pointing_modes_residuals_t + * + * @return bool continue processing flag + */ +__attribute__((weak)) bool pointing_modes_task_kb(report_mouse_t* mouse_report, pointing_modes_residuals_t* residuals) { + return true; // continue processing +} + +/** + * @brief Weakly defined function adding of pointing mode divisors at user level + * + * Takes in mode id and direction allowing for divisor values to be + * determined based on these parameters + * + * @params mode_id[in] uint8_t + * @params direction[in] uint8_t + * + * @return divisor mouse_xy_report_t + */ +__attribute__((weak)) mouse_xy_report_t pointing_modes_get_divisor_user(uint8_t mode_id, uint8_t direction) { + return 0; // continue processing +} + +/** + * @brief Weakly defined function adding of pointing mode divisors at keyboard level + * + * Takes in mode id and direction allowing for divisor values to be + * determined based on these parameters + * + * @params mode_id[in] uint8_t + * @params direction[in] uint8_t + * + * @return divisor mouse_xy_report_t + */ +__attribute__((weak)) mouse_xy_report_t pointing_modes_get_divisor_kb(uint8_t mode_id, uint8_t direction) { + return 0; // continue processing +} + +/** + * @brief Weakly defined function for setting pointing mode type at the user level + * + * Takes in mode id and returns current pointing mode type (PMT_4WAY, PMT_DPAD, PMT_8WAY, PMT_HORI, PMT_VERT). + * Also mode options may be added to the mode type (PMO_HOLD, PMO_XINV, PMO_YINV) + * + * @params mode_id uint8_t + * + * @return pointing_modes_type uint8_t + */ +__attribute__((weak)) uint8_t pointing_modes_get_type_user(uint8_t mode_id) { + return PMT_4WAY; // continue processing +} + +/** + * @brief Weakly defined function for setting pointing mode type and options at the user level + * + * Takes in mode id and returns current pointing mode type (PMT_4WAY, PMT_DPAD, PMT_8WAY, PMT_HORI, PMT_VERT). + * Also mode options may be added to the mode type (PMO_HOLD, PMO_XINV, PMO_YINV) + * + * @params mode_id uint8_t + * + * @return pointing_modes_type uint8_t + */ +__attribute__((weak)) uint8_t pointing_modes_get_type_kb(uint8_t mode_id) { + return PMT_4WAY; // continue processing +} + +#endif // POINTING_DEVICE_MODES_ENABLE diff --git a/quantum/pointing_device/pointing_device_modes.h b/quantum/pointing_device/pointing_device_modes.h new file mode 100644 index 000000000000..25fcb099d456 --- /dev/null +++ b/quantum/pointing_device/pointing_device_modes.h @@ -0,0 +1,263 @@ +/* Copyright 2023 Alabastard (@Alabastard-64) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include "quantum.h" +#include "pointing_device.h" + +/* ----------Default Settings------------------------------------------------------------------------------------- */ +#ifndef POINTING_MODES_DEFAULT_MODE +# define POINTING_MODES_DEFAULT_MODE PM_NONE +#endif + +#ifndef POINTING_MODES_MAP_START +# define POINTING_MODES_MAP_START PM_SAFE_RANGE +#endif + +#ifndef POINTING_MODES_TAP_DELAY +# define POINTING_MODES_TAP_DELAY TAP_CODE_DELAY +#endif + +#ifndef POINTING_MODES_HOLD_DECAY_RATE +# define POINTING_MODES_HOLD_DECAY_RATE 2 +#endif + +#if defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED) +# ifndef POINTING_MODES_NUM_DEVICES +# define POINTING_MODES_NUM_DEVICES 2 +# endif +#else +# ifndef POINTING_MODES_NUM_DEVICES +# define POINTING_MODES_NUM_DEVICES 1 +# endif +#endif + +#ifdef POINTING_MODES_SINGLE_CONTROL +# define POINTING_MODES_DEVICE_CONTROL_COUNT 1 +#else +# define POINTING_MODES_DEVICE_CONTROL_COUNT POINTING_MODES_NUM_DEVICES +#endif + +#ifndef POINTING_MODES_DEFAULT_DEVICE +# define POINTING_MODES_DEFAULT_DEVICE 0 +#endif // POINTING_MODES_DEFAULT_DEVICE + +#ifdef POINTING_MODES_NUM_DIRECTIONS +# undefine POINTING_MODES_NUM_DIRECTIONS +#endif +#ifdef POINTING_MODES_8WAY_MAP_ENABLE +# define POINTING_MODES_NUM_DIRECTIONS 8 +#else +# define POINTING_MODES_NUM_DIRECTIONS 4 +#endif // POINTING_MODES_8WAY_MAP_ENABLE + +#ifndef POINTING_MODES_THRESHOLD +# define POINTING_MODES_THRESHOLD 0 +#endif + +/* ----------Default Divisors and Precision Settings-------------------------------------------------------------- */ +#ifndef POINTING_MODES_DEFAULT_DIVISOR +# define POINTING_MODES_DEFAULT_DIVISOR 32 +#endif +#ifndef POINTING_MODES_PRECISION_MIN +# define POINTING_MODES_PRECISION_MIN 1 +#endif +#ifndef POINTING_MODES_PRECISION_MAX +# define POINTING_MODES_PRECISION_MAX 4 +#endif +#ifndef POINTING_MODES_PRECISION_STEP +# define POINTING_MODES_PRECISION_STEP 2 +#endif +#ifndef POINTING_MODES_DRAG_DIVISOR +# define POINTING_MODES_DRAG_DIVISOR 4 +#endif + +/* ----------Error Checking -------------------------------------------------------------------------------------- */ +#ifdef MOUSE_EXTENDED_REPORT +# if (POINTING_MODES_DEFAULT_DIVISOR <= 0 || POINTING_MODES_DEFAULT_DIVISOR > 32767) +# error POINTING_MODES_DEFAULT_DIVISOR must be greater than 0 and less than 32767 +# endif +# if (POINTING_MODES_DRAG_DIVISOR <= 0 || POINTING_MODES_DRAG_DIVISOR > 32767) +# error POINTING_MODES_DRAG_DIVISOR must be greater than 0 and less than 32767 +# endif +# if (POINTING_MODES_PRECISION_MIN <= 0 || POINTING_MODES_PRECISION_MIN > 32767) +# error POINTING_MODES_PRECISION_MIN must be greater than 0 and less than 32767 +# endif +# if (POINTING_MODES_PRECISION_MAX <= 0 || POINTING_MODES_PRECISION_MAX > 32767) +# error POINTING_MODES_PRECISION_MAX must be greater than 0 and less than 32767 +# endif +# if (POINTING_MODES_PRECISION_STEP <= 0 || POINTING_MODES_PRECISION_STEP > 32767) +# error POINTING_MODES_PRECISION_STEP must be greater than 0 and less than 32767 +# endif +#else +# if (POINTING_MODES_DEFAULT_DIVISOR <= 0 || POINTING_MODES_DEFAULT_DIVISOR > 127) +# error POINTING_MODES_DEFAULT_DIVISOR must be greater than 0 and less than 127 +# endif +# if (POINTING_MODES_DRAG_DIVISOR <= 0 || POINTING_MODES_DRAG_DIVISOR > 127) +# error POINTING_MODES_DRAG_DIVISOR must be greater than 0 and less than 127 +# endif +# if (POINTING_MODES_PRECISION_MIN <= 0 || POINTING_MODES_PRECISION_MIN > 127) +# error POINTING_MODES_PRECISION_MIN must be greater than 0 and less than 127 +# endif +# if (POINTING_MODES_PRECISION_MAX <= 0 || POINTING_MODES_PRECISION_MAX > 127) +# error POINTING_MODES_PRECISION_MAX must be greater than 0 and less than 127 +# endif +# if (POINTING_MODES_PRECISION_STEP <= 0 || POINTING_MODES_PRECISION_STEP > 127) +# error POINTING_MODES_PRECISION_STEP must be greater than 0 and less than 127 +# endif +#endif + +#if POINTING_MODES_NUM_DEVICES < 1 +# pragma message "POINTING_MODES_NUM_DEVICES should be at least 1" +# error POINTING_MODES_NUM_DEVICES set too low +#endif +#if POINTING_MODES_NUM_DEVICES < 2 && ((defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED)) || defined(POINTING_MODES_SINGLE_CONTROL)) +# pragma message "POINTING_MODES_NUM_DEVICES should be at least 2 with SPLIT_POINTING_ENABLE & POINTING_DEVICE_COMBINED or POINTING_MODES_SINGLE_CONTROL defined" +# error POINTING_MODES_NUM_DEVICES set too low +#endif + +/* ----------Macros----------------------------------------------------------------------------------------------- */ +#define member_size(t, m) (sizeof(((t *)0)-> m)) + +/* ----------Enums------------------------------------------------------------------------------------------------ */ +enum pointing_modes_axis { + PM_X_AXIS = 0x00, + PM_Y_AXIS = 0x01, + PM_H_AXIS, + PM_V_AXIS, + PM_X_KEY, + PM_Y_KEY, + PM_XY_KEY +}; + +enum pointing_modes_directions { + PMD_NONE = 0x00, // [0000] 00 + PMD_DOWN = 0x01, // [0001] 01 + PMD_UP = 0x02, // [0010] 02 + PMD_LEFT = 0x04, // [0100] 04 + PMD_RIGHT = 0x08, // [1000] 08 + PMD_VERT = 0x03, // [0011] 03 + PMD_HORI = 0x0C, // [1100] 12 + PMD_DNLT = 0x05, // [0101] 05 + PMD_UPLT = 0x06, // [0110] 06 + PMD_DNRT = 0x09, // [1001] 09 + PMD_UPRT = 0x0A // [1010] 10 +}; + +#ifdef POINTING_MODES_8WAY_MAP_ENABLE +enum pointing_modes_8_key_map { + PMK_UPLEFT = 0x00, + PMK_UP = 0x01, + PMK_UPRIGHT = 0x02, + PMK_LEFT = 0x03, + PMK_RIGHT = 0x04, + PMK_DOWNLEFT = 0x05, + PMK_DOWN = 0x06, + PMK_DOWNRIGHT = 0x07, + PMK_NONE = 0x08 +}; + +#else +enum pointing_modes_4_key_map { + PMK_UP = 0x00, + PMK_LEFT = 0x01, + PMK_RIGHT = 0x02, + PMK_DOWN = 0x03, + PMK_NONE = 0x08, + // to ensure all codess are always defined + PMK_UPLEFT = 0x04, // unused + PMK_UPRIGHT = 0x05, // unused + PMK_DOWNLEFT = 0x06, // unused + PMK_DOWNRIGHT = 0x07, // unused +}; +#endif // POINTING_MODES_8WAY_MAP_ENABLE + +enum pointing_modes_types { + PMT_4WAY = 0x00, // [00000000] (DEFAULT) + PMT_HORI = 0x01, // [00000001] + PMT_VERT = 0x02, // [00000010] + PMT_8WAY = 0x03, // [00000011] + PMT_DPAD = 0x04, // [00000100] + PMT_MODES = 0x0F, // [00001111] (MAX) + // Options + PMO_TAP = 0x00, // [00000000] (DEFAULT) + PMO_HOLD = 0x10, // [00010000] + PMO_XINV = 0x20, // [00100000] + PMO_YINV = 0x40, // [01000000] + PMO_OPTS = 0xF0 // [11110000] +}; + +enum pointing_modes_devices { +#ifdef MASTER_RIGHT + PM_RIGHT_DEVICE = 0, + PM_LEFT_DEVICE +#else + PM_LEFT_DEVICE = 0, + PM_RIGHT_DEVICE +#endif +}; + +/* ----------Local Data Structures-------------------------------------------------------------------------------- */ +typedef struct { + mouse_xy_report_t x; + mouse_xy_report_t y; +} pointing_modes_residuals_t; + +typedef struct { + uint8_t v; + uint8_t h; +} pointing_modes_held_keys_t; + +/* ----------Controlling active device pointing mode-------------------------------------------------------------- */ +void pointing_modes_set_precision(mouse_xy_report_t precision); // set active device precision divisor +void pointing_modes_set_mode(uint8_t mode_id); // set active device mode to mode_id +void pointing_modes_set_residuals(pointing_modes_residuals_t residuals); // set active device residuals values +void pointing_modes_toggle_mode(uint8_t mode_id); // set toggle mode to mode_id or default if toggle_id==mode_id +mouse_xy_report_t pointing_modes_get_precision(void); // get active device precision +uint8_t pointing_modes_get_mode(void); // return active device mode_id +uint8_t pointing_modes_get_toggled_mode(void); // return active device toggle_id +pointing_modes_residuals_t pointing_modes_get_residuals(void); // return current residual values for active device +void pointing_modes_reset(void); // reset active device to base settings + +/* ----------Crontrolling pointing mode data---------------------------------------------------------------------- */ +void pointing_modes_update(void); // update direction & divisor from current mode id, x, y +mouse_xy_report_t pointing_modes_apply_divisor(uint8_t axis); // return quotient from applying divisor & precision based on axis and update the residuals +mouse_xy_report_t pointing_modes_get_divisor(void); // access current divisor value +uint8_t pointing_modes_get_direction(void); // access current direction value +uint8_t pointing_modes_get_type(void); // access current mode type + +/* ----------For Modifying and custom modes outside of maps------------------------------------------------------- */ +bool pointing_modes_task_kb(report_mouse_t* mouse_report, pointing_modes_residuals_t* residuals); // keyboard level +bool pointing_modes_task_user(report_mouse_t* mouse_report, pointing_modes_residuals_t* residuals); // user/keymap level + +/* ----------Callbacks for modifying pointing mode settings------------------------------------------------------- */ +uint8_t pointing_modes_get_type_kb(uint8_t mode_id); // setting mode type at keyboard level +uint8_t pointing_modes_get_type_user(uint8_t mode_id); // setting mode type at user level +mouse_xy_report_t pointing_modes_get_divisor_kb(uint8_t mode_id, uint8_t direction); // setting divisors at keyboard level +mouse_xy_report_t pointing_modes_get_divisor_user(uint8_t mode_id, uint8_t direction); // setting divisors at user/keymap level + +/* ----------Core functions (only used in custom pointing devices or key processing)------------------------------ */ +report_mouse_t pointing_modes_device_task(report_mouse_t mouse_report); // intercepts mouse_report (in pointing_device_task_* stack) + +/* ----------For multiple pointing devices------------------------------------------------------------------------ */ +uint8_t pointing_modes_get_active_device(void); // get active device id +void pointing_modes_set_active_device(uint8_t device_id); // set active device (PM_LEFT_SIDE, PM_RIGHT_SIDE, etc.) + +/* ----------External Constants----------------------------------------------------------------------------------- */ + +extern const uint16_t PROGMEM pointing_modes_maps[][POINTING_MODES_NUM_DIRECTIONS]; diff --git a/quantum/process_keycode/process_pointing_modes.c b/quantum/process_keycode/process_pointing_modes.c new file mode 100644 index 000000000000..1642092cebb7 --- /dev/null +++ b/quantum/process_keycode/process_pointing_modes.c @@ -0,0 +1,149 @@ +/* Copyright 2023 Alabastard (@Alabastard) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef POINTING_DEVICE_MODES_ENABLE + +# include "process_pointing_modes.h" + +/** + * @brief Handle pointing mode change keypress: Momentary + * + * Will set pointing mode id when key is pressed and reset on release + * + * NOTE: if pointing mode has changed since key down, reset is skipped + * + * @params mode_id[in] uint8_t + * @params pressed[in] bool + */ +void pointing_modes_key_momentary(uint8_t mode_id, bool pressed) { + if (pressed) { + pointing_modes_set_mode(mode_id); + } else if (pointing_modes_get_mode() == mode_id) { + // reset mode only if the current mode matches (in case mode has changed before release) + pointing_modes_reset(); + } +} + +/** + * @brief Handle pointing mode change keypress: Toggle + * + * Toggle pointing mode on key release of active device + * + * @params mode_id[in] uint8_t + * @params pressed[in] bool + */ +void pointing_modes_key_toggle(uint8_t mode_id, bool pressed) { + // only attempt to change mode on key release event (matches layer toggle behaviour) + if (!pressed) pointing_modes_toggle_mode(mode_id); +} + +/** + * @brief Handle keypress to change active device + * + * Set active device id to device[in] on key release + * + * @params device[in] uint8_t + * @params pressed[in] bool + */ +void pointing_modes_key_set_device(uint8_t device, bool pressed) { + if (!pressed) pointing_modes_set_active_device(device); +} + +/** + * @brief Handle keypress to change precision of active device + * + * @params precision[in] uint8_t + * @params pressed[in] bool + */ +void pointing_modes_key_set_precision(uint8_t precision, bool pressed) { + if (!pressed) {pointing_modes_set_precision(precision); + } +} + +/** + * @brief Core function to process pointing mode key records + * + * Only handles built in keyrecords and functions + * + * @params keycode[in] uint16_t + * @params record[in] keyrecord_t pointer + * + * @return should keycode processing continue bool + */ +bool process_pointing_modes_records(uint16_t keycode, keyrecord_t* record) { + switch (keycode) { + // handle built in keycods for bottom 16 pointing modes (0-15) + // momentary + case QK_POINTING_MODES_MO ... QK_POINTING_MODES_MO_MAX: + pointing_modes_key_momentary((keycode - QK_POINTING_MODES_MO) & (QK_POINTING_MODES_MO_MAX - QK_POINTING_MODES_MO), record->event.pressed); + return true; // allow further processing + // toggle + case QK_POINTING_MODES_TG ... QK_POINTING_MODES_TG_MAX: + pointing_modes_key_toggle((keycode - QK_POINTING_MODES_TG) & (QK_POINTING_MODES_TG_MAX - QK_POINTING_MODES_TG), record->event.pressed); + return true; // allow further processing + + // utils: Cycle precision + case QK_PM_CYCLE_PRECISION: + if (!record->event.pressed) { + uint8_t current_precision = pointing_modes_get_precision(); + if (get_mods() & MOD_MASK_SHIFT) { + if (current_precision <= POINTING_MODES_PRECISION_STEP) { + if (current_precision > POINTING_MODES_PRECISION_MIN) { + pointing_modes_set_precision(POINTING_MODES_PRECISION_MIN); + return false; + } + pointing_modes_set_precision(POINTING_MODES_PRECISION_MAX); + return false; + } + pointing_modes_set_precision(current_precision - POINTING_MODES_PRECISION_STEP); + return false; + } + if (current_precision < POINTING_MODES_PRECISION_STEP) { + pointing_modes_set_precision(POINTING_MODES_PRECISION_STEP); + return false; + } + pointing_modes_set_precision(current_precision + POINTING_MODES_PRECISION_STEP); + return false; + } + return true; + +# if (POINTING_MODES_NUM_DEVICES > 1) + // utils: Cycle devices + case QK_PM_CYCLE_DEVICES: + if (!record->event.pressed) { + if (get_mods() & MOD_MASK_SHIFT) { + if(pointing_modes_get_active_device() == 0) { + pointing_modes_set_active_device(POINTING_MODES_NUM_DEVICES - 1); + return false; + } + pointing_modes_set_active_device(pointing_modes_get_active_device() - 1); + return false; // prevent further processing + } + pointing_modes_set_active_device(pointing_modes_get_active_device() + 1); + return false; // prevent further processing + } + return true; // allow further processing + // cut the below two functions + // utils: DEVICE RIGHT +# endif + + // end + default: + return true; + } +} + +#endif // POINTING_DEVICE_MODES_ENABLE diff --git a/quantum/process_keycode/process_pointing_modes.h b/quantum/process_keycode/process_pointing_modes.h new file mode 100644 index 000000000000..1578dbaee983 --- /dev/null +++ b/quantum/process_keycode/process_pointing_modes.h @@ -0,0 +1,29 @@ +/* Copyright 2023 Alabastard (@Alabastard-64) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "quantum.h" +#include "pointing_device_modes.h" + +/* ----------Handling pointing mode id changes on keypress-------------------------------------------------------- */ +void pointing_modes_key_momentary(uint8_t mode_id, bool pressed); // momentary change of pointing mode while button is held +void pointing_modes_key_toggle(uint8_t mode_id, bool pressed); // toggle pointing mode on/off on keypress +void pointing_modes_key_set_device(uint8_t device, bool pressed); // set active device +void pointing_modes_key_set_precision(uint8_t precision, bool pressed); // set precision of active device + +/* ----------For builtin keycodes--------------------------------------------------------------------------------- */ +bool process_pointing_modes_records(uint16_t keyrecord, keyrecord_t* record); // handle processing of built in keyrecords (in process_record stack) diff --git a/quantum/quantum.c b/quantum/quantum.c index b63300add8e6..b7851c35b7cb 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -408,6 +408,9 @@ bool process_record_quantum(keyrecord_t *record) { #ifdef TRI_LAYER_ENABLE process_tri_layer(keycode, record) && #endif +#if defined(POINTING_DEVICE_ENABLE) && defined(POINTING_DEVICE_MODES_ENABLE) + process_pointing_modes_records(keycode, record) && +#endif #if !defined(NO_ACTION_LAYER) process_default_layer(keycode, record) && #endif diff --git a/quantum/quantum.h b/quantum/quantum.h index 9db88a54d4ba..9c43a20ee6c3 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -212,6 +212,9 @@ extern layer_state_t layer_state; #ifdef POINTING_DEVICE_ENABLE # include "pointing_device.h" +# ifdef POINTING_DEVICE_MODES_ENABLE +# include "process_pointing_modes.h" +# endif #endif #ifdef MOUSEKEY_ENABLE diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index bcaf94af8b9d..eb1f19f3230c 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -226,4 +226,23 @@ #define SQ_R(n) (n < SEQUENCER_RESOLUTIONS ? SEQUENCER_RESOLUTION_MIN + n : KC_NO) #define SQ_T(n) (n < SEQUENCER_TRACKS ? SEQUENCER_TRACK_MIN + n : KC_NO) +// Pointing device mode key macros +// Momentary scroll mode +#define PM_MO(pm) (MIN((pm), (QK_POINTING_MODES_MO_MAX - QK_POINTING_MODES_MO)) + QK_POINTING_MODES_MO) +// Toggle default scroll mode +#define PM_TG(pm) (MIN((pm), (QK_POINTING_MODES_TG_MAX - QK_POINTING_MODES_TG)) + QK_POINTING_MODES_TG) + +// Default Pointing device pointing modes +enum pointing_device_mode_list { + PM_NONE = 0, + PM_DRAG, + // safe range for custom modes with built in keycodes + PM_SAFE_RANGE, + // range for custom modes requiring custom activation/new keycodes + PM_ADVANCED_RANGE_START = ((QK_POINTING_MODES_MO_MAX - QK_POINTING_MODES_MO) + (QK_POINTING_MODES_TG_MAX - QK_POINTING_MODES_TG) + 1) / 2 + 1 +}; + +// pointing mode aliases +#define PM_DRG PM_DRAG + #include "quantum_keycodes_legacy.h" diff --git a/tests/test_common/keycode_table.cpp b/tests/test_common/keycode_table.cpp index 26d0919619fc..bb0467152f49 100644 --- a/tests/test_common/keycode_table.cpp +++ b/tests/test_common/keycode_table.cpp @@ -252,6 +252,9 @@ std::map KEYCODE_ID_TABLE = { {KC_RIGHT_SHIFT, "KC_RIGHT_SHIFT"}, {KC_RIGHT_ALT, "KC_RIGHT_ALT"}, {KC_RIGHT_GUI, "KC_RIGHT_GUI"}, + {QK_PM_CYCLE_DEVICES, "QK_PM_CYCLE_DEVICES"}, + {QK_PM_DEVICE_RIGHT, "QK_PM_DEVICE_RIGHT"}, + {QK_PM_DEVICE_LEFT, "QK_PM_DEVICE_LEFT"}, {QK_SWAP_HANDS_TOGGLE, "QK_SWAP_HANDS_TOGGLE"}, {QK_SWAP_HANDS_TAP_TOGGLE, "QK_SWAP_HANDS_TAP_TOGGLE"}, {QK_SWAP_HANDS_MOMENTARY_ON, "QK_SWAP_HANDS_MOMENTARY_ON"}, diff --git a/tmk_core/protocol/report.h b/tmk_core/protocol/report.h index d854f51d5c45..e82014d4b054 100644 --- a/tmk_core/protocol/report.h +++ b/tmk_core/protocol/report.h @@ -29,7 +29,7 @@ along with this program. If not, see . // clang-format off /* HID report IDs */ -enum hid_report_ids { +enum hid_report_ids { REPORT_ID_ALL = 0, REPORT_ID_KEYBOARD = 1, REPORT_ID_MOUSE,