thumbstick.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. #include "thumbstick.h"
  2. void thumbstick_init(void) {
  3. thumbstickTimer = 0;
  4. thumbstickScrollTimer = 0;
  5. thumbstick_state.config.mode = THUMBSTICK_MODE_MOUSE;
  6. thumbstick_state.config.deadZone = THUMBSTICK_DEAD_ZONE;
  7. thumbstick_state.config.fineZone = THUMBSTICK_FINE_ZONE;
  8. thumbstick_state.config.speed = THUMBSTICK_SPEED;
  9. thumbstick_state.config.fineSpeed = THUMBSTICK_FINE_SPEED;
  10. thumbstick_state.config.axisSeparation = THUMBSTICK_AXIS_SEPARATION;
  11. thumbstick_state.config.eightAxis = THUMBSTICK_EIGHT_AXIS;
  12. #if defined THUMBSTICK_DEBUG
  13. rawX = 0;
  14. rawY = 0;
  15. distX = 0;
  16. distY = 0;
  17. thumbstickLogTimer = 0;
  18. #endif
  19. }
  20. // Axis-level wrapper to read raw value, do logging and calculate speed
  21. int16_t thumbstick_get_component(uint8_t pin) {
  22. uint16_t analogValue = analogReadPin(pin);
  23. // Compute direction
  24. bool directionIsPositive = (analogValue > THUMBSTICK_RANGE_CENTER);
  25. // Compute distance from the center
  26. uint16_t distance = directionIsPositive ? (analogValue - THUMBSTICK_RANGE_CENTER) : (THUMBSTICK_RANGE_CENTER - analogValue);
  27. #if defined THUMBSTICK_DEBUG
  28. if (pin == THUMBSTICK_PIN_X) {
  29. rawX = analogValue;
  30. distX = distance;
  31. } else {
  32. rawY = analogValue;
  33. distY = distance;
  34. }
  35. #endif
  36. // Compute component (range of [0 to 1023])
  37. return directionIsPositive ? distance : -(int16_t)distance;
  38. }
  39. void thumbstick_mode_set(thumbstick_mode_t mode) { thumbstick_state.config.mode = mode; }
  40. thumbstick_mode_t thumbstick_mode_get(void) { return thumbstick_state.config.mode; }
  41. void thumbstick_mode_cycle(bool reverse) {
  42. thumbstick_mode_t mode = thumbstick_mode_get();
  43. if (reverse) {
  44. mode = (mode == 0) ? (_THUMBSTICK_MODE_LAST - 1) : (mode - 1);
  45. } else {
  46. mode = (mode == (_THUMBSTICK_MODE_LAST - 1)) ? 0 : (mode + 1);
  47. }
  48. thumbstick_mode_set(mode);
  49. }
  50. // Get mouse speed
  51. int16_t thumbstick_get_mouse_speed(int16_t component) {
  52. int16_t maxSpeed;
  53. uint16_t distance = abs(component);
  54. if (distance > THUMBSTICK_FINE_ZONE) {
  55. maxSpeed = THUMBSTICK_SPEED;
  56. } else if (distance > THUMBSTICK_DEAD_ZONE) {
  57. maxSpeed = THUMBSTICK_FINE_SPEED;
  58. } else {
  59. return 0;
  60. }
  61. return (float)maxSpeed * component / THUMBSTICK_RANGE_CENTER;
  62. }
  63. // Fix direction within one of 8 axes (or 4 if 8-axis is disabled)
  64. thumbstick_direction_t thumbstick_get_discretized_direction(thumbstick_vector_t vector, float axisSeparation, bool eightAxis) {
  65. thumbstick_direction_t direction;
  66. uint16_t absX = abs(vector.x);
  67. uint16_t absY = abs(vector.y);
  68. uint16_t maxComponent = (absX > absY) ? absX : absY;
  69. bool insideDeadZone = (maxComponent <= THUMBSTICK_DEAD_ZONE);
  70. bool outsideDiagonalZone = ((abs(absX - absY) / (float)maxComponent) >= axisSeparation);
  71. if (insideDeadZone) {
  72. direction.up = direction.down = direction.left = direction.right = false;
  73. } else {
  74. direction.up = (vector.y < 0);
  75. direction.down = (vector.y > 0);
  76. direction.left = (vector.x < 0);
  77. direction.right = (vector.x > 0);
  78. // Let only the dominant direction remain under the right conditions
  79. if (outsideDiagonalZone || !eightAxis) {
  80. if (absX > absY) {
  81. direction.up = direction.down = false;
  82. } else {
  83. direction.left = direction.right = false;
  84. }
  85. }
  86. }
  87. return direction;
  88. }
  89. thumbstick_direction_t scrollDirection; // Declaring global to save stack space
  90. void thumbstick_process(void) {
  91. if (timer_elapsed(thumbstickTimer) > THUMBSTICK_TIMEOUT) {
  92. thumbstickTimer = timer_read();
  93. #ifndef THUMBSTICK_FLIP_X
  94. thumbstick_state.vector.x = thumbstick_get_component(THUMBSTICK_PIN_X);
  95. #else
  96. thumbstick_state.vector.x = -thumbstick_get_component(THUMBSTICK_PIN_X);
  97. #endif
  98. #ifndef THUMBSTICK_FLIP_Y
  99. thumbstick_state.vector.y = thumbstick_get_component(THUMBSTICK_PIN_Y);
  100. #else
  101. thumbstick_state.vector.y = -thumbstick_get_component(THUMBSTICK_PIN_Y);
  102. #endif
  103. switch (thumbstick_state.config.mode) {
  104. case THUMBSTICK_MODE_MOUSE:
  105. thumbstick_state.report.x = thumbstick_get_mouse_speed(thumbstick_state.vector.x);
  106. thumbstick_state.report.y = thumbstick_get_mouse_speed(thumbstick_state.vector.y);
  107. break;
  108. case THUMBSTICK_MODE_ARROWS:
  109. thumbstick_state.direction = thumbstick_get_discretized_direction(thumbstick_state.vector, thumbstick_state.config.axisSeparation, thumbstick_state.config.eightAxis);
  110. break;
  111. case THUMBSTICK_MODE_SCROLL:
  112. if (timer_elapsed(thumbstickScrollTimer) > THUMBSTICK_SCROLL_TIMEOUT) {
  113. thumbstickScrollTimer = timer_read();
  114. scrollDirection = thumbstick_get_discretized_direction(thumbstick_state.vector, thumbstick_state.config.axisSeparation, false);
  115. thumbstick_state.report.v = (scrollDirection.up || scrollDirection.down) ? (scrollDirection.up ? THUMBSTICK_SCROLL_SPEED : -THUMBSTICK_SCROLL_SPEED) : 0;
  116. thumbstick_state.report.h = (scrollDirection.left || scrollDirection.right) ? (scrollDirection.left ? -THUMBSTICK_SCROLL_SPEED : THUMBSTICK_SCROLL_SPEED) : 0;
  117. } else {
  118. thumbstick_state.report.v = thumbstick_state.report.h = 0;
  119. }
  120. break;
  121. default:
  122. break;
  123. }
  124. }
  125. }
  126. void update_keycode_status(uint16_t keycode, bool last, bool current) {
  127. if (last != current) {
  128. if (current) {
  129. register_code16(keycode);
  130. } else {
  131. unregister_code16(keycode);
  132. }
  133. }
  134. }
  135. void pointing_device_init(void) { thumbstick_init(); }
  136. void pointing_device_task(void) {
  137. report_mouse_t report = pointing_device_get_report();
  138. if (!isLeftHand) {
  139. thumbstick_process();
  140. switch (thumbstick_state.config.mode) {
  141. case THUMBSTICK_MODE_MOUSE:
  142. report.x = thumbstick_state.report.x;
  143. report.y = thumbstick_state.report.y;
  144. #ifdef THUMBSTICK_DEBUG
  145. if (timer_elapsed(thumbstickLogTimer) > 100) {
  146. thumbstickLogTimer = timer_read();
  147. uprintf("Raw (%d, %d); Dist (%u, %u); Vec (%d, %d);\n", rawX, rawY, distX, distY, thumbstick_state.vector.x, thumbstick_state.vector.y);
  148. }
  149. #endif
  150. break;
  151. case THUMBSTICK_MODE_ARROWS:
  152. update_keycode_status(KC_UP, thumbstick_state.lastDirection.up, thumbstick_state.direction.up);
  153. update_keycode_status(KC_DOWN, thumbstick_state.lastDirection.down, thumbstick_state.direction.down);
  154. update_keycode_status(KC_LEFT, thumbstick_state.lastDirection.left, thumbstick_state.direction.left);
  155. update_keycode_status(KC_RIGHT, thumbstick_state.lastDirection.right, thumbstick_state.direction.right);
  156. thumbstick_state.lastDirection = thumbstick_state.direction;
  157. #ifdef THUMBSTICK_DEBUG
  158. if (timer_elapsed(thumbstickLogTimer) > 100) {
  159. thumbstickLogTimer = timer_read();
  160. 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);
  161. }
  162. #endif
  163. break;
  164. case THUMBSTICK_MODE_SCROLL:
  165. report.v = thumbstick_state.report.v;
  166. report.h = thumbstick_state.report.h;
  167. #ifdef THUMBSTICK_DEBUG
  168. if (timer_elapsed(thumbstickLogTimer) > 100) {
  169. thumbstickLogTimer = timer_read();
  170. uprintf("Scroll (%d, %d)\n", report.h, report.v);
  171. }
  172. #endif
  173. break;
  174. default:
  175. break;
  176. }
  177. }
  178. pointing_device_set_report(report);
  179. pointing_device_send();
  180. }