logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

spleeb.c (18111B)


  1. // Copyright 2022 Chris Hoage (@chrishoage)
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "spleeb.h"
  4. #include "transactions.h"
  5. #ifdef CONSOLE_ENABLE
  6. # include "print.h"
  7. #endif // CONSOLE_ENABLE
  8. #ifdef POINTING_DEVICE_ENABLE
  9. # include "drivers/sensors/cirque_pinnacle_gestures.h"
  10. #endif // POINTING_DEVICE_ENABLE
  11. #if defined(POINTING_DEVICE_ENABLE) || defined(SPLEEB_ENCODER_MODE_MAP_ENABLE)
  12. typedef union {
  13. uint16_t raw;
  14. struct {
  15. uint8_t pointer_default_dpi : 4; // 16 steps available.
  16. uint8_t pointer_sniping_dpi : 2; // 4 steps available.
  17. uint8_t enc_modes[NUM_ENCODERS];
  18. bool is_dragscroll_enabled : 1;
  19. bool is_sniping_enabled : 1;
  20. } __attribute__((packed));
  21. } spleeb_config_t;
  22. static spleeb_config_t g_spleeb_config = {0};
  23. /**
  24. * \brief Set the value of `config` from EEPROM.
  25. *
  26. * Note that `is_dragscroll_enabled` and `is_sniping_enabled` are purposefully
  27. * ignored since we do not want to persist this state to memory. In practice,
  28. * this state is always written to maximize write-performances. Therefore, we
  29. * explicitly set them to `false` in this function.
  30. */
  31. static void read_spleeb_config_from_eeprom(spleeb_config_t* config) {
  32. config->raw = eeconfig_read_kb() & 0xffff;
  33. config->is_dragscroll_enabled = false;
  34. config->is_sniping_enabled = false;
  35. }
  36. /**
  37. * \brief Save the value of `config` to eeprom.
  38. *
  39. * Note that all values are written verbatim, including whether drag-scroll
  40. * and/or sniper mode are enabled. `read_spleeb_config_from_eeprom(…)`
  41. * resets these 2 values to `false` since it does not make sense to persist
  42. * these across reboots of the board.
  43. */
  44. static void write_spleeb_config_to_eeprom(spleeb_config_t* config) {
  45. eeconfig_update_kb(config->raw);
  46. }
  47. void eeconfig_init_kb(void) {
  48. g_spleeb_config.raw = 0;
  49. g_spleeb_config.pointer_default_dpi = 4;
  50. # ifdef SPLEEB_ENCODER_MODE_MAP_ENABLE
  51. for (size_t i = 0; i < NUM_ENCODERS; i++) {
  52. if (spleeb_encoder_mode_map[i][0].initalized) {
  53. spleeb_enc_mode_t* first_enc_mode = &spleeb_encoder_mode_map[i][0];
  54. g_spleeb_config.enc_modes[i] = first_enc_mode->mode;
  55. }
  56. }
  57. # endif // SPLEEB_ENCODER_MODE_MAP_ENABLE
  58. write_spleeb_config_to_eeprom(&g_spleeb_config);
  59. eeconfig_init_user();
  60. }
  61. void matrix_init_kb(void) {
  62. read_spleeb_config_from_eeprom(&g_spleeb_config);
  63. matrix_init_user();
  64. }
  65. void spleeb_config_sync_handler(uint8_t initiator2target_buffer_size, const void* initiator2target_buffer, uint8_t target2initiator_buffer_size, void* target2initiator_buffer) {
  66. if (initiator2target_buffer_size == sizeof(g_spleeb_config)) {
  67. memcpy(&g_spleeb_config, initiator2target_buffer, sizeof(g_spleeb_config));
  68. }
  69. }
  70. void keyboard_post_init_kb(void) {
  71. transaction_register_rpc(RPC_ID_KB_CONFIG_SYNC, spleeb_config_sync_handler);
  72. keyboard_post_init_user();
  73. }
  74. void housekeeping_task_kb(void) {
  75. if (is_keyboard_master()) {
  76. // Keep track of the last state, so that we can tell if we need to propagate to slave.
  77. static spleeb_config_t last_spleeb_config = {0};
  78. static uint32_t last_sync = 0;
  79. bool needs_sync = false;
  80. // Check if the state values are different.
  81. if (memcmp(&g_spleeb_config, &last_spleeb_config, sizeof(g_spleeb_config))) {
  82. needs_sync = true;
  83. memcpy(&last_spleeb_config, &g_spleeb_config, sizeof(g_spleeb_config));
  84. }
  85. // Send to slave every 500ms regardless of state change.
  86. if (timer_elapsed32(last_sync) > 500) {
  87. needs_sync = true;
  88. }
  89. // Perform the sync if requested.
  90. if (needs_sync) {
  91. if (transaction_rpc_send(RPC_ID_KB_CONFIG_SYNC, sizeof(g_spleeb_config), &g_spleeb_config)) {
  92. last_sync = timer_read32();
  93. }
  94. }
  95. }
  96. // No need to invoke the user-specific callback, as it's been called
  97. // already.
  98. }
  99. #endif // defined(POINTING_DEVICE_ENABLE) || defined(SPLEEB_ENCODER_MODE_MAP_ENABLE)
  100. #ifdef SPLEEB_ENCODER_MODE_MAP_ENABLE
  101. /**
  102. * \brief Handle the encoder mode action when triggered by encoder_update_kb
  103. *
  104. * Weakly defined fuction intended to be overridden in a users keymap
  105. */
  106. __attribute__((weak)) void spleeb_encoder_mode_trigger(uint8_t mode, bool clockwise) {}
  107. typedef struct {
  108. uint8_t index;
  109. spleeb_enc_mode_t* enc_mode;
  110. } spleeb_found_enc_mode_t;
  111. static spleeb_found_enc_mode_t spleeb_get_found_encoder_mode(spleeb_config_t* config, uint8_t index) {
  112. spleeb_found_enc_mode_t found_enc_mode;
  113. for (size_t i = 0; i < SPLEEB_ENCODER_MODE_COUNT; i++) {
  114. spleeb_enc_mode_t* cur_enc_mode = &spleeb_encoder_mode_map[index][i];
  115. if (cur_enc_mode->mode == config->enc_modes[index]) {
  116. found_enc_mode.index = i;
  117. found_enc_mode.enc_mode = cur_enc_mode;
  118. break;
  119. }
  120. }
  121. return found_enc_mode;
  122. }
  123. /**
  124. * \brief Step through the defined encoder modes for the encoder at the given
  125. * index
  126. *
  127. * Step though the modes defined in spleeb_encoder_mode_map at the users keymap.
  128. * Use a null terminator at the first character on the name property for the
  129. * enc_mode struct to determine if we've reached the end of the defined encoder
  130. * modes. When this happens loop back to the beginning.
  131. */
  132. static void spleeb_step_encoder_mode(spleeb_config_t* config, uint8_t index) {
  133. spleeb_found_enc_mode_t cur_enc_mode = spleeb_get_found_encoder_mode(config, index);
  134. spleeb_enc_mode_t* next_enc_mode = &spleeb_encoder_mode_map[index][(cur_enc_mode.index + 1) % SPLEEB_ENCODER_MODE_COUNT];
  135. if (!next_enc_mode->initalized) {
  136. next_enc_mode = &spleeb_encoder_mode_map[index][0];
  137. }
  138. if (next_enc_mode->initalized) {
  139. config->enc_modes[index] = next_enc_mode->mode;
  140. write_spleeb_config_to_eeprom(config);
  141. }
  142. }
  143. bool encoder_update_kb(uint8_t index, bool clockwise) {
  144. if (!encoder_update_user(index, clockwise)) {
  145. return false;
  146. }
  147. spleeb_encoder_mode_trigger(g_spleeb_config.enc_modes[index], clockwise);
  148. return true;
  149. }
  150. #endif // SPLEEB_ENCODER_MODE_MAP_ENABLE
  151. #ifdef POINTING_DEVICE_ENABLE
  152. /** \brief Return the current value of the pointer's default DPI. */
  153. static uint16_t get_pointer_default_dpi(spleeb_config_t* config) {
  154. return (uint16_t)config->pointer_default_dpi * SPLEEB_DEFAULT_DPI_CONFIG_STEP + SPLEEB_MINIMUM_DEFAULT_DPI;
  155. }
  156. /** \brief Return the current value of the pointer's sniper-mode DPI. */
  157. static uint16_t get_pointer_sniping_dpi(spleeb_config_t* config) {
  158. return (uint16_t)config->pointer_sniping_dpi * SPLEEB_SNIPING_DPI_CONFIG_STEP + SPLEEB_MINIMUM_SNIPING_DPI;
  159. }
  160. /** \brief Return the current value of the pointer's default DPI. */
  161. static uint16_t get_pointer_current_dpi(spleeb_config_t* config) {
  162. if (config->is_sniping_enabled) {
  163. return get_pointer_sniping_dpi(config);
  164. } else {
  165. return get_pointer_default_dpi(config);
  166. }
  167. }
  168. /** \brief Set the appropriate DPI for the input config. */
  169. static void maybe_update_pointing_device_cpi(spleeb_config_t* config) {
  170. if (config->is_sniping_enabled) {
  171. pointing_device_set_cpi(get_pointer_sniping_dpi(config));
  172. } else {
  173. pointing_device_set_cpi(get_pointer_default_dpi(config));
  174. }
  175. }
  176. /**
  177. * \brief Update the pointer's default DPI to the next or previous step.
  178. *
  179. * Increases the DPI value if `forward` is `true`, decreases it otherwise.
  180. * The increment/decrement steps are equal to SPLEEB_DEFAULT_DPI_CONFIG_STEP.
  181. */
  182. static void step_pointer_default_dpi(spleeb_config_t* config, bool forward) {
  183. config->pointer_default_dpi += forward ? 1 : -1;
  184. maybe_update_pointing_device_cpi(config);
  185. }
  186. /**
  187. * \brief Update the pointer's sniper-mode DPI to the next or previous step.
  188. *
  189. * Increases the DPI value if `forward` is `true`, decreases it otherwise.
  190. * The increment/decrement steps are equal to SPLEEB_SNIPING_DPI_CONFIG_STEP.
  191. */
  192. static void step_pointer_sniping_dpi(spleeb_config_t* config, bool forward) {
  193. config->pointer_sniping_dpi += forward ? 1 : -1;
  194. maybe_update_pointing_device_cpi(config);
  195. }
  196. uint16_t spleeb_get_pointer_default_dpi(void) {
  197. return get_pointer_default_dpi(&g_spleeb_config);
  198. }
  199. uint16_t spleeb_get_pointer_sniping_dpi(void) {
  200. return get_pointer_sniping_dpi(&g_spleeb_config);
  201. }
  202. void spleeb_cycle_pointer_default_dpi_noeeprom(bool forward) {
  203. step_pointer_default_dpi(&g_spleeb_config, forward);
  204. }
  205. void spleeb_cycle_pointer_default_dpi(bool forward) {
  206. step_pointer_default_dpi(&g_spleeb_config, forward);
  207. write_spleeb_config_to_eeprom(&g_spleeb_config);
  208. }
  209. void spleeb_cycle_pointer_sniping_dpi_noeeprom(bool forward) {
  210. step_pointer_sniping_dpi(&g_spleeb_config, forward);
  211. }
  212. void spleeb_cycle_pointer_sniping_dpi(bool forward) {
  213. step_pointer_sniping_dpi(&g_spleeb_config, forward);
  214. write_spleeb_config_to_eeprom(&g_spleeb_config);
  215. }
  216. bool spleeb_get_pointer_sniping_enabled(void) {
  217. return g_spleeb_config.is_sniping_enabled;
  218. }
  219. void spleeb_set_pointer_sniping_enabled(bool enable) {
  220. g_spleeb_config.is_sniping_enabled = enable;
  221. maybe_update_pointing_device_cpi(&g_spleeb_config);
  222. }
  223. bool spleeb_get_pointer_dragscroll_enabled(void) {
  224. return g_spleeb_config.is_dragscroll_enabled;
  225. }
  226. void spleeb_set_pointer_dragscroll_enabled(bool enable) {
  227. g_spleeb_config.is_dragscroll_enabled = enable;
  228. cirque_pinnacle_enable_cursor_glide(enable);
  229. maybe_update_pointing_device_cpi(&g_spleeb_config);
  230. }
  231. #endif // POINTING_DEVICE_ENABLE
  232. #ifdef POINTING_DEVICE_ENABLE
  233. void pointing_device_init_kb(void) {
  234. maybe_update_pointing_device_cpi(&g_spleeb_config);
  235. // only glide on drag scroll
  236. cirque_pinnacle_enable_cursor_glide(false);
  237. set_auto_mouse_enable(true);
  238. pointing_device_init_user();
  239. }
  240. /**
  241. * \brief Augment the pointing device behavior.
  242. *
  243. * Drag-scroll implementation borrowed from https://github.com/qmk/qmk_firmware/pull/18218
  244. */
  245. static void pointing_device_task_spleeb(report_mouse_t* mouse_report) {
  246. static int16_t scroll_x = 0;
  247. static int16_t scroll_y = 0;
  248. if (g_spleeb_config.is_dragscroll_enabled) {
  249. scroll_x -= mouse_report->x;
  250. scroll_y += mouse_report->y;
  251. mouse_report->h = scroll_x / SPLEEB_DRAGSCROLL_DIVISOR;
  252. mouse_report->v = scroll_y / SPLEEB_DRAGSCROLL_DIVISOR;
  253. mouse_report->x = 0;
  254. mouse_report->y = 0;
  255. scroll_x -= (int16_t)mouse_report->h * SPLEEB_DRAGSCROLL_DIVISOR;
  256. scroll_y -= (int16_t)mouse_report->v * SPLEEB_DRAGSCROLL_DIVISOR;
  257. }
  258. }
  259. report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) {
  260. if (is_keyboard_master()) {
  261. pointing_device_task_spleeb(&mouse_report);
  262. mouse_report = pointing_device_task_user(mouse_report);
  263. }
  264. return mouse_report;
  265. }
  266. /**
  267. * \brief Outputs the Spleeb configuration to console.
  268. *
  269. * Prints the in-memory configuration structure to console, for debugging.
  270. * Includes:
  271. * - raw value
  272. * - drag-scroll: on/off
  273. * - sniping: on/off
  274. * - default DPI: internal table index/actual DPI
  275. * - sniping DPI: internal table index/actual DPI
  276. */
  277. static void debug_spleeb_config_to_console(spleeb_config_t* config) {
  278. # ifdef CONSOLE_ENABLE
  279. pd_dprintf("(spleeb) process_record_kb: config = {\n"
  280. "\traw = 0x%u,\n"
  281. "\t{\n"
  282. "\t\tis_dragscroll_enabled=%u\n"
  283. "\t\tis_sniping_enabled=%u\n"
  284. "\t\tdefault_dpi=0x%X (%u)\n"
  285. "\t\tsniping_dpi=0x%X (%u)\n"
  286. "\t}\n"
  287. "}\n",
  288. config->raw, config->is_dragscroll_enabled, config->is_sniping_enabled, config->pointer_default_dpi, get_pointer_default_dpi(config), config->pointer_sniping_dpi, get_pointer_sniping_dpi(config));
  289. # endif // CONSOLE_ENABLE
  290. }
  291. #endif // POINTING_DEVICE_ENABLE
  292. bool process_record_kb(uint16_t keycode, keyrecord_t* record) {
  293. if (!process_record_user(keycode, record)) {
  294. #ifdef POINTING_DEVICE_ENABLE
  295. debug_spleeb_config_to_console(&g_spleeb_config);
  296. #endif // POINTING_DEVICE_ENABLE
  297. return false;
  298. }
  299. #ifdef POINTING_DEVICE_ENABLE
  300. switch (keycode) {
  301. case POINTER_DEFAULT_DPI_FORWARD:
  302. if (record->event.pressed) {
  303. spleeb_cycle_pointer_default_dpi(true);
  304. }
  305. break;
  306. case POINTER_DEFAULT_DPI_REVERSE:
  307. if (record->event.pressed) {
  308. spleeb_cycle_pointer_default_dpi(false);
  309. }
  310. break;
  311. case POINTER_SNIPING_DPI_FORWARD:
  312. if (record->event.pressed) {
  313. spleeb_cycle_pointer_sniping_dpi(true);
  314. }
  315. break;
  316. case POINTER_SNIPING_DPI_REVERSE:
  317. if (record->event.pressed) {
  318. spleeb_cycle_pointer_sniping_dpi(false);
  319. }
  320. break;
  321. case SNIPING_MODE:
  322. spleeb_set_pointer_sniping_enabled(record->event.pressed);
  323. break;
  324. case SNIPING_MODE_TOGGLE:
  325. if (record->event.pressed) {
  326. spleeb_set_pointer_sniping_enabled(!spleeb_get_pointer_sniping_enabled());
  327. }
  328. break;
  329. case DRAGSCROLL_MODE:
  330. spleeb_set_pointer_dragscroll_enabled(record->event.pressed);
  331. break;
  332. case DRAGSCROLL_MODE_TOGGLE:
  333. if (record->event.pressed) {
  334. spleeb_set_pointer_dragscroll_enabled(!spleeb_get_pointer_dragscroll_enabled());
  335. }
  336. break;
  337. }
  338. #endif // POINTING_DEVICE_ENABLE
  339. #ifdef SPLEEB_ENCODER_MODE_MAP_ENABLE
  340. switch (keycode) {
  341. case ENC_MODE_STEP_LEFT:
  342. if (record->event.pressed) {
  343. spleeb_step_encoder_mode(&g_spleeb_config, 0);
  344. }
  345. break;
  346. case ENC_MODE_STEP_RIGHT:
  347. if (record->event.pressed) {
  348. spleeb_step_encoder_mode(&g_spleeb_config, 1);
  349. }
  350. break;
  351. }
  352. #endif // SPLEEB_ENCODER_MODE_MAP_ENABLE
  353. #ifdef POINTING_DEVICE_ENABLE
  354. if ((keycode >= POINTER_DEFAULT_DPI_FORWARD && keycode <= ENC_MODE_STEP_RIGHT) || IS_MOUSEKEY(keycode)) {
  355. debug_spleeb_config_to_console(&g_spleeb_config);
  356. }
  357. #endif // POINTING_DEVICE_ENABLE
  358. return true;
  359. }
  360. #ifdef POINTING_DEVICE_ENABLE
  361. bool is_mouse_record_kb(uint16_t keycode, keyrecord_t* record) {
  362. switch (keycode) {
  363. case DRAGSCROLL_MODE:
  364. case SNIPING_MODE:
  365. return true;
  366. default:
  367. return false;
  368. }
  369. return is_mouse_record_user(keycode, record);
  370. }
  371. #endif // POINTING_DEVICE_ENABLE
  372. #ifdef OLED_ENABLE
  373. static void render_layer(void) {
  374. oled_write_P(PSTR("LAYER: "), false);
  375. switch (get_highest_layer(layer_state)) {
  376. case 0:
  377. oled_write_ln_P(PSTR("\xC0\xC1"), false);
  378. break;
  379. case 1:
  380. oled_write_ln_P(PSTR("\xC2\xC3"), false);
  381. break;
  382. case 2:
  383. oled_write_ln_P(PSTR("\xC4\xC5"), false);
  384. break;
  385. case 3:
  386. oled_write_ln_P(PSTR("\xC6\xC7"), false);
  387. break;
  388. case 4:
  389. oled_write_ln_P(PSTR("\xC8\xC9"), false);
  390. break;
  391. case 5:
  392. oled_write_ln_P(PSTR("\xCA\xCB"), false);
  393. break;
  394. default:
  395. oled_write_ln_P(get_u8_str(get_highest_layer(layer_state) + 0x30, ' '), true);
  396. }
  397. oled_write_ln_P("", false);
  398. }
  399. static void render_mods(void) {
  400. uint8_t modifiers = get_mods();
  401. oled_write_ln_P(PSTR("MODS:"), false);
  402. oled_write_ln_P("", false);
  403. oled_write_P(PSTR("\325\326"), (modifiers & MOD_MASK_SHIFT));
  404. oled_write_P(PSTR("\327\330"), (modifiers & MOD_MASK_CTRL));
  405. oled_write_P(PSTR("\331\332"), (modifiers & MOD_MASK_ALT));
  406. oled_write_ln_P(PSTR("\333\334"), (modifiers & MOD_MASK_GUI));
  407. oled_write_ln_P("", false);
  408. }
  409. static void render_lock(void) {
  410. led_t led_state = host_keyboard_led_state();
  411. oled_write_P(PSTR("LOCK: "), false);
  412. oled_write_P(PSTR("\235\236"), led_state.caps_lock);
  413. oled_write_ln_P(PSTR("\275\276"), led_state.num_lock);
  414. }
  415. static void render_pointer(void) {
  416. # ifdef POINTING_DEVICE_ENABLE
  417. oled_write_ln_P(PSTR("POINTER:"), false);
  418. oled_write_ln_P("", false);
  419. oled_write_P(PSTR("dpi:"), false);
  420. oled_write_ln_P(get_u16_str(get_pointer_current_dpi(&g_spleeb_config), ' '), false);
  421. oled_write_ln_P("", false);
  422. # endif // POINTING_DEVICE_ENABLE
  423. }
  424. # ifdef SPLEEB_ENCODER_MODE_MAP_ENABLE
  425. static uint8_t spleeb_get_encoder_mode(spleeb_config_t* config, uint8_t index) {
  426. spleeb_found_enc_mode_t found_enc_mode = spleeb_get_found_encoder_mode(config, index);
  427. return found_enc_mode.enc_mode->mode;
  428. }
  429. /**
  430. * \brief Map an encoder mode to a string to be displayed on the OLED
  431. *
  432. * Weakly defined fuction intended to be overridden in a users keymap. My be
  433. * omitted if no OLED is used.
  434. */
  435. __attribute__((weak)) const char* spleeb_encoder_mode_string(uint8_t mode) {
  436. return get_u8_str(mode, ' ');
  437. }
  438. # endif // SPLEEB_ENCODER_MODE_MAP_ENABLE
  439. static void render_encoder(void) {
  440. # ifdef SPLEEB_ENCODER_MODE_MAP_ENABLE
  441. oled_write_ln_P(PSTR("ENCODER:"), false);
  442. oled_write_ln_P("", false);
  443. oled_write_P(PSTR("R: "), false);
  444. oled_write_ln_P(spleeb_encoder_mode_string(spleeb_get_encoder_mode(&g_spleeb_config, 1)), false);
  445. oled_write_P(PSTR("L: "), false);
  446. oled_write_ln_P(spleeb_encoder_mode_string(spleeb_get_encoder_mode(&g_spleeb_config, 0)), false);
  447. # endif // SPLEEB_ENCODER_MODE_MAP_ENABLE
  448. }
  449. static void render_status(void) {
  450. render_layer();
  451. render_mods();
  452. render_lock();
  453. render_pointer();
  454. render_encoder();
  455. }
  456. oled_rotation_t oled_init_kb(oled_rotation_t rotation) {
  457. return OLED_ROTATION_90;
  458. }
  459. bool oled_task_kb(void) {
  460. if (is_keyboard_master()) {
  461. return false;
  462. }
  463. if (!oled_task_user()) {
  464. return false;
  465. }
  466. render_status();
  467. return false;
  468. }
  469. #endif // OLED_ENABLE