mirror of
https://github.com/qmk/qmk_firmware.git
synced 2024-12-25 02:49:55 +00:00
196 lines
8.1 KiB
C
196 lines
8.1 KiB
C
|
#include "thumbstick.h"
|
||
|
|
||
|
void thumbstick_init(void) {
|
||
|
thumbstickTimer = 0;
|
||
|
thumbstickScrollTimer = 0;
|
||
|
|
||
|
thumbstick_state.config.mode = THUMBSTICK_MODE_MOUSE;
|
||
|
thumbstick_state.config.deadZone = THUMBSTICK_DEAD_ZONE;
|
||
|
thumbstick_state.config.fineZone = THUMBSTICK_FINE_ZONE;
|
||
|
thumbstick_state.config.speed = THUMBSTICK_SPEED;
|
||
|
thumbstick_state.config.fineSpeed = THUMBSTICK_FINE_SPEED;
|
||
|
thumbstick_state.config.axisSeparation = THUMBSTICK_AXIS_SEPARATION;
|
||
|
thumbstick_state.config.eightAxis = THUMBSTICK_EIGHT_AXIS;
|
||
|
|
||
|
#if defined THUMBSTICK_DEBUG
|
||
|
rawX = 0;
|
||
|
rawY = 0;
|
||
|
distX = 0;
|
||
|
distY = 0;
|
||
|
thumbstickLogTimer = 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// Axis-level wrapper to read raw value, do logging and calculate speed
|
||
|
int16_t thumbstick_get_component(uint8_t pin) {
|
||
|
uint16_t analogValue = analogReadPin(pin);
|
||
|
// Compute direction
|
||
|
bool directionIsPositive = (analogValue > THUMBSTICK_RANGE_CENTER);
|
||
|
// Compute distance from the center
|
||
|
uint16_t distance = directionIsPositive ? (analogValue - THUMBSTICK_RANGE_CENTER) : (THUMBSTICK_RANGE_CENTER - analogValue);
|
||
|
#if defined THUMBSTICK_DEBUG
|
||
|
if (pin == THUMBSTICK_PIN_X) {
|
||
|
rawX = analogValue;
|
||
|
distX = distance;
|
||
|
} else {
|
||
|
rawY = analogValue;
|
||
|
distY = distance;
|
||
|
}
|
||
|
#endif
|
||
|
// Compute component (range of [0 to 1023])
|
||
|
return directionIsPositive ? distance : -(int16_t)distance;
|
||
|
}
|
||
|
|
||
|
void thumbstick_mode_set(thumbstick_mode_t mode) { thumbstick_state.config.mode = mode; }
|
||
|
|
||
|
thumbstick_mode_t thumbstick_mode_get(void) { return thumbstick_state.config.mode; }
|
||
|
|
||
|
void thumbstick_mode_cycle(bool reverse) {
|
||
|
thumbstick_mode_t mode = thumbstick_mode_get();
|
||
|
if (reverse) {
|
||
|
mode = (mode == 0) ? (_THUMBSTICK_MODE_LAST - 1) : (mode - 1);
|
||
|
} else {
|
||
|
mode = (mode == (_THUMBSTICK_MODE_LAST - 1)) ? 0 : (mode + 1);
|
||
|
}
|
||
|
thumbstick_mode_set(mode);
|
||
|
}
|
||
|
|
||
|
// Get mouse speed
|
||
|
int16_t thumbstick_get_mouse_speed(int16_t component) {
|
||
|
int16_t maxSpeed;
|
||
|
uint16_t distance = abs(component);
|
||
|
if (distance > THUMBSTICK_FINE_ZONE) {
|
||
|
maxSpeed = THUMBSTICK_SPEED;
|
||
|
} else if (distance > THUMBSTICK_DEAD_ZONE) {
|
||
|
maxSpeed = THUMBSTICK_FINE_SPEED;
|
||
|
} else {
|
||
|
return 0;
|
||
|
}
|
||
|
return (float)maxSpeed * component / THUMBSTICK_RANGE_CENTER;
|
||
|
}
|
||
|
|
||
|
// Fix direction within one of 8 axes (or 4 if 8-axis is disabled)
|
||
|
thumbstick_direction_t thumbstick_get_discretized_direction(thumbstick_vector_t vector, float axisSeparation, bool eightAxis) {
|
||
|
thumbstick_direction_t direction;
|
||
|
uint16_t absX = abs(vector.x);
|
||
|
uint16_t absY = abs(vector.y);
|
||
|
uint16_t maxComponent = (absX > absY) ? absX : absY;
|
||
|
bool insideDeadZone = (maxComponent <= THUMBSTICK_DEAD_ZONE);
|
||
|
bool outsideDiagonalZone = ((abs(absX - absY) / (float)maxComponent) >= axisSeparation);
|
||
|
if (insideDeadZone) {
|
||
|
direction.up = direction.down = direction.left = direction.right = false;
|
||
|
} else {
|
||
|
direction.up = (vector.y < 0);
|
||
|
direction.down = (vector.y > 0);
|
||
|
direction.left = (vector.x < 0);
|
||
|
direction.right = (vector.x > 0);
|
||
|
// Let only the dominant direction remain under the right conditions
|
||
|
if (outsideDiagonalZone || !eightAxis) {
|
||
|
if (absX > absY) {
|
||
|
direction.up = direction.down = false;
|
||
|
} else {
|
||
|
direction.left = direction.right = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return direction;
|
||
|
}
|
||
|
|
||
|
thumbstick_direction_t scrollDirection; // Declaring global to save stack space
|
||
|
void thumbstick_process(void) {
|
||
|
if (timer_elapsed(thumbstickTimer) > THUMBSTICK_TIMEOUT) {
|
||
|
thumbstickTimer = timer_read();
|
||
|
#ifndef THUMBSTICK_FLIP_X
|
||
|
thumbstick_state.vector.x = thumbstick_get_component(THUMBSTICK_PIN_X);
|
||
|
#else
|
||
|
thumbstick_state.vector.x = -thumbstick_get_component(THUMBSTICK_PIN_X);
|
||
|
#endif
|
||
|
#ifndef THUMBSTICK_FLIP_Y
|
||
|
thumbstick_state.vector.y = thumbstick_get_component(THUMBSTICK_PIN_Y);
|
||
|
#else
|
||
|
thumbstick_state.vector.y = -thumbstick_get_component(THUMBSTICK_PIN_Y);
|
||
|
#endif
|
||
|
switch (thumbstick_state.config.mode) {
|
||
|
case THUMBSTICK_MODE_MOUSE:
|
||
|
thumbstick_state.report.x = thumbstick_get_mouse_speed(thumbstick_state.vector.x);
|
||
|
thumbstick_state.report.y = thumbstick_get_mouse_speed(thumbstick_state.vector.y);
|
||
|
break;
|
||
|
case THUMBSTICK_MODE_ARROWS:
|
||
|
thumbstick_state.direction = thumbstick_get_discretized_direction(thumbstick_state.vector, thumbstick_state.config.axisSeparation, thumbstick_state.config.eightAxis);
|
||
|
break;
|
||
|
case THUMBSTICK_MODE_SCROLL:
|
||
|
if (timer_elapsed(thumbstickScrollTimer) > THUMBSTICK_SCROLL_TIMEOUT) {
|
||
|
thumbstickScrollTimer = timer_read();
|
||
|
scrollDirection = thumbstick_get_discretized_direction(thumbstick_state.vector, thumbstick_state.config.axisSeparation, false);
|
||
|
thumbstick_state.report.v = (scrollDirection.up || scrollDirection.down) ? (scrollDirection.up ? THUMBSTICK_SCROLL_SPEED : -THUMBSTICK_SCROLL_SPEED) : 0;
|
||
|
thumbstick_state.report.h = (scrollDirection.left || scrollDirection.right) ? (scrollDirection.left ? -THUMBSTICK_SCROLL_SPEED : THUMBSTICK_SCROLL_SPEED) : 0;
|
||
|
} else {
|
||
|
thumbstick_state.report.v = thumbstick_state.report.h = 0;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void update_keycode_status(uint16_t keycode, bool last, bool current) {
|
||
|
if (last != current) {
|
||
|
if (current) {
|
||
|
register_code16(keycode);
|
||
|
} else {
|
||
|
unregister_code16(keycode);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void pointing_device_init(void) { thumbstick_init(); }
|
||
|
|
||
|
void pointing_device_task(void) {
|
||
|
report_mouse_t report = pointing_device_get_report();
|
||
|
|
||
|
if (!isLeftHand) {
|
||
|
thumbstick_process();
|
||
|
switch (thumbstick_state.config.mode) {
|
||
|
case THUMBSTICK_MODE_MOUSE:
|
||
|
report.x = thumbstick_state.report.x;
|
||
|
report.y = thumbstick_state.report.y;
|
||
|
#ifdef THUMBSTICK_DEBUG
|
||
|
if (timer_elapsed(thumbstickLogTimer) > 100) {
|
||
|
thumbstickLogTimer = timer_read();
|
||
|
uprintf("Raw (%d, %d); Dist (%u, %u); Vec (%d, %d);\n", rawX, rawY, distX, distY, thumbstick_state.vector.x, thumbstick_state.vector.y);
|
||
|
}
|
||
|
#endif
|
||
|
break;
|
||
|
case THUMBSTICK_MODE_ARROWS:
|
||
|
update_keycode_status(KC_UP, thumbstick_state.lastDirection.up, thumbstick_state.direction.up);
|
||
|
update_keycode_status(KC_DOWN, thumbstick_state.lastDirection.down, thumbstick_state.direction.down);
|
||
|
update_keycode_status(KC_LEFT, thumbstick_state.lastDirection.left, thumbstick_state.direction.left);
|
||
|
update_keycode_status(KC_RIGHT, thumbstick_state.lastDirection.right, thumbstick_state.direction.right);
|
||
|
thumbstick_state.lastDirection = thumbstick_state.direction;
|
||
|
#ifdef THUMBSTICK_DEBUG
|
||
|
if (timer_elapsed(thumbstickLogTimer) > 100) {
|
||
|
thumbstickLogTimer = timer_read();
|
||
|
uprintf("Up %d; Down %d; Left: %d; Right %d; Vec (%d, %d);\n", direction.up, direction.down, direction.left, direction.right, thumbstick_state.vector.x, thumbstick_state.vector.y);
|
||
|
}
|
||
|
#endif
|
||
|
break;
|
||
|
case THUMBSTICK_MODE_SCROLL:
|
||
|
report.v = thumbstick_state.report.v;
|
||
|
report.h = thumbstick_state.report.h;
|
||
|
#ifdef THUMBSTICK_DEBUG
|
||
|
if (timer_elapsed(thumbstickLogTimer) > 100) {
|
||
|
thumbstickLogTimer = timer_read();
|
||
|
uprintf("Scroll (%d, %d)\n", report.h, report.v);
|
||
|
}
|
||
|
#endif
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pointing_device_set_report(report);
|
||
|
pointing_device_send();
|
||
|
}
|