#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
}
int16_t thumbstick_get_component(uint8_t pin) {
uint16_t analogValue = analogReadPin(pin);
bool directionIsPositive = (analogValue > THUMBSTICK_RANGE_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
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);
}
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;
}
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);
if (outsideDiagonalZone || !eightAxis) {
if (absX > absY) {
direction.up = direction.down = false;
} else {
direction.left = direction.right = false;
}
}
}
return direction;
}
thumbstick_direction_t scrollDirection; 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();
}