summaryrefslogtreecommitdiff
path: root/quantum/process_keycode/process_caps_word.c
diff options
context:
space:
mode:
authorPascal Getreuer <50221757+getreuer@users.noreply.github.com>2022-05-13 23:00:32 -0700
committerGitHub <noreply@github.com>2022-05-14 16:00:32 +1000
commit68b16bba68d79c63bbd649a5be58756e937c96da (patch)
treebe0d3d672c13c080f4da58687b5b1a261f465911 /quantum/process_keycode/process_caps_word.c
parent90eef4cd153cdc1b00d973e6abb029828f656294 (diff)
[Core] Add Caps Word feature to core (#16588)
Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> Co-authored-by: Drashna Jaelre <drashna@live.com>
Diffstat (limited to 'quantum/process_keycode/process_caps_word.c')
-rw-r--r--quantum/process_keycode/process_caps_word.c160
1 files changed, 160 insertions, 0 deletions
diff --git a/quantum/process_keycode/process_caps_word.c b/quantum/process_keycode/process_caps_word.c
new file mode 100644
index 0000000000..15238f04a1
--- /dev/null
+++ b/quantum/process_keycode/process_caps_word.c
@@ -0,0 +1,160 @@
1// Copyright 2021-2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "process_caps_word.h"
16
17bool process_caps_word(uint16_t keycode, keyrecord_t* record) {
18 if (keycode == CAPSWRD) { // Pressing CAPSWRD toggles Caps Word.
19 if (record->event.pressed) {
20 caps_word_toggle();
21 }
22 return false;
23 }
24
25#ifndef NO_ACTION_ONESHOT
26 const uint8_t mods = get_mods() | get_oneshot_mods();
27#else
28 const uint8_t mods = get_mods();
29#endif // NO_ACTION_ONESHOT
30
31 if (!is_caps_word_on()) {
32 // The following optionally turns on Caps Word by holding left and
33 // right shifts or by double tapping left shift. This way Caps Word
34 // may be used without needing a dedicated key and also without
35 // needing combos or tap dance.
36
37#ifdef BOTH_SHIFTS_TURNS_ON_CAPS_WORD
38 // Many keyboards enable the Command feature by default, which also
39 // uses left+right shift. It can be configured to use a different
40 // key combination by defining IS_COMMAND(). We make a non-fatal
41 // warning if Command is enabled but IS_COMMAND() is *not* defined.
42# if defined(COMMAND_ENABLE) && !defined(IS_COMMAND)
43# pragma message "BOTH_SHIFTS_TURNS_ON_CAPS_WORD and Command should not be enabled at the same time, since both use the Left Shift + Right Shift key combination. Please disable Command, or ensure that `IS_COMMAND` is not set to (get_mods() == MOD_MASK_SHIFT)."
44# else
45 if (mods == MOD_MASK_SHIFT
46# ifdef COMMAND_ENABLE
47 // Don't activate Caps Word at the same time as Command.
48 && !(IS_COMMAND())
49# endif // COMMAND_ENABLE
50 ) {
51 caps_word_on();
52 }
53# endif // defined(COMMAND_ENABLE) && !defined(IS_COMMAND)
54#endif // BOTH_SHIFTS_TURNS_ON_CAPS_WORD
55
56#ifdef DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD
57 // Double tapping left shift turns on Caps Word.
58 //
59 // NOTE: This works with KC_LSFT and one-shot left shift. It
60 // wouldn't make sense with mod-tap or Space Cadet shift since
61 // double tapping would of course trigger the tapping action.
62 if (record->event.pressed) {
63 static bool tapped = false;
64 static uint16_t timer = 0;
65 if (keycode == KC_LSFT || keycode == OSM(MOD_LSFT)) {
66 if (tapped && !timer_expired(record->event.time, timer)) {
67 // Left shift was double tapped, activate Caps Word.
68 caps_word_on();
69 }
70 tapped = true;
71 timer = record->event.time + GET_TAPPING_TERM(keycode, record);
72 } else {
73 tapped = false; // Reset when any other key is pressed.
74 }
75 }
76#endif // DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD
77
78 return true;
79 }
80
81#if CAPS_WORD_IDLE_TIMEOUT > 0
82 caps_word_reset_idle_timer();
83#endif // CAPS_WORD_IDLE_TIMEOUT > 0
84
85 // From here on, we only take action on press events.
86 if (!record->event.pressed) {
87 return true;
88 }
89
90 if (!(mods & ~MOD_MASK_SHIFT)) {
91 switch (keycode) {
92 // Ignore MO, TO, TG, TT, and OSL layer switch keys.
93 case QK_MOMENTARY ... QK_MOMENTARY_MAX:
94 case QK_TO ... QK_TO_MAX:
95 case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:
96 case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
97 case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:
98 return true;
99
100#ifndef NO_ACTION_TAPPING
101 case QK_MOD_TAP ... QK_MOD_TAP_MAX:
102 if (record->tap.count == 0) {
103 // Deactivate if a mod becomes active through holding
104 // a mod-tap key.
105 caps_word_off();
106 return true;
107 }
108 keycode &= 0xff;
109 break;
110
111# ifndef NO_ACTION_LAYER
112 case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
113# endif // NO_ACTION_LAYER
114 if (record->tap.count == 0) {
115 return true;
116 }
117 keycode &= 0xff;
118 break;
119#endif // NO_ACTION_TAPPING
120
121#ifdef SWAP_HANDS_ENABLE
122 case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:
123 if (keycode > 0x56F0 || record->tap.count == 0) {
124 return true;
125 }
126 keycode &= 0xff;
127 break;
128#endif // SWAP_HANDS_ENABLE
129 }
130
131 clear_weak_mods();
132 if (caps_word_press_user(keycode)) {
133 send_keyboard_report();
134 return true;
135 }
136 }
137
138 caps_word_off();
139 return true;
140}
141
142__attribute__((weak)) bool caps_word_press_user(uint16_t keycode) {
143 switch (keycode) {
144 // Keycodes that continue Caps Word, with shift applied.
145 case KC_A ... KC_Z:
146 case KC_MINS:
147 add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to next key.
148 return true;
149
150 // Keycodes that continue Caps Word, without shifting.
151 case KC_1 ... KC_0:
152 case KC_BSPC:
153 case KC_DEL:
154 case KC_UNDS:
155 return true;
156
157 default:
158 return false; // Deactivate Caps Word.
159 }
160}