/* * License (GPL): 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/>. * Author: © 2019, 2020 by Jos Boersema * */ /* This file contains mostly the Unicode and special macros. It contains the function: process_record_user(...) */ #include "./unicode_macros.h" #include "./unicode_weurope.h" // Definition of ƒ (Dutch currency symbol). // Best changed in user_config.h, if you like a Euro symbol instead. // # ifndef UNICODE_CURRENCY // Prior optional definition in user_config.h # define UNICODE_CURRENCY 0x0192 // Hex number. The unicode hex number for position ƒ in the default keymap. # endif // // 🛠 #define CS_USER_DEFINED 0x1F6E0 // Hammer & wrench (place holder). #define DIV10POINT TRUE // suggest to function write_number, to divide by 10 and print as a fraction: N.N // Gives Unicode code points to the relevant QMK functions. // Handles Dvorak 'descramble' Unicode mode, if compiled (only tested on Linux). void unicode_hex2output (long unsigned int unshifted, long unsigned int shifted) { long unsigned int input; // which argument to work on # ifdef DVORAK_DESCRAMBLE // Do the letter descramble if needed. char output[10]; // will hold the ascii for output int index; // count backwards 'to left' in the string long unsigned int bitmove; // move computation to next digit. long unsigned int work; // temporary value for computation # endif // What to work on if(shift_ison) input = shifted; // Trying to get everything possible here in this function, to reduce firmware size. else input = unshifted; # ifndef DVORAK_DESCRAMBLE // Only normal mode register_unicode ( (uint32_t) input ) ; # else if(_FULL_ != alternate){ register_unicode ( (uint32_t) input ) ; // normal Unicode mode }else{ // Special Dvorak-descramble mode: 0-9=0-9, a=a, b=n, c=i, d=h, e=d, f=y // Take the hex value 4 bits at a time, starting with the least significant, convert to ascii, store index = 9; output[index] = '\0'; // terminator bitmove = 0x1; while ((work = (input / bitmove)) && (index >= 0)) { index --; work &= 0xF; if (work < 0xA){ // numbers output[index] = work + 0x30; // pad to ASCII }else{ // alphas if (0xA == work) output[index] = 'a'; if (0xB == work) output[index] = 'n'; if (0xC == work) output[index] = 'i'; if (0xD == work) output[index] = 'h'; if (0xE == work) output[index] = 'd'; if (0xF == work) output[index] = 'y'; } bitmove *= 0x10; // next digit } SEND_STRING ( SS_DOWN(X_LCTRL) SS_DOWN(X_LSHIFT) "f" SS_UP(X_LSHIFT) SS_UP(X_LCTRL) ); // lead-in for Unicode on Linux, 'descramble' mode send_string (output + index); // pointer to argument with formatted string SEND_STRING ( " " ); // Ends the Unicode numerical input mode } # endif // DVORAK_DESCRAMBLE mode for that Base layer & mode setting is compiled in } // Wrapper for unicode keys that do have the same on shift. void unicode_hex2output_single (long unsigned int either) { unicode_hex2output (either, either); } // Required by QMK Unicode const uint32_t PROGMEM unicode_map[] = { }; // Macro name definitions. The Unicode layers _ACC, _DRA and _BON are defined here, // because the Unicode map system does not have enough space (at time of this programming, year 2020). enum custom_keycodes { // Macro, allowing the upper left button to switch to either _DEF_BASE base layer, or the _ALT_BASE base layer. // Alternate is set on/half/off in the _RAR layer. The word "base" is used to avoid "default," because the default // layer system call DF() is not being used. CTO_BASE = SAFE_RANGE, // 'C' for costum, "TO" for to, "BASE" for chosen base layer OTHER_BASE, // cycles modes: use _DEF_BASE, _ALT_BASE. For “dvorak²” layout (descramble) compile option, there is a third mode. # if defined(BASE_NUMPAD__ALT_BASE) OTHER_BASE_GO, // Like OTHER_BASE, but also immediately switches to the other BASE layer. # endif CTO_NUMS, // activates number-symbols layer, taking into account the dual layout mode CTO_ACCE, // accented '' CTO_DRAW, // drawings '' # ifndef CHOLTAP_ACCE_NOP CHOLTAP_ACCE, // Go to accented layer, or others in combination with other keys. # endif // Keys can be pressed together for a separate layer (like 'adjust layer' on the Planck). DUO_HOLD, CHOLTAP_RSHFT, // Go to _FUN layer, or shift modifier. CHOLTAP_LSHFT, // Go to <configurable> layer, or shift modifier. CHOLTAP_LAYR, // Go to _RAR layer, or right arrow // Shifts which on tap produce a key RSFT_TILDE, LSFT_DASH, // Special macro to make F-keys one-shot or not. _FUN_STAY, // Layer toggle to be guaranteed on up-key, therefore macro definition here. _MOV_UP, // These macros protect the critical keys like 'Power' from accidental press, by needing Shift to be pressed. C_KC_PWR, // Powers computer off. C_KC_WAKE, C_KC_SLEP, // sleep computer C_KC_PAUS, // pauze computer // Toggles side leds on/off. LEDS_ON, RGBTOG_, // Typing speed measuring SPEED_TOG, SPEED_REPORT, // Word/character counting COUNT_TOG, // starts word counting COUNT_REPORT, // writes to the computer as if typing, gives count report COUNT_WORDMIN, // reduces the word count COUNT_NULL, // resets count to zero LT__MOV__KC_ENT, // move to layer _MOV, or <enter> // The _ACC layer, additional Unicode. # ifndef REMOVE_ACC // This cuts out the whole _ACC layer. XP_ACC_AA , XP_ACC_AB , XP_ACC_AC , XP_ACC_AD , XP_ACC_AE , XP_ACC_AF , XP_ACC_AG , XP_ACC_AH , XP_ACC_AI , XP_ACC_AJ , XP_ACC_BA , XP_ACC_BB , XP_ACC_BC , XP_ACC_BD , XP_ACC_BE , XP_ACC_BF , XP_ACC_BG , XP_ACC_BH , XP_ACC_BI , XP_ACC_BJ , XP_ACC_BK , XP_ACC_CA , XP_ACC_CB , XP_ACC_CC , XP_ACC_CD , XP_ACC_CE , XP_ACC_CF , XP_ACC_CG , XP_ACC_CH , XP_ACC_CI , XP_ACC_CJ , # endif // REMOVE_ACC # ifndef REMOVE_DRA // This cuts out the whole _DRA layer // The _DRA layer, additional Unicode. XP_DRA_AA , XP_DRA_AB , XP_DRA_AC , XP_DRA_AD , XP_DRA_AE , XP_DRA_AF , XP_DRA_AG , XP_DRA_AH , XP_DRA_AI , XP_DRA_AJ , XP_DRA_BA , XP_DRA_BB , XP_DRA_BC , XP_DRA_BD , XP_DRA_BE , XP_DRA_BF , XP_DRA_BG , XP_DRA_BH , XP_DRA_BI , XP_DRA_BJ ,// XP_DRA_BK , // no 'BK' key definition on this layer XP_DRA_CA , XP_DRA_CB , XP_DRA_CC , XP_DRA_CD , XP_DRA_CE , XP_DRA_CF , # endif // REMOVE_DRA XP_DRA_CG , // Needed for ☑ on Unicode tester key in _RAR # ifndef REMOVE_DRA // This cuts out the whole _DRA layer XP_DRA_CH , XP_DRA_CI , XP_DRA_CJ , # endif // REMOVE_DRA // The _BON layer, additional Unicode. # ifndef REMOVE_BON // Removes this layer entirely, if set. XP_BON_AA , XP_BON_AB , XP_BON_AC , XP_BON_AD , XP_BON_AE , XP_BON_AF , XP_BON_AG , XP_BON_AH , XP_BON_AI , XP_BON_AJ , XP_BON_BA , XP_BON_BB , XP_BON_BC , XP_BON_BD , XP_BON_BE , XP_BON_BF , XP_BON_BG , XP_BON_BH , XP_BON_BI , XP_BON_BJ , XP_BON_BK , XP_BON_CA , XP_BON_CB , XP_BON_CC , XP_BON_CD , XP_BON_CE , XP_BON_CF , XP_BON_CG , XP_BON_CH , XP_BON_CI , XP_BON_CJ , # endif // REMOVE_BON }; // Pre-existing function, called for every key up and down. // This function is sortof the hub of the whole thing. bool process_record_user(uint16_t keycode, keyrecord_t *record) { // User input for the word count menu if (sizecount_menu) { if (record->event.pressed) { // key down switch (keycode) { case KC_0: // read in how many is maximum sizecount_max = (sizecount_max * 10); break; case KC_1: sizecount_max = (sizecount_max * 10) + 1; break; case KC_2: sizecount_max = (sizecount_max * 10) + 2; break; case KC_3: sizecount_max = (sizecount_max * 10) + 3; break; case KC_4: sizecount_max = (sizecount_max * 10) + 4; break; case KC_5: sizecount_max = (sizecount_max * 10) + 5; break; case KC_6: sizecount_max = (sizecount_max * 10) + 6; break; case KC_7: sizecount_max = (sizecount_max * 10) + 7; break; case KC_8: sizecount_max = (sizecount_max * 10) + 8; break; case KC_9: sizecount_max = (sizecount_max * 10) + 9; break; case KC_C: // count characters sizecount_max_type = SIZECOUNT_CHAR; sizecount_menu = FALSE; break; case KC_W: // count words sizecount_max_type = SIZECOUNT_WORD; sizecount_menu = FALSE; break; // Anything else ends menu input. case KC_DOT: case KC_ESC: sizecount_menu = FALSE; // break out break; } if (!sizecount_menu) { // end send_string ("->"); write_number (sizecount_max, FALSE); // just indicate something } } } // Go back to base-layer after pressing an F-key, on key-up to avoid BASE key activation if ((_fun_stay == FALSE) && // (((keycode >= KC_F1) && (keycode <= KC_F12)) || ((keycode >= KC_F13) && (keycode <= KC_F24)))) { // assumes keycodes 1-12 and 13-24 are consequtive, which seems likely, although using 1-24 failed (probably not consequtive) // Go back to base layer if (!(record->event.pressed)) { // key up if (alternate) { // layer_move (_ALT_BASE); }else{ layer_move (_DEF_BASE); } } } // Detect if Shift was pressed in isolation, by seeing if another key was pressed during the time // the right shift key was held down. // This system is also used by CHOLTAP_ACCE // This helps make sure a tapped use of these keys is correctly differentiated from their use as a // modifier/layer-hold key. The Shift and CHOLTAP_ACCE should not normally interfere with each other. if (isolate_trigger) { // speed: hoping this statement to execute a little quicker overall, than the next if ((keycode != CHOLTAP_RSHFT) // not right shift up && (keycode != CHOLTAP_LSHFT) // not left shift up && (keycode != CHOLTAP_ACCE) // _ACC layer (and others) && (keycode != RSFT_TILDE) // Shift on _NSY && (keycode != LSFT_DASH) // Shift on _NSY && (keycode != CHOLTAP_LAYR)) { // _RAR layer, or RAlt/Alt-Gr isolate_trigger = FALSE; // another key was pressed } } // This block contains the complex macros, which should not count in speed counting or word/character counting, // because they aren't typed characters. switch (keycode) { // Typing speed measuring case SPEED_TOG: // Toggle speed measuring on/off if (record->event.pressed) { // key down if (speed_measure) { speed_measure = FALSE; # ifdef RGBLIGHT_ENABLE rgblight_sethsv_noeeprom (HSV_PURPLE); // indicates stop (_RAR color) # endif }else{ // initialization of measurements speed_measure = TRUE; // activates speed = 0; // start at 0 k/s speed_countdown = SPEED_COUNTDOWN; // reset, speed is measured in batches of keypresses speed_counttime = timer_read32 ();// get current time speed_add = 0;// speed average accumulator, it keeps adding the *speed* of each batch to this total speed_batches = 0; // divider for speed_add to get the average # ifdef RGBLIGHT_ENABLE // set middle led rgblight_sethsv_noeeprom (HSV_WHITE); // indicates start # endif } # ifdef RGBLIGHT_ENABLE isolate_rgblight_set (); # endif } break; case SPEED_REPORT: // Report the current typing speed by writing it, as if typing if (record->event.pressed) { // down short added = 5; // This counts how many characters the report itself is adding into the current text, // to later delete it from the character count for text-size counting. if (speed_measure) { # ifdef WORDS_PER_MINUTE // The speed is recorded as an accumulation of keystrokes-per-second, times 10 for added precision. // This will be converted into words-per-minute by dividing by 5 characters for a word including // blank space and punctuation, and multiplied by 60 for seconds per minute. ⁶⁰/₅ = 12. Multiplied // by 12 is the simple conversion. send_string ("<"); // +1 character written // analogue to '<POWER>' added += write_number ((long int)((speed*12)/10), FALSE); // writes the speed send_string ("wpm"); // +3 character written if (0 != speed_batches) { long int average_times_ten; average_times_ten =(long int) ((speed_add * 12) / speed_batches); // *12 converts k/s to wpm send_string (";"); // +① '' added += write_number (average_times_ten / 10, FALSE); // writes the average speed, cannot use decimal because precision is not there send_string ("wpm"); // +③ added += write_number ((long int) speed_batches, FALSE); // amount of batches send_string ("x"); // +① added += 9 + write_number ((long int) SPEED_COUNTDOWN, FALSE); // amount of batches send_string ("keys"); // +④ = ⑨ speed_led ( (int) (average_times_ten / 12));// on report, show the average // we need to convert back to k/s now } # else // keystrokes per second, k/s send_string ("<"); // +1 character written // analogue to '<POWER>' added += write_number ((long int)(speed/10), FALSE); // writes the speed send_string ("k/s"); // +3 character written if (0 != speed_batches) { long int average_times_ten; average_times_ten =(long int) (speed_add / speed_batches); send_string (";"); // +① '' added += write_number (average_times_ten, DIV10POINT); // writes the average speed send_string ("k/s"); // +③ added += write_number ((long int) speed_batches, FALSE); // amount of batches send_string ("x"); // +① added += 9 + write_number ((long int) SPEED_COUNTDOWN, FALSE); // amount of batches send_string ("keys"); // +④ = ⑨ speed_led ( (int) average_times_ten );// on report, show the average. speed_led divides by 10 } # endif send_string (">"); // +1 = 5 if (sizecount_measure) sizecount_chars += added; // the user is expected to hit <backspace> }else{ send_string ("<Soff>"); // indicates off if (sizecount_measure) sizecount_chars += 5; // user: <backspace>, to take it down again } key_timer = timer_read (); }else{ // key up // This use of the key is for callibrating your colors; it is difficult otherwise to see. // This is not part of normal usage, therefore it is kept bare bones to reduce firmware size if (timer_elapsed (key_timer) > 999) { // held for one second speed += 10; write_number ((long int)(speed/10), FALSE); // writes the speed speed_led (speed); // update led } } break; case COUNT_TOG: // Toggle start/stop text size measuring if (record->event.pressed) { // key down key_timer = timer_read (); }else{ // up if (timer_elapsed (key_timer) < 500) { // held for less than half a second (tapped) if (sizecount_measure) { sizecount_measure = FALSE; # ifdef RGBLIGHT_ENABLE rgblight_sethsv_noeeprom (HSV_PURPLE); // indicates stop (color of _RAR) isolate_rgblight_set (); # endif }else{ sizecount_measure = TRUE; // start counting sizecount_word = FALSE; // detect double blanks. leading blanks are not a word # ifdef RGBLIGHT_ENABLE if (0 == sizecount_max) { rgblight_sethsv_noeeprom (HSV_BLUE); // indicates start/activated, but only without maximum set isolate_rgblight_set (); // .. if maximum set, led goes green to red. }else{ rgblight_sethsv_noeeprom (HSV_GREEN); // indicates start/activated, but only without maximum set isolate_rgblight_set (); // .. if maximum set, led goes green to red. } # endif } }else{ // held longer sizecount_menu = TRUE; send_string ("<Nc|w>"); // Menu: N amount, c|w character|word counting. Input is a number then ‛c’ or ‛w’ sizecount_max = 0; } } break; case COUNT_NULL: // Sets the count to zero, which allows on/off to have a suspend/resume if (record->event.pressed) { // key up sizecount_blanks = 0; // sizecount_chars = 0; # ifdef RGBLIGHT_ENABLE rgblight_sethsv_noeeprom (HSV_CYAN); // indicates reset isolate_rgblight_set (); # endif } break; case COUNT_REPORT: // Report the current typing speed if (record->event.pressed) { // We assume the user is writing a report in its active document, and then likely deleting it. short added = 0; // This counts how much characters the report adds into the user document. if (sizecount_measure) { send_string ("<"); // + 1 and ① characters (1 is one logical stream, ① another) if (0 == sizecount_max) { // no size counting maximum, print both characters and words added += write_number (sizecount_chars, FALSE); // returns how many characters where printed send_string ("c;"); // + 2 added += write_number (sizecount_blanks, FALSE) + 5; // adds here send_string ("w>"); // + 2 = 5 }else{ // Only show the one for which the maximum is set, don't throw off that mental focus if (SIZECOUNT_WORD == sizecount_max_type ) { added += write_number (sizecount_blanks, FALSE) + 3; send_string ("w>"); // + ② = ③ }else{ // characters added += write_number (sizecount_chars, FALSE) + 3; // returns how many characters where printed send_string ("c>"); // + ② = ③ } // add current maximum setting send_string ("["); // + 1 added += write_number (sizecount_max, FALSE) + 3; if (SIZECOUNT_WORD == sizecount_max_type) send_string ("w]"); // + 2 else send_string ("c]"); // + 2 } sizecount_chars += added; // Account for the written characters in the report itself. }else{ // no size counting, also here: keep the report terse send_string ("<Coff>"); // indicates off (no need to add to count, since counting is off) } } break; // This allows the user to manually remove word counts, when he has deleted a word. // This is not needed for character count, because <backspace> counts as minus. case COUNT_WORDMIN: // Take down one word in the word-count. if (record->event.pressed) { // down key_timer = timer_read (); }else{ // up if (timer_elapsed (key_timer) < 500) { // held for less than half a second (tapped) sizecount_blanks--; }else{ sizecount_blanks -= 10; } } break; // Shift detection system. // Disused because it turned out 'one shot' like Unicode input. Shift detection copied from. // https://github.com/kyleterry/qmk_firmware/blob/master/quantum/quantum.c //uint8_t shifted = get_mods() & (MOD_BIT(KC_LSHIFT)|MOD_BIT(KC_RSHIFT)); // Crude but self contained in this source file shift detection. // ... right shift case KC_RSFT: // + ... left shift case KC_LSFT: if (record->event.pressed) { // key down shift_ison = 1; // shift depressed }else{ // key up shift_ison = 0; // shift released } // There are macros on Shift who also alter this variable. break; case OTHER_BASE: // Switching through the default/alternate BASE modes, and Descramble for that Dvorak compile if (record->event.pressed) { ; }else{ // key up // Cycles through the modes # ifdef DVORAK_DESCRAMBLE // version Dvorak+Dvorak-descramble has 3 modes if (_NORMAL_ == alternate) { alternate = _FULL_;// alternate layers default_layer_set (_ALT_BASE_MASK); // This is needed only for a rare case, // where _DEF_BASE and _ALT_BASE their layer switching keys don't line up, // such as with Qwerty Base Arrow } else if (_HALF_ == alternate) { alternate = _NORMAL_;// normal layers default_layer_set (_DEF_BASE_MASK); }else{ // _FULL_ == alternate alternate = _HALF_;// alternate layers, without 'descramble' recomputing Unicode //default_layer_set (_ALT_BASE_MASK); // it cycles, and this comes always after it was set _FULL_ } # else // Only switching the BASE layers between alternate and default if (_NORMAL_ == alternate) { alternate = _FULL_;// alternate base layers default_layer_set (_ALT_BASE_MASK); }else{ alternate = _NORMAL_;// default base layers default_layer_set (_DEF_BASE_MASK); } # endif indicate_base (); // activate led change } break; # if defined(BASE_NUMPAD__ALT_BASE) case OTHER_BASE_GO: // Switching through the default/alternate BASE modes, and Descramble for that Dvorak compile if (record->event.pressed) { ; }else{ // key up // Cycles through the modes # ifdef DVORAK_DESCRAMBLE // version Dvorak+Dvorak-descramble has 3 modes if (_NORMAL_ == alternate) { alternate = _FULL_;// alternate layers default_layer_set (_ALT_BASE_MASK); } else if (_HALF_ == alternate) { alternate = _NORMAL_;// normal layers default_layer_set (_DEF_BASE_MASK); }else{ // _FULL_ == alternate alternate = _HALF_;// alternate layers, without 'descramble' recomputing Unicode //default_layer_set (_ALT_BASE_MASK); // it cycles, and this comes always after it was set _FULL_ } # else // Only switching the BASE layers between alternate and default if (_NORMAL_ == alternate) { alternate = _FULL_;// alternate base layers default_layer_set (_ALT_BASE_MASK); }else{ alternate = _NORMAL_;// default base layers default_layer_set (_DEF_BASE_MASK); } # endif // make the switch to the other Base layer if (alternate) { // layer_move (_ALT_BASE); }else{ layer_move (_DEF_BASE); } } break; # endif // Switching to layers: case CTO_BASE: // User pressed upper/left button, while not on BASE layer: ‛escape’ from a layer to BASE layer. // On BASE itself, that key is <Escape>. if (record->event.pressed) { // key down ; }else{ // key up if (alternate) { // go to the alternate version (bit of a hack maybe, but all alternate // ... modes are non-zero) layer_move (_ALT_BASE); }else{ layer_move (_DEF_BASE); } } break; case CTO_NUMS: // activates number-symbols layer if (record->event.pressed) { // key down ; }else{ // key up, so that upon key down the target layer isn't already activated, triggering that key on up if (alternate) { // go to the alternate version layer_move (_ALT_NSY); }else{ layer_move (_DEF_NSY); } } break; case CTO_ACCE: // Unicode layer if (record->event.pressed) { // key down ; }else{ // key up # ifndef REMOVE_ACC // This cuts out the whole _ACC layer. layer_move (_ACC); // activates normal accented layer # else # ifdef _ACC_KEY_ALT_LAYER layer_move (_ACC_KEY_ALT_LAYER); // Alternative layer user configuration # endif # endif } break; case CTO_DRAW: // Unicode layer if (record->event.pressed) { // key down ; }else{ // key up # ifndef REMOVE_DRA // This cuts out the whole _DRA layer. layer_move (_DRA); // activates normal accented layer # else # ifdef _DRA_KEY_ALT_LAYER layer_move (_DRA_KEY_ALT_LAYER); // Alternative layer user configuration # endif # endif } break; // The below are a simulated LT(layer,kc), layer-tap. // Double-tap-hold repeater functionality: not done. // They switch what layer to use depending on 'alternate' variable // Basically it starts the right layer on key down, goes back to base layer on key up, // and throws in a keypress as well if tapped. // It also integrates with DUO_HOLD, to reach the _BON layer. # ifndef CHOLTAP_ACCE_NOP // When this key has been eviscerated, this macro becomes useless case CHOLTAP_ACCE: // Works with DUO_HOLD macro to activate one of several layers. if (record->event.pressed) { // key down key_timer = timer_read (); isolate_trigger = TRUE; // keep track of whether another key gets pressed. duo_press_acc_bon ++; // This signals to the two DUO_HOLD keys, whether a move to _BON is desired. if (duo_press_nsy_dra) { // One or more of the DUO_HOLD layer keys was already pressed; move to _BON # ifndef REMOVE_BON // Removes this layer entirely, if set. layer_move (_BON); // Bonus Unicode layer # else # ifdef _BON_KEY_ALT_LAYER layer_move (_BON_KEY_ALT_LAYER); // Alternative layer user configuration # endif # endif }else{ // pressed in isolation # ifndef REMOVE_ACC // This cuts out the whole _ACC layer. layer_move (_ACC); // Accented layer # else # ifdef _ACC_KEY_ALT_LAYER layer_move (_ACC_KEY_ALT_LAYER); // Alternative layer user configuration # endif # endif } }else{ // key up duo_press_acc_bon --; if (1 == duo_press_nsy_dra) { // One DUO_HOLD layer keys is still pressed; move to numbers/symbols if (_FULL_ == alternate) { layer_move (_ALT_NSY); }else{ layer_move (_DEF_NSY); } }else if (2 == duo_press_nsy_dra) { // Two of the DUO_HOLD layer keys are still pressed: move to _DRA # ifndef REMOVE_DRA // This cuts out the whole _DRA layer. layer_move (_DRA); // activates normal accented layer # else # ifdef _DRA_KEY_ALT_LAYER layer_move (_DRA_KEY_ALT_LAYER); // Alternative layer user configuration # endif # endif }else{ if (alternate) { // No _DEF_NSY layer keys remain pressed; Go back to base layer layer_move (_ALT_BASE); }else{ layer_move (_DEF_BASE); } } // Pressed in isolation if (isolate_trigger) { if (timer_elapsed (key_timer) <= TAPPING_TERM_HOLTAP) { // tapped SEND_STRING (SS_TAP (X_DEL)); } } } break; # endif // CHOLTAP_ACCE_NOP case CHOLTAP_LAYR: //to _RAR on hold, otherwise a keycode if (record->event.pressed) { // key down key_timer = timer_read (); isolate_trigger = TRUE; // keep track of whether another key gets pressed. # ifdef BASE_RIGHT_ALT SEND_STRING (SS_DOWN (X_RALT)); # else layer_move (_RAR); // activates descrambled drawings layer # endif }else{ // key up // Go back to base layer if (speed_measure) speed_led (speed); // The _RAR layer overwrites the middle led, //.. for use with alternate _HALF_ led colors (middle); thus needs to be set back to speed // led color upon leaving. # ifdef BASE_RIGHT_ALT SEND_STRING (SS_UP (X_RALT)); # else if (alternate) { layer_move (_ALT_BASE); }else{ layer_move (_DEF_BASE); } # endif // Pressed in isolation if (isolate_trigger) { if (timer_elapsed (key_timer) <= TAPPING_TERM_HOLTAP) { // tapped SEND_STRING (SS_TAP (X_RIGHT)); } } } break; # ifndef DUO_HOLD_BASIC // This is the normal 'big' version, dealing with switching between _DEF_NSY/_ALT_NSY, _ACC, _DRA and _BON, in // .. conjunction with the CHOLTAP_ACCE macro. case DUO_HOLD: // The macro around the split space-bar. Both keys come to this macro. if (record->event.pressed) { // key down duo_press_nsy_dra ++; // simple way to keep track of how many are pressed if (duo_press_acc_bon){ // Preceded by holding the _ACC/_BON layer switch key: move to _BON # ifndef REMOVE_BON // Removes this layer entirely, if set. layer_move (_BON); // Bonus Unicode layer # else # ifdef _BON_KEY_ALT_LAYER layer_move (_BON_KEY_ALT_LAYER); // Alternative layer user configuration # endif # endif }else if (1 == duo_press_nsy_dra) { // This is the first press of either of the DUO_HOLD keys on BASE if (_NORMAL_ == alternate) { layer_move (_DEF_NSY); }else{ layer_move (_ALT_NSY); } } else if (2 == duo_press_nsy_dra) { // both are pressed # ifndef REMOVE_DRA // This cuts out the whole _DRA layer. layer_move (_DRA); // activates normal accented layer # else # ifdef _DRA_KEY_ALT_LAYER layer_move (_DRA_KEY_ALT_LAYER); // Alternative layer user configuration # endif # endif } }else{ // key up duo_press_nsy_dra --; if (1 == duo_press_nsy_dra) { if (duo_press_acc_bon){ // Still holding the _ACC/_BON layer switch key, and one DUO_HOLD keys # ifndef REMOVE_BON // Removes this layer entirely, if set. layer_move (_BON); // Bonus Unicode layer # else # ifdef _BON_KEY_ALT_LAYER layer_move (_BON_KEY_ALT_LAYER); // Alternative layer user configuration # endif # endif }else{ if (_NORMAL_ == alternate) { layer_move (_DEF_NSY); }else{ layer_move (_ALT_NSY); } } } else { // Has to be zero. if (duo_press_acc_bon){ // Still holding the _ACC/_BON layer switch key, but zero DUO_HOLD layer keys # ifndef REMOVE_ACC // This cuts out the whole _ACC layer. layer_move (_ACC); // Accented layer # else # ifdef _ACC_KEY_ALT_LAYER layer_move (_ACC_KEY_ALT_LAYER); // Alternative layer user configuration # endif # endif }else{ if (alternate) { // Back to letters layer_move (_ALT_BASE); }else{ layer_move (_DEF_BASE); } } } } break; # else // This is the eviscerated version, compiled when all Unicode layers _ACC, _DRA, _BON are cut, and layer key // .. combinations have not been assigned other uses. case DUO_HOLD: // The macro for the keys around the split space-bar. Both keys come to this macro. if (record->event.pressed) { // key down duo_press_nsy_dra ++; // simple way to keep track of how many are pressed if (0 != duo_press_nsy_dra) { // One or more of the DUO_HOLD keys is pressed if (_NORMAL_ == alternate) { layer_move (_DEF_NSY); }else{ layer_move (_ALT_NSY); } } }else{ // key up duo_press_nsy_dra --; if (0 == duo_press_nsy_dra) { // None of the DUO_HOLD keys remains pressed if (alternate) { // Back to letters layer_move (_ALT_BASE); }else{ layer_move (_DEF_BASE); } } } break; # endif // DUO_HOLD_BASIC // When held the key is shift. When tapped it is computed if the tap is short enough, // and if no other key was pressed, in which case: right-shift-up is a toggle to the _FUN layer. // The timing is meant to be able to undo an erroneous shift press just by holding longer, // and the test if another key was pressed is to prevent an erroneous detection when typing // very fast. // The reason for this on shift is to isolate GUI, where _FUN was previously more easily // located. No alternative tapping function with GUI because some systems do not treat GUI // purely as a modifier it seems. Since it is a toggle anyway, _FUN can fit away from the thumb-hold row. case CHOLTAP_RSHFT: // When tapped it toggles the _FUN layer, when held it is Shift if (record->event.pressed) { // key down SEND_STRING (SS_DOWN (X_RSFT)); shift_ison = 1; // shift depressed key_timer = timer_read (); isolate_trigger = TRUE; // keep track of whether another key gets pressed until key-up }else{ // key up SEND_STRING (SS_UP (X_RSFT)); shift_ison = 0; // shift released if (isolate_trigger) { // no other key was hit since key down // Held medium long: _PAD, long: _MOV. // The reason to have a switch to _MOV on the left hand, is to be able to reach arrows on a toggle, // all by the left hand, when the right hand is on the mouse. if ((timer_elapsed (key_timer) <= 200)) { // tapped short (milliseconds) # ifndef SWITCH_RSHIFT_FUN_RAR // user config to reverse what this key its timing toggles to layer_move (_FUN); // activates function layer as a toggle } else { // held for longer layer_move (_RAR); # else layer_move (_RAR); // activates function layer as a toggle } else { // held for longer layer_move (_FUN); # endif } } } break; // The left-shift version of the above keycode. User can configure something (_PAD is default) case CHOLTAP_LSHFT: // When tapped it toggles the _MOV layer, when held it is Shift // _RAR was the first idea, but some of its keys are too dangerous regarding accidents. if (record->event.pressed) { // key down SEND_STRING (SS_DOWN (X_LSFT)); shift_ison = 1; // shift depressed # ifndef REMOVE_PAD // The _PAD layer exists, we will use a timer … key_timer = timer_read (); # endif // This variable is re-used, for speed and because using both shifts is useless, // .. thus very rare, and also not a usage problem if it occured. isolate_trigger = TRUE; // keep track of whether another key gets pressed. }else{ // key up SEND_STRING (SS_UP (X_LSFT)); shift_ison = 0; // shift released if (isolate_trigger) { // no other key was hit since key down # ifndef REMOVE_PAD // The _PAD layer exists, differentiate meaning by timer. // Held medium long: _PAD, long: _MOV. // The reason to have a switch to _MOV on the left hand, is to be able to reach arrows on a toggle, // all by the left hand, when the right hand is on the mouse. if ((timer_elapsed (key_timer) <= 200)) { // tapped medium-long (milliseconds) # ifndef SWITCH_LSHIFT_PAD_MOV // user config to reverse what this key its timing toggles to layer_move (_PAD); } else { // held for longer layer_move (_MOV); # else layer_move (_MOV); } else { // held for longer layer_move (_PAD); # endif } # else // _PAD layer was eviscerated layer_move (_MOV); # endif } } break; /* case RSFT_TILDE: if (record->event.pressed) { // key down SEND_STRING (SS_DOWN (X_RSFT)); shift_ison = 1; // shift depressed key_timer = timer_read (); isolate_trigger = TRUE; // keep track of whether another key gets pressed until key-up }else{ // key up SEND_STRING (SS_UP (X_RSFT)); shift_ison = 0; // shift released if (isolate_trigger) { // no other key was hit since key down // Held medium long: _PAD, long: _MOV. // The reason to have a switch to _MOV on the left hand, is to be able to reach arrows on a toggle, // all by the left hand, when the right hand is on the mouse. if ((timer_elapsed (key_timer) <= 200)) { // tapped short (milliseconds) SEND_STRING ("~"); } } } break; */ case RSFT_TILDE: // firmware size optimization, saves 36 bytes case LSFT_DASH: if (record->event.pressed) { // key down if (RSFT_TILDE == keycode) { // this is probably not needed, both can be left or right shift SEND_STRING (SS_DOWN (X_RSFT)); }else{ SEND_STRING (SS_DOWN (X_LSFT)); } shift_ison = 1; // shift depressed key_timer = timer_read (); isolate_trigger = TRUE; // keep track of whether another key gets pressed until key-up }else{ // key up if (RSFT_TILDE == keycode) { SEND_STRING (SS_UP (X_RSFT)); }else{ SEND_STRING (SS_UP (X_LSFT)); } shift_ison = 0; // shift released if (isolate_trigger) { // no other key was hit since key down // Held medium long: _PAD, long: _MOV. // The reason to have a switch to _MOV on the left hand, is to be able to reach arrows on a toggle, // all by the left hand, when the right hand is on the mouse. if ((timer_elapsed (key_timer) <= 200)) { // tapped short (milliseconds) if (RSFT_TILDE == keycode) { SEND_STRING ("~"); }else{ SEND_STRING ("-"); } } } } break; case _FUN_STAY: // toggles if the f-keys return _FUN layer to BASE after one press if (record->event.pressed) { // key down if (_fun_stay == FALSE) { _fun_stay = TRUE; }else{ _fun_stay = FALSE; } indicate_fun_stay (); // leds } break; # ifdef MOREKEY2_ARROW_CLUSTER case _MOV_UP: // To be sure it activates on up key, and not already has triggered the _MOV layer during up-key. if (record->event.pressed) { // key down ; }else{ // key up layer_move (_MOV); } break; # endif // These keys are so disruptive on an erroneous key press, that they are behind a shift lock. // When used unshifted, they print a memory aid string: their name. case C_KC_PWR: if (record->event.pressed) { // key down if (shift_ison) { SEND_STRING (SS_TAP (X_PWR)); }else{ SEND_STRING ("<POWER>"); // Memory aid } } break; case C_KC_WAKE: if (record->event.pressed) { // key down if (shift_ison) { SEND_STRING (SS_TAP (X_WAKE)); }else{ SEND_STRING ("<WAKE>"); // Memory aid } } break; case C_KC_SLEP: if (record->event.pressed) { // key down if (shift_ison) { SEND_STRING (SS_TAP (X_SLEP)); }else{ SEND_STRING ("<SLEEP>"); // Memory aid } } break; case C_KC_PAUS: if (record->event.pressed) { // key down if (shift_ison) { SEND_STRING (SS_TAP (X_PAUS)); }else{ SEND_STRING ("<PAUSE>"); // Memory aid } } break; case LEDS_ON: // Toggles left/right leds on or off if (record->event.pressed) { // key down if (leds_on == FALSE) { leds_on = TRUE; }else{ leds_on = FALSE; } } break; # ifdef LEDS_OFF_BASE_DEF // This messes with led effect on/off, so we need to track the state of this setting now. case RGBTOG_: // Toggles middle led on or off if (record->event.pressed) { // key down if (led_middle_on == FALSE) { led_middle_on = TRUE; rgblight_enable_noeeprom (); }else{ led_middle_on = FALSE; rgblight_disable_noeeprom (); } } break; # endif // Some keycodes treated specially for the two counting systems (speed, text size) // Deletions: case KC_BSPC: // non-counting speed case KC_DEL: // non-counting speed if (record->event.pressed) { // key down if (sizecount_measure) sizecount_chars--; // minus count for text size (removed a character) } break; // White space for counting words case LT__MOV__KC_ENT: // We want to count the <enter> for word-counts, sadly this looses the key repetition of LT(…) if (record->event.pressed) { // key down key_timer = timer_read (); layer_move (_MOV); }else{ // key up if (alternate) { // Back to letters layer_move (_ALT_BASE); }else{ layer_move (_DEF_BASE); } if (timer_elapsed (key_timer) <= TAPPING_TERM_HOLTAP) { // tapped send_string ("\n"); if (sizecount_measure) { sizecount_chars++; if (sizecount_word) sizecount_blanks++; // count a word sizecount_word = FALSE; // don't count immediately next blank as a word } } } break; // Word counting case KC_SPC: if (record->event.pressed) { // key down if (sizecount_measure) { sizecount_chars++; if (sizecount_word) sizecount_blanks++; // count a word sizecount_word = FALSE; // don't count immediately next blank as a word } } break; // These are excluded from counting for text size/speed, they prevent the “default“ in the case statement to execute. case KC_LEFT: case KC_UP: case KC_DOWN: case KC_RIGHT: case KC_PGUP: case KC_PGDN: case KC_HOME: case KC_END: case LALT_T ( KC_LEFT ): if (speed_measure) speed_countdown++; // Navigation could be integral to someone typing and correcting mistakes, // but those keys don't add any text. // Mouse movement is discounted in both speed and text size case KC_WH_L: case KC_WH_D: case KC_WH_U: case KC_WH_R: case KC_MS_L: case KC_MS_D: case KC_MS_U: case KC_MS_R: case KC_BTN1: case KC_BTN5: case KC_BTN4: case KC_BTN3: case KC_BTN2: break; default: // If something else, it is a speed- and text measurement counting key if (record->event.pressed) { // key down if (speed_measure) speed_countdown--; if (sizecount_measure) sizecount_chars++; } } // If speed measuring is on, count keypresses // The idea is to more/less follow common standard with typing speed counting: shift is not counted, // layer-switching or its equivalent is neither. Arrows are not counted. if (speed_measure) { if (record->event.pressed) { // key down if (0 >= speed_countdown) { // key presses per second, but times ten for added precision of one digit // This calculation quickly looses precision if not calculated with high enough numbers, but low enough to fit. speed = (int) ( (SPEED_COUNTDOWN * 1000 ) / ((timer_read32 () - speed_counttime)/10) ); // counts time in ms speed_led (speed); // updates led // record for average if (0 < (speed/10)) { // ignore 0 k/s batches, we assume the typer took a break speed_batches++; speed_add += speed; } // reset for next batch speed_countdown = SPEED_COUNTDOWN; // reset speed_counttime = timer_read32 (); } } } // For word-counting, ignore double blanks if (sizecount_measure) { if (record->event.pressed) { bool within = TRUE; // When text size is maximized, this indicates we are not yet at that maximum. # ifdef RGBLIGHT_ENABLE unsigned short size_fraction = 0; // Used to compute led color as a fraction of a set maximum which is already typed. # endif // ignoring blanks wordcount if ((keycode != KC_SPC) && (keycode != KC_TAB) // This is ok, but a tab on BASE layer combo with Control, is not caught by this XXX (problem ignored, I never write Tab in a text worth counting) && (keycode != LT__MOV__KC_ENT) && (keycode != LT__MOV__KC_ENT)) { sizecount_word = TRUE; // current key is not a blank, so we set this trigger for next key press } // computing maximum count effects: leds if (0 != sizecount_max) { if (SIZECOUNT_WORD == sizecount_max_type) { if (sizecount_blanks > sizecount_max) within = FALSE; }else{ // count chars if (sizecount_chars > sizecount_max) within = FALSE; } // led colors if (within) { // green to red middle led # ifdef RGBLIGHT_ENABLE if (SIZECOUNT_WORD == sizecount_max_type) { size_fraction = (90 * sizecount_blanks) / sizecount_max; }else{ size_fraction = (90 * sizecount_chars) / sizecount_max; } rgblight_sethsv_noeeprom (90 - size_fraction , 255, 255); // green to red, full saturation, full lit # endif }else{ // when at or over the limit: blink led red/white if ((KC_BSPC != keycode) && (KC_DEL != keycode) // User already deleting, doubling is confusing && (CHOLTAP_LAYR != keycode)) { // This brings up the _RAR layer, to access the Count settings. SEND_STRING (SS_TAP(X_BSPC)); // refuses to type further, the user is stopped from typing to make it obvious } # ifdef RGBLIGHT_ENABLE if (sizecount_chars & 0x1) { // flip by every keypress rgblight_sethsv_noeeprom (HSV_RED); }else{ rgblight_sethsv_noeeprom (HSV_WHITE); } # endif } # ifdef RGBLIGHT_ENABLE rgblight_set (); // only center led is altered, no need to go through isolate_rgblight_set() # endif } } } // Simple macros, printing a character. switch (keycode) { /* _ACC layer definitions. */ // ------------------------- row 4 # ifndef REMOVE_ACC // This cuts out the whole _ACC layer. case XP_ACC_AA: // because a if (record->event.pressed) { // key down unicode_hex2output (CAL_ACU, CAU_ACU);// á Á } break; case XP_ACC_AB: // because o (Dvorak) if (record->event.pressed) { // key down unicode_hex2output (COL_ACU, COU_ACU);// ó Ó } break; case XP_ACC_AC: // because e (Dvorak) if (record->event.pressed) { // key down unicode_hex2output (CEL_ACU, CEU_ACU);// é É } break; case XP_ACC_AD: // because u (Dvorak) if (record->event.pressed) { // key down unicode_hex2output (CUL_ACU, CUU_ACU);// ú Ú } break; case XP_ACC_AE: // because i (Dvorak) if (record->event.pressed) { // key down unicode_hex2output (CIL_ACU, CIU_ACU);// í Í } break; case XP_ACC_AF: // Because near Y if (record->event.pressed) { // key down unicode_hex2output (CYL_ACU, CYU_ACU);// ý Ý } break; case XP_ACC_AG: // because near Y if (record->event.pressed) { // key down unicode_hex2output (CIJL_BI, CIJU_BI);// ij IJ } break; case XP_ACC_AH: // because c (Dvorak) if (record->event.pressed) { // key down unicode_hex2output (CCL_CDL, CCU_CDL);// ç Ç } break; case XP_ACC_AI: // because ring-finger left is o (Dvorak) if (record->event.pressed) { // key down unicode_hex2output (COL_STK, COU_STK);// ø Ø } break; case XP_ACC_AJ: // because pinky finger left is a if (record->event.pressed) { // key down unicode_hex2output (CAL_RNG, CAU_RNG);// å Å } break; // ------------------------- row 3 case XP_ACC_BA: // because a if (record->event.pressed) { // key down unicode_hex2output (CAL_DIA, CAU_DIA);// ä Ä } break; case XP_ACC_BB: // because o (Dvorak) if (record->event.pressed) { // key down unicode_hex2output (COL_DIA, COU_DIA);// ö Ö } break; case XP_ACC_BC: // because e (Dvorak) if (record->event.pressed) { // key down unicode_hex2output (CEL_DIA, CEU_DIA);// ë Ë } break; case XP_ACC_BD: // because u (Dvorak) if (record->event.pressed) { // key down unicode_hex2output (CUL_DIA, CUU_DIA);// ü Ü } break; case XP_ACC_BE: // because i if (record->event.pressed) { // key down unicode_hex2output (CIL_DIA, CIU_DIA);// ï Ï } break; case XP_ACC_BF: // because near y if (record->event.pressed) { // key down unicode_hex2output (CYL_DIA, CYU_DIA);// ÿ Ÿ } break; case XP_ACC_BG: // because vague logic about other hand having ae near on similar fingers if (record->event.pressed) { // key down unicode_hex2output (COEL_BI, COEU_BI);// œ Œ } break; case XP_ACC_BH: // because near œ, toward the side of a (pinky) if (record->event.pressed) { // key down unicode_hex2output (CAEL_BI, CAEU_BI);// æ Æ } break; case XP_ACC_BI: // because n if (record->event.pressed) { // key down unicode_hex2output (CNL_TLD, CNU_TLD);// ñ Ñ } break; case XP_ACC_BJ: // because s if (record->event.pressed) { // key down unicode_hex2output_single (CSL_SHP);// ß ß } break; case XP_ACC_BK: // because roughly the location on French keyboard if (record->event.pressed) { // key down unicode_hex2output_single (C_MU_L);// μ } break; // ------------------------- row 2 case XP_ACC_CA: // because a if (record->event.pressed) { // key down unicode_hex2output (CAL_GRA, CAU_GRA);//à À } break; case XP_ACC_CB: // because o (Dvorak) if (record->event.pressed) { // key down unicode_hex2output (COL_GRA, COU_GRA);// ò Ò } break; case XP_ACC_CC: // because e (Dvorak) if (record->event.pressed) { // key down unicode_hex2output (CEL_GRA, CEU_GRA);// è È } break; case XP_ACC_CD: // because u (Dvorak) if (record->event.pressed) { // key down unicode_hex2output (CUL_GRA, CUU_GRA);// ù Ù } break; case XP_ACC_CE: // because i (Dvorak) if (record->event.pressed) { // key down unicode_hex2output (CIL_GRA, CIU_GRA);// ì Ì } break; case XP_ACC_CF: // because other hand same finger i if (record->event.pressed) { // key down unicode_hex2output (CIL_CAR, CIU_CAR);// î Î } break; case XP_ACC_CG: // because other hand same finger u if (record->event.pressed) { // key down unicode_hex2output (CUL_CAR, CUU_CAR);// û Û } break; case XP_ACC_CH: // because other hand same finger e if (record->event.pressed) { // key down unicode_hex2output (CEL_CAR, CEU_CAR);// ê Ê } break; case XP_ACC_CI: // because other hand same finger o if (record->event.pressed) { // key down unicode_hex2output (COL_CAR, COU_CAR);// ô Ô } break; case XP_ACC_CJ: // because other hand same finger a if (record->event.pressed) { // key down unicode_hex2output (CAL_CAR, CAU_CAR);// â  } break; # endif // REMOVE_ACC // This cuts out the whole _ACC layer. /* _DRA layer definitions. */ # ifndef REMOVE_DRA // This cuts out the whole _DRA layer // ------------------------- row 4 case XP_DRA_AA: // because '", the opening „“ at the ‛open’ of the keyboard (left/up) if (record->event.pressed) { // key down # ifdef FULL_DRA_4THROW unicode_hex2output (CS_DQUHR, CS_DQUL);// “ „ # else unicode_hex2output_single (CS_DQUHR);// “ # endif } break; case XP_DRA_AB: // because to the right of opening “, ≤ on < if (record->event.pressed) { // key down # ifdef FULL_DRA_4THROW unicode_hex2output (CS_DQUH, CS_ELTHAN);// ” ≤ # else unicode_hex2output_single (CS_DQUH);// ” # endif } break; case XP_DRA_AC: // because this is where the £ is on an English keyboard, on 'any' money symbols ¤; ≥ on > if (record->event.pressed) { // key down # ifdef FULL_DRA_4THROW unicode_hex2output (CS_POUND, CS_EGTHAN);// £ ≥ # else unicode_hex2output_single (CS_POUND);// £ # endif } break; case XP_DRA_AD: // because ∅ looks like ¢, and ¢ (cent) is on $ (money) ? if (record->event.pressed) { // key down # ifdef FULL_DRA_4THROW unicode_hex2output (CS_NONE, CS_CENT);// ∅ ¢ # endif } break; case XP_DRA_AE: // because percentages %‰‱ and money ƒ are numerical ? if (record->event.pressed) { // key down # ifdef FULL_DRA_4THROW unicode_hex2output (CS_PLMI, CS_LGULDEN);// ± ƒ # else unicode_hex2output_single (CS_PLMI);// ± # endif } break; case XP_DRA_AF: // Because left of 🙂, on top of ★ if (record->event.pressed) { // key down # ifdef FULL_DRA_4THROW unicode_hex2output (CS_FLEUR, CS_HEART);// ❦ ♥ # else unicode_hex2output_single (CS_HEART);// ♥ # endif } break; case XP_DRA_AG: // because 😊 ⍨ if (record->event.pressed) { // key down # ifdef FULL_DRA_4THROW unicode_hex2output (CS_SMIL, CS_SAD_);// 🙂 🙁 # endif } break; case XP_DRA_AH: // because «no reason», next to 😊 (emoticons) if (record->event.pressed) { // key down # ifdef FULL_DRA_4THROW unicode_hex2output (CS_THUP, CS_THDN);// 👍 👎 # endif } break; case XP_DRA_AI: // because ( if (record->event.pressed) { // key down # ifdef FULL_DRA_4THROW unicode_hex2output (CS_OPSUP, CS_OPSUB);// ⁽ ₍ # endif } break; case XP_DRA_AJ: // because ) if (record->event.pressed) { // key down # ifdef FULL_DRA_4THROW unicode_hex2output (CS_CPSUP, CS_CPSUB);// ⁾ ₎ # endif } break; // ------------------------- row 3 case XP_DRA_BA: // because 1 if (record->event.pressed) { // key down # ifdef SUB_SCRIPT_NUMS unicode_hex2output (CN_1SUP, CN_1SUB);// ¹ ₁ # else unicode_hex2output_single (CN_1SUP);// ¹ # endif } break; case XP_DRA_BB: // because 2 if (record->event.pressed) { // key down # ifdef SUB_SCRIPT_NUMS unicode_hex2output (CN_2SUP, CN_2SUB);// ² ₂ # else unicode_hex2output_single (CN_2SUP);// ² # endif } break; case XP_DRA_BC: // because 3 if (record->event.pressed) { // key down # ifdef SUB_SCRIPT_NUMS unicode_hex2output (CN_3SUP, CN_3SUB);// ³ ₃ # else unicode_hex2output_single (CN_3SUP);// ³ # endif } break; case XP_DRA_BD: // because 4 if (record->event.pressed) { // key down # ifdef SUB_SCRIPT_NUMS unicode_hex2output (CN_4SUP, CN_4SUB);// ⁴ ₄ # else unicode_hex2output_single (CN_4SUP);// ⁴ # endif } break; case XP_DRA_BE: // because 5 if (record->event.pressed) { // key down # ifdef SUB_SCRIPT_NUMS unicode_hex2output (CN_5SUP, CN_5SUB);// ⁵ ₅ # else unicode_hex2output_single (CN_5SUP);// ⁵ # endif } break; case XP_DRA_BF: // because 6 if (record->event.pressed) { // key down # ifdef SUB_SCRIPT_NUMS unicode_hex2output (CN_6SUP, CN_6SUB);// ⁶ ₆ # else unicode_hex2output_single (CN_6SUP);// ⁶ # endif } break; case XP_DRA_BG: // because 7 if (record->event.pressed) { // key down # ifdef SUB_SCRIPT_NUMS unicode_hex2output (CN_7SUP, CN_7SUB);// ⁷ ₇ # else unicode_hex2output_single (CN_7SUP);// ⁷ # endif } break; case XP_DRA_BH: // because 8 if (record->event.pressed) { // key down # ifdef SUB_SCRIPT_NUMS unicode_hex2output (CN_8SUP, CN_8SUB);// ⁸ ₈ # else unicode_hex2output_single (CN_8SUP);// ⁸ # endif } break; case XP_DRA_BI: // because 9 if (record->event.pressed) { // key down # ifdef SUB_SCRIPT_NUMS unicode_hex2output (CN_9SUP, CN_9SUB);// ⁹ ₉ # else unicode_hex2output_single (CN_9SUP);// ⁹ # endif } break; case XP_DRA_BJ: // because 0 if (record->event.pressed) { // key down # ifdef SUB_SCRIPT_NUMS unicode_hex2output (CN_0SUP, CN_0SUB);// ⁰ ₀ # else unicode_hex2output_single (CN_0SUP);// ⁰ # endif } break; // ------------------------- row 2 case XP_DRA_CA: // because [ if (record->event.pressed) { // key down # ifdef FULL_DRA_2NDROW unicode_hex2output (CS_OCBRA, CB_HHORI);// 「 ━ # else unicode_hex2output_single (CB_HHORI);// ━ # endif } break; case XP_DRA_CB: // because ] if (record->event.pressed) { // key down # ifdef FULL_DRA_2NDROW unicode_hex2output (CS_CCBRA, CB_LHORI);// 」 ─ # else unicode_hex2output_single (CB_LHORI);// ─ # endif } break; case XP_DRA_CC: // because «no reason» if (record->event.pressed) { // key down # ifdef FULL_DRA_2NDROW unicode_hex2output (CS_DEGREE, CS_CIRCLE);// ° 〇 # else unicode_hex2output_single (CS_DEGREE);// ° # endif } break; case XP_DRA_CD: // because «no reason» if (record->event.pressed) { // key down # ifdef FULL_DRA_2NDROW unicode_hex2output (CS_BULLET, CS_PARA);// • § # else unicode_hex2output_single (CS_BULLET);// • # endif } break; case XP_DRA_CE: // because «no reason» if (record->event.pressed) { // key down # ifdef FULL_DRA_2NDROW unicode_hex2output (CS_ELLIPS, CS_MIDDOT);// … · # else unicode_hex2output_single (CS_ELLIPS);// … # endif } break; case XP_DRA_CF: // because «no reason» (+ resembles ‛☒’ ?), ✗ if (record->event.pressed) { // key down unicode_hex2output (CS_CHECK_B, CS_CHECK_N);// ☐ ☒ } break; # endif // This one must be included for _RAR layer case XP_DRA_CG: // because next to ✗ ☐ ☒ if (record->event.pressed) { // key down # ifdef FULL_DRA_2NDROW unicode_hex2output (CS_CHECK_Y, CS_CHECK_H);// ☑ 🗹 # else unicode_hex2output_single (CS_CHECK_Y);// ☑ # endif } break; # ifndef REMOVE_DRA // This cuts out the whole _DRA layer case XP_DRA_CH: // because ? if (record->event.pressed) { // key down unicode_hex2output (CQU_INV, CEX_INV);// ¿ ¡ } break; case XP_DRA_CI: // because {, ┄ «no reason» (opposite side from ━) if (record->event.pressed) { // key down # ifdef FULL_DRA_2NDROW unicode_hex2output (CS_ODABRA, CB_LHORID);// 《 ┄ # else unicode_hex2output_single (CB_LHORID);// ┄ # endif } break; case XP_DRA_CJ: // because }, ┅ «no reason» (opposite side from ─) if (record->event.pressed) { // key down # ifdef FULL_DRA_2NDROW unicode_hex2output (CS_CDABRA, CB_HHORID);// 》 ┅ # else unicode_hex2output_single (CB_HHORID);// ┅ # endif } break; # endif // REMOVE_DRA /* _BON layer definitions. Due to running out of X(…), XP(…) space.*/ // ------------------------- row 4 # ifndef REMOVE_BON // Removes this layer entirely, if set. case XP_BON_AA: // because of ' " if (record->event.pressed) { // key down # ifdef FULL_BON_4THROW unicode_hex2output (CS_HQUOSB, CS_USER_DEFINED);// ‛ 🛠 # else unicode_hex2output_single (CS_HQUOSB);// ‛ # endif } break; case XP_BON_AB: // because of <, because "WASD" on _MOV // 0x2019, single quotation mark: ’ // 0x2B06 arrow up: ⬆ if (record->event.pressed) { // key down unicode_hex2output (CS_HQUOSE, CS_ARR_UP);// ’ ⬆ } break; case XP_BON_AC: // because of > // 0x00A4 any currency symbol: ¤ // 0x1F12F Copyleft: 🄯 (means free to copy, see also © for not free to copy.) if (record->event.pressed) { // key down # ifdef FULL_BON_4THROW unicode_hex2output (CS_CURREN, CS_COPYL);// ¤ 🄯 # else unicode_hex2output_single (CS_CURREN);// ¤ # endif } break; case XP_BON_AD: // because $ and ¢ can be about money, and money is often added together // because … no reason, left over space. // 0x2211, summation: ∑ // 0xA9, copyright: © if (record->event.pressed) { // key down # ifdef FULL_BON_4THROW unicode_hex2output (CS_CUMMU, CS_COPY);// ∑ © # else unicode_hex2output_single (CS_CUMMU);// ∑ # endif } break; case XP_BON_AE: // because % for percent // 0x2030,// promille: ‰ // 0x2031,// pro ten thousandth: ‱ if (record->event.pressed) { // key down # ifdef FULL_BON_4THROW unicode_hex2output (CS_PROM, CS_PROTT);// ‰ ‱ # else unicode_hex2output_single (CS_PROM);// ‰ # endif } break; case XP_BON_AF: // Because ♥ is a star, ❦ and stars can be used as bullet points // 0x2605, star: ★ // 0x066D, star small: ٭ if (record->event.pressed) { // key down # ifdef FULL_BON_4THROW unicode_hex2output (CS_STARB, CS_STARL);// ★ ٭ # else unicode_hex2output_single (CS_STARB);// ★ # endif } break; case XP_BON_AG: // because of 🙂 🙁 // 0x1f60A,// <smile> ^^ 😊 // 0x2368,// "Squiggly" face <sad> ⍨ # ifdef FULL_BON_4THROW if (record->event.pressed) { // key down unicode_hex2output (CS_SMILP, CS_SQUIG);// 😊 ⍨ } # endif break; case XP_BON_AH: // because * also for multiply, because asterisk * // 0x00D7,// multiply: × // 0x20F0 high asterisk: ⃰(this thing seems to behave a bit weird in vim(1) or terminal) if (record->event.pressed) { // key down # ifdef FULL_BON_4THROW unicode_hex2output (CS_MULT, CS_ASTL);// × ⃰ # else unicode_hex2output_single (CS_MULT);// × # endif } break; case XP_BON_AI: // because ø sort of seems to divide something, and √ also does that, and close to ⁻⁺ (exponential) // 0x221A,// square root: √ if (record->event.pressed) { // key down # ifdef FULL_BON_4THROW unicode_hex2output_single (CS_SQRT);// √ # endif } break; case XP_BON_AJ: // because å points in the circle where this exponential minus goes, and it is right/"up" on the board // because ⁻⁺ belong together // 0x207B,// exponential minus sign: ⁻ // 0x207A,// exponential plus: ⁺ # ifdef FULL_BON_4THROW if (record->event.pressed) { // key down unicode_hex2output (CS_EXPMIN, CS_EXPPLS);// ⁻ ⁺ } # endif break; // ------------------------- row 3 case XP_BON_BA: // because 1, because "WASD" on _MOV (depending on setup) // 0x2460, "1" : ① // 0x2B05, arrow left: ⬅ if (record->event.pressed) { // key down unicode_hex2output (CN_1CIRC, CS_ARR_LE);// ① ⬅ } break; case XP_BON_BB: // because 2, because "WASD" on _MOV (depending) // 0x2461, "2" : ② // 0x2B07, arrow down: ⬇ if (record->event.pressed) { // key down unicode_hex2output (CN_2CIRC, CS_ARR_DN);// ② ⬇ } break; case XP_BON_BC: // because 3, because "WASD" on _MOV (depending) // 0x2462, "3" : ③ // 0x27A1, arrow right: ➡ if (record->event.pressed) { // key down unicode_hex2output (CN_3CIRC, CS_ARR_RI);// ③ ➡ } break; case XP_BON_BD: // because 4, because ┏ forms a box with the other box drawings to the right/down // 0x2463, "4" : ④ // 0x250F, box drawing heavy: ┏ if (record->event.pressed) { // key down # ifdef BOX_DRAWINGS unicode_hex2output (CN_4CIRC, CB_C_RIDN);// ④ ┏ # else unicode_hex2output_single (CN_4CIRC);// ④ # endif } break; case XP_BON_BE: // because 5, because ┓ forms a box // 0x2513,box drawing heavy: ┓ if (record->event.pressed) { // key down # ifdef BOX_DRAWINGS unicode_hex2output (CN_5CIRC, CB_C_LEDN);// ⑤ ┓ # else unicode_hex2output_single (CN_5CIRC);// ⑤ # endif } break; case XP_BON_BF: // because 6, because ┃ continues box block // 0x2465, "6" : ⑥ // 0x2503, box drawing heavy: ┃ if (record->event.pressed) { // key down # ifdef BOX_DRAWINGS unicode_hex2output (CN_6CIRC, CB_VE);// ⑥ ┃ # else unicode_hex2output_single (CN_6CIRC);// ⑥ # endif } break; case XP_BON_BG: // because 7, because ┇ continues box block // 0x2466, "7" : ⑦ // 0x2507, dotted line verticle (heavy): ┇ if (record->event.pressed) { // key down # ifdef BOX_DRAWINGS unicode_hex2output (CN_7CIRC, CB_VE_DOT);// ⑦ ┇ # else unicode_hex2output_single (CN_7CIRC);// ⑦ # endif } break; case XP_BON_BH: // because 8, ╋ because 8 also has a crossing line in it // 0x254B, crossing lines: ╋ // 0x2467, "8" : ⑨ if (record->event.pressed) { // key down # ifdef BOX_DRAWINGS unicode_hex2output (CN_8CIRC, CB_VE_BI);// ⑧ ╋ # else unicode_hex2output_single (CN_8CIRC);// ⑧ # endif } break; case XP_BON_BI: // because 9 // 0x2468, "9" : ⑨ // 0x2513,box drawing heavy: ┓ if (record->event.pressed) { // key down unicode_hex2output_single (CN_9CIRC);// ⑨ } break; case XP_BON_BJ: // because 0, because a "0" can also be a symbol for infinity, round & round // 0x24EA, "0" : ⓪ // 0x221E,// infinity:∞ if (record->event.pressed) { // key down unicode_hex2output (CN_0CIRC, CS_INFIN);// ⓪ ∞ } break; case XP_BON_BK: // because -, because ~ // 0x2014,// dash: — (might not render differently than a hyphen - in some applications. Dash is longer). // 0x2248,// about equal to: ≈ if (record->event.pressed) { // key down unicode_hex2output (CS_DASH, CS_ABOUT);// — ≈ } break; // ------------------------- row 2 case XP_BON_CA: // because 1 above, because 「[ // 0x2039, opening single corner quotation: ‹ if (record->event.pressed) { // key down unicode_hex2output_single (CS_GUILSLE);// ‹ } break; case XP_BON_CB: // because 2 above, because 」] // 0x203A, closing sinle corner quotation: › if (record->event.pressed) { // key down unicode_hex2output_single (CS_GUILSRI);// › } break; case XP_BON_CC: // because 3 above, because / (division) // 0x00F7,// division: ÷ if (record->event.pressed) { // key down unicode_hex2output_single (CS_DIVI);// ÷ } break; case XP_BON_CD: // because 4 above, ┗ because forms box // 0x261E, hand bullet point: ☞ // 0x2517, box drawing heavy: ┗ if (record->event.pressed) { // key down # ifdef BOX_DRAWINGS unicode_hex2output (CS_FINGER, CB_C_RIUP);// ☞ ┗ # else unicode_hex2output_single (CS_FINGER);// ☞ # endif } break; case XP_BON_CE: // because 5 above, because =, ┛ because forms box // 0x2260,// inequal: ≠ // 0x251B, box drawing heavy: ┛ if (record->event.pressed) { // key down # ifdef BOX_DRAWINGS unicode_hex2output (CS_UNEQL, CB_C_LEUP);// ≠ ┛ # else unicode_hex2output_single (CS_UNEQL);// ≠ # endif } break; case XP_BON_CF: // because ☒ , ┣ box drawings block, some place // 0x2717, cross mark: ✗ (complements ✓) // 0x2523, box drawing: ┣ if (record->event.pressed) { // key down # ifdef BOX_DRAWINGS unicode_hex2output (CS_BOTCH, CB_VE_RI);// ✗ ┣ # else unicode_hex2output_single (CS_BOTCH);// ✗ # endif } break; case XP_BON_CG: // because 7 above, because ☑ 🗹 , ┫ complements with key to its left // 0x2713, checkmark: ✓ // 0x252B, box drawing: ┫ if (record->event.pressed) { // key down # ifdef BOX_DRAWINGS unicode_hex2output (CS_CHECK, CB_VE_LE);// ✓ ┫ # else unicode_hex2output_single (CS_CHECK);// ✓ # endif } break; case XP_BON_CH: // because 8 above, because ¡ (inverted exclamation mark) // 0x26A0,// alert: ⚠ if (record->event.pressed) { // key down unicode_hex2output_single (CS_ALERT);// ⚠ } break; case XP_BON_CI: // because 9 above, because 《 // 0xAB, French quotation opening: « if (record->event.pressed) { // key down unicode_hex2output_single (CS_GUILLE);// « } break; case XP_BON_CJ: // because 0 above, because 》 // 0xBB, French quotation closing: » if (record->event.pressed) { // key down unicode_hex2output_single (CS_GUILRI);// » } break; # endif // REMOVE_BON } return true; };