lizard_map.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. #include "lizard_map.h"
  2. #include <Adafruit_LittleFS.h>
  3. #include <InternalFileSystem.h>
  4. #include <string.h>
  5. #include <class/hid/hid.h>
  6. using namespace Adafruit_LittleFS_Namespace;
  7. LizardMap g_lizardMap;
  8. #define LZ_FILE "/lizard_map.bin"
  9. #define LZ_MAGIC 0xB1u
  10. #define LZ_VERSION 2u
  11. // Version-1 records use 32-bit masks and borrow bits 28..31 for virtual
  12. // left-stick directions. The current format stores those directions above the physical word.
  13. struct LizardBindingV1 {
  14. uint8_t outType;
  15. uint8_t outData[7];
  16. uint32_t trigMask;
  17. uint32_t holdMask;
  18. };
  19. static_assert(sizeof(LizardBindingV1) == 16,
  20. "version-1 lizard binding layout changed");
  21. static uint64_t lizardMaskFromV1(uint32_t m)
  22. {
  23. uint64_t out = (uint64_t)(m & 0x0FFFFFFFu);
  24. if (m & 0x10000000u)
  25. out |= LZ_BTN_LSTICK_RT;
  26. if (m & 0x20000000u)
  27. out |= LZ_BTN_LSTICK_LF;
  28. if (m & 0x40000000u)
  29. out |= LZ_BTN_LSTICK_DN;
  30. if (m & 0x80000000u)
  31. out |= LZ_BTN_LSTICK_UP;
  32. return out;
  33. }
  34. // KB modifier bits (from TinyUSB hid.h)
  35. #define KM_LCTRL 0x01u
  36. #define KM_LSHIFT 0x02u
  37. #define KM_LALT 0x04u
  38. #define KM_LGUI 0x08u
  39. // Helper: append a binding to g_lizardMap
  40. static void addBind(uint8_t type, const uint8_t *od7, uint64_t trig,
  41. uint64_t hold)
  42. {
  43. if (g_lizardMap.count >= LZ_MAX_BINDINGS)
  44. return;
  45. LizardBinding &b = g_lizardMap.bindings[g_lizardMap.count++];
  46. b.outType = type;
  47. for (int i = 0; i < 7; i++)
  48. b.outData[i] = od7[i];
  49. b.trigMask = trig;
  50. b.holdMask = hold;
  51. }
  52. static inline void addAxis(uint8_t src, uint8_t gyroAct)
  53. {
  54. uint8_t d[7] = { src, gyroAct, 0, 0, 0, 0, 0 };
  55. addBind(LZ_OUT_MOUSE_AXIS, d, 0, 0);
  56. }
  57. static inline void addScroll(uint8_t src)
  58. {
  59. uint8_t d[7] = { src, 0, 0, 0, 0, 0, 0 };
  60. addBind(LZ_OUT_SCROLL, d, 0, 0);
  61. }
  62. static inline void addMouseBtn(uint8_t btn, uint64_t trig, uint64_t hold)
  63. {
  64. uint8_t d[7] = { btn, 0, 0, 0, 0, 0, 0 };
  65. addBind(LZ_OUT_MOUSE_BTN, d, trig, hold);
  66. }
  67. static inline void addKey(uint8_t mod, uint8_t k0, uint64_t trig, uint64_t hold)
  68. {
  69. uint8_t d[7] = { mod, k0, 0, 0, 0, 0, 0 };
  70. addBind(LZ_OUT_KBD_CHORD, d, trig, hold);
  71. }
  72. static inline void addKey3(uint8_t mod, uint8_t k0, uint8_t k1, uint8_t k2,
  73. uint64_t trig, uint64_t hold)
  74. {
  75. uint8_t d[7] = { mod, k0, k1, k2, 0, 0, 0 };
  76. addBind(LZ_OUT_KBD_CHORD, d, trig, hold);
  77. }
  78. static inline void addConsumer(uint8_t bits, uint64_t trig, uint64_t hold)
  79. {
  80. uint8_t d[7] = { bits, 0, 0, 0, 0, 0, 0 };
  81. addBind(LZ_OUT_CONSUMER, d, trig, hold);
  82. }
  83. void defaultLizardMap()
  84. {
  85. g_lizardMap.count = 0;
  86. // Analog sources (always active; no trigger mask)
  87. addAxis(LZ_MSRC_RPAD, LZ_GYRO_ALWAYS); // right pad → mouse
  88. addScroll(LZ_MSRC_LPAD); // left pad → scroll
  89. // Mouse buttons
  90. // right pad click → left mouse
  91. addMouseBtn(1, 0x400000u /*TB_RPADC*/, 0);
  92. // right trigger (digital click) → left mouse
  93. addMouseBtn(1, 0x800000u /*TB_R2*/, 0);
  94. // left trigger → right mouse
  95. addMouseBtn(2, 0x8000000u /*TB_L2*/, 0);
  96. // left pad click → middle mouse
  97. addMouseBtn(4, 0x4000000u /*TB_LPADC*/, 0);
  98. // Hold-modifier shortcuts (Steam-button combos: place BEFORE generic X/L4 so they consume first)
  99. // Steam+L4 → Ctrl+Alt+Delete
  100. addKey3(KM_LCTRL | KM_LALT, HID_KEY_DELETE, 0, 0, 0x20000u /*TB_L4*/,
  101. 0x10000u /*TB_STEAM*/);
  102. // Steam+X → Win+Ctrl+O (on-screen keyboard)
  103. addKey3(KM_LGUI | KM_LCTRL, HID_KEY_O, 0, 0, 0x4u /*TB_X*/,
  104. 0x10000u /*TB_STEAM*/);
  105. // Steam+L5 → volume down (consumer bit1=0x02)
  106. addConsumer(0x02, 0x40000u /*TB_L5*/, 0x10000u /*TB_STEAM*/);
  107. // Steam+R5 → volume up (consumer bit0=0x01)
  108. addConsumer(0x01, 0x100u /*TB_R5*/, 0x10000u /*TB_STEAM*/);
  109. // Keyboard keys
  110. addKey(0, HID_KEY_ENTER, 0x1u /*TB_A*/, 0);
  111. addKey(0, HID_KEY_ESCAPE, 0x2u /*TB_B*/, 0);
  112. addKey(0, HID_KEY_PAGE_UP, 0x4u /*TB_X*/, 0);
  113. addKey(0, HID_KEY_PAGE_DOWN, 0x8u /*TB_Y*/, 0);
  114. addKey(0, HID_KEY_TAB, 0x40u /*TB_VIEW*/, 0);
  115. addKey(0, HID_KEY_ESCAPE, 0x4000u /*TB_MENU*/, 0);
  116. // D-pad → arrow keys
  117. addKey(0, HID_KEY_ARROW_UP, 0x2000u /*TB_DUP*/, 0);
  118. addKey(0, HID_KEY_ARROW_DOWN, 0x400u /*TB_DDN*/, 0);
  119. addKey(0, HID_KEY_ARROW_LEFT, 0x1000u /*TB_DLF*/, 0);
  120. addKey(0, HID_KEY_ARROW_RIGHT, 0x800u /*TB_DRT*/, 0);
  121. // Left stick → arrow keys (virtual deflection bits)
  122. addKey(0, HID_KEY_ARROW_UP, LZ_BTN_LSTICK_UP, 0);
  123. addKey(0, HID_KEY_ARROW_DOWN, LZ_BTN_LSTICK_DN, 0);
  124. addKey(0, HID_KEY_ARROW_LEFT, LZ_BTN_LSTICK_LF, 0);
  125. addKey(0, HID_KEY_ARROW_RIGHT, LZ_BTN_LSTICK_RT, 0);
  126. // Shoulder buttons → modifier keys only (no keycode)
  127. addKey(KM_LCTRL, 0, 0x80000u /*TB_LB*/, 0);
  128. addKey(KM_LALT, 0, 0x200u /*TB_RB*/, 0);
  129. }
  130. void saveLizardMap()
  131. {
  132. InternalFS.remove(LZ_FILE);
  133. File f(InternalFS);
  134. if (!f.open(LZ_FILE, FILE_O_WRITE))
  135. return;
  136. uint8_t hdr[3] = { LZ_MAGIC, LZ_VERSION, g_lizardMap.count };
  137. f.write(hdr, 3);
  138. f.write((uint8_t *)g_lizardMap.bindings,
  139. g_lizardMap.count * sizeof(LizardBinding));
  140. f.close();
  141. }
  142. void loadLizardMap()
  143. {
  144. bool migrated = false;
  145. File f(InternalFS);
  146. if (f.open(LZ_FILE, FILE_O_READ)) {
  147. uint8_t hdr[3] = { 0, 0, 0 };
  148. if (f.read(hdr, 3) == 3 && hdr[0] == LZ_MAGIC &&
  149. hdr[2] <= LZ_MAX_BINDINGS) {
  150. uint8_t cnt = hdr[2];
  151. g_lizardMap.count = 0;
  152. if (hdr[1] == LZ_VERSION) {
  153. int got =
  154. f.read((uint8_t *)g_lizardMap.bindings,
  155. cnt * sizeof(LizardBinding));
  156. if (got == (int)(cnt * sizeof(LizardBinding)))
  157. g_lizardMap.count = cnt;
  158. } else if (hdr[1] == 1u) {
  159. static LizardBindingV1 old[LZ_MAX_BINDINGS];
  160. int got = f.read((uint8_t *)old,
  161. cnt * sizeof(LizardBindingV1));
  162. if (got ==
  163. (int)(cnt * sizeof(LizardBindingV1))) {
  164. for (uint8_t i = 0; i < cnt; i++) {
  165. LizardBinding &b =
  166. g_lizardMap.bindings[i];
  167. b.outType = old[i].outType;
  168. memcpy(b.outData,
  169. old[i].outData,
  170. sizeof b.outData);
  171. b.trigMask = lizardMaskFromV1(
  172. old[i].trigMask);
  173. b.holdMask = lizardMaskFromV1(
  174. old[i].holdMask);
  175. }
  176. g_lizardMap.count = cnt;
  177. migrated = true;
  178. }
  179. }
  180. }
  181. f.close();
  182. }
  183. // If nothing loaded, install + persist defaults
  184. if (g_lizardMap.count == 0) {
  185. defaultLizardMap();
  186. saveLizardMap();
  187. } else if (migrated) {
  188. saveLizardMap();
  189. }
  190. }