-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
2,092 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
/* Copyright 2021 Andrew Rae [email protected] @andrewjrae | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
#include "casemodes.h" | ||
#include "status.h" | ||
#include "keycodes.h" | ||
|
||
/* The caps word concept started with @iaap on splitkb.com discord. | ||
* However it has been implemented and extended by many splitkb.com users: | ||
* - @theol0403 made many improvements to initial implementation | ||
* - @precondition used caps lock rather than shifting | ||
* - @dnaq his own implementation which also used caps lock | ||
* - @sevanteri added underscores on spaces | ||
* - @metheon extended on @sevanteri's work and added specific modes for | ||
* snake_case and SCREAMING_SNAKE_CASE | ||
* - @baffalop came up with the idea for xcase, which he implements in his own | ||
* repo, however this is implemented by @iaap with support also for one-shot-shift. | ||
* - @sevanteri | ||
* - fixed xcase waiting mode to allow more modified keys and keys from other layers. | ||
* - Added @baffalop's separator defaulting on first keypress, with a | ||
* configurable default separator and overrideable function to determine | ||
* if the default should be used. | ||
*/ | ||
|
||
#ifndef DEFAULT_XCASE_SEPARATOR | ||
# define DEFAULT_XCASE_SEPARATOR KC_UNDS | ||
#endif | ||
|
||
#define IS_OSM(keycode) (keycode >= QK_ONE_SHOT_MOD && keycode <= QK_ONE_SHOT_MOD_MAX) | ||
|
||
// enum for the xcase states | ||
enum xcase_state { | ||
XCASE_OFF = 0, // xcase is off | ||
XCASE_ON, // xcase is actively on | ||
XCASE_WAIT, // xcase is waiting for the delimiter input | ||
}; | ||
|
||
// bool to keep track of the caps word state | ||
static bool caps_word_on = false; | ||
|
||
// enum to keep track of the xcase state | ||
static enum xcase_state xcase_state = XCASE_OFF; | ||
// the keycode of the xcase delimiter | ||
static uint16_t xcase_delimiter; | ||
|
||
// Check whether caps word is on | ||
bool caps_word_enabled(void) { | ||
return caps_word_on; | ||
} | ||
|
||
// Enable caps word | ||
void enable_caps_word(void) { | ||
caps_word_on = true; | ||
if (!host_keyboard_led_state().caps_lock) { | ||
tap_code(is_caps_swapped() ? KC_ESC : KC_CAPS); | ||
} | ||
} | ||
|
||
// Disable caps word | ||
void disable_caps_word(void) { | ||
caps_word_on = false; | ||
if (host_keyboard_led_state().caps_lock) { | ||
tap_code(is_caps_swapped() ? KC_ESC : KC_CAPS); | ||
} | ||
} | ||
|
||
// Toggle caps word | ||
void toggle_caps_word(void) { | ||
if (caps_word_on) { | ||
disable_caps_word(); | ||
} else { | ||
enable_caps_word(); | ||
} | ||
} | ||
|
||
// Check whether xcase is on | ||
bool xcase_enabled(void) { | ||
return xcase_state == XCASE_ON; | ||
} | ||
|
||
// Check whether xcase is waiting for a keypress | ||
bool xcase_waiting(void) { | ||
return xcase_state == XCASE_WAIT; | ||
} | ||
|
||
// Enable xcase and pickup the next keystroke as the delimiter | ||
void enable_xcase(void) { | ||
xcase_state = XCASE_WAIT; | ||
} | ||
|
||
// Enable xcase with the specified delimiter | ||
void enable_xcase_with(uint16_t delimiter) { | ||
xcase_state = XCASE_ON; | ||
xcase_delimiter = delimiter; | ||
} | ||
|
||
// Disable xcase | ||
void disable_xcase(void) { | ||
xcase_state = XCASE_OFF; | ||
} | ||
|
||
// Place the current xcase delimiter | ||
static void place_delimiter(void) { | ||
switch (xcase_delimiter) { | ||
case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: { | ||
// apparently set_oneshot_mods() is dumb and doesn't deal with handedness for you | ||
uint8_t mods = xcase_delimiter & 0x10 ? (xcase_delimiter & 0x0F) << 4 : xcase_delimiter & 0xFF; | ||
set_oneshot_mods(mods); | ||
break; | ||
} | ||
default: | ||
tap_code16(xcase_delimiter); | ||
break; | ||
} | ||
} | ||
|
||
// overrideable function to determine whether the case mode should stop | ||
__attribute__((weak)) bool terminate_case_modes(uint16_t keycode, const keyrecord_t *record) { | ||
switch (keycode) { | ||
// Keycodes to ignore (don't disable caps word) | ||
case KC_A ... KC_Z: | ||
case KC_1 ... KC_0: | ||
case KC_MINS: | ||
case KC_UNDS: | ||
case KC_BSPC: | ||
// If mod chording disable the mods | ||
if (record->event.pressed && (get_mods() != 0)) { | ||
return true; | ||
} | ||
break; | ||
default: | ||
if (record->event.pressed) { | ||
return true; | ||
} | ||
break; | ||
} | ||
return false; | ||
} | ||
|
||
/* overrideable function to determine whether to use the default separator on | ||
* first keypress when waiting for the separator. */ | ||
__attribute__((weak)) bool use_default_xcase_separator(uint16_t keycode, const keyrecord_t *record) { | ||
// for example: | ||
/* switch (keycode) { */ | ||
/* case KC_A ... KC_Z: */ | ||
/* case KC_1 ... KC_0: */ | ||
/* return true; */ | ||
/* } */ | ||
return false; | ||
} | ||
|
||
bool process_case_modes(uint16_t keycode, const keyrecord_t *record) { | ||
if (xcase_state == XCASE_WAIT) { | ||
// grab the next input to be the delimiter | ||
if (use_default_xcase_separator(keycode, record)) { | ||
enable_xcase_with(DEFAULT_XCASE_SEPARATOR); | ||
} else if (record->event.pressed) { | ||
if (keycode > QK_MODS_MAX || IS_MODIFIER_KEYCODE(keycode)) { | ||
// let special keys and normal modifiers go through | ||
return true; | ||
} else { | ||
// factor in mods | ||
if (get_mods() & MOD_MASK_SHIFT) { | ||
keycode = LSFT(keycode); | ||
} else if (get_mods() & MOD_BIT(KC_RALT)) { | ||
keycode = RALT(keycode); | ||
} | ||
enable_xcase_with(keycode); | ||
return false; | ||
} | ||
return false; | ||
} else { | ||
if (IS_OSM(keycode)) { | ||
// this catches the OSM release if no other key was pressed | ||
set_oneshot_mods(0); | ||
enable_xcase_with(keycode); | ||
} | ||
// let other special keys go through | ||
return true; | ||
} | ||
} | ||
|
||
if (caps_word_on || xcase_state) { | ||
// Get the base keycode of a mod or layer tap key | ||
switch (keycode) { | ||
case QK_MOD_TAP ... QK_MOD_TAP_MAX: | ||
case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: | ||
case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: | ||
// Earlier return if this has not been considered tapped yet | ||
if (record->tap.count == 0) return true; | ||
keycode = keycode & 0xFF; | ||
break; | ||
default: | ||
break; | ||
} | ||
|
||
if (record->event.pressed) { | ||
// handle xcase mode | ||
if (xcase_state == XCASE_ON) { | ||
// place the delimiter if needed | ||
if (keycode == XCASE_DELIMITER_KEY) { | ||
place_delimiter(); | ||
return false; | ||
} | ||
} // end XCASE_ON | ||
|
||
// check if the case modes have been terminated | ||
if (terminate_case_modes(keycode, record)) { | ||
disable_caps_word(); | ||
disable_xcase(); | ||
} | ||
} // end if event.pressed | ||
|
||
return true; | ||
} | ||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#pragma once | ||
|
||
#include QMK_KEYBOARD_H | ||
|
||
// Check whether caps word is on | ||
bool caps_word_enabled(void); | ||
// Enable caps word | ||
void enable_caps_word(void); | ||
// Disable caps word | ||
void disable_caps_word(void); | ||
// Toggle caps word | ||
void toggle_caps_word(void); | ||
|
||
// Check whether xcase is on | ||
bool xcase_enabled(void); | ||
// Check whether xcase is waiting for a keypress | ||
bool xcase_waiting(void); | ||
// Enable xcase and pickup the next keystroke as the delimiter | ||
void enable_xcase(void); | ||
// Enable xcase with the specified delimiter | ||
void enable_xcase_with(uint16_t delimiter); | ||
// Disable xcase | ||
void disable_xcase(void); | ||
|
||
// Function to be put in process user | ||
bool process_case_modes(uint16_t keycode, const keyrecord_t *record); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
// Left top row | ||
COMB(q_comb, SE_Q, SE_J, SE_C) | ||
COMB(qu_comb, QU, SE_C, SE_Y) | ||
COMB(z_comb, SE_Z, SE_Y, SE_F) | ||
COMB(ctrl_w, C(SE_W), SE_C, SE_Y, SE_F) | ||
COMB(clear, CLEAR, SE_F, SE_P) | ||
// Right top row | ||
SUBS(el_str_int, "#{}"SS_TAP(X_LEFT), SE_X, SE_W) | ||
COMB(eql, SE_EQL, SE_W, SE_O) | ||
COMB(backsp, KC_BSPC, SE_O, SE_U) | ||
COMB(ctrl_w2, C(SE_W), SE_W, SE_O, SE_U) | ||
COMB(rev_rep, REV_REP, SE_U, SE_DOT) | ||
|
||
// Left home row | ||
COMB(ctrl_combo_l, OS_CTRL, SE_S, SE_T) | ||
COMB(escape_sym, ESC_SYM, SE_T, SE_H) | ||
COMB(dquo, SE_DQUO, SE_S, SE_H) | ||
COMB(tab_mod, TAB_MOD, SE_S, SE_T, SE_H) | ||
COMB(del, KC_DEL, SE_H, SE_K) | ||
// Right home row | ||
COMB(coln_sym, COLN_SYM, SE_N, SE_A) | ||
COMB(ctrl_combo_r, OS_CTRL, SE_A, SE_I) | ||
COMB(quot, SE_QUOT, SE_N, SE_I) | ||
COMB(ent, KC_ENT, SE_N, SE_A, SE_I) | ||
COMB(save_vim, SAVE_VIM, SE_N, SE_A, SE_I, REPEAT) | ||
// Mixed | ||
COMB(capsword, CAPSWORD, SE_T, SE_A) | ||
|
||
// Left bottom row | ||
COMB(vsp, VIM_VS, SE_V, SE_G) | ||
COMB(gui_combo_l, OS_GUI, SE_G, SE_D) | ||
COMB(lalt, MY_LALT, SE_V, SE_D) | ||
COMB(close_win, CLOSE_WIN, SE_V, SE_G, SE_D) | ||
COMB(shift_combo_l, KC_LSFT, SE_D, SE_B) | ||
// Right bottom row | ||
COMB(shift_combo_r, KC_RSFT, SE_SLSH, SE_L) | ||
COMB(gui_combo_r, OS_GUI, SE_L, SE_LPRN) | ||
COMB(dlr, SE_DLR, SE_LPRN, SE_RPRN) | ||
COMB(leader, LEADER, SE_L, SE_RPRN) | ||
COMB(swe, TG_SWE, SE_L, SE_LPRN, SE_RPRN) | ||
|
||
// Mixed home row + other | ||
COMB(scln, SE_SCLN, SE_T, SE_D) | ||
COMB(win_alt, WIN_ALT, SE_L, SE_A) | ||
|
||
// Vertical top left | ||
SUBS(small_left_arrow, "<-", SE_Y, SE_T) | ||
SUBS(lt_eq, "<=", SE_F, SE_H) | ||
// Vertical top right | ||
SUBS(large_right_arrow, "=>", SE_W, SE_N) | ||
SUBS(small_right_arrow, "->", SE_O, SE_A) | ||
SUBS(pipe_to, "|>", SE_U, SE_I) | ||
// Vertical bottom left | ||
COMB(sp, VIM_SP, SE_S, SE_V) | ||
SUBS(gt_eq, ">=", SE_H, SE_D) | ||
|
||
// Thumbs | ||
COMB(num, NUMWORD, MT_SPC, SE_E) | ||
|
||
// Numbers with cross side thumb | ||
COMB(comb_6, SE_6, SE_E, SE_R) | ||
COMB(comb_4, SE_4, SE_E, SE_S) | ||
COMB(comb_0, SE_0, SE_E, SE_T) | ||
COMB(comb_2, SE_2, SE_E, SE_H) | ||
COMB(comb_8, SE_8, SE_E, SE_D) | ||
COMB(comb_9, SE_9, MT_SPC, SE_L) | ||
COMB(comb_3, SE_3, MT_SPC, SE_N) | ||
COMB(comb_1, SE_1, MT_SPC, SE_A) | ||
COMB(comb_5, SE_5, MT_SPC, SE_I) | ||
COMB(comb_7, SE_7, MT_SPC, REPEAT) | ||
// é | ||
COMB(comb_e_acut, E_ACUT, SE_E, SE_K) | ||
|
||
// Symbols with same side thumb | ||
// Upper | ||
COMB(comb_tild, TILD, MT_SPC, SE_J) | ||
COMB(comb_plus, SE_PLUS, MT_SPC, SE_C) | ||
COMB(comb_astr, SE_ASTR, MT_SPC, SE_Y) | ||
COMB(comb_exlm, SE_EXLM, MT_SPC, SE_F) | ||
COMB(comb_hash, SE_HASH, SE_E, SE_W) | ||
COMB(comb_at, SE_AT, SE_E, SE_O) | ||
COMB(comb_circ, CIRC, SE_E, SE_U) | ||
// Home-row | ||
COMB(comb_pipe, SE_PIPE, MT_SPC, SE_R) | ||
COMB(comb_lcbr, SE_LCBR, MT_SPC, SE_S) | ||
COMB(comb_rcbr, SE_RCBR, MT_SPC, SE_T) | ||
COMB(comb_mins, SE_MINS, MT_SPC, SE_H) | ||
COMB(comb_bsls, SE_BSLS, MT_SPC, SE_K) | ||
COMB(comb_grv, GRV, SE_E, SE_M) | ||
COMB(comb_ques, SE_QUES, SE_E, SE_N) | ||
COMB(comb_lbrc, SE_LBRC, SE_E, SE_A) | ||
COMB(comb_rbrc, SE_RBRC, SE_E, SE_I) | ||
// Lower | ||
COMB(comb_labk, SE_LABK, MT_SPC, SE_V) | ||
COMB(comb_rabk, SE_RABK, MT_SPC, SE_G) | ||
COMB(comb_perc, SE_PERC, MT_SPC, SE_D) | ||
COMB(comb_ampr, SE_AMPR, SE_E, SE_L) | ||
COMB(comb_lprn, SE_LPRN, SE_E, SE_LPRN) | ||
COMB(comb_rprn, SE_RPRN, SE_E, SE_RPRN) | ||
COMB(comb_unds, SE_UNDS, SE_E, SE_UNDS) | ||
|
||
// Swedish letters with cross side thumb | ||
COMB(arng, SE_ARNG, MT_SPC, SE_LPRN) | ||
COMB(adia, SE_ADIA, MT_SPC, SE_RPRN) | ||
COMB(odia, SE_ODIA, MT_SPC, SE_UNDS) | ||
|
||
// Specials | ||
SUBS(https, "https://", MT_SPC, SE_SLSH) | ||
COMB(ctrl_gui_space, C(G(KC_SPACE)), MT_SPC, SE_N, SE_A) |
Oops, something went wrong.