You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

134 lines
6.4 KiB

  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. bool process_key_lock(uint16_t *keycode, keyrecord_t *record) {
  55. // We start by categorizing the keypress event. In the event of a down
  56. // event, there are several possibilities:
  57. // 1. The key is not being locked, and we are not watching for new keys.
  58. // In this case, we bail immediately. This is the common case for down events.
  59. // 2. The key was locked, and we need to unlock it. In this case, we will
  60. // reset the state in our map and return false. When the user releases the
  61. // key, the up event will no longer be masked and the OS will observe the
  62. // released key.
  63. // 3. KC_LOCK was just pressed. In this case, we set up the state machine
  64. // to watch for the next key down event, and finish processing
  65. // 4. The keycode is below 0xFF, and we are watching for new keys. In this case,
  66. // we will send the key down event to the os, and set the key_state for that
  67. // key to mask the up event.
  68. // 5. The keycode is above 0xFF, and we're wathing for new keys. In this case,
  69. // the user pressed a key that we cannot "lock", as it's a series of keys,
  70. // or a macro invocation, or a layer transition, or a custom-defined key, or
  71. // or some other arbitrary code. In this case, we bail immediately, reset
  72. // our watch state, and return true.
  73. //
  74. // In the event of an up event, there are these possibilities:
  75. // 1. The key is not being locked. In this case, we return true and bail
  76. // immediately. This is the common case.
  77. // 2. The key is being locked. In this case, we will mask the up event
  78. // by returning false, so the OS never sees that the key was released
  79. // until the user pressed the key again.
  80. // We translate any OSM keycodes back to their original keycodes, so that if the key being
  81. // one-shot modded is a standard keycode, we can handle it. This is the only set of special
  82. // keys that we handle
  83. uint16_t translated_keycode = translate_keycode(*keycode);
  84. if (record->event.pressed) {
  85. // Non-standard keycode, reset and return
  86. if (!(IS_STANDARD_KEYCODE(translated_keycode) || translated_keycode == KC_LOCK)) {
  87. watching = false;
  88. return true;
  89. }
  90. // If we're already watching, turn off the watch.
  91. if (translated_keycode == KC_LOCK) {
  92. watching = !watching;
  93. return false;
  94. }
  95. if (IS_STANDARD_KEYCODE(translated_keycode)) {
  96. // We check watching first. This is so that in the following scenario, we continue to
  97. // hold the key: KC_LOCK, KC_F, KC_LOCK, KC_F
  98. // If we checked in reverse order, we'd end up holding the key pressed after the second
  99. // KC_F press is registered, when the user likely meant to hold F
  100. if (watching) {
  101. watching = false;
  102. SET_KEY_STATE(translated_keycode);
  103. // We need to set the keycode passed in to be the translated keycode, in case we
  104. // translated a OSM back to the original keycode.
  105. *keycode = translated_keycode;
  106. // Let the standard keymap send the keycode down event. The up event will be masked.
  107. return true;
  108. }
  109. if (KEY_STATE(translated_keycode)) {
  110. UNSET_KEY_STATE(translated_keycode);
  111. // The key is already held, stop this process. The up event will be sent when the user
  112. // releases the key.
  113. return false;
  114. }
  115. }
  116. // Either the key isn't a standard key, or we need to send the down event. Continue standard
  117. // processing
  118. return true;
  119. } else {
  120. // Stop processing if it's a standard key and we're masking up.
  121. return !(IS_STANDARD_KEYCODE(translated_keycode) && KEY_STATE(translated_keycode));
  122. }
  123. }