[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>
This commit is contained in:
		
							parent
							
								
									90eef4cd15
								
							
						
					
					
						commit
						68b16bba68
					
				
					 19 changed files with 983 additions and 1 deletions
				
			
		
							
								
								
									
										80
									
								
								quantum/caps_word.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								quantum/caps_word.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,80 @@
 | 
			
		|||
// Copyright 2021-2022 Google LLC
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     https://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
#include "caps_word.h"
 | 
			
		||||
 | 
			
		||||
/** @brief True when Caps Word is active. */
 | 
			
		||||
static bool caps_word_active = false;
 | 
			
		||||
 | 
			
		||||
#if CAPS_WORD_IDLE_TIMEOUT > 0
 | 
			
		||||
// Constrain timeout to a sensible range. With 16-bit timers, the longest
 | 
			
		||||
// timeout possible is 32768 ms, rounded here to 30000 ms = half a minute.
 | 
			
		||||
#    if CAPS_WORD_IDLE_TIMEOUT < 100 || CAPS_WORD_IDLE_TIMEOUT > 30000
 | 
			
		||||
#        error "CAPS_WORD_IDLE_TIMEOUT must be between 100 and 30000 ms"
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
/** @brief Deadline for idle timeout. */
 | 
			
		||||
static uint16_t idle_timer = 0;
 | 
			
		||||
 | 
			
		||||
void caps_word_task(void) {
 | 
			
		||||
    if (caps_word_active && timer_expired(timer_read(), idle_timer)) {
 | 
			
		||||
        caps_word_off();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void caps_word_reset_idle_timer(void) {
 | 
			
		||||
    idle_timer = timer_read() + CAPS_WORD_IDLE_TIMEOUT;
 | 
			
		||||
}
 | 
			
		||||
#endif // CAPS_WORD_IDLE_TIMEOUT > 0
 | 
			
		||||
 | 
			
		||||
void caps_word_on(void) {
 | 
			
		||||
    if (caps_word_active) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    clear_mods();
 | 
			
		||||
#ifndef NO_ACTION_ONESHOT
 | 
			
		||||
    clear_oneshot_mods();
 | 
			
		||||
#endif // NO_ACTION_ONESHOT
 | 
			
		||||
#if CAPS_WORD_IDLE_TIMEOUT > 0
 | 
			
		||||
    caps_word_reset_idle_timer();
 | 
			
		||||
#endif // CAPS_WORD_IDLE_TIMEOUT > 0
 | 
			
		||||
 | 
			
		||||
    caps_word_active = true;
 | 
			
		||||
    caps_word_set_user(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void caps_word_off(void) {
 | 
			
		||||
    if (!caps_word_active) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unregister_weak_mods(MOD_MASK_SHIFT); // Make sure weak shift is off.
 | 
			
		||||
    caps_word_active = false;
 | 
			
		||||
    caps_word_set_user(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void caps_word_toggle(void) {
 | 
			
		||||
    if (caps_word_active) {
 | 
			
		||||
        caps_word_off();
 | 
			
		||||
    } else {
 | 
			
		||||
        caps_word_on();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool is_caps_word_on(void) {
 | 
			
		||||
    return caps_word_active;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) void caps_word_set_user(bool active) {}
 | 
			
		||||
							
								
								
									
										43
									
								
								quantum/caps_word.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								quantum/caps_word.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
// Copyright 2021-2022 Google LLC
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     https://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "quantum.h"
 | 
			
		||||
 | 
			
		||||
#ifndef CAPS_WORD_IDLE_TIMEOUT
 | 
			
		||||
#    define CAPS_WORD_IDLE_TIMEOUT 5000 // Default timeout of 5 seconds.
 | 
			
		||||
#endif                                  // CAPS_WORD_IDLE_TIMEOUT
 | 
			
		||||
 | 
			
		||||
#if CAPS_WORD_IDLE_TIMEOUT > 0
 | 
			
		||||
/** @brief Matrix scan task for Caps Word feature */
 | 
			
		||||
void caps_word_task(void);
 | 
			
		||||
 | 
			
		||||
/** @brief Resets timer for Caps Word idle timeout. */
 | 
			
		||||
void caps_word_reset_idle_timer(void);
 | 
			
		||||
#else
 | 
			
		||||
static inline void caps_word_task(void) {}
 | 
			
		||||
#endif // CAPS_WORD_IDLE_TIMEOUT > 0
 | 
			
		||||
 | 
			
		||||
void caps_word_on(void);     /**< Activates Caps Word. */
 | 
			
		||||
void caps_word_off(void);    /**< Deactivates Caps Word. */
 | 
			
		||||
void caps_word_toggle(void); /**< Toggles Caps Word. */
 | 
			
		||||
bool is_caps_word_on(void);  /**< Gets whether currently active. */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Caps Word set callback.
 | 
			
		||||
 *
 | 
			
		||||
 * @param active True if Caps Word is active, false otherwise
 | 
			
		||||
 */
 | 
			
		||||
void caps_word_set_user(bool active);
 | 
			
		||||
| 
						 | 
				
			
			@ -108,6 +108,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#ifdef BLUETOOTH_ENABLE
 | 
			
		||||
#    include "outputselect.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef CAPS_WORD_ENABLE
 | 
			
		||||
#    include "caps_word.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static uint32_t last_input_modification_time = 0;
 | 
			
		||||
uint32_t        last_input_activity_time(void) {
 | 
			
		||||
| 
						 | 
				
			
			@ -549,6 +552,10 @@ void quantum_task(void) {
 | 
			
		|||
    autoshift_matrix_scan();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CAPS_WORD_ENABLE
 | 
			
		||||
    caps_word_task();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef SECURE_ENABLE
 | 
			
		||||
    secure_task();
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										160
									
								
								quantum/process_keycode/process_caps_word.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								quantum/process_keycode/process_caps_word.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,160 @@
 | 
			
		|||
// Copyright 2021-2022 Google LLC
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     https://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
#include "process_caps_word.h"
 | 
			
		||||
 | 
			
		||||
bool process_caps_word(uint16_t keycode, keyrecord_t* record) {
 | 
			
		||||
    if (keycode == CAPSWRD) { // Pressing CAPSWRD toggles Caps Word.
 | 
			
		||||
        if (record->event.pressed) {
 | 
			
		||||
            caps_word_toggle();
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifndef NO_ACTION_ONESHOT
 | 
			
		||||
    const uint8_t mods = get_mods() | get_oneshot_mods();
 | 
			
		||||
#else
 | 
			
		||||
    const uint8_t mods = get_mods();
 | 
			
		||||
#endif // NO_ACTION_ONESHOT
 | 
			
		||||
 | 
			
		||||
    if (!is_caps_word_on()) {
 | 
			
		||||
        // The following optionally turns on Caps Word by holding left and
 | 
			
		||||
        // right shifts or by double tapping left shift. This way Caps Word
 | 
			
		||||
        // may be used without needing a dedicated key and also without
 | 
			
		||||
        // needing combos or tap dance.
 | 
			
		||||
 | 
			
		||||
#ifdef BOTH_SHIFTS_TURNS_ON_CAPS_WORD
 | 
			
		||||
        // Many keyboards enable the Command feature by default, which also
 | 
			
		||||
        // uses left+right shift. It can be configured to use a different
 | 
			
		||||
        // key combination by defining IS_COMMAND(). We make a non-fatal
 | 
			
		||||
        // warning if Command is enabled but IS_COMMAND() is *not* defined.
 | 
			
		||||
#    if defined(COMMAND_ENABLE) && !defined(IS_COMMAND)
 | 
			
		||||
#        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)."
 | 
			
		||||
#    else
 | 
			
		||||
        if (mods == MOD_MASK_SHIFT
 | 
			
		||||
#        ifdef COMMAND_ENABLE
 | 
			
		||||
            // Don't activate Caps Word at the same time as Command.
 | 
			
		||||
            && !(IS_COMMAND())
 | 
			
		||||
#        endif // COMMAND_ENABLE
 | 
			
		||||
        ) {
 | 
			
		||||
            caps_word_on();
 | 
			
		||||
        }
 | 
			
		||||
#    endif     // defined(COMMAND_ENABLE) && !defined(IS_COMMAND)
 | 
			
		||||
#endif         // BOTH_SHIFTS_TURNS_ON_CAPS_WORD
 | 
			
		||||
 | 
			
		||||
#ifdef DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD
 | 
			
		||||
        // Double tapping left shift turns on Caps Word.
 | 
			
		||||
        //
 | 
			
		||||
        // NOTE: This works with KC_LSFT and one-shot left shift. It
 | 
			
		||||
        // wouldn't make sense with mod-tap or Space Cadet shift since
 | 
			
		||||
        // double tapping would of course trigger the tapping action.
 | 
			
		||||
        if (record->event.pressed) {
 | 
			
		||||
            static bool     tapped = false;
 | 
			
		||||
            static uint16_t timer  = 0;
 | 
			
		||||
            if (keycode == KC_LSFT || keycode == OSM(MOD_LSFT)) {
 | 
			
		||||
                if (tapped && !timer_expired(record->event.time, timer)) {
 | 
			
		||||
                    // Left shift was double tapped, activate Caps Word.
 | 
			
		||||
                    caps_word_on();
 | 
			
		||||
                }
 | 
			
		||||
                tapped = true;
 | 
			
		||||
                timer  = record->event.time + GET_TAPPING_TERM(keycode, record);
 | 
			
		||||
            } else {
 | 
			
		||||
                tapped = false; // Reset when any other key is pressed.
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
#endif // DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if CAPS_WORD_IDLE_TIMEOUT > 0
 | 
			
		||||
    caps_word_reset_idle_timer();
 | 
			
		||||
#endif // CAPS_WORD_IDLE_TIMEOUT > 0
 | 
			
		||||
 | 
			
		||||
    // From here on, we only take action on press events.
 | 
			
		||||
    if (!record->event.pressed) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!(mods & ~MOD_MASK_SHIFT)) {
 | 
			
		||||
        switch (keycode) {
 | 
			
		||||
            // Ignore MO, TO, TG, TT, and OSL layer switch keys.
 | 
			
		||||
            case QK_MOMENTARY ... QK_MOMENTARY_MAX:
 | 
			
		||||
            case QK_TO ... QK_TO_MAX:
 | 
			
		||||
            case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:
 | 
			
		||||
            case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
 | 
			
		||||
            case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:
 | 
			
		||||
                return true;
 | 
			
		||||
 | 
			
		||||
#ifndef NO_ACTION_TAPPING
 | 
			
		||||
            case QK_MOD_TAP ... QK_MOD_TAP_MAX:
 | 
			
		||||
                if (record->tap.count == 0) {
 | 
			
		||||
                    // Deactivate if a mod becomes active through holding
 | 
			
		||||
                    // a mod-tap key.
 | 
			
		||||
                    caps_word_off();
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
                keycode &= 0xff;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
#    ifndef NO_ACTION_LAYER
 | 
			
		||||
            case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
 | 
			
		||||
#    endif // NO_ACTION_LAYER
 | 
			
		||||
                if (record->tap.count == 0) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
                keycode &= 0xff;
 | 
			
		||||
                break;
 | 
			
		||||
#endif // NO_ACTION_TAPPING
 | 
			
		||||
 | 
			
		||||
#ifdef SWAP_HANDS_ENABLE
 | 
			
		||||
            case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:
 | 
			
		||||
                if (keycode > 0x56F0 || record->tap.count == 0) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
                keycode &= 0xff;
 | 
			
		||||
                break;
 | 
			
		||||
#endif // SWAP_HANDS_ENABLE
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        clear_weak_mods();
 | 
			
		||||
        if (caps_word_press_user(keycode)) {
 | 
			
		||||
            send_keyboard_report();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    caps_word_off();
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) bool caps_word_press_user(uint16_t keycode) {
 | 
			
		||||
    switch (keycode) {
 | 
			
		||||
        // Keycodes that continue Caps Word, with shift applied.
 | 
			
		||||
        case KC_A ... KC_Z:
 | 
			
		||||
        case KC_MINS:
 | 
			
		||||
            add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to next key.
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        // Keycodes that continue Caps Word, without shifting.
 | 
			
		||||
        case KC_1 ... KC_0:
 | 
			
		||||
        case KC_BSPC:
 | 
			
		||||
        case KC_DEL:
 | 
			
		||||
        case KC_UNDS:
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            return false; // Deactivate Caps Word.
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								quantum/process_keycode/process_caps_word.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								quantum/process_keycode/process_caps_word.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
// Copyright 2021-2022 Google LLC
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     https://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "quantum.h"
 | 
			
		||||
#include "caps_word.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Process handler for Caps Word feature.
 | 
			
		||||
 *
 | 
			
		||||
 * @param keycode  Keycode registered by matrix press, per keymap
 | 
			
		||||
 * @param record   keyrecord_t structure
 | 
			
		||||
 * @return true    Continue processing keycodes, and send to host
 | 
			
		||||
 * @return false   Stop processing keycodes, and don't send to host
 | 
			
		||||
 */
 | 
			
		||||
bool process_caps_word(uint16_t keycode, keyrecord_t* record);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Weak function for user-level Caps Word press modification.
 | 
			
		||||
 *
 | 
			
		||||
 * @param keycode   Keycode registered by matrix press, per keymap
 | 
			
		||||
 * @return true     Continue Caps Word
 | 
			
		||||
 * @return false    Stop Caps Word
 | 
			
		||||
 */
 | 
			
		||||
bool caps_word_press_user(uint16_t keycode);
 | 
			
		||||
| 
						 | 
				
			
			@ -307,6 +307,9 @@ bool process_record_quantum(keyrecord_t *record) {
 | 
			
		|||
#ifdef TERMINAL_ENABLE
 | 
			
		||||
            process_terminal(keycode, record) &&
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef CAPS_WORD_ENABLE
 | 
			
		||||
            process_caps_word(keycode, record) &&
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef SPACE_CADET_ENABLE
 | 
			
		||||
            process_space_cadet(keycode, record) &&
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -233,6 +233,11 @@ extern layer_state_t layer_state;
 | 
			
		|||
#    include "pointing_device.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CAPS_WORD_ENABLE
 | 
			
		||||
#    include "caps_word.h"
 | 
			
		||||
#    include "process_caps_word.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// For tri-layer
 | 
			
		||||
void          update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3);
 | 
			
		||||
layer_state_t update_tri_layer_state(layer_state_t state, uint8_t layer1, uint8_t layer2, uint8_t layer3);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -602,6 +602,8 @@ enum quantum_keycodes {
 | 
			
		|||
    SECURE_UNLOCK,
 | 
			
		||||
    SECURE_TOGGLE,
 | 
			
		||||
 | 
			
		||||
    CAPS_WORD,
 | 
			
		||||
 | 
			
		||||
    // Start of custom keycode range for keyboards and keymaps - always leave at the end
 | 
			
		||||
    SAFE_RANGE
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -964,5 +966,6 @@ enum quantum_keycodes {
 | 
			
		|||
#define PB_32 PROGRAMMABLE_BUTTON_32
 | 
			
		||||
#define PROGRAMMABLE_BUTTON_MIN PROGRAMMABLE_BUTTON_1
 | 
			
		||||
#define PROGRAMMABLE_BUTTON_MAX PROGRAMMABLE_BUTTON_32
 | 
			
		||||
#define CAPSWRD CAPS_WORD
 | 
			
		||||
 | 
			
		||||
#include "quantum_keycodes_legacy.h"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue