diff options
Diffstat (limited to 'quantum/mousekey.c')
| -rw-r--r-- | quantum/mousekey.c | 147 | 
1 files changed, 147 insertions, 0 deletions
| diff --git a/quantum/mousekey.c b/quantum/mousekey.c index 25a89bdba7..703fb39fa6 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         = ((int32_t)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) { | 
