From 425fb9ad888ab259f8c0f3d16203a722af84a233 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Tue, 15 Mar 2022 21:47:27 -0500 Subject: [PATCH] Rewrite MouseKeys plugin This is a major rewrite of the MouseKeys plugin, primarily focused on mouse cursor movement keys. There is no change to the keys themselves, and the behaviour is fairly similar, but there are now better configuration parameters and defaults. The new parameters are a minimum speed, a maximum speed, and the length of time it takes to reach that maximum speed, without needing to worry about the report update interval. Signed-off-by: Michael Richters --- plugins/Kaleidoscope-MouseKeys/README.md | 87 ++++---- .../src/kaleidoscope/plugin/MouseKeys.cpp | 195 +++++++++++++----- .../src/kaleidoscope/plugin/MouseKeys.h | 94 +++++++-- .../plugin/mousekeys/MouseWrapper.cpp | 70 +------ .../plugin/mousekeys/MouseWrapper.h | 72 ++++--- tests/plugins/MouseKeys/basic/basic.ino | 2 + tests/plugins/MouseKeys/basic/test.ktest | 30 ++- 7 files changed, 332 insertions(+), 218 deletions(-) diff --git a/plugins/Kaleidoscope-MouseKeys/README.md b/plugins/Kaleidoscope-MouseKeys/README.md index c0dde420..4fab30f3 100644 --- a/plugins/Kaleidoscope-MouseKeys/README.md +++ b/plugins/Kaleidoscope-MouseKeys/README.md @@ -37,21 +37,23 @@ void setup() { The plugin provides a number of keys one can put on the keymap, that allow control of the mouse. They can be divided into a few groups: -### Cursor movement +### Mouse buttons + +Mouse button keys are straightforward; pressing one is the same as pressing the +corresponding button on a physical mouse. You can hold a mouse button key to +perform drag gestures, as you might expect. MouseKeys supports five mouse +buttons: left, right, middle, previous, and next. + +* `Key_mouseBtnL`, `Key_mouseBtnM`, `Key_mouseBtnR`, `Key_mouseBtnP`, + `Key_mouseBtnN`: The left, middle, right, previous, and next mouse buttons, + respectively. + -The simplest set of keys are the mouse cursor movement keys. These move the -cursor one direction or the other, with speed and acceleration factored in. When -a mouse cursor movement key is held down, it will move `.speed` pixels each -`.speedDelay` milliseconds without acceleration. But when `.accelSpeed` is -non-zero (and it is not zero by default, -see [below](#accelspeed-and-acceldelay)), the speed will increase by -`.accelSpeed` every `.accelDelay` milliseconds. Thus, unless configured -otherwise, holding a direction will move that way at increasing speed. +### Cursor movement -One can hold more than one key down at the same time, and the cursor will move -towards a direction that is the combination of the keys held. For example, -holding the "mouse up" and "mouse right" keys together will move the cursor -diagonally up and right. +When a cursor movement key is pressed, the mouse cursor will begin to move +slowly, then accelerate to full speed. Both the full speed and the time it +takes to reach full speed are configurable. The cursor movement keys are as follows: @@ -60,26 +62,18 @@ The cursor movement keys are as follows: * `Key_mouseUpL`, `Key_mouseUpR`, `Key_mouseDnL`, `Key_mouseDnR`: Move the cursor up-left, up-right, down-left, down-right, respectively. -### Scroll wheel +### Scroll wheels -Controlling the scroll wheel is similarly simple. It does not have acceleration, -but one can control the speed with the `.wheelSpeed` and `.wheelDelay` -properties (see below). +Controlling the scroll wheel is similarly simple. It does not have +acceleration, but one can control the speed with the +`MouseKeys.setScrollInterval()` function, which controls the length of time +between scroll events. * `Key_mouseScrollUp`, `Key_mouseScrollDn`: Scroll the mouse wheel up or down, respectively. * `Key_mouseScrollL`, `Key_mouseScrollR`: Scroll the mouse wheel left or right, respectively. -### Buttons - -Buttons are even simpler than movement: there is no movement speed, nor -acceleration involved. One just presses them. - -* `Key_mouseBtnL`, `Key_mouseBtnM`, `Key_mouseBtnR`, `Key_mouseBtnP`, - `Key_mouseBtnN`: The left, middle, right, previous, and next mouse buttons, - respectively. - ## Warping Warping is one of the most interesting features of the plugin, and is a feature @@ -210,37 +204,32 @@ the following additions: The plugin provides a `MouseKeys` object, with the following methods and properties available: -### `.speed` and `.speedDelay` +### `.setCursorInitSpeed(speed)`/`.getCursorInitSpeed()` -> These two control the speed of the mouse cursor, when a movement key is held. -> The former, `.speed`, controls the amount of pixels the cursor moves, when it -> has to move, and defaults to 1. The latter, `.speedDelay` is the amount of -> time - in milliseconds - to wait between two movements, and defaults to 0, no -> delay. +> Controls (or returns) the current starting speed value for mouse cursor +> movement. When a mouse movement key is pressed, the cursor starts moving at +> this speed, then accelerates. The number is abstract, but linear, with higher +> numbers representing faster speeds. Default starting speed is `1`. -### `.accelSpeed` and `.accelDelay` +### `.setCursorBaseSpeed(speed)`/`.getCursorBaseSpeed()` -> These two properties control the speed of acceleration. The former, -> `.accelSpeed`, controls how much the speed shall be increased at each step, -> while the second, `.accelDelay`, controls how often (in milliseconds) -> acceleration should be applied. -> -> They default to 1 pixel and 50 milliseconds, respectively. +> Controls (or returns) the current top speed value for mouse cursor movement. +> When a mouse movement key is pressed, the cursor accelerates until it reaches +> this speed. The number is abstract, but linear, with higher numbers +> representing faster speeds. Default full-speed value is `50`. -### `.wheelSpeed` and `.wheelDelay` +### `.setCursorAccelDuration(duration)`/`.getCursorAccelDuration()` -> The last two properties supported by the plugin control the mouse wheel -> scrolling speed. The former, `.wheelSpeed`, controls the amount of ticks the -> wheel shall scroll, and defaults to 1. The second, `.wheelDelay`, controls the -> delay between two scroll events, and defaults to 50 milliseconds. +> Controls (or returns) the current time it takes for the mouse cursor to reach +> full speed (in milliseconds), starting from when the first movement key is +> pressed. Default value is `800` ms. -### `.setSpeedLimit` +### `.setScrollInterval(interval)`/`.getScrollInterval()` -> This method sets the maximum speed after which acceleration stops. -> The default is 127, and the minimum value is 16 (things will not work -> properly below 16). +> Controls (or returns) the current scrolling speed, by setting the time between +> mouse scroll reports (in milliseconds). Default value is `50` ms. -### `.setWarpGridSize` +### `.setWarpGridSize(size)` > This method changes the size of the grid used for [warping](#warping). The > following are valid sizes: `MOUSE_WARP_GRID_2X2`, `MOUSE_WARP_GRID_3X3` diff --git a/plugins/Kaleidoscope-MouseKeys/src/kaleidoscope/plugin/MouseKeys.cpp b/plugins/Kaleidoscope-MouseKeys/src/kaleidoscope/plugin/MouseKeys.cpp index 58a9455c..a1190ff8 100644 --- a/plugins/Kaleidoscope-MouseKeys/src/kaleidoscope/plugin/MouseKeys.cpp +++ b/plugins/Kaleidoscope-MouseKeys/src/kaleidoscope/plugin/MouseKeys.cpp @@ -1,5 +1,5 @@ /* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope. - * Copyright (C) 2017-2021 Keyboard.io, Inc. + * Copyright (C) 2017-2022 Keyboard.io, Inc. * * 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 @@ -52,11 +52,7 @@ uint16_t MouseKeys::wheelDelay = 50; // Configuration functions void MouseKeys::setWarpGridSize(uint8_t grid_size) { - mousekeys::wrapper.warp_grid_size = grid_size; -} - -void MouseKeys::setSpeedLimit(uint8_t speed_limit) { - mousekeys::wrapper.speed_limit = speed_limit; + MouseWrapper.warp_grid_size = grid_size; } // ============================================================================= @@ -97,7 +93,6 @@ EventHandlerResult MouseKeys::onNameQuery() { // ----------------------------------------------------------------------------- EventHandlerResult MouseKeys::onSetup() { - kaleidoscope::Runtime.hid().mouse().setup(); kaleidoscope::Runtime.hid().absoluteMouse().setup(); return EventHandlerResult::OK; @@ -105,23 +100,20 @@ EventHandlerResult MouseKeys::onSetup() { // ----------------------------------------------------------------------------- EventHandlerResult MouseKeys::afterEachCycle() { - // Check timeout for accel update interval. - if (Runtime.hasTimeExpired(accel_start_time_, accelDelay)) { - accel_start_time_ = Runtime.millisAtCycleStart(); - // `accel_step` determines the movement speed of the mouse pointer, and gets - // reset to zero when no mouse movement keys is pressed (see below). - if (mousekeys::wrapper.accel_step < 255 - accelSpeed) { - mousekeys::wrapper.accel_step += accelSpeed; - } - } + if (directions_ == 0) + return EventHandlerResult::OK; // Check timeout for position update interval. - if (Runtime.hasTimeExpired(move_start_time_, speedDelay)) + if (Runtime.hasTimeExpired(last_cursor_update_time_, cursor_update_interval_)) { sendMouseMoveReport(); + last_cursor_update_time_ += cursor_update_interval_; + } // Check timeout for scroll report interval. - if (Runtime.hasTimeExpired(wheel_start_time_, wheelDelay)) + if (Runtime.hasTimeExpired(last_wheel_update_time_, settings_.wheel_update_interval)) { sendMouseWheelReport(); + last_wheel_update_time_ += settings_.wheel_update_interval; + } return EventHandlerResult::OK; } @@ -162,20 +154,27 @@ EventHandlerResult MouseKeys::afterReportingState(const KeyEvent &event) { sendMouseButtonReport(); } + // If no mouse move keys were active before this event, and a mouse movement + // key toggled on, we need to set the move start time so that acceleration can + // begin correctly. + if ((directions_ & cursor_mask_) == 0) { + cursor_start_time_ = Runtime.millisAtCycleStart(); + } + // A mouse key event has been successfully registered, and we have now // gathered all the information on held mouse movement and wheel keys, so it's // safe to update the direction information. directions_ = pending_directions_; pending_directions_ = 0; - if (isMouseMoveKey(event.key)) { - // When a cursor movement key toggles on, set the acceleration start time in - // order to get consistent behavior. - accel_start_time_ = Runtime.millisAtCycleStart(); - sendMouseMoveReport(); - - } else if (isMouseWheelKey(event.key)) { - sendMouseWheelReport(); + if (keyToggledOn(event.state)) { + if (isMouseMoveKey(event.key)) { + sendMouseMoveReport(); + last_cursor_update_time_ = Runtime.millisAtCycleStart(); + } else if (isMouseWheelKey(event.key)) { + sendMouseWheelReport(); + last_wheel_update_time_ = Runtime.millisAtCycleStart(); + } } return EventHandlerResult::OK; @@ -215,7 +214,7 @@ void MouseKeys::sendMouseButtonReport() const { // ----------------------------------------------------------------------------- void MouseKeys::sendMouseWarpReport(const KeyEvent &event) const { - mousekeys::wrapper.warp( + MouseWrapper.warp( ((event.key.getKeyCode() & KEY_MOUSE_WARP_END) ? WARP_END : 0x00) | ((event.key.getKeyCode() & KEY_MOUSE_UP) ? WARP_UP : 0x00) | ((event.key.getKeyCode() & KEY_MOUSE_DOWN) ? WARP_DOWN : 0x00) | @@ -224,57 +223,145 @@ void MouseKeys::sendMouseWarpReport(const KeyEvent &event) const { } // ----------------------------------------------------------------------------- -void MouseKeys::sendMouseMoveReport() { - move_start_time_ = Runtime.millisAtCycleStart(); +void MouseKeys::sendMouseMoveReport() const { + int8_t dx = 0; + int8_t dy = 0; + uint8_t direction = directions_ & cursor_mask_; - int8_t vx = 0; - int8_t vy = 0; - uint8_t direction = directions_ & move_mask_; - - if (direction == 0) { - // If there are no mouse movement keys held, reset speed to zero. - mousekeys::wrapper.accel_step = 0; - } else { - // For each active direction, add the mouse movement speed. + if (direction != 0) { + // Calculate + uint8_t delta = cursorDelta(); + // For each active direction, add the move update interval value to + // normalize speed of motion regardless of the frequency of updates. if (direction & KEY_MOUSE_LEFT) - vx -= speed; + dx -= delta; if (direction & KEY_MOUSE_RIGHT) - vx += speed; + dx += delta; if (direction & KEY_MOUSE_UP) - vy -= speed; + dy -= delta; if (direction & KEY_MOUSE_DOWN) - vy += speed; + dy += delta; - // Prepare the mouse report. - mousekeys::wrapper.move(vx, vy); // Send the report. + Runtime.hid().mouse().move(dx, dy, 0, 0); Runtime.hid().mouse().sendReport(); } } // ----------------------------------------------------------------------------- -void MouseKeys::sendMouseWheelReport() { - wheel_start_time_ = Runtime.millisAtCycleStart(); +// Get the current point on the acceleration curve's x axis, translating time +// elapsed since mouse movement started to a value between 0 and 255. +uint8_t MouseKeys::accelStep() const { + uint16_t elapsed_time = Runtime.millisAtCycleStart() - cursor_start_time_; + uint16_t accel_duration = settings_.cursor_accel_duration; + if (elapsed_time > accel_duration) + return 255; + uint16_t accel_step = (uint32_t(elapsed_time) * 256) / accel_duration; + return uint8_t(accel_step); +} - int8_t vx = 0; - int8_t vy = 0; +// ----------------------------------------------------------------------------- +// Compute the acceleration factor for mouse movement. When a movement key is +// first pressed, the cursor starts out slow then accelerates to full speed. +// The speed during acceleration follows an approximation of a sigmoid function, +// using two parabolas for simplicity. +uint8_t accelFactor(uint8_t accel_step) { + if (accel_step < 128) { + uint16_t y = accel_step * accel_step; + return 1 + (y >> 7); + } else { + uint16_t remaining_steps = 256 - accel_step; + uint16_t y = remaining_steps * remaining_steps; + return 255 - (y >> 7); + } +} + +// ----------------------------------------------------------------------------- +// Compute the distance the mouse cursor should move in subpixels, return the +// number of pixels the mouse should move (in active directions), and store the +// remaining subpixels for the next move. +uint8_t MouseKeys::cursorDelta() const { + // When the cursor speed is slow, it can be moving less than one pixel per + // update, so we need to calculate movement in "subpixels" and store the + // remaining subpixels to add to the next update's movement. + static uint8_t subpixel_remainder{0}; + + // First, we calculate where we are on the "time" axis of the acceleration + // curve, based on the time passed since the first cursor movement key was + // pressed. + uint8_t accel_step = accelStep(); + + // Next, we translate that into a speed scaling factor (from 1-255). If we + // had an FPU, we would do this in floating point, with a scale between 0 and + // 1, so this is how we emulate that using only integer (i.e. fixed-point) + // arithmetic. + uint8_t accel_factor = accelFactor(accel_step); + + // We want the cursor to start out with some minimum speed, otherwise the user + // presses a movement key and then waits for a while before the cursor moves + // even one pixel. We need to multiply our speed-scaling factor by the + // difference between the starting speed and the full speed, then add the + // starting speed (multiplied by the full value of the scaling factor) to get + // the current speed. + uint8_t max_speed = settings_.cursor_base_speed; + uint8_t min_speed = settings_.cursor_init_speed; + uint8_t speed_range = max_speed - min_speed; + uint16_t subpixel_speed = (speed_range * accel_factor); + subpixel_speed += (min_speed * 256); + + // We want to end up with small numbers of pixels, otherwise the speed will be + // too fast to be useful. But we also want to be able to make fine + // adjustments to the speed, so `settings_.cursor_base_speed` should be + // allowed to have a reasonbly high value, using all eight bits. This means + // that "decimal point" needs to be somewhere in the high byte of this 16-bit + // value. In order to store only eight bits of subpixel remainder, we need to + // do a shift first. This amount is arbitrary, but seems like a reasonable + // compromise. + subpixel_speed >>= 4; + + // `max_speed` and `accel_factor` can both be up to 255. So we can't + // just multiply by `cursor_update_interval_ without risk of overflow. The + // update interval should be some low number, anyway (8 or less, I think), and + // should probably be fixed as a constexpr, so we could just leave it out. + subpixel_speed *= cursor_update_interval_; + + // There's no risk of overflow here: (255 * 255) + 255 = 65535 + subpixel_speed += subpixel_remainder; + + // Set minimum speed + subpixel_speed += 64; + + // This shift should be more than eight pixels; a single update of 100 pixels + // is a huge jump. See above. + uint8_t pixel_speed = subpixel_speed >> 8; + // Truncate to get only lower 8 bits. + subpixel_remainder = subpixel_speed; + //subpixel_remainder = subpixel_speed - (uint16_t(pixel_speed) << 8); + return pixel_speed; +} + +// ----------------------------------------------------------------------------- +// Wheel speed should be controlled by changing the update interval, not by +// setting `wheel_speed_`. +void MouseKeys::sendMouseWheelReport() const { + int8_t dh = 0; + int8_t dv = 0; uint8_t direction = directions_ >> wheel_offset_; if (direction != 0) { // Horizontal scroll wheel: if (direction & KEY_MOUSE_LEFT) - vx -= wheelSpeed; + dh -= 1; if (direction & KEY_MOUSE_RIGHT) - vx += wheelSpeed; + dh += 1; // Vertical scroll wheel (note coordinates are opposite movement): if (direction & KEY_MOUSE_UP) - vy += wheelSpeed; + dv += 1; if (direction & KEY_MOUSE_DOWN) - vy -= wheelSpeed; + dv -= 1; - // Add scroll wheel changes to HID report. - Runtime.hid().mouse().move(0, 0, vy, vx); // Send the report. + Runtime.hid().mouse().move(0, 0, dv, dh); Runtime.hid().mouse().sendReport(); } } diff --git a/plugins/Kaleidoscope-MouseKeys/src/kaleidoscope/plugin/MouseKeys.h b/plugins/Kaleidoscope-MouseKeys/src/kaleidoscope/plugin/MouseKeys.h index a1f5a1af..496d56a6 100644 --- a/plugins/Kaleidoscope-MouseKeys/src/kaleidoscope/plugin/MouseKeys.h +++ b/plugins/Kaleidoscope-MouseKeys/src/kaleidoscope/plugin/MouseKeys.h @@ -1,5 +1,5 @@ /* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope. - * Copyright (C) 2017-2021 Keyboard.io, Inc. + * Copyright (C) 2017-2022 Keyboard.io, Inc. * * 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 @@ -24,9 +24,13 @@ #include "kaleidoscope/plugin.h" // for Plugin // ============================================================================= -// Deprecated MouseKeys code +// Deprecated MousKeys code #include "kaleidoscope_internal/deprecations.h" // for DEPRECATED +#define _DEPRECATED_MESSAGE_MOUSEKEYS_SET_SPEED_LIMIT \ + "The `MouseKeys.setSpeedLimit()` function is deprecated. It no longer has\n" \ + "any function, and can be safely removed." + #define _DEPRECATED_MESSAGE_MOUSEKEYS_SPEED \ "Direct access to the `MouseKeys.speed` variable has been deprecated.\n" \ "Please refer to the MouseKeys documentation for instructions on how to\n" \ @@ -76,10 +80,57 @@ class MouseKeys : public kaleidoscope::Plugin { static uint8_t wheelSpeed; DEPRECATED(MOUSEKEYS_WHEEL_DELAY) static uint16_t wheelDelay; + + DEPRECATED(MOUSEKEYS_SET_SPEED_LIMIT) + static void setSpeedLimit(uint8_t speed_limit) {} #endif - static void setWarpGridSize(uint8_t grid_size); - static void setSpeedLimit(uint8_t speed_limit); + void setWarpGridSize(uint8_t grid_size); + + /// Get the current mouse (full) speed setting + /// + /// This returns the value for full-speed mouse movement (after the initial + /// acceleration period), not the current speed of the mouse cursor on screen. + /// The value does not have straightforward units, but it is linear. + uint8_t getCursorBaseSpeed() const { + return settings_.cursor_base_speed; + } + /// Set the full-speed mouse movement value + void setCursorBaseSpeed(uint8_t speed) { + settings_.cursor_base_speed = speed; + } + + /// Get the initial mouse cursor movement speed setting + uint8_t getCursorInitSpeed() const { + return settings_.cursor_init_speed; + } + /// Set the initial mouse cursor movement speed + void setCursorInitSpeed(uint8_t speed) { + settings_.cursor_init_speed = speed; + } + + /// Get the current acceleration window duration + uint16_t getCursorAccelDuration() const { + return settings_.cursor_accel_duration; + } + /// Set the acceleration window duration + void setCursorAccelDuration(uint16_t duration) { + settings_.cursor_accel_duration = duration; + } + + /// Get the current mouse wheel update interval + /// + /// Returns the interval (in milliseconds) between mouse wheel updates while a + /// mouse wheel key is active (held). + uint8_t getScrollInterval() const { + return settings_.wheel_update_interval; + } + /// Set the current mouse wheel update interval + /// + /// Sets the wheel update interval to the specified number of milliseconds. + void setScrollInterval(uint8_t interval) { + settings_.wheel_update_interval = interval; + } EventHandlerResult onSetup(); EventHandlerResult onNameQuery(); @@ -88,20 +139,34 @@ class MouseKeys : public kaleidoscope::Plugin { EventHandlerResult onAddToReport(Key key); EventHandlerResult afterReportingState(const KeyEvent &event); + // --------------------------------------------------------------------------- + // Structure for storing all user-configurable settings. + struct Settings { + uint8_t wheel_update_interval = 50; + uint8_t cursor_init_speed = 1; + uint8_t cursor_base_speed = 50; + uint16_t cursor_accel_duration = 1000; + }; + private: - uint16_t move_start_time_ = 0; - uint16_t accel_start_time_ = 0; - uint16_t wheel_start_time_ = 0; + static constexpr uint8_t cursor_update_interval_ = 4; + + Settings settings_; + + uint16_t cursor_start_time_ = 0; + uint8_t last_cursor_update_time_ = 0; + uint8_t last_wheel_update_time_ = 0; // Mouse cursor and wheel movement directions are stored in a single bitfield // to save space. The low four bits are for cursor movement, and the high // four are for wheel movement. static constexpr uint8_t wheel_offset_ = 4; static constexpr uint8_t wheel_mask_ = 0b11110000; - static constexpr uint8_t move_mask_ = 0b00001111; - uint8_t directions_ = 0; - uint8_t pending_directions_ = 0; - uint8_t buttons_ = 0; + static constexpr uint8_t cursor_mask_ = 0b00001111; + + uint8_t directions_ = 0; + uint8_t pending_directions_ = 0; + uint8_t buttons_ = 0; bool isMouseKey(const Key &key) const; bool isMouseButtonKey(const Key &key) const; @@ -111,8 +176,11 @@ class MouseKeys : public kaleidoscope::Plugin { void sendMouseButtonReport() const; void sendMouseWarpReport(const KeyEvent &event) const; - void sendMouseMoveReport(); - void sendMouseWheelReport(); + void sendMouseMoveReport() const; + void sendMouseWheelReport() const; + + uint8_t accelStep() const; + uint8_t cursorDelta() const; }; } // namespace plugin diff --git a/plugins/Kaleidoscope-MouseKeys/src/kaleidoscope/plugin/mousekeys/MouseWrapper.cpp b/plugins/Kaleidoscope-MouseKeys/src/kaleidoscope/plugin/mousekeys/MouseWrapper.cpp index 768a11d9..b17e2271 100644 --- a/plugins/Kaleidoscope-MouseKeys/src/kaleidoscope/plugin/mousekeys/MouseWrapper.cpp +++ b/plugins/Kaleidoscope-MouseKeys/src/kaleidoscope/plugin/mousekeys/MouseWrapper.cpp @@ -1,5 +1,5 @@ /* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope. - * Copyright (C) 2017-2018 Keyboard.io, Inc. + * Copyright (C) 2017-2022 Keyboard.io, Inc. * * 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 @@ -21,22 +21,17 @@ #include "kaleidoscope/Runtime.h" // for Runtime, Runtime_ #include "kaleidoscope/device/device.h" // for Base<>::HID, VirtualProps:... #include "kaleidoscope/driver/hid/keyboardio/AbsoluteMouse.h" // for AbsoluteMouse -#include "kaleidoscope/driver/hid/keyboardio/Mouse.h" // for Mouse -#include "kaleidoscope/plugin/mousekeys/MouseWarpModes.h" // for MOUSE_WARP_GRID_2X2 namespace kaleidoscope { namespace plugin { namespace mousekeys { -uint8_t MouseWrapper::warp_grid_size = MOUSE_WARP_GRID_2X2; -uint16_t MouseWrapper::next_width; -uint16_t MouseWrapper::next_height; -uint16_t MouseWrapper::section_top; -uint16_t MouseWrapper::section_left; -bool MouseWrapper::is_warping; - -uint8_t MouseWrapper::accel_step; -uint8_t MouseWrapper::speed_limit = 127; +// uint8_t MouseWrapper::warp_grid_size = MOUSE_WARP_GRID_2X2; +// uint16_t MouseWrapper::next_width; +// uint16_t MouseWrapper::next_height; +// uint16_t MouseWrapper::section_top; +// uint16_t MouseWrapper::section_left; +// bool MouseWrapper::is_warping; void MouseWrapper::warpJump(uint16_t left, uint16_t top, uint16_t height, uint16_t width) { uint16_t x_center = left + width / 2; @@ -100,52 +95,9 @@ void MouseWrapper::warp(uint8_t warp_cmd) { warpJump(section_left, section_top, next_height, next_width); } -// To approximate a sine wave, this uses two parabolas. Acceleration begins -// slowly, grows rapidly in the middle, and slows again near the top. -uint8_t MouseWrapper::acceleration(uint8_t cycles) { - if (cycles < 128) { - uint16_t c2 = cycles * cycles; - return 1 + (c2 >> 7); - } else { - uint16_t remaining_cycles = 256 - cycles; - uint16_t c2 = remaining_cycles * remaining_cycles; - return 255 - (c2 >> 7); - } -} - -void MouseWrapper::move(int8_t x, int8_t y) { - int16_t moveX = 0; - int16_t moveY = 0; - static int8_t remainderX = 0; - static int8_t remainderY = 0; - int16_t effectiveSpeedLimit = speed_limit; - - if (x != 0) { - moveX = remainderX + (x * acceleration(accel_step)); - if (moveX > effectiveSpeedLimit) - moveX = effectiveSpeedLimit; - else if (moveX < -effectiveSpeedLimit) - moveX = -effectiveSpeedLimit; - } - - if (y != 0) { - moveY = remainderY + (y * acceleration(accel_step)); - if (moveY > effectiveSpeedLimit) - moveY = effectiveSpeedLimit; - else if (moveY < -effectiveSpeedLimit) - moveY = -effectiveSpeedLimit; - } - - endWarping(); - // move by whole pixels, not subpixels - Runtime.hid().mouse().move(moveX / subpixels_per_pixel, moveY / subpixels_per_pixel); - // save leftover subpixel movements for later - remainderX = moveX - moveX / subpixels_per_pixel * subpixels_per_pixel; - remainderY = moveY - moveY / subpixels_per_pixel * subpixels_per_pixel; -} +} // namespace mousekeys -MouseWrapper wrapper; +mousekeys::MouseWrapper MouseWrapper; -} // namespace mousekeys -} // namespace plugin -} // namespace kaleidoscope +} // namespace plugin +} // namespace kaleidoscope diff --git a/plugins/Kaleidoscope-MouseKeys/src/kaleidoscope/plugin/mousekeys/MouseWrapper.h b/plugins/Kaleidoscope-MouseKeys/src/kaleidoscope/plugin/mousekeys/MouseWrapper.h index e8807c2e..9e2813dd 100644 --- a/plugins/Kaleidoscope-MouseKeys/src/kaleidoscope/plugin/mousekeys/MouseWrapper.h +++ b/plugins/Kaleidoscope-MouseKeys/src/kaleidoscope/plugin/mousekeys/MouseWrapper.h @@ -1,5 +1,5 @@ /* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope. - * Copyright (C) 2017-2018 Keyboard.io, Inc. + * Copyright (C) 2017-2022 Keyboard.io, Inc. * * 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 @@ -16,58 +16,56 @@ #pragma once -#include // for uint16_t, uint8_t, int8_t +#include // for uint8_t, uint16_t + +#include "kaleidoscope/plugin/mousekeys/MouseWarpModes.h" + +// Mouse acceleration + +namespace kaleidoscope { +namespace plugin { // Warping commands -#define WARP_END 1 -#define WARP_UP 2 -#define WARP_DOWN 4 -#define WARP_LEFT 8 -#define WARP_RIGHT 16 +constexpr uint8_t WARP_END = 1 << 0; +constexpr uint8_t WARP_UP = 1 << 1; +constexpr uint8_t WARP_DOWN = 1 << 2; +constexpr uint8_t WARP_LEFT = 1 << 3; +constexpr uint8_t WARP_RIGHT = 1 << 4; // apparently, the mac discards 15% of the value space for mouse movement. // need to test this on other platforms -#define MAX_WARP_WIDTH 32767 -#define MAX_WARP_HEIGHT 32767 +constexpr uint16_t MAX_WARP_WIDTH = 32767; +constexpr uint16_t MAX_WARP_HEIGHT = 32767; -#define WARP_ABS_TOP 0 -#define WARP_ABS_LEFT 0 +constexpr uint8_t WARP_ABS_TOP = 0; +constexpr uint8_t WARP_ABS_LEFT = 0; -// Mouse acceleration - -namespace kaleidoscope { -namespace plugin { namespace mousekeys { class MouseWrapper { public: - static void move(int8_t x, int8_t y); - static void warp(uint8_t warp_cmd); + void warp(uint8_t warp_cmd); - static uint8_t accel_step; - static uint8_t speed_limit; - static constexpr uint8_t subpixels_per_pixel = 16; - static uint8_t warp_grid_size; + uint8_t warp_grid_size = MOUSE_WARP_GRID_2X2; private: - static uint16_t next_width; - static uint16_t next_height; - static uint16_t section_top; - static uint16_t section_left; - static bool is_warping; - - static uint8_t acceleration(uint8_t cycles); - - static void beginWarping(); - static void endWarping(); - static void resetWarping(); - static void warpJump(uint16_t left, uint16_t top, uint16_t height, uint16_t width); + uint16_t next_width; + uint16_t next_height; + uint16_t section_top; + uint16_t section_left; + bool is_warping = false; + + void beginWarping(); + void endWarping(); + void resetWarping(); + void warpJump(uint16_t left, uint16_t top, uint16_t height, uint16_t width); }; -extern MouseWrapper wrapper; +} // namespace mousekeys + +extern mousekeys::MouseWrapper MouseWrapper; -} // namespace mousekeys -} // namespace plugin -} // namespace kaleidoscope +} // namespace plugin +} // namespace kaleidoscope diff --git a/tests/plugins/MouseKeys/basic/basic.ino b/tests/plugins/MouseKeys/basic/basic.ino index 09df9b29..ab50c9c8 100644 --- a/tests/plugins/MouseKeys/basic/basic.ino +++ b/tests/plugins/MouseKeys/basic/basic.ino @@ -42,6 +42,8 @@ KALEIDOSCOPE_INIT_PLUGINS(MouseKeys); void setup() { Kaleidoscope.setup(); + + MouseKeys.setCursorAccelDuration(200); } void loop() { diff --git a/tests/plugins/MouseKeys/basic/test.ktest b/tests/plugins/MouseKeys/basic/test.ktest index aaf85b2f..ad882b96 100644 --- a/tests/plugins/MouseKeys/basic/test.ktest +++ b/tests/plugins/MouseKeys/basic/test.ktest @@ -21,15 +21,33 @@ RUN 3 ms PRESS MOVE_UP RUN 1 cycle -RUN 15 ms +RUN 4 ms EXPECT mouse-report y=-1 -RUN 1 cycle - -RUN 15 ms +RUN 8 ms EXPECT mouse-report y=-1 -RUN 1 cycle +RUN 8 ms +EXPECT mouse-report y=-1 +RUN 4 ms +EXPECT mouse-report y=-1 +RUN 4 ms +EXPECT mouse-report y=-1 +RUN 4 ms +EXPECT mouse-report y=-1 +RUN 4 ms +EXPECT mouse-report y=-1 +RUN 4 ms +EXPECT mouse-report y=-2 +RUN 4 ms +EXPECT mouse-report y=-2 +RUN 4 ms +EXPECT mouse-report y=-2 +RUN 4 ms +EXPECT mouse-report y=-2 +RUN 4 ms +EXPECT mouse-report y=-2 +RUN 4 ms +EXPECT mouse-report y=-3 -RUN 5 ms RELEASE MOVE_UP RUN 1 cycle EXPECT no mouse-report