From 454dee382013a168fee404dc2cb3047a91f61f07 Mon Sep 17 00:00:00 2001 From: Dialgatrainer02 Date: Sun, 3 Nov 2024 23:22:30 +0000 Subject: [PATCH] add keymap --- .../keymaps/dialgatrainer02/autocorrect.txt | 472 ++++++++++++++++++ .../ansi/keymaps/dialgatrainer02/config.h | 2 + .../dialgatrainer02/features/sentence_case.c | 366 ++++++++++++++ .../dialgatrainer02/features/sentence_case.h | 226 +++++++++ .../dialgatrainer02/features/socd_cleaner.c | 82 +++ .../dialgatrainer02/features/socd_cleaner.h | 141 ++++++ .../ansi/keymaps/dialgatrainer02/keymap.c | 135 +++++ .../ansi/keymaps/dialgatrainer02/rules.mk | 7 + 8 files changed, 1431 insertions(+) create mode 100644 keyboards/framework/ansi/keymaps/dialgatrainer02/autocorrect.txt create mode 100644 keyboards/framework/ansi/keymaps/dialgatrainer02/config.h create mode 100644 keyboards/framework/ansi/keymaps/dialgatrainer02/features/sentence_case.c create mode 100644 keyboards/framework/ansi/keymaps/dialgatrainer02/features/sentence_case.h create mode 100644 keyboards/framework/ansi/keymaps/dialgatrainer02/features/socd_cleaner.c create mode 100644 keyboards/framework/ansi/keymaps/dialgatrainer02/features/socd_cleaner.h create mode 100644 keyboards/framework/ansi/keymaps/dialgatrainer02/keymap.c create mode 100644 keyboards/framework/ansi/keymaps/dialgatrainer02/rules.mk diff --git a/keyboards/framework/ansi/keymaps/dialgatrainer02/autocorrect.txt b/keyboards/framework/ansi/keymaps/dialgatrainer02/autocorrect.txt new file mode 100644 index 000000000000..c6885bbcc4b9 --- /dev/null +++ b/keyboards/framework/ansi/keymaps/dialgatrainer02/autocorrect.txt @@ -0,0 +1,472 @@ +# Copyright 2021 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. +# +# +# This is a larger example typo dictionary containing 400 entries. It builds to +# a table of about 6000 bytes, so you'll need a keyboard with a generous +# amount of free firmware space to use the full dictionary. Alternatively, pick +# out a subset of entries to a separate file, then build a table from that. +# +# Dictionary syntax: +# Each line of this file defines one typo correction entry with the syntax +# "typo -> correction". Typos and corrections are case insensitive, and any +# whitespace before or after the typo and correction is ignored. The typo must be +# only the letters a-z, or the special character : representing a word break. +# +# For documentation about how to use this dictionary, see +# https://getreuer.info/posts/keyboards/autocorrection +# +# Further resources: +# * Wikipedia has a large list of common typos at +# https://en.wikipedia.org/wiki/Wikipedia:Lists_of_common_misspellings/For_machines +# +# * EmacsWiki has another list of typos at +# https://www.emacswiki.org/emacs/autocorrection_abbrev_defs +# +# * You can find data on English word frequencies at +# https://www.wordfrequency.info/samples.asp + + +## 10 most common words. +# The words "there", "about", "their", "would", "people", "which", "could", +# "think", "other", and "because" are among the most common words in English +# that are 5 letters or longer. (We don't include entries for words shorter than +# that to avoid false triggering.) +:htere -> there +abbout -> about +abotu -> about +baout -> about +:theri -> their +:thier -> their +:owudl -> would +woudl -> would +peaple -> people +peolpe -> people +peopel -> people +poeple -> people +poeople -> people +:hwihc -> which +whcih -> which +whihc -> which +whlch -> which +wihch -> which +coudl -> could +:htikn -> think +:htink -> think +thikn -> think +thiunk -> think +tihkn -> think +:olther -> other +:otehr -> other +baceause -> because +beacuse -> because +becasue -> because +beccause -> because +becouse -> because +becuase -> because + +## Common words, 11-20. +theese -> these +:goign -> going +:gonig -> going +:yaers -> years +:yeasr -> years +:thsoe -> those +shoudl -> should +raelly -> really +realy -> really +relaly -> really +bedore -> before +befoer -> before +littel -> little +beeing -> being +:hwile -> while + +## Common words, 21-30. +aroud -> around +arround -> around +arund -> around +thign -> thing +thigsn -> things +thnigs -> things +anohter -> another +beteen -> between +beween -> between +bewteen -> between +:eveyr -> every +:graet -> great +:agian -> again +:sicne -> since +alwasy -> always +alwyas -> always +throught -> thought + + +## Words especially susceptible to skipping or transposing a letter. +# These entries are for words that are easy enough to spell, but not necessarily +# easy to press the keys in the right order. +# Catch misspellings of "achieves", "achieving", etc. +:acheiv -> achiev +almsot -> almost +alomst -> almost +chnage -> change +chekc -> check +childen -> children +claer -> clear +comapny -> company +contian -> contain +elasped -> elapsed +feild -> field +fitler -> filter +firts -> first +follwo -> follow +:foudn -> found +frequecy -> frequency +firend -> friend +freind -> friend +heigth -> height +iamge -> image +inital -> initial +intput -> input +laguage -> language +lenght -> length +levle -> level +libary -> library +:moeny -> money +mysefl -> myself +ouptut -> output +ouput -> output +probaly -> probably +probelm -> problem +recrod -> record +reponse -> response +reprot -> report +singel -> single +stregth -> strength +strengh -> strength +tkaes -> takes +therfore -> therefore +todya -> today +toghether -> together +unkown -> unknown +unqiue -> unique +widht -> width + + +## Words with tricky spelling. +# If you are a good speller, you could drop this section. +aberation -> aberration +accross -> across +adviced -> advised +aledge -> allege +alledge -> allege +amature -> amateur +anomolous -> anomalous +anomoly -> anomaly +aparent -> apparent +aparrent -> apparent +apparant -> apparent +apparrent -> apparent +asthetic -> aesthetic +auxilary -> auxiliary +auxillary -> auxiliary +auxilliary -> auxiliary +bankrupcy -> bankruptcy +busness -> business +bussiness -> business +calander -> calendar +commitee -> committee +comittee -> committee +competance -> competence +competant -> competent +concensus -> consensus +cognizent -> cognizant +copywrite: -> copyright +choosen -> chosen +collegue -> colleague +excercise -> exercise +:grammer -> grammar +:guage -> gauge +govement -> government +govenment -> government +goverment -> government +governmnet -> government +govorment -> government +govornment -> government +guaratee -> guarantee +garantee -> guarantee +gaurantee -> guarantee +heirarchy -> hierarchy +hygeine -> hygiene +hypocracy -> hypocrisy +hypocrasy -> hypocrisy +hypocricy -> hypocrisy +hypocrit: -> hypocrite +looses: -> loses +maintence -> maintenance +morgage -> mortgage +neccesary -> necessary +necesary -> necessary +pallete -> palette +paralel -> parallel +parralel -> parallel +parrallel -> parallel +priviledge -> privilege +probablly -> probably +prominant -> prominent +propogate -> propagate +proove -> prove +psuedo -> pseudo +reciept -> receipt +# Catch misspellings of "receives", "receiving", etc. +receiev -> receiv +reciev -> receiv +recepient -> recipient +recipiant -> recipient +relevent -> relevant +repitition -> repetition +safty -> safety +saftey -> safety +# Catch misspellings of "separate", "separating", etc. +seperat -> separat +spectogram -> spectrogram +symetric -> symmetric +tolerence -> tolerance + + +## Words particularly for coding. +# Entries for common code keywords ("const") and terminology ("lookup"). +cacheing -> caching +complier -> compiler +doulbe -> double +dyanmic -> dynamic +# As in "execute", "executable", "executing", ... +excecut -> execut +failse -> false +fales -> false +fasle -> false +flase -> false +indeces -> indices +indecies -> indices +indicies -> indices +interator -> iterator +looup -> lookup +namesapce -> namespace +namespcae -> namespace +nulltpr -> nullptr +operaotr -> operator +overide -> override +ovveride -> override +poitner -> pointer +:rference -> reference +referece -> reference +singed -> signed +stirng -> string +strign -> string +swithc -> switch +swtich -> switch +teamplate -> template +tempalte -> template +:ture -> true +retrun -> return +retun -> return +reutrn -> return +cosnt -> const +virutal -> virtual +vitual -> virtual +yeild -> yield + + +## Catch skipped spaces between common words. +:alot: -> a lot +:andteh -> and the +:andthe -> and the +:asthe -> as the +:atthe -> at the +abouta -> about a +aboutit -> about it +aboutthe -> about the +:tothe -> to the +didnot -> did not +fromthe -> from the + + +## Various additional entries. +:agred -> agreed +:ajust -> adjust +:anual -> annual +:asign -> assign +:aslo: -> also +:casue -> cause +:choses -> chooses +:gaurd -> guard +:haev -> have +:hapen -> happen +:idaes -> ideas +:jsut: -> just +:jstu: -> just +:knwo -> know +:konw -> know +:kwno -> know +:ocuntry -> country +:ocur -> occur +:socre -> score +:szie -> size +:the:the: -> the +:turth -> truth +:uesd: -> used +:usally -> usually +abilties -> abilities +abilty -> ability +abvove -> above +accesories -> accessories +accomodate -> accommodate +acommodate -> accommodate +acomplish -> accomplish +actualy -> actually +acurate -> accurate +acutally -> actually +addtion -> addition +againnst -> against +aganist -> against +aggreed -> agreed +agianst -> against +ahppn -> happen +allign -> align +anytying -> anything +aquire -> acquire +availabe -> available +availaible -> available +availalbe -> available +availble -> available +availiable -> available +avalable -> available +avaliable -> available +avilable -> available +bandwith -> bandwidth +begginer -> beginner +beleif -> belief +beleive -> believe +belive -> believe +breif -> brief +burried -> buried +caluclate -> calculate +caluculate -> calculate +calulate -> calculate +catagory -> category +cauhgt -> caught +ceratin -> certain +certian -> certain +cheif -> chief +cieling -> ceiling +circut -> circuit +clasic -> classic +cmoputer -> computer +coform -> conform +comming: -> coming +considerd -> considered +dervied -> derived +desicion -> decision +diferent -> different +diferrent -> different +differnt -> different +diffrent -> different +divison -> division +effecient -> efficient +eligable -> eligible +elpased -> elapsed +embarass -> embarrass +embeded -> embedded +encypt -> encrypt +finaly -> finally +foriegn -> foreign +foward -> forward +fraciton -> fraction +fucntion -> function +fufill -> fulfill +fullfill -> fulfill +futher -> further +ganerate -> generate +generaly -> generally +greatful -> grateful +heigher -> higher +higest -> highest +howver -> however +hydogen -> hydrogen +importamt -> important +inclued -> include +insted -> instead +intrest -> interest +invliad -> invalid +largst -> largest +learnign -> learning +liasion -> liaison +likly -> likely +lisense -> license +listner -> listener +macthing -> matching +manefist -> manifest +mesage -> message +naturual -> natural +occassion -> occasion +occured -> occurred +particualr -> particular +paticular -> particular +peice -> piece +perhasp -> perhaps +perheaps -> perhaps +perhpas -> perhaps +perphas -> perhaps +persue -> pursue +posess -> possess +postion -> position +preiod -> period +primarly -> primarily +privte -> private +proccess -> process +proeprty -> property +propery -> property +realtion -> relation +reasearch -> research +recuring -> recurring +refered -> referred +regluar -> regular +releated -> related +resutl -> result +reuslt -> result +reveiw -> review +satisifed -> satisfied +scheduel -> schedule +sequnce -> sequence +similiar -> similar +simmilar -> similar +slighly -> slightly +somehwat -> somewhat +statment -> statement +sucess -> success +succsess -> success +sugest -> suggest +sumary -> summary +supress -> suppress +surpress -> suppress +thresold -> threshold +tongiht -> tonight +tranpose -> transpose +typcial -> typical +udpate -> update +ususally -> usually +verticies -> vertices +whereever -> wherever +wherre -> where +wierd -> weird diff --git a/keyboards/framework/ansi/keymaps/dialgatrainer02/config.h b/keyboards/framework/ansi/keymaps/dialgatrainer02/config.h new file mode 100644 index 000000000000..f0d4ded58630 --- /dev/null +++ b/keyboards/framework/ansi/keymaps/dialgatrainer02/config.h @@ -0,0 +1,2 @@ +#define DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD + diff --git a/keyboards/framework/ansi/keymaps/dialgatrainer02/features/sentence_case.c b/keyboards/framework/ansi/keymaps/dialgatrainer02/features/sentence_case.c new file mode 100644 index 000000000000..623c6b3655f2 --- /dev/null +++ b/keyboards/framework/ansi/keymaps/dialgatrainer02/features/sentence_case.c @@ -0,0 +1,366 @@ +// Copyright 2022-2024 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. + +/** + * @file sentence_case.c + * @brief Sentence Case implementation + * + * For full documentation, see + * + */ + +#include "sentence_case.h" + +#include + +#if !defined(IS_QK_MOD_TAP) +// Attempt to detect out-of-date QMK installation, which would fail with +// implicit-function-declaration errors in the code below. +#error "sentence_case: QMK version is too old to build. Please update QMK." +#elif defined(NO_ACTION_ONESHOT) +// One-shot keys must be enabled for Sentence Case. One-shot keys are enabled +// by default, but are disabled by `#define NO_ACTION_ONESHOT` in config.h. If +// your config.h includes such a line, please remove it. +#error "sentence_case: Please enable oneshot." +#else + +// Number of keys of state history to retain for backspacing. +#define STATE_HISTORY_SIZE 6 + +// clang-format off +/** States in matching the beginning of a sentence. */ +enum { + STATE_INIT, /**< Initial enabled state. */ + STATE_WORD, /**< Within a word. */ + STATE_ABBREV, /**< Within an abbreviation like "e.g.". */ + STATE_ENDING, /**< Sentence ended. */ + STATE_PRIMED, /**< "Primed" state, in the space following an ending. */ + STATE_DISABLED, /**< Sentence Case is disabled. */ +}; +// clang-format on + +#if SENTENCE_CASE_TIMEOUT > 0 +static uint16_t idle_timer = 0; +#endif // SENTENCE_CASE_TIMEOUT > 0 +#if SENTENCE_CASE_BUFFER_SIZE > 1 +static uint16_t key_buffer[SENTENCE_CASE_BUFFER_SIZE] = {0}; +#endif // SENTENCE_CASE_BUFFER_SIZE > 1 +static uint8_t state_history[STATE_HISTORY_SIZE]; +static uint16_t suppress_key = KC_NO; +static uint8_t sentence_state = STATE_INIT; + +// Sets the current state to `new_state`. +static void set_sentence_state(uint8_t new_state) { +#if !defined(NO_DEBUG) && defined(SENTENCE_CASE_DEBUG) + if (debug_enable && sentence_state != new_state) { + static const char* state_names[] = { + "INIT", "WORD", "ABBREV", "ENDING", "PRIMED", "DISABLED", + }; + dprintf("Sentence case: %s\n", state_names[new_state]); + } +#endif // !NO_DEBUG && SENTENCE_CASE_DEBUG + + const bool primed = (new_state == STATE_PRIMED); + if (primed != (sentence_state == STATE_PRIMED)) { + sentence_case_primed(primed); + } + sentence_state = new_state; +} + +static void clear_state_history(void) { +#if SENTENCE_CASE_TIMEOUT > 0 + idle_timer = 0; +#endif // SENTENCE_CASE_TIMEOUT > 0 + memset(state_history, STATE_INIT, sizeof(state_history)); + if (sentence_state != STATE_DISABLED) { + set_sentence_state(STATE_INIT); + } +} + +void sentence_case_clear(void) { + clear_state_history(); + suppress_key = KC_NO; +#if SENTENCE_CASE_BUFFER_SIZE > 1 + memset(key_buffer, 0, sizeof(key_buffer)); +#endif // SENTENCE_CASE_BUFFER_SIZE > 1 +} + +void sentence_case_on(void) { + if (sentence_state == STATE_DISABLED) { + sentence_state = STATE_INIT; + sentence_case_clear(); + } +} + +void sentence_case_off(void) { + if (sentence_state != STATE_DISABLED) { + set_sentence_state(STATE_DISABLED); + } +} + +void sentence_case_toggle(void) { + if (sentence_state != STATE_DISABLED) { + sentence_case_off(); + } else { + sentence_case_on(); + } +} + +bool is_sentence_case_on(void) { return sentence_state != STATE_DISABLED; } + +#if SENTENCE_CASE_TIMEOUT > 0 +#if SENTENCE_CASE_TIMEOUT < 100 || SENTENCE_CASE_TIMEOUT > 30000 +// Constrain timeout to a sensible range. With the 16-bit timer, the longest +// representable timeout is 32768 ms, rounded here to 30000 ms = half a minute. +#error "sentence_case: SENTENCE_CASE_TIMEOUT must be between 100 and 30000 ms" +#endif + +void sentence_case_task(void) { + if (idle_timer && timer_expired(timer_read(), idle_timer)) { + clear_state_history(); // Timed out; clear all state. + } +} +#endif // SENTENCE_CASE_TIMEOUT > 0 + +bool process_sentence_case(uint16_t keycode, keyrecord_t* record) { + // Only process while enabled, and only process press events. + if (sentence_state == STATE_DISABLED || !record->event.pressed) { + return true; + } + +#if SENTENCE_CASE_TIMEOUT > 0 + idle_timer = (record->event.time + SENTENCE_CASE_TIMEOUT) | 1; +#endif // SENTENCE_CASE_TIMEOUT > 0 + + switch (keycode) { + case KC_LCTL ... KC_RGUI: // Ignore mod keys. + case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: // Ignore one-shot mod. + // Ignore MO, TO, TG, TT, OSL, TL 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: // Ignore one-shot layer. +#ifdef TRI_LAYER_ENABLE // Ignore Tri Layer keys. + case QK_TRI_LAYER_LOWER: + case QK_TRI_LAYER_UPPER: +#endif // TRI_LAYER_ENABLE + return true; + +#ifndef NO_ACTION_TAPPING + case QK_MOD_TAP ... QK_MOD_TAP_MAX: + if (record->tap.count == 0) { + return true; + } + keycode = QK_MOD_TAP_GET_TAP_KEYCODE(keycode); + break; +#ifndef NO_ACTION_LAYER + case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: + if (record->tap.count == 0) { + return true; + } + keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(keycode); + break; +#endif // NO_ACTION_LAYER +#endif // NO_ACTION_TAPPING + +#ifdef SWAP_HANDS_ENABLE + case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX: + if (IS_SWAP_HANDS_KEYCODE(keycode) || record->tap.count == 0) { + return true; + } + keycode = QK_SWAP_HANDS_GET_TAP_KEYCODE(keycode); + break; +#endif // SWAP_HANDS_ENABLE + } + + if (keycode == KC_BSPC) { + // Backspace key pressed. Rewind the state and key buffers. + set_sentence_state(state_history[STATE_HISTORY_SIZE - 1]); + + memmove(state_history + 1, state_history, STATE_HISTORY_SIZE - 1); + state_history[0] = STATE_INIT; +#if SENTENCE_CASE_BUFFER_SIZE > 1 + memmove(key_buffer + 1, key_buffer, + (SENTENCE_CASE_BUFFER_SIZE - 1) * sizeof(uint16_t)); + key_buffer[0] = KC_NO; +#endif // SENTENCE_CASE_BUFFER_SIZE > 1 + return true; + } + + const uint8_t mods = get_mods() | get_weak_mods() | get_oneshot_mods(); + uint8_t new_state = STATE_INIT; + + // We search for sentence beginnings using a simple finite state machine. It + // matches things like "a. a" and "a. a" but not "a.. a" or "a.a. a". The + // state transition matrix is: + // + // 'a' '.' ' ' '\'' + // +------------------------------------- + // INIT | WORD INIT INIT INIT + // WORD | WORD ENDING INIT WORD + // ABBREV | ABBREV ABBREV INIT ABBREV + // ENDING | ABBREV INIT PRIMED ENDING + // PRIMED | match! INIT PRIMED PRIMED + char code = sentence_case_press_user(keycode, record, mods); +#if defined SENTENCE_CASE_DEBUG + dprintf("Sentence Case: code = '%c' (%d)\n", code, (int)code); +#endif // SENTENCE_CASE_DEBUG + switch (code) { + case '\0': // Current key should be ignored. + return true; + + case 'a': // Current key is a letter. + switch (sentence_state) { + case STATE_ABBREV: + case STATE_ENDING: + new_state = STATE_ABBREV; + break; + + case STATE_PRIMED: + // This is the start of a sentence. + if (keycode != suppress_key) { + suppress_key = keycode; + set_oneshot_mods(MOD_BIT(KC_LSFT)); // Shift mod to capitalize. + new_state = STATE_WORD; + } + break; + + default: + new_state = STATE_WORD; + } + break; + + case '.': // Current key is sentence-ending punctuation. + switch (sentence_state) { + case STATE_WORD: + new_state = STATE_ENDING; + break; + + default: + new_state = STATE_ABBREV; + } + break; + + case ' ': // Current key is a space. + if (sentence_state == STATE_PRIMED || + (sentence_state == STATE_ENDING +#if SENTENCE_CASE_BUFFER_SIZE > 1 + && sentence_case_check_ending(key_buffer) +#endif // SENTENCE_CASE_BUFFER_SIZE > 1 + )) { + new_state = STATE_PRIMED; + suppress_key = KC_NO; + } + break; + + case '\'': // Current key is a quote. + new_state = sentence_state; + break; + } + + // Slide key_buffer and state_history buffers one element to the left. + // Optimization note: Using manual loops instead of memmove() here saved + // ~100 bytes on AVR. +#if SENTENCE_CASE_BUFFER_SIZE > 1 + for (int8_t i = 0; i < SENTENCE_CASE_BUFFER_SIZE - 1; ++i) { + key_buffer[i] = key_buffer[i + 1]; + } +#endif // SENTENCE_CASE_BUFFER_SIZE > 1 + for (int8_t i = 0; i < STATE_HISTORY_SIZE - 1; ++i) { + state_history[i] = state_history[i + 1]; + } + +#if SENTENCE_CASE_BUFFER_SIZE > 1 + key_buffer[SENTENCE_CASE_BUFFER_SIZE - 1] = keycode; + if (new_state == STATE_ENDING && !sentence_case_check_ending(key_buffer)) { +#if defined SENTENCE_CASE_DEBUG + dprintf("Not a real ending.\n"); +#endif // SENTENCE_CASE_DEBUG + new_state = STATE_INIT; + } +#endif // SENTENCE_CASE_BUFFER_SIZE > 1 + state_history[STATE_HISTORY_SIZE - 1] = sentence_state; + + set_sentence_state(new_state); + return true; +} + +bool sentence_case_just_typed_P(const uint16_t* buffer, const uint16_t* pattern, + int8_t pattern_len) { +#if SENTENCE_CASE_BUFFER_SIZE > 1 + buffer += SENTENCE_CASE_BUFFER_SIZE - pattern_len; + for (int8_t i = 0; i < pattern_len; ++i) { + if (buffer[i] != pgm_read_word(pattern + i)) { + return false; + } + } + return true; +#else + return false; +#endif // SENTENCE_CASE_BUFFER_SIZE > 1 +} + +__attribute__((weak)) bool sentence_case_check_ending(const uint16_t* buffer) { +#if SENTENCE_CASE_BUFFER_SIZE >= 5 + // Don't consider the abbreviations "vs." and "etc." to end the sentence. + if (SENTENCE_CASE_JUST_TYPED(KC_SPC, KC_V, KC_S, KC_DOT) || + SENTENCE_CASE_JUST_TYPED(KC_SPC, KC_E, KC_T, KC_C, KC_DOT)) { + return false; // Not a real sentence ending. + } +#endif // SENTENCE_CASE_BUFFER_SIZE >= 5 + return true; // Real sentence ending; capitalize next letter. +} + +__attribute__((weak)) char sentence_case_press_user(uint16_t keycode, + keyrecord_t* record, + uint8_t mods) { + if ((mods & ~(MOD_MASK_SHIFT | MOD_BIT(KC_RALT))) == 0) { + const bool shifted = mods & MOD_MASK_SHIFT; + switch (keycode) { + case KC_A ... KC_Z: + return 'a'; // Letter key. + + case KC_DOT: // . is punctuation, Shift . is a symbol (>) + return !shifted ? '.' : '#'; + case KC_1: + case KC_SLSH: + return shifted ? '.' : '#'; + case KC_EXLM: + case KC_QUES: + return '.'; + case KC_2 ... KC_0: // 2 3 4 5 6 7 8 9 0 + case KC_AT ... KC_RPRN: // @ # $ % ^ & * ( ) + case KC_MINS ... KC_SCLN: // - = [ ] backslash ; + case KC_UNDS ... KC_COLN: // _ + { } | : + case KC_GRV: + case KC_COMM: + return '#'; // Symbol key. + + case KC_SPC: + return ' '; // Space key. + + case KC_QUOT: + return '\''; // Quote key. + } + } + + // Otherwise clear Sentence Case to initial state. + sentence_case_clear(); + return '\0'; +} + +__attribute__((weak)) void sentence_case_primed(bool primed) {} + +#endif // NO_ACTION_ONESHOT diff --git a/keyboards/framework/ansi/keymaps/dialgatrainer02/features/sentence_case.h b/keyboards/framework/ansi/keymaps/dialgatrainer02/features/sentence_case.h new file mode 100644 index 000000000000..4694e94a9800 --- /dev/null +++ b/keyboards/framework/ansi/keymaps/dialgatrainer02/features/sentence_case.h @@ -0,0 +1,226 @@ +// Copyright 2022, 2024 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. + +/** + * @file sentence_case.h + * @brief Sentence case: automatically capitalize the first letter of sentences. + * + * This library automatically capitalizes the first letter of sentences, + * reducing the need to explicitly use shift. To use it, you simply type as + * usual but without shifting at the start of sentences. The feature detects + * when new sentences begin and capitalizes automatically. + * + * Sentence Case matches patterns like + * + * "a. a" + * "a. a" + * "a? a" + * "a!' 'a" + * + * but not + * + * "a... a" + * "a.a. a" + * + * Additionally by default, abbreviations "vs." and "etc." are exceptionally + * detected as not real sentence endings. You can use the callback + * `sentence_case_check_ending()` to define other exceptions. + * + * @note One-shot keys must be enabled. + * + * For full documentation, see + * + */ + +#pragma once + +#include "quantum.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// The size of the keycode buffer for `sentence_case_check_ending()`. It must be +// at least as large as the longest pattern checked. If less than 2, buffering +// is disabled and the callback is not called. +#ifndef SENTENCE_CASE_BUFFER_SIZE +#define SENTENCE_CASE_BUFFER_SIZE 8 +#endif // SENTENCE_CASE_BUFFER_SIZE + +/** + * Handler function for Sentence Case. + * + * Call this function from `process_record_user()` to implement Sentence Case. + */ +bool process_sentence_case(uint16_t keycode, keyrecord_t* record); + +/** + * @fn sentence_case_task(void) + * Matrix task function for Sentence Case. + * + * If using `SENTENCE_CASE_TIMEOUT`, call this function from your + * `matrix_scan_user()` function in keymap.c. (If no timeout is set, calling + * `sentence_case_task()` has no effect.) + */ +#if SENTENCE_CASE_TIMEOUT > 0 +void sentence_case_task(void); +#else +static inline void sentence_case_task(void) {} +#endif + +void sentence_case_on(void); /**< Enables Sentence Case. */ +void sentence_case_off(void); /**< Disables Sentence Case. */ +void sentence_case_toggle(void); /**< Toggles Sentence Case. */ +bool is_sentence_case_on(void); /**< Gets whether currently enabled. */ +void sentence_case_clear(void); /**< Clears Sentence Case to initial state. */ + +/** + * Optional callback to indicate primed state. + * + * This callback gets called when Sentence Case changes to or from a "primed" + * state, useful to indicate with an LED or otherwise that the next letter typed + * will be capitalized. + */ +void sentence_case_primed(bool primed); + +/** + * Optional callback to determine whether there is a real sentence ending. + * + * When a sentence-ending punctuation key is typed, this callback is called to + * determine whether it is a real sentence ending, meaning the first letter of + * the following word should be capitalized. For instance, abbreviations like + * "vs." are usually not real sentence endings. The input argument is a buffer + * of the last SENTENCE_CASE_BUFFER_SIZE keycodes. Returning true means it is a + * real sentence ending; returning false means it is not. + * + * The default implementation checks for the abbreviations "vs." and "etc.": + * + * bool sentence_case_check_ending(const uint16_t* buffer) { + * // Don't consider "vs." and "etc." to end the sentence. + * if (SENTENCE_CASE_JUST_TYPED(KC_SPC, KC_V, KC_S, KC_DOT) || + * SENTENCE_CASE_JUST_TYPED(KC_SPC, KC_E, KC_T, KC_C, KC_DOT)) { + * return false; // Not a real sentence ending. + * } + * return true; // Real sentence ending; capitalize next letter. + * } + * + * @note This callback is used only if `SENTENCE_CASE_BUFFER_SIZE >= 2`. + * Otherwise it has no effect. + * + * @param buffer Buffer of the last `SENTENCE_CASE_BUFFER_SIZE` keycodes. + * @return whether there is a real sentence ending. + */ +bool sentence_case_check_ending(const uint16_t* buffer); + +/** + * Macro to be used in `sentence_case_check_ending()`. + * + * Returns true if a given pattern of keys was just typed by comparing with the + * keycode buffer. This is useful for defining exceptions in + * `sentence_case_check_ending()`. + * + * For example, `SENTENCE_CASE_JUST_TYPED(KC_SPC, KC_V, KC_S, KC_DOT)` returns + * true if " vs." were the last four keys typed. + * + * @note The pattern must be no longer than `SENTENCE_CASE_BUFFER_SIZE`. + */ +#define SENTENCE_CASE_JUST_TYPED(...) \ + ({ \ + static const uint16_t PROGMEM pattern[] = {__VA_ARGS__}; \ + sentence_case_just_typed_P(buffer, pattern, \ + sizeof(pattern) / sizeof(uint16_t)); \ + }) +bool sentence_case_just_typed_P(const uint16_t* buffer, const uint16_t* pattern, + int8_t pattern_len); + +/** + * Optional callback defining which keys are letter, punctuation, etc. + * + * This callback may be useful if you type non-US letters or have customized the + * shift behavior of the punctuation keys. The return value tells Sentence Case + * how to interpret the key: + * + * 'a' Key is a letter, by default KC_A to KC_Z. If occurring at the start of + * a sentence, Sentence Case applies shift to capitalize it. + * + * '.' Key is sentence-ending punctuation. Default: . ? ! + * + * '#' Key types a backspaceable character that isn't part of a word. + * Default includes - = [ ] ; ' , < > / _ + @ # $ % ^ & * ( ) { } digits + * + * ' ' Key is a space. Default: KC_SPC + * + * '\'' Key is a quote or double quote character. Default: KC_QUOT. + * + * '\0' Sentence Case should ignore this key. + * + * If a hotkey or navigation key is pressed (or another key that performs an + * action that backspace doesn't undo), then the callback should call + * `sentence_case_clear()` to clear the state and then return '\0'. + * + * The default callback is: + * + * char sentence_case_press_user(uint16_t keycode, + * keyrecord_t* record, + * uint8_t mods) { + * if ((mods & ~(MOD_MASK_SHIFT | MOD_BIT(KC_RALT))) == 0) { + * const bool shifted = mods & MOD_MASK_SHIFT; + * switch (keycode) { + * case KC_A ... KC_Z: + * return 'a'; // Letter key. + * + * case KC_DOT: // . is punctuation, Shift . is a symbol (>) + * return !shifted ? '.' : '#'; + * case KC_1: + * case KC_SLSH: + * return shifted ? '.' : '#'; + * case KC_EXLM: + * case KC_QUES: + * return '.'; + * case KC_2 ... KC_0: // 2 3 4 5 6 7 8 9 0 + * case KC_AT ... KC_RPRN: // @ # $ % ^ & * ( ) + * case KC_MINS ... KC_SCLN: // - = [ ] backslash ; + * case KC_UNDS ... KC_COLN: // _ + { } | : + * case KC_GRV: + * case KC_COMM: + * return '#'; // Symbol key. + * + * case KC_SPC: + * return ' '; // Space key. + * + * case KC_QUOT: + * return '\''; // Quote key. + * } + * } + * + * // Otherwise clear Sentence Case to initial state. + * sentence_case_clear(); + * return '\0'; + * } + * + * To customize, copy the above function into your keymap and add/remove + * keycodes to the above cases. + * + * @param keycode Current keycode. + * @param record record_t for the current press event. + * @param mods equal to `get_mods() | get_weak_mods() | get_oneshot_mods()` + * @return char code 'a', '.', '#', ' ', or '\0' indicating how the key is to be + * interpreted as described above. + */ +char sentence_case_press_user(uint16_t keycode, keyrecord_t* record, + uint8_t mods); + +#ifdef __cplusplus +} +#endif diff --git a/keyboards/framework/ansi/keymaps/dialgatrainer02/features/socd_cleaner.c b/keyboards/framework/ansi/keymaps/dialgatrainer02/features/socd_cleaner.c new file mode 100644 index 000000000000..546efb7f668b --- /dev/null +++ b/keyboards/framework/ansi/keymaps/dialgatrainer02/features/socd_cleaner.c @@ -0,0 +1,82 @@ +// Copyright 2024 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. + +/** + * @file socd_cleaner.c + * @brief SOCD Cleaner implementation + * + * For full documentation, see + * + */ + +#include "socd_cleaner.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool socd_cleaner_enabled = true; + +static void update_key(uint8_t keycode, bool press) { + if (press) { + add_key(keycode); + } else { + del_key(keycode); + } +} + +bool process_socd_cleaner(uint16_t keycode, keyrecord_t* record, + socd_cleaner_t* state) { + if (!socd_cleaner_enabled || !state->resolution || + (keycode != state->keys[0] && keycode != state->keys[1])) { + return true; // Quick return when disabled or on unrelated events. + } + // The current event corresponds to index `i`, 0 or 1, in the SOCD key pair. + const uint8_t i = (keycode == state->keys[1]); + const uint8_t opposing = i ^ 1; // Index of the opposing key. + + // Track which keys are physically held (vs. keys in the report). + state->held[i] = record->event.pressed; + + // Perform SOCD resolution for events where the opposing key is held. + if (state->held[opposing]) { + switch (state->resolution) { + case SOCD_CLEANER_LAST: // Last input priority with reactivation. + // If the current event is a press, then release the opposing key. + // Otherwise if this is a release, then press the opposing key. + update_key(state->keys[opposing], !state->held[i]); + break; + + case SOCD_CLEANER_NEUTRAL: // Neutral resolution. + // Same logic as SOCD_CLEANER_LAST, but skip default handling so that + // the current key has no effect while the opposing key is held. + update_key(state->keys[opposing], !state->held[i]); + // Send updated report (normally, default handling would do this). + send_keyboard_report(); + return false; // Skip default handling. + + case SOCD_CLEANER_0_WINS: // Key 0 wins. + case SOCD_CLEANER_1_WINS: // Key 1 wins. + if (opposing == (state->resolution - SOCD_CLEANER_0_WINS)) { + // The opposing key is the winner. The current key has no effect. + return false; // Skip default handling. + } else { + // The current key is the winner. Update logic is same as above. + update_key(state->keys[opposing], !state->held[i]); + } + break; + } + } + return true; // Continue default handling to press/release current key. +} diff --git a/keyboards/framework/ansi/keymaps/dialgatrainer02/features/socd_cleaner.h b/keyboards/framework/ansi/keymaps/dialgatrainer02/features/socd_cleaner.h new file mode 100644 index 000000000000..bb37d72a9ccc --- /dev/null +++ b/keyboards/framework/ansi/keymaps/dialgatrainer02/features/socd_cleaner.h @@ -0,0 +1,141 @@ +// Copyright 2024 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. + +/** + * @file socd_cleaner.h + * @brief SOCD Cleaner - enhance WASD for fast inputs for gaming + * + * Overview + * -------- + * + * SOCD Cleaner is a QMK library for Simultaneous Opposing Cardinal Directions + * (SOCD) filtering, which is popular for fast inputs on the WASD keys in + * gaming. When two keys of opposing direction are physically held at the same + * time, a rule is applied to decide which key is sent to the computer. + * + * + * Add it to your keymap + * --------------------- + * + * In rules.mk, add `SRC += features/socd_cleaner.c`. Then in keymap.c, add + * + * #include "features/socd_cleaner.h" + * + * socd_cleaner_t socd_v = {{KC_W, KC_S}, SOCD_CLEANER_LAST}; + * socd_cleaner_t socd_h = {{KC_A, KC_D}, SOCD_CLEANER_LAST}; + * + * bool process_record_user(uint16_t keycode, keyrecord_t* record) { + * if (!process_socd_cleaner(keycode, record, &socd_v)) { return false; } + * if (!process_socd_cleaner(keycode, record, &socd_h)) { return false; } + * // Your macros... + * return true; + * } + * + * Each `socd_cleaner_t` instance defines a pair of opposing keys and an SOCD + * resolution strategy (explained below). In `process_record_user()`, handler + * `process_socd_cleaner()` is called with each `socd_cleaner_t` instance. + * + * NOTE: The keys don't have to be WASD. But they must be basic keycodes + * (https://docs.qmk.fm/keycodes_basic). + * + * + * Enabling / disabling + * -------------------- + * + * SOCD filtering is enabled/disabled globally by assigning to variable + * `socd_cleaner_enabled`. For instance, to enable only on a GAME layer: + * + * layer_state_t layer_state_set_user(layer_state_t state) { + * socd_cleaner_enabled = IS_LAYER_ON_STATE(state, GAME); + * return state; + * } + * + * Or filtering can be disabled per `socd_cleaner_t` instance by setting its + * resolution to SOCD_CLEANER_OFF. + * + * + * Resolution strategies + * --------------------- + * + * As controls vary across games, there are multiple possible SOCD resolution + * strategies. SOCD Cleaner implements the following resolutions: + * + * - SOCD_CLEANER_LAST: (Recommended) Last input priority with reactivation. + * The last key pressed wins. Rapid alternating inputs can be made. + * Repeatedly tapping the D key while A is held sends "ADADADAD." + * + * - SOCD_CLEANER_NEUTRAL: Neutral resolution. When both keys are pressed, they + * cancel and neither is sent. + * + * - SOCD_CLEANER_0_WINS: Key 0 always wins, the first key listed in defining + * the `socd_cleaner_t`. For example, the W key always wins in + * + * socd_cleaner_t socd_ud = {{KC_W, KC_S}, SOCD_CLEANER_0_WINS}; + * + * - SOCD_CLEANER_1_WINS: Key 1 always wins, the second key listed. + * + * If you don't know what to pick, SOCD_CLEANER_LAST is recommended. The + * resolution strategy on a `socd_cleaner_t` may be changed at run time by + * assigning to `.resolution`. + * + * + * For full documentation, see + * + */ + +#pragma once + +#include "quantum.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum socd_cleaner_resolution { + // Disable SOCD filtering for this key pair. + SOCD_CLEANER_OFF, + // Last input priority with reactivation. + SOCD_CLEANER_LAST, + // Neutral resolution. When both keys are pressed, they cancel. + SOCD_CLEANER_NEUTRAL, + // Key 0 always wins. + SOCD_CLEANER_0_WINS, + // Key 1 always wins. + SOCD_CLEANER_1_WINS, + // Sentinel to count the number of resolution strategies. + SOCD_CLEANER_NUM_RESOLUTIONS, +}; + +typedef struct { + uint8_t keys[2]; // Basic keycodes for the two opposing keys. + uint8_t resolution; // Resolution strategy. + bool held[2]; // Tracks which keys are physically held. +} socd_cleaner_t; + +/** + * Handler function for SOCD cleaner. + * + * This function should be called from process_record_user(). The function may + * be called multiple times with different socd_cleaner_t instances to filter + * more than one SOCD key pair. + */ +bool process_socd_cleaner(uint16_t keycode, keyrecord_t* record, + socd_cleaner_t* state); + +/** Determines globally whether SOCD cleaner is enabled. */ +extern bool socd_cleaner_enabled; + +#ifdef __cplusplus +} +#endif diff --git a/keyboards/framework/ansi/keymaps/dialgatrainer02/keymap.c b/keyboards/framework/ansi/keymaps/dialgatrainer02/keymap.c new file mode 100644 index 000000000000..dea1ad4ed9a6 --- /dev/null +++ b/keyboards/framework/ansi/keymaps/dialgatrainer02/keymap.c @@ -0,0 +1,135 @@ +// Copyright 2022 Framework Computer +// SPDX-License-Identifier: GPL-2.0-or-later + +#include QMK_KEYBOARD_H +#include "framework.h" +#include "features/socd_cleaner.h" +#include "features/sentence_case.h" + +// clang-format off +enum _layers { + _BASE, + _FN, + _FN_LOCK, + _FM +}; + +// Changes from regular layout +// - Capslock => Map as ctrl and escape +// - FN+Backtick => Sleep + + +// Control when held and combined, Escape when tapped +#define KC_ECPS CTL_T(KC_ESC) + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + /* + * ┌─────┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬────┐ + * 14 keys │Esc │F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│ Del│ + * ├───┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤ + * 14 keys │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Backsp│ + * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬────┤ + * 14 keys │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │ \ │ + * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴────┤ + * 13 keys │ECaps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ Enter │ + * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───────┤ + * 12 keys │ Shift │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift │ + * ├────┬───┼───┼───┼───┴───┴───┴───┴───┼───┼───┼───┴┬───┬────┤ + * │ │ │ │ │ │ │ │ │↑ │ │ + * 11 keys │Ctrl│FN │GUI│Alt│ │Alt│Ctl│ ← ├───┤ → │ + * │ │ │ │ │ │ │ │ │ ↓│ │ + * └────┴───┴───┴───┴───────────────────┴───┴───┴────┴───┴────┘ + * 78 total + */ + [_BASE] = LAYOUT( + KC_ESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_DEL, + KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, + KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, + KC_ECPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, + KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, + KC_LCTL, MO(_FN), KC_LGUI, KC_LALT, KC_SPC, KC_RALT, KC_RCTL, KC_LEFT, KC_UP, KC_DOWN, KC_RGHT + ), + /* + * Function layer + * ┌─────┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬────┐ + * 14 keys │FN lk│Mut│vDn│vUp│Prv│Ply│Nxt│bDn│bUp│Scn│Air│Prt│App│Ins │ + * ├───┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤ + * 14 keys │SLP│ │ │ │ │ │ │ │ │ │ │ │ │ │ + * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬────┤ + * 14 keys │ │ │RGB│Nxt│Hue│Sat│Spd│Brt│ │ │Pau│ │ │ │ + * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴────┤ + * 13 keys │ │ │SRq│Prv│Hue│Sat│Spd│Brt│ScL│ │ │ │ │ + * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───────┤ + * 12 keys │ │ │ │ │ │ │Brk│ │ │ │ │ │ + * ├────┬───┼───┼───┼───┴───┴───┴───┴───┼───┼───┼───┴┬───┬────┤ + * │ │ │ │ │ │ │ │ │PgU│ │ + * 11 keys │ │ │ │ │ Toggle Backlight │ │ │Home├───┤End │ + * │ │ │ │ │ │ │ │ │PgD│ │ + * └────┴───┴───┴───┴───────────────────┴───┴───┴────┴───┴────┘ + * 78 total + */ + [_FN] = LAYOUT( + FN_LOCK, KC_MUTE, KC_VOLD, KC_VOLU, KC_MPRV, KC_MPLY, KC_MNXT, KC_BRID, KC_BRIU, G(KC_P), KC_AIRP, KC_PSCR, KC_MSEL, KC_INS, + KC_SLEP, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + _______, _______, RGB_TOG, RGB_MOD, RGB_HUI, RGB_SAI, RGB_SPI, RGB_VAI, _______, _______, KC_PAUS, _______, _______, _______, + _______, _______, KC_SYRQ, RGB_RMOD,RGB_HUD, RGB_SAD, RGB_SPD, RGB_VAD, KC_SCRL, _______, _______, _______, _______, + _______, _______, _______, BL_BRTG, _______, KC_BRK, _______, _______, _______, _______, _______, _______, + _______, _______, _______, _______, BL_STEP, _______, _______, KC_HOME, KC_PGUP, KC_PGDN, KC_END + ), + // Function lock layer + // Everything on F-row locked to function layer, except ESC and DEL + [_FN_LOCK] = LAYOUT( + _______, KC_MUTE, KC_VOLD, KC_VOLU, KC_MPRV, KC_MPLY, KC_MNXT, KC_BRID, KC_BRIU, G(KC_P), KC_AIRP, KC_PSCR, KC_MSEL, _______, + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + QK_BOOT, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + EE_CLR, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + _______, MO(_FM), _______, _______, _______, _______, _______, _______, _______, _______, _______ + ), + [_FM] = LAYOUT( + FN_LOCK, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_INS, + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + _______, _______, RGB_TOG, RGB_MOD, RGB_HUI, RGB_SAI, RGB_SPI, RGB_VAI, _______, _______, KC_PAUS, _______, _______, _______, + _______, _______, KC_SYRQ, RGB_RMOD,RGB_HUD, RGB_SAD, RGB_SPD, RGB_VAD, KC_SCRL, _______, _______, _______, _______, + _______, _______, _______, BL_BRTG, _______, KC_BRK, _______, _______, _______, _______, _______, _______, + _______, _______, _______, _______, BL_STEP, _______, _______, KC_HOME, KC_PGUP, KC_PGDN, KC_END + ), +}; +// clang-format on + + +socd_cleaner_t socd_v = {{KC_W, KC_S}, SOCD_CLEANER_LAST}; +socd_cleaner_t socd_h = {{KC_A, KC_D}, SOCD_CLEANER_LAST}; + +// Make sure to keep FN Lock even after reset + +bool process_record_user(uint16_t keycode, keyrecord_t* record) { + if (!process_sentence_case(keycode, record)) { return false; } + if (!process_socd_cleaner(keycode, record, &socd_v)) { return false; } + if (!process_socd_cleaner(keycode, record, &socd_h)) { return false; } + switch (keycode) { + case FN_LOCK: + if (record->event.pressed) { + if (layer_state_is(_FN)) { + set_single_persistent_default_layer(_FN_LOCK); + } + if (layer_state_is(_FM)) { + set_single_persistent_default_layer(_BASE); + } + } + return false; + break; + default: + break; + } + return true; +} + +void caps_word_set_user(bool active) { + writePin(GP24, active); +} + + +void sentence_case_primed(bool active) { + writePin(GP24, active); +} diff --git a/keyboards/framework/ansi/keymaps/dialgatrainer02/rules.mk b/keyboards/framework/ansi/keymaps/dialgatrainer02/rules.mk new file mode 100644 index 000000000000..ae32cb0d9011 --- /dev/null +++ b/keyboards/framework/ansi/keymaps/dialgatrainer02/rules.mk @@ -0,0 +1,7 @@ +SRC += features/socd_cleaner.c +SRC += features/sentence_case.c + +LTO_ENABLE = yes +CONSOLE_ENABLE = no + +CAPS_WORD_ENABLE = yes