twschum.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. #include "twschum.h"
  2. #ifdef TWSCHUM_TAPPING_CTRL_PREFIX
  3. // state for the great state machine of custom actions!
  4. #define TIMEOUT_DELAY 200 // ms
  5. static uint16_t idle_timer;
  6. static bool timeout_is_active = false;
  7. static bool ctrl_shortcuts_enabled_g = false;
  8. //static bool B_down = 0; // TODO just use top bit from count
  9. //static int8_t B_count = 0;
  10. #define N_TAPPING_CTRL_KEYS 2
  11. static struct Tapping_ctrl_key_t special_keys_g[N_TAPPING_CTRL_KEYS] = {
  12. {false, 0, KC_B}, {false, 0, KC_A}
  13. };
  14. static inline void start_idle_timer(void) {
  15. idle_timer = timer_read();
  16. timeout_is_active = true;
  17. }
  18. static inline void clear_state_after_idle_timeout(void) {
  19. idle_timer = 0;
  20. timeout_is_active = false;
  21. // send timed out plain keys from tapping ctrl mod
  22. for (int i = 0; i < N_TAPPING_CTRL_KEYS; ++i) {
  23. struct Tapping_ctrl_key_t* key = special_keys_g + i;
  24. repeat_send_keys(key->count, key->keycode);
  25. key->count = 0;
  26. }
  27. }
  28. inline void matrix_scan_user(void) {
  29. if (timeout_is_active && timer_elapsed(idle_timer) > TIMEOUT_DELAY) {
  30. clear_state_after_idle_timeout();
  31. }
  32. }
  33. static inline bool tap_ctrl_event(struct Tapping_ctrl_key_t* key, keyrecord_t* record) {
  34. if (!ctrl_shortcuts_enabled_g) {
  35. // normal operation, just send the plain keycode
  36. if (record->event.pressed) {
  37. register_code(key->keycode);
  38. }
  39. else {
  40. unregister_code(key->keycode);
  41. }
  42. return false;
  43. }
  44. key->down = record->event.pressed;
  45. // increment count and reset timer when key pressed
  46. // start the timeout when released
  47. if (key->down) {
  48. ++(key->count);
  49. timeout_is_active = false;
  50. idle_timer = 0;
  51. }
  52. else {
  53. if (key->count) {
  54. start_idle_timer();
  55. }
  56. }
  57. return false;
  58. }
  59. static inline bool tap_ctrl_other_pressed(void) {
  60. for (int i = 0; i < N_TAPPING_CTRL_KEYS; ++i) {
  61. struct Tapping_ctrl_key_t* key = special_keys_g + i;
  62. if (key->count) {
  63. if (key->down) {
  64. // another key has been pressed while the leader key is down,
  65. // so send number of ctrl-KEY combos before the other key
  66. repeat_send_keys(key->count, KC_LCTL, key->keycode);
  67. key->count = 0;
  68. }
  69. else {
  70. // another key pressed after leader key released,
  71. // need to send the plain keycode plus potential mods
  72. if (get_mods() & MOD_MASK_CTRL) {
  73. // make sure to send a shift if prssed
  74. repeat_send_keys(key->count, KC_RSFT, key->keycode);
  75. }
  76. else {
  77. repeat_send_keys(key->count, key->keycode);
  78. }
  79. key->count = 0;
  80. }
  81. return true; // will send the other keycode
  82. }
  83. }
  84. return true; // safe default
  85. }
  86. #endif /* TWSCHUM_TAPPING_CTRL_PREFIX */
  87. /* Use RGB underglow to indicate layer
  88. * https://docs.qmk.fm/reference/customizing-functionality
  89. */
  90. #ifdef RGBLIGHT_ENABLE
  91. static bool rgb_layers_enabled = true;
  92. static bool rgb_L0_enabled = false;
  93. layer_state_t layer_state_set_user(layer_state_t state) {
  94. if (!rgb_layers_enabled) {
  95. return state;
  96. }
  97. switch (get_highest_layer(state)) {
  98. case _Base:
  99. if (rgb_L0_enabled) {
  100. rgblight_sethsv_noeeprom(_Base_HSV_ON);
  101. }
  102. else {
  103. rgblight_sethsv_noeeprom(_Base_HSV_OFF);
  104. }
  105. break;
  106. case _Vim:
  107. rgblight_sethsv_noeeprom(_Vim_HSV);
  108. break;
  109. case _Fn:
  110. rgblight_sethsv_noeeprom(_Fn_HSV);
  111. break;
  112. case _Nav:
  113. rgblight_sethsv_noeeprom(_Nav_HSV);
  114. break;
  115. case _Num:
  116. rgblight_sethsv_noeeprom(_Num_HSV);
  117. break;
  118. case _Cfg:
  119. rgblight_sethsv_noeeprom(_Cfg_HSV);
  120. break;
  121. case _None:
  122. rgblight_sethsv_noeeprom(_None_HSV);
  123. break;
  124. }
  125. return state;
  126. }
  127. #endif /* RGBLIGHT_ENABLE */
  128. /* process_record_vimlayer: handles the VIM_ keycodes from xtonhasvim's vim
  129. * emulation layer
  130. * add process_record_keymap to allow specific keymap to still add keys
  131. * Makes the callstack look like:
  132. * process_record_
  133. * _quantum
  134. * _kb
  135. * _user
  136. * _keymap
  137. * _vimlayer
  138. */
  139. __attribute__ ((weak))
  140. bool process_record_keymap(uint16_t keycode, keyrecord_t *record) {
  141. return true;
  142. }
  143. /* Return True to continue processing keycode, false to stop further processing
  144. * process_record_keymap to be call by process_record_user in the vim addon */
  145. bool process_record_user(uint16_t keycode, keyrecord_t *record) {
  146. /* keymap gets first whack, then vimlayer */
  147. if(!process_record_keymap(keycode, record)) return false;
  148. if(!process_record_vimlayer(keycode, record)) return false;
  149. switch (keycode) {
  150. /* KC_MAKE is a keycode to be used with any keymap
  151. * Outputs `make <keyboard>:<keymap>`
  152. * Holding shift will add the appropriate flashing command (:dfu,
  153. * :teensy, :avrdude, :dfu-util) for a majority of keyboards.
  154. * Holding control will add some commands that will speed up compiling
  155. * time by processing multiple files at once
  156. * For the boards that lack a shift key, or that you want to always
  157. * attempt the flashing part, you can add FLASH_BOOTLOADER = yes to the
  158. * rules.mk of that keymap.
  159. */
  160. case KC_MAKE: // Compiles the firmware, and adds the flash command based on keyboard bootloader
  161. if (!record->event.pressed) {
  162. uint8_t temp_mod = get_mods();
  163. uint8_t temp_osm = get_oneshot_mods();
  164. clear_mods(); clear_oneshot_mods();
  165. SEND_STRING("make " QMK_KEYBOARD ":" QMK_KEYMAP);
  166. #ifndef FLASH_BOOTLOADER
  167. if ( (temp_mod | temp_osm) & MOD_MASK_SHIFT ) {
  168. SEND_STRING(":flash");
  169. }
  170. #endif
  171. if ( (temp_mod | temp_osm) & MOD_MASK_CTRL) {
  172. SEND_STRING(" -j8 --output-sync");
  173. }
  174. SEND_STRING(SS_TAP(X_ENTER));
  175. set_mods(temp_mod);
  176. }
  177. break;
  178. #ifdef RGBLIGHT_ENABLE
  179. case TG_LAYER_RGB:
  180. if (record->event.pressed) {
  181. rgb_layers_enabled = !rgb_layers_enabled;
  182. }
  183. return false;
  184. case TG_L0_RGB:
  185. if (record->event.pressed) {
  186. rgb_L0_enabled = !rgb_L0_enabled;
  187. }
  188. return false;
  189. #endif
  190. case SALT_CMD:
  191. if (!record->event.pressed) {
  192. SEND_STRING(SALT_CMD_MACRO);
  193. }
  194. return false;
  195. case LESS_PD:
  196. if (!record->event.pressed) {
  197. SEND_STRING(LESS_PD_MACRO);
  198. }
  199. return false;
  200. case CODE_PASTE:
  201. if (!record->event.pressed) {
  202. SEND_STRING(CODE_PASTE_MACRO);
  203. }
  204. return false;
  205. #ifdef TWSCHUM_TAPPING_CTRL_PREFIX
  206. case EN_CTRL_SHORTCUTS:
  207. if (record->event.pressed) {
  208. ctrl_shortcuts_enabled_g = !ctrl_shortcuts_enabled_g;
  209. start_idle_timer(); // need to clear out state in some cases
  210. }
  211. return false;
  212. case CTRL_A:
  213. return tap_ctrl_event(&special_keys_g[1], record);
  214. case CTRL_B:
  215. return tap_ctrl_event(&special_keys_g[0], record);
  216. default:
  217. if (record->event.pressed) {
  218. return tap_ctrl_other_pressed();
  219. }
  220. #endif
  221. }
  222. return true;
  223. }
  224. #ifdef RGBLIGHT_ENABLE
  225. void matrix_init_user(void) {
  226. // called once on board init
  227. rgblight_enable();
  228. }
  229. #endif
  230. void suspend_power_down_user(void) {
  231. // TODO shut off backlighting
  232. }
  233. void suspend_wakeup_init_user(void) {
  234. // TODO turn on backlighting
  235. }