Skip to content

Commit

Permalink
Implement kinetic mouse movement algorithm (#6739)
Browse files Browse the repository at this point in the history
* Implement kinetic mouse movement algorithm

* Adjust mouse wheel speed

* Remove unused math.h include

* Wrap mouse_timer definition in ifdef

* Replace double space by single space

* Clarify documentation of kinetic mouse speed

Co-Authored-By: lf <software@lfcode.ca>

* Clarify documentation of kinetic mouse speed

Co-Authored-By: lf <software@lfcode.ca>

* Remove superfluous definition of speed

* fix(variable): remove unused variable

Co-authored-by: lf <software@lfcode.ca>
  • Loading branch information
jceb and lf- authored Dec 24, 2020
1 parent 4551e57 commit 010271d
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 3 deletions.
28 changes: 27 additions & 1 deletion docs/feature_mouse_keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ In your keymap you can use the following keycodes to map key presses to mouse ac
Mouse keys supports three different modes to move the cursor:

* **Accelerated (default):** Holding movement keys accelerates the cursor until it reaches its maximum speed.
* **Kinetic:** Holding movement keys accelerates the cursor with its speed following a quadratic curve until it reaches its maximum speed.
* **Constant:** Holding movement keys moves the cursor at constant speeds.
* **Combined:** Holding movement keys accelerates the cursor until it reaches its maximum speed, but holding acceleration and movement keys simultaneously moves the cursor at constant speeds.

Expand All @@ -56,7 +57,8 @@ This is the default mode. You can adjust the cursor and scrolling acceleration u
|Define |Default|Description |
|----------------------------|-------|---------------------------------------------------------|
|`MOUSEKEY_DELAY` |300 |Delay between pressing a movement key and cursor movement|
|`MOUSEKEY_INTERVAL` |50 |Time between cursor movements |
|`MOUSEKEY_INTERVAL` |50 |Time between cursor movements in milliseconds |
|`MOUSEKEY_MOVE_DELTA` |5 |Step size |
|`MOUSEKEY_MAX_SPEED` |10 |Maximum cursor speed at which acceleration stops |
|`MOUSEKEY_TIME_TO_MAX` |20 |Time until maximum cursor speed is reached |
|`MOUSEKEY_WHEEL_DELAY` |300 |Delay between pressing a wheel key and wheel movement |
Expand All @@ -73,6 +75,30 @@ Tips:

Cursor acceleration 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).

### Kinetic Mode

This is an extension of the accelerated mode. The kinetic mode uses a quadratic curve on the cursor speed which allows precise movements at the beginning and allows to cover large distances by increasing cursor speed quickly thereafter. You can adjust the cursor and scrolling acceleration using the following settings in your keymap’s `config.h` file:

|Define |Default |Description |
|--------------------------------------|---------|---------------------------------------------------------------|
|`MK_KINETIC_SPEED` |undefined|Enable kinetic mode |
|`MOUSEKEY_DELAY` |8 |Delay between pressing a movement key and cursor movement |
|`MOUSEKEY_INTERVAL` |8 |Time between cursor movements in milliseconds |
|`MOUSEKEY_MOVE_DELTA` |25 |Step size for accelerating from initial to base speed |
|`MOUSEKEY_INITIAL_SPEED` |100 |Initial speed of the cursor in pixel per second |
|`MOUSEKEY_BASE_SPEED` |1000 |Maximum cursor speed at which acceleration stops |
|`MOUSEKEY_DECELERATED_SPEED` |400 |Decelerated cursor speed |
|`MOUSEKEY_ACCELERATED_SPEED` |3000 |Accelerated cursor speed |
|`MOUSEKEY_WHEEL_INITIAL_MOVEMENTS` |16 |Initial number of movements of the mouse wheel |
|`MOUSEKEY_WHEEL_BASE_MOVEMENTS` |32 |Maximum number of movements at which acceleration stops |
|`MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS`|48 |Accelerated wheel movements |
|`MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS`|8 |Decelerated wheel movements |

Tips:

* The smoothness of the cursor movement depends on the `MOUSEKEY_INTERVAL` setting. The shorter the interval is set the smoother the movement will be. Setting the value too low makes the cursor unresponsive. Lower settings are possible if the micro processor is fast enough. For example: At an interval of `8` milliseconds, `125` movements per second will be initiated. With a base speed of `1000` each movement will move the cursor by `8` pixels.
* Mouse wheel movements are implemented differently from cursor movements. While it's okay for the cursor to move multiple pixels at once for the mouse wheel this would lead to jerky movements. Instead, the mouse wheel operates at step size `1`. Setting mouse wheel speed is done by adjusting the number of wheel movements per second.

### Constant mode

In this mode you can define multiple different speeds for both the cursor and the mouse wheel. There is no acceleration. `KC_ACL0`, `KC_ACL1` and `KC_ACL2` change the cursor and scroll speed to their respective setting.
Expand Down
82 changes: 80 additions & 2 deletions tmk_core/common/mousekey.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,17 @@ static void mousekey_debug(void);
static uint8_t mousekey_accel = 0;
static uint8_t mousekey_repeat = 0;
static uint8_t mousekey_wheel_repeat = 0;
#ifdef MK_KINETIC_SPEED
static uint16_t mouse_timer = 0;
#endif

#ifndef MK_3_SPEED

static uint16_t last_timer_c = 0;
static uint16_t last_timer_w = 0;

/*
* Mouse keys acceleration algorithm
* Mouse keys acceleration algorithm
* http://en.wikipedia.org/wiki/Mouse_keys
*
* speed = delta * max_speed * (repeat / time_to_max)**((1000+curve)/1000)
Expand Down Expand Up @@ -105,6 +108,69 @@ static uint8_t wheel_unit(void) {
}

# else /* #ifndef MK_COMBINED */
# ifndef MK_KINETIC_SPEED

/*
* Kinetic movement acceleration algorithm
*
* current speed = I + A * T/50 + A * 0.5 * T^2 | maximum B
*
* T: time since the mouse movement started
* E: mouse events per second (set through MOUSEKEY_INTERVAL, UHK sends 250, the
* pro micro on my Signum 3.0 sends only 125!)
* I: initial speed at time 0
* A: acceleration
* B: base mouse travel speed
*/
const uint16_t mk_accelerated_speed = MOUSEKEY_ACCELERATED_SPEED;
const uint16_t mk_base_speed = MOUSEKEY_BASE_SPEED;
const uint16_t mk_decelerated_speed = MOUSEKEY_DECELERATED_SPEED;
const uint16_t mk_initial_speed = MOUSEKEY_INITIAL_SPEED;

static uint8_t move_unit(void) {
float speed = mk_initial_speed;

if (mousekey_accel & ((1<<0) | (1<<2))) {
speed = mousekey_accel & (1<<2) ? mk_accelerated_speed : mk_decelerated_speed;
} else if (mousekey_repeat && mouse_timer) {
const float time_elapsed = timer_elapsed(mouse_timer) / 50;
speed = mk_initial_speed +
MOUSEKEY_MOVE_DELTA * time_elapsed +
MOUSEKEY_MOVE_DELTA * 0.5 * time_elapsed * time_elapsed;

speed = speed > mk_base_speed ? mk_base_speed : speed;
}

/* convert speed to USB mouse speed 1 to 127 */
speed = (uint8_t)(speed / (1000.0f / mk_interval));
speed = speed < 1 ? 1 : speed;

return speed > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : speed;
}

float mk_wheel_interval = 1000.0f / MOUSEKEY_WHEEL_INITIAL_MOVEMENTS;

static uint8_t wheel_unit(void) {
float speed = MOUSEKEY_WHEEL_INITIAL_MOVEMENTS;

if (mousekey_accel & ((1<<0) | (1<<2))) {
speed = mousekey_accel & (1<<2) ? MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS : MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS;
} else if (mousekey_repeat && mouse_timer) {
if (mk_wheel_interval != MOUSEKEY_WHEEL_BASE_MOVEMENTS) {
const float time_elapsed = timer_elapsed(mouse_timer) / 50;
speed = MOUSEKEY_WHEEL_INITIAL_MOVEMENTS +
1 * time_elapsed +
1 * 0.5 * time_elapsed * time_elapsed;
}
speed = speed > MOUSEKEY_WHEEL_BASE_MOVEMENTS ? MOUSEKEY_WHEEL_BASE_MOVEMENTS : speed;
}

mk_wheel_interval = 1000.0f / speed;

return 1;
}

# else /* #ifndef MK_KINETIC_SPEED */

static uint8_t move_unit(void) {
uint16_t unit;
Expand Down Expand Up @@ -142,6 +208,7 @@ static uint8_t wheel_unit(void) {
return (unit > MOUSEKEY_WHEEL_MAX ? MOUSEKEY_WHEEL_MAX : (unit == 0 ? 1 : unit));
}

# endif /* #ifndef MK_KINETIC_SPEED */
# endif /* #ifndef MK_COMBINED */

void mousekey_task(void) {
Expand Down Expand Up @@ -193,6 +260,12 @@ void mousekey_task(void) {
}

void mousekey_on(uint8_t code) {
#ifdef MK_KINETIC_SPEED
if (mouse_timer == 0) {
mouse_timer = timer_read();
}
#endif /* #ifdef MK_KINETIC_SPEED */

if (code == KC_MS_UP)
mouse_report.y = move_unit() * -1;
else if (code == KC_MS_DOWN)
Expand Down Expand Up @@ -260,7 +333,12 @@ void mousekey_off(uint8_t code) {
mousekey_accel &= ~(1 << 1);
else if (code == KC_MS_ACCEL2)
mousekey_accel &= ~(1 << 2);
if (mouse_report.x == 0 && mouse_report.y == 0) mousekey_repeat = 0;
if (mouse_report.x == 0 && mouse_report.y == 0) {
mousekey_repeat = 0;
#ifdef MK_KINETIC_SPEED
mouse_timer = 0;
#endif /* #ifdef MK_KINETIC_SPEED */
}
if (mouse_report.v == 0 && mouse_report.h == 0) mousekey_wheel_repeat = 0;
}

Expand Down
37 changes: 37 additions & 0 deletions tmk_core/common/mousekey.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,28 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
# endif

# ifndef MOUSEKEY_MOVE_DELTA
#ifndef MK_KINETIC_SPEED
# define MOUSEKEY_MOVE_DELTA 5
#else
# define MOUSEKEY_MOVE_DELTA 25
#endif
# endif
# ifndef MOUSEKEY_WHEEL_DELTA
# define MOUSEKEY_WHEEL_DELTA 1
# endif
# ifndef MOUSEKEY_DELAY
#ifndef MK_KINETIC_SPEED
# define MOUSEKEY_DELAY 300
#else
# define MOUSEKEY_DELAY 8
#endif
# endif
# ifndef MOUSEKEY_INTERVAL
#ifndef MK_KINETIC_SPEED
# define MOUSEKEY_INTERVAL 50
#else
# define MOUSEKEY_INTERVAL 8
#endif
# endif
# ifndef MOUSEKEY_MAX_SPEED
# define MOUSEKEY_MAX_SPEED 10
Expand All @@ -68,6 +80,31 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
# define MOUSEKEY_WHEEL_TIME_TO_MAX 40
# endif

#ifndef MOUSEKEY_INITIAL_SPEED
#define MOUSEKEY_INITIAL_SPEED 100
#endif
#ifndef MOUSEKEY_BASE_SPEED
#define MOUSEKEY_BASE_SPEED 1000
#endif
#ifndef MOUSEKEY_DECELERATED_SPEED
#define MOUSEKEY_DECELERATED_SPEED 400
#endif
#ifndef MOUSEKEY_ACCELERATED_SPEED
#define MOUSEKEY_ACCELERATED_SPEED 3000
#endif
#ifndef MOUSEKEY_WHEEL_INITIAL_MOVEMENTS
#define MOUSEKEY_WHEEL_INITIAL_MOVEMENTS 16
#endif
#ifndef MOUSEKEY_WHEEL_BASE_MOVEMENTS
#define MOUSEKEY_WHEEL_BASE_MOVEMENTS 32
#endif
#ifndef MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS
#define MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS 48
#endif
#ifndef MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS
#define MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS 8
#endif

#else /* #ifndef MK_3_SPEED */

# ifndef MK_C_OFFSET_UNMOD
Expand Down

0 comments on commit 010271d

Please sign in to comment.