mode_lizard.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. #include "mode_lizard.h"
  2. #include "lizard_map.h"
  3. #include "triton.h"
  4. #include "config.h"
  5. #include <string.h>
  6. // Evaluate the binding table for ONE controller's button word, merging its keyboard/mouse-button/
  7. // consumer outputs into the shared accumulators. Pulled out of rfLizard so every bonded controller can
  8. // feed the same outputs. doRpadMouse/doLpadScroll are map-level (set if such a binding exists at all).
  9. static void lizardEvalSlot(uint32_t buttons, uint8_t &outMod,
  10. uint8_t outKeys[6], uint8_t &nKeys, uint8_t &outMBtn,
  11. uint8_t &consumerBits, bool &doRpadMouse,
  12. bool &doLpadScroll)
  13. {
  14. // kbdConsumed (per controller): trig bits claimed by a hold-modifier binding so simpler
  15. // single-button bindings sharing those bits are suppressed for THIS controller.
  16. uint32_t kbdConsumed = 0;
  17. for (uint8_t i = 0; i < g_lizardMap.count; i++) {
  18. const LizardBinding &b = g_lizardMap.bindings[i];
  19. if (b.outType == LZ_OUT_NONE)
  20. continue;
  21. if (b.outType == LZ_OUT_MOUSE_AXIS) {
  22. if (b.outData[0] == LZ_MSRC_RPAD)
  23. doRpadMouse = true;
  24. // LZ_MSRC_LSTICK and LZ_MSRC_GYRO could be added later
  25. continue;
  26. }
  27. if (b.outType == LZ_OUT_SCROLL) {
  28. if (b.outData[0] == LZ_MSRC_LPAD)
  29. doLpadScroll = true;
  30. continue;
  31. }
  32. // Check hold modifier (AND) then trigger (any-of OR)
  33. if (b.holdMask && (buttons & b.holdMask) != b.holdMask)
  34. continue;
  35. if (b.trigMask && (buttons & b.trigMask) == 0)
  36. continue;
  37. switch (b.outType) {
  38. case LZ_OUT_MOUSE_BTN:
  39. if (!b.trigMask || (buttons & b.trigMask))
  40. outMBtn |= b.outData[0];
  41. break;
  42. case LZ_OUT_KBD_CHORD:
  43. // suppress if trig bits already claimed by a hold-modifier binding
  44. if (b.holdMask == 0 && b.trigMask != 0 &&
  45. (b.trigMask & kbdConsumed))
  46. break;
  47. outMod |= b.outData[0];
  48. for (int j = 1; j < 7 && b.outData[j]; j++) {
  49. if (nKeys < 6)
  50. outKeys[nKeys++] = b.outData[j];
  51. }
  52. // claim trig bits so simpler bindings don't double-fire
  53. if (b.holdMask != 0)
  54. kbdConsumed |= b.trigMask;
  55. break;
  56. case LZ_OUT_CONSUMER:
  57. if (g_usbMode == MODE_LIZARD)
  58. consumerBits |= b.outData[0];
  59. break;
  60. default:
  61. break;
  62. }
  63. }
  64. }
  65. // Add the virtual stick-deflection bits so bindings can trigger on left-stick direction.
  66. // The LZ_BTN_LSTICK_* flags live on bits 28..31, which OVERLAP real controller inputs: bit 28 = right
  67. // grip touch, bit 29 = left grip touch (PROTOCOL.md §8). Merely holding the controller sets the grip
  68. // bits, which would masquerade as an L-stick deflection and fire the bound key. Clear the top 4 bits of
  69. // the physical word (grips are not a bindable lizard input) so these flags reflect ONLY stick deflection.
  70. static uint32_t lizardButtons(const PuckInput &in)
  71. {
  72. uint32_t buttons = in.buttons & 0x0FFFFFFFu;
  73. if (in.lx > 12000)
  74. buttons |= LZ_BTN_LSTICK_RT;
  75. if (in.lx < -12000)
  76. buttons |= LZ_BTN_LSTICK_LF;
  77. if (in.ly < -12000)
  78. buttons |= LZ_BTN_LSTICK_DN;
  79. if (in.ly > 12000)
  80. buttons |= LZ_BTN_LSTICK_UP;
  81. return buttons;
  82. }
  83. void rfLizard(Adafruit_USBD_HID *mdev, Adafruit_USBD_HID *kdev, uint8_t mrid,
  84. uint8_t krid)
  85. {
  86. // ---- merge binding outputs across every bonded controller ----
  87. uint8_t outMod = 0;
  88. uint8_t outKeys[6] = { 0, 0, 0, 0, 0, 0 };
  89. uint8_t nKeys = 0;
  90. uint8_t outMBtn = 0;
  91. bool doRpadMouse = false, doLpadScroll = false;
  92. uint8_t consumerBits = 0;
  93. for (int s = 0; s < NSLOT; s++) {
  94. if (!g_slot[s].used)
  95. continue;
  96. lizardEvalSlot(lizardButtons(g_in[s]), outMod, outKeys, nKeys,
  97. outMBtn, consumerBits, doRpadMouse,
  98. doLpadScroll);
  99. }
  100. // ---- right pad -> mouse motion with glide ----
  101. // Per-slot velocity/last-position so each controller's pad integrates independently; the resulting
  102. // deltas are SUMMED into one cursor (sub-pixel carry rmx/rmy is shared -- one mouse).
  103. static int prx[NSLOT] = { 0 }, pry[NSLOT] = { 0 };
  104. static bool prt[NSLOT] = { false };
  105. static float vx[NSLOT] = { 0 }, vy[NSLOT] = { 0 }, rmx = 0, rmy = 0;
  106. int dx = 0, dy = 0;
  107. if (doRpadMouse) {
  108. float sumx = 0, sumy = 0;
  109. float f = g_mFric / 100.0f;
  110. for (int s = 0; s < NSLOT; s++) {
  111. if (!g_slot[s].used) {
  112. vx[s] = vy[s] = 0;
  113. prt[s] = false;
  114. continue;
  115. }
  116. const PuckInput &in = g_in[s];
  117. bool rtouch = (in.buttons & TB_RPADT) != 0;
  118. int rx = in.rpx, ry = in.rpy;
  119. if (rtouch && prt[s]) {
  120. vx[s] += (rx - prx[s]);
  121. vy[s] += (ry - pry[s]);
  122. }
  123. if (rtouch) {
  124. prx[s] = rx;
  125. pry[s] = ry;
  126. }
  127. prt[s] = rtouch;
  128. sumx += vx[s];
  129. sumy += vy[s];
  130. // friction = glide/decay (per slot)
  131. vx[s] *= f;
  132. vy[s] *= f;
  133. if (vx[s] > -1 && vx[s] < 1)
  134. vx[s] = 0;
  135. if (vy[s] > -1 && vy[s] < 1)
  136. vy[s] = 0;
  137. }
  138. // Y inverted; *10 = desktop cursor sensitivity (g_mDiv 64 -> eff 640)
  139. float mxf = sumx / (float)(g_mDiv * 10) + rmx,
  140. myf = -(sumy / (float)(g_mDiv * 10)) + rmy;
  141. dx = (int)mxf;
  142. dy = (int)myf;
  143. rmx = mxf - dx;
  144. rmy = myf - dy; // sub-pixel carry
  145. if (dx > 127)
  146. dx = 127;
  147. if (dx < -127)
  148. dx = -127;
  149. if (dy > 127)
  150. dy = 127;
  151. if (dy < -127)
  152. dy = -127;
  153. }
  154. // ---- left pad -> scroll wheel ----
  155. // Per-slot last-position; accumulator shared (one wheel). Reset only when NO controller is scrolling.
  156. static int ply[NSLOT] = { 0 };
  157. static bool plt[NSLOT] = { false };
  158. static float sacc = 0;
  159. int dw = 0;
  160. if (doLpadScroll) {
  161. bool anyTouch = false;
  162. for (int s = 0; s < NSLOT; s++) {
  163. if (!g_slot[s].used) {
  164. plt[s] = false;
  165. continue;
  166. }
  167. const PuckInput &in = g_in[s];
  168. bool ltouch = (in.buttons & TB_LPADT) != 0;
  169. int ly = in.lpy;
  170. if (ltouch && plt[s])
  171. sacc += (ly - ply[s]) / (float)(g_mDiv * 24);
  172. if (ltouch) {
  173. ply[s] = ly;
  174. anyTouch = true;
  175. }
  176. plt[s] = ltouch;
  177. }
  178. if (!anyTouch)
  179. sacc = 0;
  180. dw = (int)sacc;
  181. sacc -= dw;
  182. if (dw > 15)
  183. dw = 15;
  184. if (dw < -15)
  185. dw = -15;
  186. }
  187. // ---- mouse report ----
  188. static uint8_t pmbtn = 0;
  189. if (dx || dy || dw || outMBtn != pmbtn) {
  190. pmbtn = outMBtn;
  191. hid_mouse_report_t m;
  192. m.buttons = outMBtn;
  193. m.x = (int8_t)dx;
  194. m.y = (int8_t)dy;
  195. m.wheel = (int8_t)dw;
  196. m.pan = 0;
  197. if (mdev->ready())
  198. mdev->sendReport(mrid, &m, sizeof m);
  199. }
  200. // ---- consumer control (edge-triggered) ----
  201. static uint8_t prevCC = 0;
  202. if (g_usbMode == MODE_LIZARD) {
  203. if (consumerBits != prevCC) {
  204. if (mdev->ready())
  205. mdev->sendReport(0x03, &consumerBits, 1);
  206. prevCC = consumerBits;
  207. }
  208. }
  209. // ---- keyboard report ----
  210. static uint8_t pmod = 0, pkc[6] = { 0, 0, 0, 0, 0, 0 };
  211. bool chg = (outMod != pmod);
  212. for (int i = 0; i < 6; i++)
  213. if (outKeys[i] != pkc[i])
  214. chg = true;
  215. if (chg) {
  216. pmod = outMod;
  217. for (int i = 0; i < 6; i++)
  218. pkc[i] = outKeys[i];
  219. uint8_t krep[8] = { outMod, 0, outKeys[0],
  220. outKeys[1], outKeys[2], outKeys[3],
  221. outKeys[4], outKeys[5] };
  222. if (kdev->ready())
  223. kdev->sendReport(krid, krep, 8);
  224. }
  225. }