NOTE: This gist is deprecated. I've moved everything into
/users/muppetjones
, and I have an open PR on the QMK repo. Eventually, this will probably be merged, and I'll (hopefully) remember to update this note.
Last active
August 26, 2023 15:18
-
-
Save muppetjones/bd7e7ff9bd4c503b37f567289a356721 to your computer and use it in GitHub Desktop.
QMK Mouse Movement via Encoders
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Copyright 2020 Stephen J. Bush | |
* | |
* 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, either version 2 of the License, or | |
* (at your option) any later version. | |
* | |
* 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 <http://www.gnu.org/licenses/>. | |
# Example | |
See https://github.com/muppetjones/qmk_firmware/tree/master/keyboards/kyria/keymaps/muppetjones | |
# Description | |
Use encoders to control mouse movement. Movement speed is determined by | |
how quickly and how many times you've "clicked" the encoder. Turning both | |
encoders together will yield diagonal movement. | |
Several of the macros defined in quantum/mousekey.h are reused here. | |
However, the mouse movement is actually performed using the pointing_device | |
features instead. | |
# Usage | |
- Add the following to your rules.mk | |
ENCODER_ENABLE = yes # Enables the use of one or more encoders | |
POINTING_DEVICE_ENABLE = yes | |
SRC += "encoder_mouse.c" | |
- Add the following block to your keymap.c | |
(NOTE: The `encoder_update_*` signature returns a bool in | |
more recent versions of QMK.) | |
#ifdef ENCODER_ENABLE | |
void encoder_update_user(uint8_t index, bool clockwise) { | |
# ifdef POINTING_DEVICE_ENABLE | |
encoder_update_mouse(index, clockwise); | |
# endif | |
return; | |
#endif | |
# Description | |
- The faster you turn, the faster the mouse moves. | |
- The more turns for a given axis, the greater the movement on that axis. | |
*/ | |
#include QMK_KEYBOARD_H | |
#include "encoder_mouse.h" | |
#include "pointing_device.h" | |
#ifdef POINTING_DEVICE_ENABLE | |
# ifdef ENCODER_ENABLE | |
/** Track movement separately in both directions. This will allow us to | |
* smooth out the movement along diagonals | |
*/ | |
typedef struct { | |
bool clockwise; | |
uint8_t count; | |
uint16_t timer; | |
uint16_t elapsed; | |
} key_tracker_t; | |
static key_tracker_t tracker_x = {false, 0, 0, 0}; | |
static key_tracker_t tracker_y = {false, 0, 0, 0}; | |
/** | |
* @brief Calculate the mouse move units for the given tracker. | |
* | |
* By using a key tracker rederence, we can minimize the amount of space | |
* required on the stack. As we will have the tracker object, we will also | |
* take the clockwise direction into account, which completely internalizes | |
* the movement unit logic within this single function. | |
* | |
* @param tracker: Pointer to a key tracker object. | |
* @return A integer from -127 to 127 | |
*/ | |
static int8_t move_unit(key_tracker_t *tracker) { | |
if (0 == tracker->count) return 0; | |
const uint16_t modifier = TAPPING_TERM_MOUSE_ENCODER < tracker->elapsed ? 1 : (TAPPING_TERM_MOUSE_ENCODER - tracker->elapsed) >> 1; | |
uint16_t speed = MOUSEKEY_INITIAL_SPEED + MOUSEKEY_MOVE_DELTA * modifier * (tracker->count >> 1); | |
/* convert speed to USB mouse speed 1 to 127 */ | |
speed = (uint8_t)(speed / (1000.0f / MOUSEKEY_INTERVAL)); | |
speed = speed < 1 ? 1 : speed; | |
return (tracker->clockwise ? 1 : -1) * (speed > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : speed); | |
} | |
/** | |
* @brief Update key press tracker | |
* | |
* Update the time elapsed since the last keypress. | |
* If the key has not been pressed since the tapping term, then reset the count to zero. | |
* If the key was pressed, update the timer and increment the count. | |
* Number of keypresses will degrade based on tapping term and zero out based | |
* on the persistenc term. | |
* | |
* @param tracker: The object to update | |
* @param pressed: A boolean indicating whether or not the key was pressed | |
* @return None. | |
*/ | |
static void update_tracker(key_tracker_t *tracker, bool pressed, bool clockwise) { | |
tracker->elapsed = timer_elapsed(tracker->timer); | |
if (pressed) { | |
tracker->timer = timer_read(); | |
tracker->count += 1; | |
tracker->clockwise = clockwise; | |
} else if (TAPPING_TERM_PERSISTENCE < tracker->elapsed) { | |
tracker->count = 0; | |
} else if (TAPPING_TERM_MOUSE_ENCODER < tracker->elapsed) { | |
tracker->count >>= 1; | |
} | |
} | |
void encoder_update_mouse(uint8_t index, bool clockwise) { | |
report_mouse_t curr_report = pointing_device_get_report(); | |
update_tracker(&tracker_x, 0 == index, clockwise); | |
update_tracker(&tracker_y, 1 == index, clockwise); | |
curr_report.x += move_unit(&tracker_x); | |
curr_report.y += move_unit(&tracker_y); | |
pointing_device_set_report(curr_report); | |
pointing_device_send(); | |
} | |
# endif | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Copyright 2020 Stephen J. Bush | |
* | |
* 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, either version 2 of the License, or | |
* (at your option) any later version. | |
* | |
* 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 <http://www.gnu.org/licenses/>. | |
*/ | |
#ifdef POINTING_DEVICE_ENABLE | |
# ifdef ENCODER_ENABLE | |
/* max value on report descriptor */ | |
# ifndef MOUSEKEY_MOVE_MAX | |
# define MOUSEKEY_MOVE_MAX 127 | |
# elif MOUSEKEY_MOVE_MAX > 127 | |
# error MOUSEKEY_MOVE_MAX needs to be smaller than 127 | |
# endif | |
# ifndef MOUSEKEY_MOVE_DELTA | |
# define MOUSEKEY_MOVE_DELTA 25 | |
# endif | |
# ifndef MOUSEKEY_INITIAL_SPEED | |
# define MOUSEKEY_INITIAL_SPEED 100 | |
# endif | |
# ifndef MOUSEKEY_INTERVAL | |
# define MOUSEKEY_INTERVAL 50 | |
# endif | |
/** Amount of time (ms) before zeroing out the count. | |
* A higher value will result in smoother curves but may lower accuracy | |
*/ | |
# ifndef TAPPING_TERM_PERSISTENCE | |
# define TAPPING_TERM_PERSISTENCE 601 | |
# endif | |
/** Amount of time (ms) to register consecutive key presses | |
* A higher value will smooth out mouse movement and increase speed for | |
* consecutive presses. | |
*/ | |
# ifndef TAPPING_TERM_MOUSE_ENCODER | |
# define TAPPING_TERM_MOUSE_ENCODER 200 | |
# endif | |
/** @brief Update mouse position based on encoder movement. | |
* @param index The encoder index. 0 controls x-axis; 1 controls y-axis. | |
* @param clockwise Indicates direction encoder was turned. | |
* @returns None. | |
*/ | |
void encoder_update_mouse(uint8_t index, bool clockwise); | |
# endif | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@rflcrz I've incorporated this into my QMK userspace. I recently updated it, and the compile worked (I was not able to flash by board at the time).
I'd recommend starting with the updates found in that PR. Happy to help if that doesn't solve the issue.