process_key_lock.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. /* Copyright 2017 Fredric Silberberg
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation, either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <inttypes.h>
  17. #include <stdint.h>
  18. #include "process_key_lock.h"
  19. #define BV_64(shift) (((uint64_t)1) << (shift))
  20. #define GET_KEY_ARRAY(code) (((code) < 0x40) ? key_state[0] : ((code) < 0x80) ? key_state[1] : ((code) < 0xC0) ? key_state[2] : key_state[3])
  21. #define GET_CODE_INDEX(code) (((code) < 0x40) ? (code) : ((code) < 0x80) ? (code)-0x40 : ((code) < 0xC0) ? (code)-0x80 : (code)-0xC0)
  22. #define KEY_STATE(code) (GET_KEY_ARRAY(code) & BV_64(GET_CODE_INDEX(code))) == BV_64(GET_CODE_INDEX(code))
  23. #define SET_KEY_ARRAY_STATE(code, val) \
  24. do { \
  25. switch (code) { \
  26. case 0x00 ... 0x3F: \
  27. key_state[0] = (val); \
  28. break; \
  29. case 0x40 ... 0x7F: \
  30. key_state[1] = (val); \
  31. break; \
  32. case 0x80 ... 0xBF: \
  33. key_state[2] = (val); \
  34. break; \
  35. case 0xC0 ... 0xFF: \
  36. key_state[3] = (val); \
  37. break; \
  38. } \
  39. } while (0)
  40. #define SET_KEY_STATE(code) SET_KEY_ARRAY_STATE(code, (GET_KEY_ARRAY(code) | BV_64(GET_CODE_INDEX(code))))
  41. #define UNSET_KEY_STATE(code) SET_KEY_ARRAY_STATE(code, (GET_KEY_ARRAY(code)) & ~(BV_64(GET_CODE_INDEX(code))))
  42. #define IS_STANDARD_KEYCODE(code) ((code) <= 0xFF)
  43. // Locked key state. This is an array of 256 bits, one for each of the standard keys supported qmk.
  44. uint64_t key_state[4] = {0x0, 0x0, 0x0, 0x0};
  45. bool watching = false;
  46. // Translate any OSM keycodes back to their unmasked versions.
  47. static inline uint16_t translate_keycode(uint16_t keycode) {
  48. if (keycode > QK_ONE_SHOT_MOD && keycode <= QK_ONE_SHOT_MOD_MAX) {
  49. return keycode ^ QK_ONE_SHOT_MOD;
  50. } else {
  51. return keycode;
  52. }
  53. }
  54. void cancel_key_lock(void) {
  55. watching = false;
  56. UNSET_KEY_STATE(0x0);
  57. }
  58. bool process_key_lock(uint16_t *keycode, keyrecord_t *record) {
  59. // We start by categorizing the keypress event. In the event of a down
  60. // event, there are several possibilities:
  61. // 1. The key is not being locked, and we are not watching for new keys.
  62. // In this case, we bail immediately. This is the common case for down events.
  63. // 2. The key was locked, and we need to unlock it. In this case, we will
  64. // reset the state in our map and return false. When the user releases the
  65. // key, the up event will no longer be masked and the OS will observe the
  66. // released key.
  67. // 3. KC_LOCK was just pressed. In this case, we set up the state machine
  68. // to watch for the next key down event, and finish processing
  69. // 4. The keycode is below 0xFF, and we are watching for new keys. In this case,
  70. // we will send the key down event to the os, and set the key_state for that
  71. // key to mask the up event.
  72. // 5. The keycode is above 0xFF, and we're wathing for new keys. In this case,
  73. // the user pressed a key that we cannot "lock", as it's a series of keys,
  74. // or a macro invocation, or a layer transition, or a custom-defined key, or
  75. // or some other arbitrary code. In this case, we bail immediately, reset
  76. // our watch state, and return true.
  77. //
  78. // In the event of an up event, there are these possibilities:
  79. // 1. The key is not being locked. In this case, we return true and bail
  80. // immediately. This is the common case.
  81. // 2. The key is being locked. In this case, we will mask the up event
  82. // by returning false, so the OS never sees that the key was released
  83. // until the user pressed the key again.
  84. // We translate any OSM keycodes back to their original keycodes, so that if the key being
  85. // one-shot modded is a standard keycode, we can handle it. This is the only set of special
  86. // keys that we handle
  87. uint16_t translated_keycode = translate_keycode(*keycode);
  88. if (record->event.pressed) {
  89. // Non-standard keycode, reset and return
  90. if (!(IS_STANDARD_KEYCODE(translated_keycode) || translated_keycode == KC_LOCK)) {
  91. watching = false;
  92. return true;
  93. }
  94. // If we're already watching, turn off the watch.
  95. if (translated_keycode == KC_LOCK) {
  96. watching = !watching;
  97. return false;
  98. }
  99. if (IS_STANDARD_KEYCODE(translated_keycode)) {
  100. // We check watching first. This is so that in the following scenario, we continue to
  101. // hold the key: KC_LOCK, KC_F, KC_LOCK, KC_F
  102. // If we checked in reverse order, we'd end up holding the key pressed after the second
  103. // KC_F press is registered, when the user likely meant to hold F
  104. if (watching) {
  105. watching = false;
  106. SET_KEY_STATE(translated_keycode);
  107. // We need to set the keycode passed in to be the translated keycode, in case we
  108. // translated a OSM back to the original keycode.
  109. *keycode = translated_keycode;
  110. // Let the standard keymap send the keycode down event. The up event will be masked.
  111. return true;
  112. }
  113. if (KEY_STATE(translated_keycode)) {
  114. UNSET_KEY_STATE(translated_keycode);
  115. // The key is already held, stop this process. The up event will be sent when the user
  116. // releases the key.
  117. return false;
  118. }
  119. }
  120. // Either the key isn't a standard key, or we need to send the down event. Continue standard
  121. // processing
  122. return true;
  123. } else {
  124. // Stop processing if it's a standard key and we're masking up.
  125. return !(IS_STANDARD_KEYCODE(translated_keycode) && KEY_STATE(translated_keycode));
  126. }
  127. }