diff --git a/doc/plugin/MouseKeys.md b/doc/plugin/MouseKeys.md new file mode 100644 index 00000000..a095a726 --- /dev/null +++ b/doc/plugin/MouseKeys.md @@ -0,0 +1,246 @@ +# Kaleidoscope-MouseKeys + +Have you ever wanted to control the mouse cursor from the comfort of your +keyboard? With this plugin, you can. While it may not replace the mouse in all +situations, there are plenty of cases where one will not have to lift their +hands off the keyboard just to nudge the mouse cursor away a little. + +Of course, there are a lot more one can do with the plugin than to nudge the +cursor! Mouse keys are provided for all four *and* diagonal movement; mouse +buttons; and a unique warping mechanism too. And not only these: the speed of +the cursor, the mouse wheel, and that of acceleration can all be configured to +match one's desired behaviour. + +## Using the plugin + +To use the plugin, simply include the header in your Sketch, tell the firmware +to use the `MouseKeys` object, and place mouse keys on your keymap. It is best +illustrated with an example: + +```c++ +#include +#include + +// Somewhere in the keymap: +Key_mouseUp, Key_mouseDn, Key_mouseL, Key_mouseR, +Key_mouseBtnL, Key_mouseBtnR + +KALEIDOSCOPE_INIT_PLUGINS(MouseKeys); + +void setup() { + Kaleidoscope.setup (); +} +``` + +## Keys provided by the plugin + +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 + +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. + +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. + +The cursor movement keys are as follows: + +* `Key_mouseUp`, `Key_mouseDn`, `Key_mouseL`, `Key_mouseR`: Move the cursor up, + down, left, or right, respectively. +* `Key_mouseUpL`, `Key_mouseUpR`, `Key_mouseDnL`, `Key_mouseDnR`: Move the + cursor up-left, up-right, down-left, down-right, respectively. + +### Scroll wheel + +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). + +* `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 +unique to Kaleidoscope, as far as we can tell. The warping keys position the +mouse cursor within a sector of the screen on first press, and any subsequent +taps will warp within the previously selected sector. For example, pressing the +north-west warp key twice will first jump to the middle of the north-west +sector of your screen, then select the north-west sector of that, and jump to +the middle of it. + +To stop warping, use any other mouse key, or hit the "warp end" key. + +### Warp grid size + +The warp grid size determines how MouseKeys partitions the screen to select the +next position to jump to when pressing a warp key. The plugin provides two grid +sizes to choose from: a *2x2* grid that splits the screen into quadrants, and a +*3x3* grid with nine cells similar to a navigation feature included with some +speech recognition software. By default, the plugin splits the screen into the +2x2 grid. + +To change the warp grid size, call the plugin's `setWarpGridSize()` method: + +```c++ +MouseKeys.setWarpGridSize(MOUSE_WARP_GRID_3X3); +``` + +#### 2x2 grid + +As described above, MouseKeys warps the pointer using a grid model that reflects +locations on the screen. By default, the plugin uses a 2x2 grid. To understand +how warping works, examine this diagram of a screen split into that 2x2 grid: + + +-----------------------|-----------------------+ + | | | | + | G | tab | | + | | | | + |-----------|-----------| tab | + | | | | + | B | esc | | + | | | | + +-----------------------|-----------------------+ + | | | + | | | + | | | + | B | esc | + | | | + | | | + | | | + +-----------------------|-----------------------+ + +Each quadrant is labed with a key that, when pressed, moves the mouse pointer +to the center of that quadrant. With this layout, pressing G warps +the pointer to the top-left quadant. Then, the plugin "zooms" into that sector +with a smaller grid so that the next warp key pressed jumps the pointer more +precisely within the sector. In this case, if we press esc next, +the pointer warps to the bottom-right corner within that quadrant. + +The warping keys for the 2x2 grid are the following: + +* `Key_mouseWarpNW`, `Key_mouseWarpNE`, `Key_mouseWarpSW`, `Key_mouseWarpSE`: + Warp towards the north-west, north-east, south-west, or south-east quadrants, + respectively. +* `Key_mouseWarpEnd`: End the warping sequence, resetting it to the default + state. Using any of the warping keys after this will start from the whole + screen again. + +#### 3x3 grid + +A 3x3 warp grid assigns a key to each of nine sectors of the screen. The next +diagram shows a screen with a key label that warps to each sector. As we can +see, pressing W warps the pointer into the top-left sector, and +pressing V warps to the bottom-right corner within that sector: + + +-----------------|-----------------|-----------------+ + | W | E | R | | | + |-----|-----|-----| | | + | S | D | F | E | R | + |-----|-----|-----| | | + | X | C | V | | | + +-----------------|-----------------|-----------------+ + | | | | + | | | | + | S | D | F | + | | | | + | | | | + +-----------------|-----------------|-----------------+ + | | | | + | | | | + | X | C | V | + | | | | + | | | | + +-----------------|-----------------|-----------------+ + +To use a 3x3 warp grid, we may need to remap some keys. A suggested warp key +mapping is shown below on the left side of a keyboard with a QWERTY layout: + + W | E | R T A - End Warping (Key_mouseWarpEnd) + ---|---|--- W - Warp NW Sector (Key_mouseWarpNW) + A S | D | F G E - Warp N Sector (Key_mouseWarpN) + ---|---|--- R - Warp NE Sector (Key_mouseWarpNE) + X | C | V B S - Warp E Sector (Key_mouseWarpE) + D - Warp/Zoom Center (Key_mouseWarpIn) + F - Warp W Sector (Key_mouseWarpW) + K - Warp SE Sector (Key_mouseWarpSE) + C - Warp S Sector (Key_mouseWarpS) + V - Warp SW Sector (Key_mouseWarpSW) + T - Right Click (Key_mouseBtnR) + G - Left Click (Key_mouseBtnL) + B - Middle Click (Key_mouseBtnM) + +This example layout replaces the default directional mouse keys and sets the +warp keys in a comfortable position for a warp-only configuration. Of course, +a Kaleidoscope user may retain the directional keys and map the warp keys +elsewhere according to his or her liking. + +A 3x3 warp grid layout contains all of the keys from the 2x2 grid layout with +the following additions: + +* `Key_mouseWarpN`, `Key_mouseWarpE`, `Key_mouseWarpS`, `Key_mouseWarpW`: + Warp towards the north, east, south, and west sectors, respectively. +* `Key_mouseWarpIn`: Warp to the center sector of the grid. The plugin will + continue to "zoom" into center of the current cell with each consecutive + press of this key. + +## Plugin methods + +The plugin provides a `MouseKeys` object, with the following methods and +properties available: + +### `.speed` and `.speedDelay` + +> 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. + +### `.accelSpeed` and `.accelDelay` + +> 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. + +### `.wheelSpeed` and `.wheelDelay` + +> 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. + +### `.setSpeedLimit` + +> 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). + +### `.setWarpGridSize` + +> 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/src/Kaleidoscope-MouseKeys.h b/src/Kaleidoscope-MouseKeys.h new file mode 100644 index 00000000..b948baea --- /dev/null +++ b/src/Kaleidoscope-MouseKeys.h @@ -0,0 +1,19 @@ +/* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope. + * Copyright (C) 2017-2018 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 + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include "kaleidoscope/plugin/MouseKeys.h" diff --git a/src/MouseWrapper.h b/src/MouseWrapper.h new file mode 100644 index 00000000..ad127ce7 --- /dev/null +++ b/src/MouseWrapper.h @@ -0,0 +1,21 @@ +/* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope. + * Copyright (C) 2017-2018 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 + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#warning The "MouseWrapper.h" header is deprecated, the same functionality is included by default when using . It can be safely removed. + +#include "kaleidoscope/plugin/MouseKeys/MouseWrapper.h" diff --git a/src/kaleidoscope/plugin/MouseKeys.cpp b/src/kaleidoscope/plugin/MouseKeys.cpp new file mode 100644 index 00000000..52fbe618 --- /dev/null +++ b/src/kaleidoscope/plugin/MouseKeys.cpp @@ -0,0 +1,190 @@ +/* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope. + * Copyright (C) 2017-2018 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 + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include + +#include "Kaleidoscope.h" +#include "Kaleidoscope-MouseKeys.h" + +namespace kaleidoscope { +namespace plugin { + +uint8_t MouseKeys_::mouseMoveIntent; + +uint8_t MouseKeys_::speed = 1; +uint16_t MouseKeys_::speedDelay = 1; + +uint8_t MouseKeys_::accelSpeed = 1; +uint16_t MouseKeys_::accelDelay = 64; + +uint8_t MouseKeys_::wheelSpeed = 1; +uint16_t MouseKeys_::wheelDelay = 50; + +uint32_t MouseKeys_::accelEndTime; +uint32_t MouseKeys_::endTime; +uint32_t MouseKeys_::wheelEndTime; + +void MouseKeys_::setWarpGridSize(uint8_t grid_size) { + MouseWrapper.warp_grid_size = grid_size; +} + +void MouseKeys_::setSpeedLimit(uint8_t speed_limit) { + MouseWrapper.speedLimit = speed_limit; +} + +void MouseKeys_::scrollWheel(uint8_t keyCode) { + if (millis() < wheelEndTime) + return; + + wheelEndTime = millis() + wheelDelay; + + if (keyCode & KEY_MOUSE_UP) + kaleidoscope::hid::moveMouse(0, 0, wheelSpeed); + else if (keyCode & KEY_MOUSE_DOWN) + kaleidoscope::hid::moveMouse(0, 0, -wheelSpeed); + else if (keyCode & KEY_MOUSE_LEFT) + kaleidoscope::hid::moveMouse(0, 0, 0, -wheelSpeed); + else if (keyCode & KEY_MOUSE_RIGHT) + kaleidoscope::hid::moveMouse(0, 0, 0, wheelSpeed); +} + +EventHandlerResult MouseKeys_::afterEachCycle() { + kaleidoscope::hid::sendMouseReport(); + kaleidoscope::hid::releaseAllMouseButtons(); + mouseMoveIntent = 0; + + return EventHandlerResult::OK; +} + +EventHandlerResult MouseKeys_::beforeReportingState() { + if (mouseMoveIntent == 0) { + MouseWrapper.accelStep = 0; + endTime = 0; + accelEndTime = 0; + return EventHandlerResult::OK; + } + + if (millis() < endTime) + return EventHandlerResult::OK; + + endTime = millis() + speedDelay; + + int8_t moveX = 0, moveY = 0; + + if (millis() >= accelEndTime) { + if (MouseWrapper.accelStep < 255 - accelSpeed) { + MouseWrapper.accelStep += accelSpeed; + } + accelEndTime = millis() + accelDelay; + } + + if (mouseMoveIntent & KEY_MOUSE_UP) + moveY = -speed; + else if (mouseMoveIntent & KEY_MOUSE_DOWN) + moveY = speed; + + if (mouseMoveIntent & KEY_MOUSE_LEFT) + moveX = -speed; + else if (mouseMoveIntent & KEY_MOUSE_RIGHT) + moveX = speed; + + MouseWrapper.move(moveX, moveY); + + return EventHandlerResult::OK; +} + +EventHandlerResult MouseKeys_::onKeyswitchEvent(Key &mappedKey, byte row, byte col, uint8_t keyState) { + if (mappedKey.flags != (SYNTHETIC | IS_MOUSE_KEY)) + return EventHandlerResult::OK; + + if (mappedKey.keyCode & KEY_MOUSE_BUTTON && !(mappedKey.keyCode & KEY_MOUSE_WARP)) { + uint8_t button = mappedKey.keyCode & ~KEY_MOUSE_BUTTON; + + if (keyIsPressed(keyState)) { + // Reset warp state on initial mouse button key-down only so we can use + // warp keys to drag-and-drop: + if (keyToggledOn(keyState)) { + MouseWrapper.reset_warping(); + } + + MouseWrapper.pressButton(button); + } else if (keyToggledOff(keyState)) { + MouseWrapper.release_button(button); + } + } else if (!(mappedKey.keyCode & KEY_MOUSE_WARP)) { + if (keyToggledOn(keyState)) { + endTime = millis() + speedDelay; + accelEndTime = millis() + accelDelay; + wheelEndTime = 0; + } + if (keyIsPressed(keyState)) { + if (mappedKey.keyCode & KEY_MOUSE_WHEEL) { + scrollWheel(mappedKey.keyCode); + } else { + mouseMoveIntent |= mappedKey.keyCode; + } + } else if (keyToggledOff(keyState)) { + /* If a mouse key toggles off, we want to explicitly stop moving (or + * scrolling) in that direction. We want to do this to support use-cases + * where we send multiple reports per cycle (such as macros), and can't + * rely on the main loop clearing the report for us. We do not want to + * clear the whole report either, because we want any other mouse keys + * to still have their desired effect. Therefore, we selectively stop + * movement or scrolling. */ + mouseMoveIntent &= ~mappedKey.keyCode; + bool x = false, y = false, vWheel = false, hWheel = false; + + if (mappedKey.keyCode & KEY_MOUSE_UP || + mappedKey.keyCode & KEY_MOUSE_DOWN) { + if (mappedKey.keyCode & KEY_MOUSE_WHEEL) { + vWheel = true; + } else { + y = true; + } + } else if (mappedKey.keyCode & KEY_MOUSE_LEFT || + mappedKey.keyCode & KEY_MOUSE_RIGHT) { + if (mappedKey.keyCode & KEY_MOUSE_WHEEL) { + hWheel = true; + } else { + x = true; + } + } + + kaleidoscope::hid::stopMouse(x, y, vWheel, hWheel); + } + } else if (keyToggledOn(keyState)) { + if (mappedKey.keyCode & KEY_MOUSE_WARP && mappedKey.flags & IS_MOUSE_KEY) { + MouseWrapper.warp(((mappedKey.keyCode & KEY_MOUSE_WARP_END) ? WARP_END : 0x00) | + ((mappedKey.keyCode & KEY_MOUSE_UP) ? WARP_UP : 0x00) | + ((mappedKey.keyCode & KEY_MOUSE_DOWN) ? WARP_DOWN : 0x00) | + ((mappedKey.keyCode & KEY_MOUSE_LEFT) ? WARP_LEFT : 0x00) | + ((mappedKey.keyCode & KEY_MOUSE_RIGHT) ? WARP_RIGHT : 0x00)); + } + } + + return EventHandlerResult::EVENT_CONSUMED; +} + +EventHandlerResult MouseKeys_::onSetup(void) { + MouseWrapper.begin(); + + return EventHandlerResult::OK; +} + +} +} + +kaleidoscope::plugin::MouseKeys_ MouseKeys; diff --git a/src/kaleidoscope/plugin/MouseKeys.h b/src/kaleidoscope/plugin/MouseKeys.h new file mode 100644 index 00000000..71ba3fd4 --- /dev/null +++ b/src/kaleidoscope/plugin/MouseKeys.h @@ -0,0 +1,56 @@ +/* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope. + * Copyright (C) 2017-2018 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 + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include "Kaleidoscope.h" +#include "kaleidoscope/plugin/MouseKeys/MouseKeyDefs.h" +#include "kaleidoscope/plugin/MouseKeys/MouseWarpModes.h" +#include "kaleidoscope/plugin/MouseKeys/MouseWrapper.h" + +namespace kaleidoscope { +namespace plugin { +class MouseKeys_ : public kaleidoscope::Plugin { + public: + MouseKeys_(void) {} + + static uint8_t speed; + static uint16_t speedDelay; + static uint8_t accelSpeed; + static uint16_t accelDelay; + static uint8_t wheelSpeed; + static uint16_t wheelDelay; + + static void setWarpGridSize(uint8_t grid_size); + static void setSpeedLimit(uint8_t speed_limit); + + EventHandlerResult onSetup(); + EventHandlerResult beforeReportingState(); + EventHandlerResult afterEachCycle(); + EventHandlerResult onKeyswitchEvent(Key &mappedKey, byte row, byte col, uint8_t keyState); + + private: + static uint8_t mouseMoveIntent; + static uint32_t endTime; + static uint32_t accelEndTime; + static uint32_t wheelEndTime; + + static void scrollWheel(uint8_t keyCode); +}; +} +} + +extern kaleidoscope::plugin::MouseKeys_ MouseKeys; diff --git a/src/kaleidoscope/plugin/MouseKeys/MouseKeyDefs.h b/src/kaleidoscope/plugin/MouseKeys/MouseKeyDefs.h new file mode 100644 index 00000000..a6e8c6b7 --- /dev/null +++ b/src/kaleidoscope/plugin/MouseKeys/MouseKeyDefs.h @@ -0,0 +1,67 @@ +/* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope. + * Copyright (C) 2017-2018 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 + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#define IS_MOUSE_KEY B00010000 + +// Synthetic, not internal +#define KEY_MOUSE_BTN_L MOUSE_LEFT // Synthetic key +#define KEY_MOUSE_BTN_M MOUSE_MIDDLE // Synthetic key +#define KEY_MOUSE_BTN_R MOUSE_RIGHT // Synthetic key +#define KEY_MOUSE_BTN_P MOUSE_PREV +#define KEY_MOUSE_BTN_N MOUSE_NEXT + +#define KEY_MOUSE_UP B0000001 +#define KEY_MOUSE_DOWN B0000010 +#define KEY_MOUSE_LEFT B0000100 +#define KEY_MOUSE_RIGHT B0001000 +#define KEY_MOUSE_WHEEL B0010000 +#define KEY_MOUSE_WARP B0100000 +#define KEY_MOUSE_WARP_END B1000000 +// all buttons end warp. also, we're out of bits +#define KEY_MOUSE_BUTTON B1000000 + + +#define Key_mouseWarpNW (Key) { KEY_MOUSE_WARP| KEY_MOUSE_UP | KEY_MOUSE_LEFT, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseWarpN (Key) { KEY_MOUSE_WARP| KEY_MOUSE_UP, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseWarpNE (Key) { KEY_MOUSE_WARP| KEY_MOUSE_UP | KEY_MOUSE_RIGHT, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseWarpW (Key) { KEY_MOUSE_WARP| KEY_MOUSE_LEFT, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseWarpIn (Key) { KEY_MOUSE_WARP| KEY_MOUSE_UP | KEY_MOUSE_DOWN, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseWarpE (Key) { KEY_MOUSE_WARP| KEY_MOUSE_RIGHT, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseWarpSW (Key) { KEY_MOUSE_WARP| KEY_MOUSE_DOWN | KEY_MOUSE_LEFT, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseWarpS (Key) { KEY_MOUSE_WARP| KEY_MOUSE_DOWN, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseWarpSE (Key) { KEY_MOUSE_WARP| KEY_MOUSE_DOWN | KEY_MOUSE_RIGHT, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseWarpEnd (Key) { KEY_MOUSE_WARP| KEY_MOUSE_WARP_END, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } + + +#define Key_mouseUpL (Key) { KEY_MOUSE_UP | KEY_MOUSE_LEFT, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseUp (Key) { KEY_MOUSE_UP, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseUpR (Key) { KEY_MOUSE_UP | KEY_MOUSE_RIGHT, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseL (Key) { KEY_MOUSE_LEFT, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseR (Key) { KEY_MOUSE_RIGHT, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseDnL (Key) { KEY_MOUSE_DOWN | KEY_MOUSE_LEFT, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseDn (Key) { KEY_MOUSE_DOWN, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseDnR (Key) { KEY_MOUSE_DOWN | KEY_MOUSE_RIGHT, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseScrollUp (Key) { KEY_MOUSE_WHEEL | KEY_MOUSE_UP, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseScrollDn (Key) { KEY_MOUSE_WHEEL | KEY_MOUSE_DOWN, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseScrollL (Key) { KEY_MOUSE_WHEEL | KEY_MOUSE_LEFT, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseScrollR (Key) { KEY_MOUSE_WHEEL | KEY_MOUSE_RIGHT, KEY_FLAGS|SYNTHETIC|IS_MOUSE_KEY } +#define Key_mouseBtnL (Key) { KEY_MOUSE_BUTTON | KEY_MOUSE_BTN_L, KEY_FLAGS | SYNTHETIC | IS_MOUSE_KEY } +#define Key_mouseBtnM (Key) { KEY_MOUSE_BUTTON | KEY_MOUSE_BTN_M, KEY_FLAGS | SYNTHETIC | IS_MOUSE_KEY } +#define Key_mouseBtnR (Key) { KEY_MOUSE_BUTTON | KEY_MOUSE_BTN_R, KEY_FLAGS | SYNTHETIC | IS_MOUSE_KEY } +#define Key_mouseBtnP (Key) { KEY_MOUSE_BUTTON | KEY_MOUSE_BTN_P, KEY_FLAGS | SYNTHETIC | IS_MOUSE_KEY } +#define Key_mouseBtnN (Key) { KEY_MOUSE_BUTTON | KEY_MOUSE_BTN_N, KEY_FLAGS | SYNTHETIC | IS_MOUSE_KEY } diff --git a/src/kaleidoscope/plugin/MouseKeys/MouseWarpModes.h b/src/kaleidoscope/plugin/MouseKeys/MouseWarpModes.h new file mode 100644 index 00000000..a81355ac --- /dev/null +++ b/src/kaleidoscope/plugin/MouseKeys/MouseWarpModes.h @@ -0,0 +1,26 @@ +/* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope. + * Copyright (C) 2017-2018 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 + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +// Warp modes determine how the plugin jumps the mouse pointer to a screen +// location when pressing a warp key. + + +// Grid Based - the constant represents the number of cells in a row or column: + +#define MOUSE_WARP_GRID_2X2 2 +#define MOUSE_WARP_GRID_3X3 3 diff --git a/src/kaleidoscope/plugin/MouseKeys/MouseWrapper.cpp b/src/kaleidoscope/plugin/MouseKeys/MouseWrapper.cpp new file mode 100644 index 00000000..b52fb2c1 --- /dev/null +++ b/src/kaleidoscope/plugin/MouseKeys/MouseWrapper.cpp @@ -0,0 +1,182 @@ +/* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope. + * Copyright (C) 2017-2018 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 + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + + +// Mouse-related methods +// +// +#include "kaleidoscope/plugin/MouseKeys/MouseWrapper.h" +#include "kaleidoscope/hid.h" + +namespace kaleidoscope { +namespace plugin { + +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; +boolean MouseWrapper_::is_warping; + +uint8_t MouseWrapper_::accelStep; +uint8_t MouseWrapper_::speedLimit = 127; +uint8_t MouseWrapper_::subpixelsPerPixel = 16; + +MouseWrapper_::MouseWrapper_(void) { +} + +void MouseWrapper_::begin(void) { + kaleidoscope::hid::initializeMouse(); + kaleidoscope::hid::initializeAbsoluteMouse(); +} + +void MouseWrapper_::pressButton(uint8_t button) { + kaleidoscope::hid::pressMouseButtons(button); +} + +void MouseWrapper_::release_button(uint8_t button) { + kaleidoscope::hid::releaseMouseButtons(button); + end_warping(); +} + +void MouseWrapper_::warp_jump(uint16_t left, uint16_t top, uint16_t height, uint16_t width) { + uint16_t x_center = left + width / 2; + uint16_t y_center = top + height / 2; + kaleidoscope::hid::moveAbsoluteMouseTo(x_center, y_center, 0); +} + +void MouseWrapper_::begin_warping() { + section_left = WARP_ABS_LEFT; + section_top = WARP_ABS_TOP; + next_width = MAX_WARP_WIDTH; + next_height = MAX_WARP_HEIGHT; + is_warping = true; +} + +void MouseWrapper_::end_warping() { + is_warping = false; +} + +void MouseWrapper_::reset_warping() { + if (is_warping == true) { + begin_warping(); + } +} + +void MouseWrapper_::warp(uint8_t warp_cmd) { + if (is_warping == false) { + begin_warping(); + } + + if (warp_cmd & WARP_END) { + end_warping(); + return; + } + + next_width /= warp_grid_size; + next_height /= warp_grid_size; + + // WARP_UP + WARP_DOWN means "zoom in" to center sector + if (warp_cmd & WARP_UP && warp_cmd & WARP_DOWN) { + section_left += next_width; + section_top += next_height; + + warp_jump(section_left, section_top, next_height, next_width); + + return; + } + + if (warp_cmd & WARP_DOWN) { + section_top += next_height * (warp_grid_size - 1); + } else if (!(warp_cmd & WARP_UP)) { + section_top += next_height; + } + + if (warp_cmd & WARP_RIGHT) { + section_left += next_width * (warp_grid_size - 1); + } else if (!(warp_cmd & WARP_LEFT)) { + section_left += next_width; + } + + warp_jump(section_left, section_top, next_height, next_width); +} + +// cubic wave function based on code from FastLED +// produces a shape similar to a sine curve from 0 to 255 +// (slow growth at 0, fast growth in the middle, slow growth at 255) +// http://www.wolframalpha.com/input/?i=((3((x)**2)%2F256)+-+((2((x)(x)(x%2F256))%2F256)))+%2B+1 +uint8_t MouseWrapper_::acceleration(uint8_t cycles) { + uint16_t i = cycles; + + uint16_t ii = (i * i) >> 8; + uint16_t iii = (ii * i) >> 8; + + i = ((3 * ii) - (2 * iii)) + 1; + + // Just in case (may go up to 256 at peak) + if (i > 255) i = 255; + + return i; +} + +// Get the diagonalized version of a value, i.e. value * sqrt(2) / 2. If the +// value ends up being zero, return the original value instead. +static int16_t diagonalize(int16_t value) { + // 99 / 140 closely approximates sqrt(2) / 2. Since integer division + // truncates towards zero we do not need to worry about truncation errors. + int16_t diagonalValue = value * 99 / 140; + return (diagonalValue == 0 ? value : diagonalValue); +} + +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 = speedLimit; + + if (x != 0 && y != 0) { + // For diagonal movements, we apply a diagonalized speed limit. The + // effective speed limit is set based on whether we are moving diagonally. + effectiveSpeedLimit = diagonalize(effectiveSpeedLimit); + + x = diagonalize(x); + y = diagonalize(y); + } + + if (x != 0) { + moveX = remainderX + (x * acceleration(accelStep)); + if (moveX > effectiveSpeedLimit) moveX = effectiveSpeedLimit; + else if (moveX < -effectiveSpeedLimit) moveX = -effectiveSpeedLimit; + } + + if (y != 0) { + moveY = remainderY + (y * acceleration(accelStep)); + if (moveY > effectiveSpeedLimit) moveY = effectiveSpeedLimit; + else if (moveY < -effectiveSpeedLimit) moveY = -effectiveSpeedLimit; + } + + end_warping(); + // move by whole pixels, not subpixels + kaleidoscope::hid::moveMouse(moveX / subpixelsPerPixel, moveY / subpixelsPerPixel, 0); + // save leftover subpixel movements for later + remainderX = moveX - moveX / subpixelsPerPixel * subpixelsPerPixel; + remainderY = moveY - moveY / subpixelsPerPixel * subpixelsPerPixel; +} +} +} + +kaleidoscope::plugin::MouseWrapper_ MouseWrapper; diff --git a/src/kaleidoscope/plugin/MouseKeys/MouseWrapper.h b/src/kaleidoscope/plugin/MouseKeys/MouseWrapper.h new file mode 100644 index 00000000..c2641771 --- /dev/null +++ b/src/kaleidoscope/plugin/MouseKeys/MouseWrapper.h @@ -0,0 +1,74 @@ +/* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope. + * Copyright (C) 2017-2018 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 + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include "Arduino.h" +#include "kaleidoscope/plugin/MouseKeys/MouseWarpModes.h" + +// Warping commands + +#define WARP_END 1 +#define WARP_UP 2 +#define WARP_DOWN 4 +#define WARP_LEFT 8 +#define WARP_RIGHT 16 + +// 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 + +#define WARP_ABS_TOP 0 +#define WARP_ABS_LEFT 0 + +// Mouse acceleration + +namespace kaleidoscope { +namespace plugin { + +class MouseWrapper_ { + public: + MouseWrapper_(void); + + static void begin(void); + static void move(int8_t x, int8_t y); + static void warp(uint8_t warp_cmd); + static void reset_warping(); + static void pressButton(uint8_t button); + static void release_button(uint8_t button); + static uint8_t accelStep; + static uint8_t speedLimit; + static uint8_t subpixelsPerPixel; + static uint8_t warp_grid_size; + + private: + static uint16_t next_width; + static uint16_t next_height; + static uint16_t section_top; + static uint16_t section_left; + static boolean is_warping; + + static uint8_t acceleration(uint8_t cycles); + static void begin_warping(); + static void end_warping(); + static void warp_jump(uint16_t left, uint16_t top, uint16_t height, uint16_t width); +}; +} +} + +extern kaleidoscope::plugin::MouseWrapper_ MouseWrapper;