diff options
author | Selene ToyKeeper <ToyKeeper@users.noreply.github.com> | 2022-10-26 13:49:44 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-26 20:49:44 +0100 |
commit | 2a61bfc27d6915877d18a09bc440d462a2051857 (patch) | |
tree | 72070605dda5410359774a33f52f0a459b8b184e /quantum | |
parent | 4c0c491e31535292194cc812cde483d223d16ed7 (diff) |
add "inertia" mode for mouse keys (#18774)
Co-authored-by: Selene ToyKeeper <git@toykeeper.net>
Diffstat (limited to 'quantum')
-rw-r--r-- | quantum/mousekey.c | 147 | ||||
-rw-r--r-- | quantum/mousekey.h | 39 |
2 files changed, 175 insertions, 11 deletions
diff --git a/quantum/mousekey.c b/quantum/mousekey.c index 25a89bdba7..b91db80de6 100644 --- a/quantum/mousekey.c +++ b/quantum/mousekey.c @@ -37,6 +37,13 @@ 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 MOUSEKEY_INERTIA +static uint8_t mousekey_frame = 0; // track whether gesture is inactive, first frame, or repeating +static int8_t mousekey_x_dir = 0; // -1 / 0 / 1 = left / neutral / right +static int8_t mousekey_y_dir = 0; // -1 / 0 / 0 = up / neutral / down +static int8_t mousekey_x_inertia = 0; // current velocity, limit +/- MOUSEKEY_TIME_TO_MAX +static int8_t mousekey_y_inertia = 0; // ... +#endif #ifdef MK_KINETIC_SPEED static uint16_t mouse_timer = 0; #endif @@ -76,6 +83,7 @@ uint8_t mk_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX; # ifndef MK_COMBINED # ifndef MK_KINETIC_SPEED +# ifndef MOUSEKEY_INERTIA /* Default accelerated mode */ @@ -97,6 +105,53 @@ static uint8_t move_unit(void) { return (unit > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : (unit == 0 ? 1 : unit)); } +# else // MOUSEKEY_INERTIA mode + +static int8_t move_unit(uint8_t axis) { + int16_t unit; + + // handle X or Y axis + int8_t inertia, dir; + if (axis) { + inertia = mousekey_y_inertia; + dir = mousekey_y_dir; + } else { + inertia = mousekey_x_inertia; + dir = mousekey_x_dir; + } + + if (mousekey_frame < 2) { // first frame(s): initial keypress moves one pixel + mousekey_frame = 1; + unit = dir * MOUSEKEY_MOVE_DELTA; + } else { // acceleration + // linear acceleration (is here for reference, but doesn't feel as good during use) + // unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed * inertia) / mk_time_to_max; + + // x**2 acceleration (quadratic, more precise for short movements) + int16_t percent = (inertia << 8) / mk_time_to_max; + percent = (percent * percent) >> 8; + if (inertia < 0) percent = -percent; + + // unit = sign(inertia) + (percent of max speed) + if (inertia > 0) + unit = 1; + else if (inertia < 0) + unit = -1; + else + unit = 0; + + unit = unit + ((mk_max_speed * percent) >> 8); + } + + if (unit > MOUSEKEY_MOVE_MAX) + unit = MOUSEKEY_MOVE_MAX; + else if (unit < -MOUSEKEY_MOVE_MAX) + unit = -MOUSEKEY_MOVE_MAX; + return unit; +} + +# endif // end MOUSEKEY_INERTIA mode + static uint8_t wheel_unit(void) { uint16_t unit; if (mousekey_accel & (1 << 0)) { @@ -213,6 +268,28 @@ static uint8_t wheel_unit(void) { # endif /* #ifndef MK_COMBINED */ +# ifdef MOUSEKEY_INERTIA + +static int8_t calc_inertia(int8_t direction, int8_t velocity) { + // simulate acceleration and deceleration + + // deceleration + if ((direction > -1) && (velocity < 0)) + velocity = (velocity + 1) * (256 - MOUSEKEY_FRICTION) / 256; + else if ((direction < 1) && (velocity > 0)) + velocity = velocity * (256 - MOUSEKEY_FRICTION) / 256; + + // acceleration + if ((direction > 0) && (velocity < mk_time_to_max)) + velocity++; + else if ((direction < 0) && (velocity > -mk_time_to_max)) + velocity--; + + return velocity; +} + +# endif + void mousekey_task(void) { // report cursor and scroll movement independently report_mouse_t tmpmr = mouse_report; @@ -222,6 +299,32 @@ void mousekey_task(void) { mouse_report.v = 0; mouse_report.h = 0; +# ifdef MOUSEKEY_INERTIA + + // if an animation is in progress and it's time for the next frame + if ((mousekey_frame) && timer_elapsed(last_timer_c) > ((mousekey_frame > 1) ? mk_interval : mk_delay * 10)) { + mousekey_x_inertia = calc_inertia(mousekey_x_dir, mousekey_x_inertia); + mousekey_y_inertia = calc_inertia(mousekey_y_dir, mousekey_y_inertia); + + mouse_report.x = move_unit(0); + mouse_report.y = move_unit(1); + + // prevent sticky "drift" + if ((!mousekey_x_dir) && (!mousekey_x_inertia)) tmpmr.x = 0; + if ((!mousekey_y_dir) && (!mousekey_y_inertia)) tmpmr.y = 0; + + if (mousekey_frame < 2) mousekey_frame++; + } + + // reset if not moving and no movement keys are held + if ((!mousekey_x_dir) && (!mousekey_y_dir) && (!mousekey_x_inertia) && (!mousekey_y_inertia)) { + mousekey_frame = 0; + tmpmr.x = 0; + tmpmr.y = 0; + } + +# else // default acceleration + if ((tmpmr.x || tmpmr.y) && timer_elapsed(last_timer_c) > (mousekey_repeat ? mk_interval : mk_delay * 10)) { if (mousekey_repeat != UINT8_MAX) mousekey_repeat++; if (tmpmr.x != 0) mouse_report.x = move_unit() * ((tmpmr.x > 0) ? 1 : -1); @@ -239,6 +342,9 @@ void mousekey_task(void) { } } } + +# endif // MOUSEKEY_INERTIA or not + if ((tmpmr.v || tmpmr.h) && timer_elapsed(last_timer_w) > (mousekey_wheel_repeat ? mk_wheel_interval : mk_wheel_delay * 10)) { if (mousekey_wheel_repeat != UINT8_MAX) mousekey_wheel_repeat++; if (tmpmr.v != 0) mouse_report.v = wheel_unit() * ((tmpmr.v > 0) ? 1 : -1); @@ -260,6 +366,7 @@ void mousekey_task(void) { if (has_mouse_report_changed(&mouse_report, &tmpmr) || should_mousekey_report_send(&mouse_report)) { mousekey_send(); } + // save the state for later memcpy(&mouse_report, &tmpmr, sizeof(tmpmr)); } @@ -270,6 +377,19 @@ void mousekey_on(uint8_t code) { } # endif /* #ifdef MK_KINETIC_SPEED */ +# ifdef MOUSEKEY_INERTIA + + // initial keypress sets impulse and activates first frame of movement + if ((code == KC_MS_UP) || (code == KC_MS_DOWN)) { + mousekey_y_dir = (code == KC_MS_DOWN) ? 1 : -1; + if (mousekey_frame < 2) mouse_report.y = move_unit(1); + } else if ((code == KC_MS_LEFT) || (code == KC_MS_RIGHT)) { + mousekey_x_dir = (code == KC_MS_RIGHT) ? 1 : -1; + if (mousekey_frame < 2) mouse_report.x = move_unit(0); + } + +# else // no inertia + if (code == KC_MS_UP) mouse_report.y = move_unit() * -1; else if (code == KC_MS_DOWN) @@ -278,6 +398,9 @@ void mousekey_on(uint8_t code) { mouse_report.x = move_unit() * -1; else if (code == KC_MS_RIGHT) mouse_report.x = move_unit(); + +# endif // inertia or not + else if (code == KC_MS_WH_UP) mouse_report.v = wheel_unit(); else if (code == KC_MS_WH_DOWN) @@ -297,6 +420,20 @@ void mousekey_on(uint8_t code) { } void mousekey_off(uint8_t code) { +# ifdef MOUSEKEY_INERTIA + + // key release clears impulse unless opposite direction is held + if ((code == KC_MS_UP) && (mousekey_y_dir < 1)) + mousekey_y_dir = 0; + else if ((code == KC_MS_DOWN) && (mousekey_y_dir > -1)) + mousekey_y_dir = 0; + else if ((code == KC_MS_LEFT) && (mousekey_x_dir < 1)) + mousekey_x_dir = 0; + else if ((code == KC_MS_RIGHT) && (mousekey_x_dir > -1)) + mousekey_x_dir = 0; + +# else // no inertia + if (code == KC_MS_UP && mouse_report.y < 0) mouse_report.y = 0; else if (code == KC_MS_DOWN && mouse_report.y > 0) @@ -305,6 +442,9 @@ void mousekey_off(uint8_t code) { mouse_report.x = 0; else if (code == KC_MS_RIGHT && mouse_report.x > 0) mouse_report.x = 0; + +# endif // inertia or not + 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) @@ -476,6 +616,13 @@ void mousekey_clear(void) { mousekey_repeat = 0; mousekey_wheel_repeat = 0; mousekey_accel = 0; +#ifdef MOUSEKEY_INERTIA + mousekey_frame = 0; + mousekey_x_inertia = 0; + mousekey_y_inertia = 0; + mousekey_x_dir = 0; + mousekey_y_dir = 0; +#endif } static void mousekey_debug(void) { diff --git a/quantum/mousekey.h b/quantum/mousekey.h index da2edb481a..e968e000c0 100644 --- a/quantum/mousekey.h +++ b/quantum/mousekey.h @@ -36,34 +36,48 @@ 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 8 -# else +# if defined(MK_KINETIC_SPEED) # define MOUSEKEY_MOVE_DELTA 16 +# elif defined(MOUSEKEY_INERTIA) +# define MOUSEKEY_MOVE_DELTA 1 +# else +# define MOUSEKEY_MOVE_DELTA 8 # endif # endif # ifndef MOUSEKEY_WHEEL_DELTA # define MOUSEKEY_WHEEL_DELTA 1 # endif # ifndef MOUSEKEY_DELAY -# ifndef MK_KINETIC_SPEED -# define MOUSEKEY_DELAY 10 -# else +# if defined(MK_KINETIC_SPEED) # define MOUSEKEY_DELAY 5 +# elif defined(MOUSEKEY_INERTIA) +# define MOUSEKEY_DELAY 150 // allow single-pixel movements before repeat activates +# else +# define MOUSEKEY_DELAY 10 # endif # endif # ifndef MOUSEKEY_INTERVAL -# ifndef MK_KINETIC_SPEED -# define MOUSEKEY_INTERVAL 20 -# else +# if defined(MK_KINETIC_SPEED) # define MOUSEKEY_INTERVAL 10 +# elif defined(MOUSEKEY_INERTIA) +# define MOUSEKEY_INTERVAL 16 // 60 fps +# else +# define MOUSEKEY_INTERVAL 20 # endif # endif # ifndef MOUSEKEY_MAX_SPEED -# define MOUSEKEY_MAX_SPEED 10 +# if defined(MOUSEKEY_INERTIA) +# define MOUSEKEY_MAX_SPEED 32 +# else +# define MOUSEKEY_MAX_SPEED 10 +# endif # endif # ifndef MOUSEKEY_TIME_TO_MAX -# define MOUSEKEY_TIME_TO_MAX 30 +# if defined(MOUSEKEY_INERTIA) +# define MOUSEKEY_TIME_TO_MAX 32 +# else +# define MOUSEKEY_TIME_TO_MAX 30 +# endif # endif # ifndef MOUSEKEY_WHEEL_DELAY # define MOUSEKEY_WHEEL_DELAY 10 @@ -78,6 +92,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. # define MOUSEKEY_WHEEL_TIME_TO_MAX 40 # endif +# ifndef MOUSEKEY_FRICTION +# define MOUSEKEY_FRICTION 24 // 0 to 255 +# endif # ifndef MOUSEKEY_INITIAL_SPEED # define MOUSEKEY_INITIAL_SPEED 100 # endif |