Add 3 speed mousekey movement option (#2246)

This allows for constant, non-accelerated mouse movement, while retaining the original functionality. Configurable by a define.
This commit is contained in:
Jon Arintok 2019-03-27 04:34:33 +02:00 committed by Drashna Jaelre
parent 509668ca7c
commit 346cbd8816
3 changed files with 373 additions and 188 deletions

View File

@ -1,28 +1,27 @@
# Mousekeys
Mousekeys is a feature that allows you to emulate a mouse using your keyboard. You can move the pointer around, click up to 5 buttons, and even scroll in all 4 directions. QMK uses the same algorithm as the X Window System MouseKeysAccel feature. You can read more about it [on Wikipedia](https://en.wikipedia.org/wiki/Mouse_keys).
Mousekeys is a feature that allows you to emulate a mouse using your keyboard. You can move the pointer around, click up to 5 buttons, and even scroll in all 4 directions.
## Adding Mousekeys to a Keymap
There are 2 ways to define how the mousekeys behave, using "[auto-accelerating](#configuring-the-behavior-of-mousekeys-with-auto-accelerated-movement)" or "[3-speed constant](#configuring-the-behavior-of-mousekeys-with-3-speed-constant-movement)" behavior.
There are two steps to adding Mousekeys support to your keyboard. You must enable support in the `rules.mk` file and you must map mouse actions to keys on your keyboard.
In either case, you will need to enable mousekeys in your makefile,
and add the relevant [keycodes](#mapping-mouse-actions-to-keyboard-keys) to your keymap.
### Adding Mousekeys Support in the `rules.mk`
#### Enable Mousekeys
To add support for Mousekeys you simply need to add a single line to your keymap's `rules.mk`:
To enable the mousekey functionality, add the following line to your keymap's `rules.mk`:
```
MOUSEKEY_ENABLE = yes
```
You can see an example here: https://github.com/qmk/qmk_firmware/blob/master/keyboards/clueboard/66/keymaps/mouse_keys/rules.mk
### Mapping Mouse Actions to Keyboard Keys
#### Mapping Mouse Actions to Keyboard Keys
You can use these keycodes within your keymap to map button presses to mouse actions:
|Key |Aliases |Description |
|----------------|---------|---------------------------|
|----------------|---------|-----------------------------------|
|`KC_MS_UP` |`KC_MS_U`|Mouse Cursor Up |
|`KC_MS_DOWN` |`KC_MS_D`|Mouse Cursor Down |
|`KC_MS_LEFT` |`KC_MS_L`|Mouse Cursor Left |
@ -36,13 +35,14 @@ You can use these keycodes within your keymap to map button presses to mouse act
|`KC_MS_WH_DOWN` |`KC_WH_D`|Mouse Wheel Down |
|`KC_MS_WH_LEFT` |`KC_WH_L`|Mouse Wheel Left |
|`KC_MS_WH_RIGHT`|`KC_WH_R`|Mouse Wheel Right |
|`KC_MS_ACCEL0` |`KC_ACL0`|Set mouse acceleration to 0|
|`KC_MS_ACCEL1` |`KC_ACL1`|Set mouse acceleration to 1|
|`KC_MS_ACCEL2` |`KC_ACL2`|Set mouse acceleration to 2|
|`KC_MS_ACCEL0` |`KC_ACL0`|Set mouse acceleration to 0(slow) |
|`KC_MS_ACCEL1` |`KC_ACL1`|Set mouse acceleration to 1(medium)|
|`KC_MS_ACCEL2` |`KC_ACL2`|Set mouse acceleration to 2(fast) |
You can see an example in the `_ML` here: https://github.com/qmk/qmk_firmware/blob/master/keyboards/clueboard/66/keymaps/mouse_keys/keymap.c#L46
## Configuring the Behavior of Mousekeys
## Configuring the Behavior of Mousekeys with auto-accelerated movement
This behavior is intended to emulate the X Window System MouseKeysAccel feature. You can read more about it [on Wikipedia](https://en.wikipedia.org/wiki/Mouse_keys).
The default speed for controlling the mouse with the keyboard is intentionally slow. You can adjust these parameters by adding these settings to your keymap's `config.h` file. All times are specified in milliseconds (ms).
@ -55,27 +55,60 @@ The default speed for controlling the mouse with the keyboard is intentionally s
#define MOUSEKEY_WHEEL_TIME_TO_MAX 40
```
### `MOUSEKEY_DELAY`
#### `MOUSEKEY_DELAY`
When one of the mouse movement buttons is pressed this setting is used to define the delay between that button press and the mouse cursor moving. Some people find that small movements are impossible if this setting is too low, while settings that are too high feel sluggish.
### `MOUSEKEY_INTERVAL`
#### `MOUSEKEY_INTERVAL`
When a movement key is held down this specifies how long to wait between each movement report. Lower settings will translate into an effectively higher mouse speed.
### `MOUSEKEY_MAX_SPEED`
#### `MOUSEKEY_MAX_SPEED`
As a movement key is held down the speed of the mouse cursor will increase until it reaches `MOUSEKEY_MAX_SPEED`.
### `MOUSEKEY_TIME_TO_MAX`
#### `MOUSEKEY_TIME_TO_MAX`
How long you want to hold down a movement key for until `MOUSEKEY_MAX_SPEED` is reached. This controls how quickly your cursor will accelerate.
### `MOUSEKEY_WHEEL_MAX_SPEED`
#### `MOUSEKEY_WHEEL_MAX_SPEED`
The top speed for scrolling movements.
### `MOUSEKEY_WHEEL_TIME_TO_MAX`
#### `MOUSEKEY_WHEEL_TIME_TO_MAX`
How long you want to hold down a scroll key for until `MOUSEKEY_WHEEL_MAX_SPEED` is reached. This controls how quickly your scrolling will accelerate.
## Configuring the Behavior of Mousekeys with 3-speed constant movement
In your keymap's `config.h`, you must add the line:
```
#define MK_3_SPEED
```
Then you can precisely define 3 different speeds for both the cursor and the mouse wheel, and also whether speed selection is momentary or tap-to-select.
For each speed, you can specify how many milliseconds you want between reports(interval), and how far you want to it to move per report(offset).
For example:
```
#define MK_3_SPEED
#define MK_MOMENTARY_ACCEL // comment this out for tap-to-select acceleration
// cursor speeds:
#define MK_C_OFFSET_SLOW 1 // pixels
#define MK_C_INTERVAL_SLOW 100 // milliseconds
#define MK_C_OFFSET_MED 4
#define MK_C_INTERVAL_MED 16
#define MK_C_OFFSET_FAST 12
#define MK_C_INTERVAL_FAST 16
// scroll wheel speeds:
#define MK_W_OFFSET_SLOW 1 // wheel clicks
#define MK_W_INTERVAL_SLOW 400 // milliseconds
#define MK_W_OFFSET_MED 1
#define MK_W_INTERVAL_MED 200
#define MK_W_OFFSET_FAST 1
#define MK_W_INTERVAL_FAST 100
```
Medium values will be used as the default or unmodified speed.
The speed at which both the cursor and scrolling move can be selected with KC_ACL0, KC_ACL1, KC_ACL2 for slow, medium, and fast. However, if you leave MK_MOMENTARY_ACCEL defined then there is no need to ever send KC_ACL1, since that will be the unmodified speed.

View File

@ -23,13 +23,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "debug.h"
#include "mousekey.h"
inline int8_t times_inv_sqrt2(int8_t x) {
// 181/256 is pretty close to 1/sqrt(2)
// 0.70703125 0.707106781
// 1 too small for x=99 and x=198
// This ends up being a mult and discard lower 8 bits
return (x * 181) >> 8;
}
static report_mouse_t mouse_report = {};
static uint8_t mousekey_repeat = 0;
static uint8_t mousekey_accel = 0;
static report_mouse_t mouse_report = {0};
static void mousekey_debug(void);
static uint8_t mousekey_accel = 0;
static uint8_t mousekey_repeat = 0;
static uint16_t last_timer = 0;
#ifndef MK_3_SPEED
/*
@ -52,20 +63,7 @@ uint8_t mk_time_to_max = MOUSEKEY_TIME_TO_MAX;
uint8_t mk_wheel_max_speed = MOUSEKEY_WHEEL_MAX_SPEED;
uint8_t mk_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX;
static uint16_t last_timer = 0;
inline int8_t times_inv_sqrt2(int8_t x)
{
// 181/256 is pretty close to 1/sqrt(2)
// 0.70703125 0.707106781
// 1 too small for x=99 and x=198
// This ends up being a mult and discard lower 8 bits
return (x * 181) >> 8;
}
static uint8_t move_unit(void)
{
static uint8_t move_unit(void) {
uint16_t unit;
if (mousekey_accel & (1<<0)) {
unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed)/4;
@ -83,8 +81,7 @@ static uint8_t move_unit(void)
return (unit > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : (unit == 0 ? 1 : unit));
}
static uint8_t wheel_unit(void)
{
static uint8_t wheel_unit(void) {
uint16_t unit;
if (mousekey_accel & (1<<0)) {
unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed)/4;
@ -102,41 +99,31 @@ static uint8_t wheel_unit(void)
return (unit > MOUSEKEY_WHEEL_MAX ? MOUSEKEY_WHEEL_MAX : (unit == 0 ? 1 : unit));
}
void mousekey_task(void)
{
if (timer_elapsed(last_timer) < (mousekey_repeat ? mk_interval : mk_delay*10))
void mousekey_task(void) {
if (timer_elapsed(last_timer) < (mousekey_repeat ? mk_interval : mk_delay*10)) {
return;
if (mouse_report.x == 0 && mouse_report.y == 0 && mouse_report.v == 0 && mouse_report.h == 0)
}
if (mouse_report.x == 0 && mouse_report.y == 0 && mouse_report.v == 0 && mouse_report.h == 0) {
return;
if (mousekey_repeat != UINT8_MAX)
mousekey_repeat++;
}
if (mousekey_repeat != UINT8_MAX) mousekey_repeat++;
if (mouse_report.x > 0) mouse_report.x = move_unit();
if (mouse_report.x < 0) mouse_report.x = move_unit() * -1;
if (mouse_report.y > 0) mouse_report.y = move_unit();
if (mouse_report.y < 0) mouse_report.y = move_unit() * -1;
/* diagonal move [1/sqrt(2)] */
if (mouse_report.x && mouse_report.y) {
mouse_report.x = times_inv_sqrt2(mouse_report.x);
mouse_report.x = mouse_report.x == 0 ? 1 : mouse_report.x;
mouse_report.y = times_inv_sqrt2(mouse_report.y);
mouse_report.y = mouse_report.y == 0 ? 1 : mouse_report.y;
}
if (mouse_report.v > 0) mouse_report.v = wheel_unit();
if (mouse_report.v < 0) mouse_report.v = wheel_unit() * -1;
if (mouse_report.h > 0) mouse_report.h = wheel_unit();
if (mouse_report.h < 0) mouse_report.h = wheel_unit() * -1;
mousekey_send();
}
void mousekey_on(uint8_t code)
{
void mousekey_on(uint8_t code) {
if (code == KC_MS_UP) mouse_report.y = move_unit() * -1;
else if (code == KC_MS_DOWN) mouse_report.y = move_unit();
else if (code == KC_MS_LEFT) mouse_report.x = move_unit() * -1;
@ -155,8 +142,7 @@ void mousekey_on(uint8_t code)
else if (code == KC_MS_ACCEL2) mousekey_accel |= (1<<2);
}
void mousekey_off(uint8_t code)
{
void mousekey_off(uint8_t code) {
if (code == KC_MS_UP && mouse_report.y < 0) mouse_report.y = 0;
else if (code == KC_MS_DOWN && mouse_report.y > 0) mouse_report.y = 0;
else if (code == KC_MS_LEFT && mouse_report.x < 0) mouse_report.x = 0;
@ -173,27 +159,153 @@ void mousekey_off(uint8_t code)
else if (code == KC_MS_ACCEL0) mousekey_accel &= ~(1<<0);
else if (code == KC_MS_ACCEL1) mousekey_accel &= ~(1<<1);
else if (code == KC_MS_ACCEL2) mousekey_accel &= ~(1<<2);
if (mouse_report.x == 0 && mouse_report.y == 0 && mouse_report.v == 0 && mouse_report.h == 0)
mousekey_repeat = 0;
}
void mousekey_send(void)
{
#else /* #ifndef MK_3_SPEED */
enum {
mkspd_slow,
mkspd_med,
mkspd_fast,
mkspd_COUNT
};
static uint8_t mk_speed = mkspd_med;
#ifdef MK_MOMENTARY_ACCEL
static uint8_t mkspd_DEFAULT = mkspd_med;
#endif
static uint16_t last_timer_c = 0;
static uint16_t last_timer_w = 0;
uint16_t c_offsets[mkspd_COUNT] = {
MK_C_OFFSET_SLOW, MK_C_OFFSET_MED, MK_C_OFFSET_FAST
};
uint16_t c_intervals[mkspd_COUNT] = {
MK_C_INTERVAL_SLOW, MK_C_INTERVAL_MED, MK_C_INTERVAL_FAST
};
uint16_t w_offsets[mkspd_COUNT] = {
MK_W_OFFSET_SLOW, MK_W_OFFSET_MED, MK_W_OFFSET_FAST
};
uint16_t w_intervals[mkspd_COUNT] = {
MK_W_INTERVAL_SLOW, MK_W_INTERVAL_MED, MK_W_INTERVAL_FAST
};
void mousekey_task(void) {
// report cursor and scroll movement independently
report_mouse_t const tmpmr = mouse_report;
if ((mouse_report.x || mouse_report.y) && timer_elapsed(last_timer_c) > c_intervals[mk_speed]) {
mouse_report.h = 0;
mouse_report.v = 0;
mousekey_send();
last_timer_c = last_timer;
mouse_report = tmpmr;
}
if ((mouse_report.h || mouse_report.v) && timer_elapsed(last_timer_w) > w_intervals[mk_speed]) {
mouse_report.x = 0;
mouse_report.y = 0;
mousekey_send();
last_timer_w = last_timer;
mouse_report = tmpmr;
}
}
void adjust_speed(void) {
uint16_t const c_offset = c_offsets[mk_speed];
uint16_t const w_offset = w_offsets[mk_speed];
if (mouse_report.x > 0) mouse_report.x = c_offset;
if (mouse_report.x < 0) mouse_report.x = c_offset * -1;
if (mouse_report.y > 0) mouse_report.y = c_offset;
if (mouse_report.y < 0) mouse_report.y = c_offset * -1;
if (mouse_report.h > 0) mouse_report.h = w_offset;
if (mouse_report.h < 0) mouse_report.h = w_offset * -1;
if (mouse_report.v > 0) mouse_report.v = w_offset;
if (mouse_report.v < 0) mouse_report.v = w_offset * -1;
// adjust for diagonals
if (mouse_report.x && mouse_report.y) {
mouse_report.x = times_inv_sqrt2(mouse_report.x);
mouse_report.y = times_inv_sqrt2(mouse_report.y);
}
if (mouse_report.h && mouse_report.v) {
mouse_report.h = times_inv_sqrt2(mouse_report.h);
mouse_report.v = times_inv_sqrt2(mouse_report.v);
}
}
void mousekey_on(uint8_t code) {
uint16_t const c_offset = c_offsets[mk_speed];
uint16_t const w_offset = w_offsets[mk_speed];
uint8_t const old_speed = mk_speed;
if (code == KC_MS_UP) mouse_report.y = c_offset * -1;
else if (code == KC_MS_DOWN) mouse_report.y = c_offset;
else if (code == KC_MS_LEFT) mouse_report.x = c_offset * -1;
else if (code == KC_MS_RIGHT) mouse_report.x = c_offset;
else if (code == KC_MS_WH_UP) mouse_report.v = w_offset;
else if (code == KC_MS_WH_DOWN) mouse_report.v = w_offset * -1;
else if (code == KC_MS_WH_LEFT) mouse_report.h = w_offset * -1;
else if (code == KC_MS_WH_RIGHT) mouse_report.h = w_offset;
else if (code == KC_MS_BTN1) mouse_report.buttons |= MOUSE_BTN1;
else if (code == KC_MS_BTN2) mouse_report.buttons |= MOUSE_BTN2;
else if (code == KC_MS_BTN3) mouse_report.buttons |= MOUSE_BTN3;
else if (code == KC_MS_BTN4) mouse_report.buttons |= MOUSE_BTN4;
else if (code == KC_MS_BTN5) mouse_report.buttons |= MOUSE_BTN5;
else if (code == KC_MS_ACCEL0) mk_speed = mkspd_slow;
else if (code == KC_MS_ACCEL1) mk_speed = mkspd_med;
else if (code == KC_MS_ACCEL2) mk_speed = mkspd_fast;
if (mk_speed != old_speed) adjust_speed();
}
void mousekey_off(uint8_t code) {
#ifdef MK_MOMENTARY_ACCEL
uint8_t const old_speed = mk_speed;
#endif
if (code == KC_MS_UP && mouse_report.y < 0) mouse_report.y = 0;
else if (code == KC_MS_DOWN && mouse_report.y > 0) mouse_report.y = 0;
else if (code == KC_MS_LEFT && mouse_report.x < 0) mouse_report.x = 0;
else if (code == KC_MS_RIGHT && mouse_report.x > 0) mouse_report.x = 0;
else if (code == KC_MS_WH_UP && mouse_report.v > 0) mouse_report.v = 0;
else if (code == KC_MS_WH_DOWN && mouse_report.v < 0) mouse_report.v = 0;
else if (code == KC_MS_WH_LEFT && mouse_report.h < 0) mouse_report.h = 0;
else if (code == KC_MS_WH_RIGHT && mouse_report.h > 0) mouse_report.h = 0;
else if (code == KC_MS_BTN1) mouse_report.buttons &= ~MOUSE_BTN1;
else if (code == KC_MS_BTN2) mouse_report.buttons &= ~MOUSE_BTN2;
else if (code == KC_MS_BTN3) mouse_report.buttons &= ~MOUSE_BTN3;
else if (code == KC_MS_BTN4) mouse_report.buttons &= ~MOUSE_BTN4;
else if (code == KC_MS_BTN5) mouse_report.buttons &= ~MOUSE_BTN5;
#ifdef MK_MOMENTARY_ACCEL
else if (code == KC_MS_ACCEL0) mk_speed = mkspd_DEFAULT;
else if (code == KC_MS_ACCEL1) mk_speed = mkspd_DEFAULT;
else if (code == KC_MS_ACCEL2) mk_speed = mkspd_DEFAULT;
if (mk_speed != old_speed) adjust_speed();
#endif
}
#endif /* #ifndef MK_3_SPEED */
void mousekey_send(void) {
mousekey_debug();
host_mouse_send(&mouse_report);
last_timer = timer_read();
}
void mousekey_clear(void)
{
void mousekey_clear(void) {
mouse_report = (report_mouse_t){};
mousekey_repeat = 0;
mousekey_accel = 0;
}
static void mousekey_debug(void)
{
static void mousekey_debug(void) {
if (!debug_mouse) return;
print("mousekey [btn|x y v h](rep/acl): [");
phex(mouse_report.buttons); print("|");

View File

@ -17,10 +17,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef MOUSEKEY_H
#define MOUSEKEY_H
#endif
#include <stdbool.h>
#include "host.h"
#ifndef MK_3_SPEED
/* max value on report descriptor */
#ifndef MOUSEKEY_MOVE_MAX
@ -60,6 +62,47 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MOUSEKEY_WHEEL_TIME_TO_MAX 40
#endif
#else /* #ifndef MK_3_SPEED */
#ifndef MK_C_OFFSET_SLOW
#define MK_C_OFFSET_SLOW 1
#endif
#ifndef MK_C_INTERVAL_SLOW
#define MK_C_INTERVAL_SLOW 100
#endif
#ifndef MK_C_OFFSET_MED
#define MK_C_OFFSET_MED 4
#endif
#ifndef MK_C_INTERVAL_MED
#define MK_C_INTERVAL_MED 16
#endif
#ifndef MK_C_OFFSET_FAST
#define MK_C_OFFSET_FAST 12
#endif
#ifndef MK_C_INTERVAL_FAST
#define MK_C_INTERVAL_FAST 16
#endif
#ifndef MK_W_OFFSET_SLOW
#define MK_W_OFFSET_SLOW 1
#endif
#ifndef MK_W_INTERVAL_SLOW
#define MK_W_INTERVAL_SLOW 400
#endif
#ifndef MK_W_OFFSET_MED
#define MK_W_OFFSET_MED 1
#endif
#ifndef MK_W_INTERVAL_MED
#define MK_W_INTERVAL_MED 200
#endif
#ifndef MK_W_OFFSET_FAST
#define MK_W_OFFSET_FAST 1
#endif
#ifndef MK_W_INTERVAL_FAST
#define MK_W_INTERVAL_FAST 100
#endif
#endif /* #ifndef MK_3_SPEED */
#ifdef __cplusplus
extern "C" {
@ -72,7 +115,6 @@ extern uint8_t mk_time_to_max;
extern uint8_t mk_wheel_max_speed;
extern uint8_t mk_wheel_time_to_max;
void mousekey_task(void);
void mousekey_on(uint8_t code);
void mousekey_off(uint8_t code);
@ -82,5 +124,3 @@ void mousekey_send(void);
#ifdef __cplusplus
}
#endif
#endif