context_magic is working.

This commit is contained in:
Matt Skalecki 2024-02-23 12:16:44 -05:00
commit e5acbb294a
19 changed files with 687 additions and 687 deletions

View file

@ -3,4 +3,4 @@
#include QMK_KEYBOARD_H #include QMK_KEYBOARD_H
#include "general/custom_keys.h" #include "general/custom_keys.h"
#include "magic_sturdy/__init__.h" #include "magic_sturdy/context_magic.h"

View file

@ -23,3 +23,5 @@
// User Added // User Added
#define COMBO_COUNT 10 #define COMBO_COUNT 10
#define TAPPING_TOGGLE 2 #define TAPPING_TOGGLE 2
#define USER_PRINT
#define DEBUG

View file

@ -109,17 +109,21 @@ combo_t key_combos[COMBO_COUNT] = {
COMBO(combo_RB_IR, US_QUOT_S), COMBO(combo_RB_IR, US_QUOT_S),
}; };
extern rgb_config_t rgb_matrix_config; // extern rgb_config_t rgb_matrix_config;
void keyboard_post_init_user(void) { // void keyboard_post_init_user(void) {
rgb_matrix_enable(); // rgb_matrix_enable();
} // }
bool process_record_user(uint16_t keycode, keyrecord_t *record) { bool process_record_user(uint16_t keycode, keyrecord_t *record) {
bool return_value; // bool return_value;
if (sturdy_pr(keycode, record, &return_value)) // if (sturdy_pr(keycode, record, &return_value))
return return_value; // return return_value;
uprintf("Process_record_user for keycode: %d", keycode);
if (!process_context_magic(keycode, record))
return false;
if (record->event.pressed) { if (record->event.pressed) {
switch (keycode) { switch (keycode) {

View file

@ -1,55 +0,0 @@
// Originally from QKekos
#pragma once
#include "../__init__.h"
#include "general.h"
#include "magic_keys.h"
#define single_queue_check(p_key) queue(-1) == p_key
#define double_queue_check(pp_key, p_key) queue(-2) == pp_key && single_queue_check(p_key)
#define triple_queue_check(ppp_key, pp_key, p_key) queue(-3) == ppp_key && double_queue_check(pp_key, p_key)
#define quadruple_queue_check(pppp_key, ppp_key, pp_key, p_key) queue(-4) == pppp_key && triple_queue_check(ppp_key, pp_key, p_key)
#define magic_case_core(trigger, condition, supplement) \
case trigger: \
if (condition) { \
record_send_string(supplement); \
return; \
} \
break
#define magic_switch_core(trigger, body, index) \
case trigger: \
switch (queue(index)) { \
body \
} \
break
#define magic_case(trigger, supplement) \
case trigger: \
record_send_string(supplement); \
return
#define double_magic_switch(trigger, body) \
magic_switch_core(trigger, body, -1)
#define triple_magic_switch(trigger, body) \
magic_switch_core(trigger, body, -2)
#define quadruple_magic_switch(trigger, body) \
magic_switch_core(trigger, body, -3)
#define quintuple_magic_switch(trigger, body) \
magic_switch_core(trigger, body, -4)
#define double_magic_case(trigger, p_key, supplement) \
magic_case_core(trigger, single_queue_check(p_key), supplement)
#define triple_magic_case(trigger, pp_key, p_key, supplement) \
magic_case_core(trigger, double_queue_check(pp_key, p_key), supplement)
#define quadruple_magic_case(trigger, ppp_key, pp_key, p_key, supplement) \
magic_case_core(trigger, triple_queue_check(ppp_key, pp_key, p_key), supplement)
#define quintuple_magic_case(trigger, pppp_key, ppp_key, pp_key, p_key, supplement) \
magic_case_core(trigger, quadruple_queue_check(pppp_key, ppp_key, pp_key, p_key), supplement)

View file

@ -0,0 +1,396 @@
// Copyright 2021 Google LLC
// Copyright 2021 @filterpaper
// Copyright 2023 Pablo Martinez (@elpekenin) <elpekenin@elpekenin.dev>
// Copyright 2024 Guillaume Stordeur <guillaume.stordeur@gmail.com>
// SPDX-License-Identifier: Apache-2.0
// Original source: https://getreuer.info/posts/keyboards/autocorrection
#include "context_magic.h"
#include <string.h>
#include "keycodes.h"
#include "quantum.h"
#include "quantum_keycodes.h"
#include "keycode_config.h"
#include "send_string.h"
#include "action_util.h"
#include "magickey_data.h"
#include "../general/custom_keys.h"
#include "print.h"
#include "utils.h"
// todo: compute max in script
#define CONTEXT_MAGIC_MAX_LENGTH MAGICKEY_MAX_LENGTH
#define MAGIC_DICTIONARY_SIZE DICTIONARY_SIZE
#define TDATA(L) pgm_read_word(&trie->data[L])
#define CDATA(L) pgm_read_byte(&trie->completions[L])
#define IS_ALPHA_KEYCODE(code) ((code) >= KC_A && (code) <= KC_Z)
//////////////////////////////////////////////////////////////////////////////////////////
// Add KC_SPC on timeout
#if MAGIC_IDLE_TIMEOUT > 0
static uint32_t magic_timer = 0;
void context_magic_task(void)
{
if (timer_elapsed32(magic_timer) > MAGIC_IDLE_TIMEOUT) {
reset_buffer();
magic_timer = timer_read32();
}
}
#endif
//////////////////////////////////////////////////////////////////
// Key history buffer
static uint16_t key_buffer[CONTEXT_MAGIC_MAX_LENGTH] = {KC_SPC};
static uint16_t key_buffer_size = 1;
//////////////////////////////////////////////////////////////////
// List of tries
static trie_t trie = {
MAGIC_DICTIONARY_SIZE, magickey_data, COMPLETIONS_SIZE, magickey_completions_data
};
/**
* @brief determine if context_magic should process this keypress,
* and remove any mods from keycode.
*
* @param keycode Keycode registered by matrix press, per keymap
* @param record keyrecord_t structure
* @param mods allow processing of mod status
* @return true Allow context_magic
* @return false Stop processing and escape from context_magic.
*/
bool process_check(uint16_t *keycode, keyrecord_t *record, uint8_t *mods)
{
// See quantum_keycodes.h for reference on these matched ranges.
switch (*keycode) {
// Exclude these keycodes from processing.
case KC_LSFT:
case KC_RSFT:
case KC_CAPS:
case QK_TO ... QK_TO_MAX:
case QK_MOMENTARY ... QK_MOMENTARY_MAX:
case QK_DEF_LAYER ... QK_DEF_LAYER_MAX:
case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:
case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:
case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
case QK_LAYER_MOD ... QK_LAYER_MOD_MAX:
case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX:
return false;
// Mask for base keycode from shifted keys.
case QK_LSFT ... QK_LSFT + 255:
case QK_RSFT ... QK_RSFT + 255:
if (*keycode >= QK_LSFT && *keycode <= (QK_LSFT + 255)) {
*mods |= MOD_LSFT;
} else {
*mods |= MOD_RSFT;
}
*keycode = QK_MODS_GET_BASIC_KEYCODE(*keycode); // Get the basic keycode.
return true;
#ifndef NO_ACTION_TAPPING
// Exclude tap-hold keys when they are held down
// and mask for base keycode when they are tapped.
case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
# ifdef NO_ACTION_LAYER
// Exclude Layer Tap, if layers are disabled
// but action tapping is still enabled.
return false;
# else
// Exclude hold keycode
if (!record->tap.count) {
return false;
}
*keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(*keycode);
break;
# endif
case QK_MOD_TAP ... QK_MOD_TAP_MAX:
// Exclude hold keycode
if (!record->tap.count) {
return false;
}
*keycode = QK_MOD_TAP_GET_TAP_KEYCODE(*keycode);
break;
#else
case QK_MOD_TAP ... QK_MOD_TAP_MAX:
case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
// Exclude if disabled
return false;
#endif
// Exclude swap hands keys when they are held down
// and mask for base keycode when they are tapped.
case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:
#ifdef SWAP_HANDS_ENABLE
// Note: IS_SWAP_HANDS_KEYCODE() actually tests for the special action keycodes like SH_TOGG, SH_TT, ...,
// which currently overlap the SH_T(kc) range.
if (IS_SWAP_HANDS_KEYCODE(*keycode)
# ifndef NO_ACTION_TAPPING
|| !record->tap.count
# endif // NO_ACTION_TAPPING
) {
return false;
}
*keycode = QK_SWAP_HANDS_GET_TAP_KEYCODE(*keycode);
break;
#else
// Exclude if disabled
return false;
#endif
}
// Disable autocorrect while a mod other than shift is active.
if ((*mods & ~MOD_MASK_SHIFT) != 0) {
reset_buffer();
return false;
}
return true;
}
/**
* @brief Add keycode to our key buffer.
*
* @param keycode Keycode registered by matrix press, per keymap
*/
void enqueue_keycode(uint16_t keycode)
{
// Store all alpha chars as lowercase
const bool shifted = keycode & QK_LSFT;
const uint8_t lowkey = keycode & 0xFF;
if (shifted && IS_ALPHA_KEYCODE(lowkey))
keycode = lowkey;
// Rotate oldest character if buffer is full.
if (key_buffer_size >= CONTEXT_MAGIC_MAX_LENGTH) {
memmove(key_buffer, key_buffer + 1, sizeof(uint16_t) * (CONTEXT_MAGIC_MAX_LENGTH - 1));
key_buffer_size = CONTEXT_MAGIC_MAX_LENGTH - 1;
}
key_buffer[key_buffer_size++] = keycode;
}
//////////////////////////////////////////////////////////////////
void reset_buffer(void)
{
key_buffer[0] = KC_SPC;
key_buffer_size = 1;
}
/**
* @brief Remove num keys from our buffer.
*
* @param num number of keys to remove
*/
void dequeue_keycodes(uint8_t num)
{
key_buffer_size -= MIN(num, key_buffer_size);
if (!key_buffer_size)
reset_buffer();
}
/**
* @brief Find longest chain in trie matching our current key_buffer.
*
* @param trie trie_t struct containing trie data/size
* @param res result containing current best
* @param offset current offset in trie data
* @param depth current depth in trie
* @return true if match found
*/
bool find_longest_chain(trie_t *trie, trie_search_result_t *res, int offset, int depth)
{
// Sanity checks
if (offset >= trie->data_size) {
uprintf("find_longest_chain() Error: tried reading outside trie data! Offset: %d", offset);
return false;
}
uint16_t code = TDATA(offset);
if (!code) {
uprintf("find_longest_chain() Error: unexpected null code! Offset: %d", offset);
return false;
}
uprintf("FIND_LONGEST_CHAIN (%d, %d)", offset, code);
// Match Node if bit 15 is set
if (code & 0x8000) {
uprintf("Match found at Offset: %d", offset);
// match nodes are side attachments, so decrease depth
depth--;
// If bit 14 is also set, there is a child node after the completion string
if ((code & 0x4000) && find_longest_chain(trie, res, offset+2, depth+1)) {
uprintf("Looking for deeper match at Offset: %d", offset+2);
return true;
}
// If no better match found deeper, this is the result!
// char buf[20];
// sprintf(buf, "|0x%04x|", code);
// send_string(buf);
res->completion_offset = TDATA(offset + 1);
res->func_code = (code >> 11 & 0x0007);
res->num_backspaces = (code & 0x003F);
// Found a match so return true!
return true;
}
// Branch Node (with multiple children) if bit 14 is set
if (code & 0x4000) {
if (depth > key_buffer_size)
return false;
code &= 0x3FFF;
// Find child that matches our current buffer location
const uint16_t cur_key = key_buffer[key_buffer_size - depth];
for (; code; offset += 2, code = TDATA(offset)) {
if (code == cur_key) {
// 16bit offset to child node is built from next uint16_t
const int child_offset = TDATA(offset+1);
// Traverse down child node
return find_longest_chain(trie, res, child_offset, depth+1);
}
}
// Couldn't go deeper, so return false.
return false;
}
// No high bits set, so this is a chain node
// Travel down chain until we reach a zero byte, or we no longer match our buffer
for (; code; depth++, code = TDATA(++offset)) {
if (depth > key_buffer_size ||
code != key_buffer[key_buffer_size - depth])
return false;
}
// After a chain, there should be a leaf or branch
return find_longest_chain(trie, res, offset+1, depth);
}
//////////////////////////////////////////////////////////////////////////////////////////
void record_send_key(uint16_t keycode)
{
enqueue_keycode(keycode);
// Apply shift to sent key if caps word is enabled.
#ifdef CAPS_WORD_ENABLED
if (is_caps_word_on() && IS_ALPHA_KEYCODE(keycode))
add_weak_mods(MOD_BIT(KC_LSFT));
#endif
tap_code16(keycode);
}
//////////////////////////////////////////////////////////////////////////////////////////
void handle_repeat_key()
{
uint16_t keycode = KC_NO;
for (int i = key_buffer_size - 1; i >= 0; --i) {
keycode = key_buffer[i];
if (!(keycode & 0x0100)) {
break;
}
}
if (keycode && !(keycode & 0x0100)) {
dequeue_keycodes(1);
enqueue_keycode(keycode);
// Apply shift to sent key if caps word is enabled.
#ifdef CAPS_WORD_ENABLED
if (is_caps_word_on() && IS_ALPHA_KEYCODE(keycode))
add_weak_mods(MOD_BIT(KC_LSFT));
#endif
tap_code16(keycode);
}
}
//////////////////////////////////////////////////////////////////////////////////////////
void handle_result(trie_t *trie, trie_search_result_t *res)
{
// Send backspaces
multi_tap(KC_BSPC, res->num_backspaces);
// Send completion string
for (int i = res->completion_offset; i < trie->completions_size; ++i) {
char ascii_code = CDATA(i);
if (!ascii_code) {
break;
}
tap_code16(char_to_keycode(ascii_code));
}
switch (res->func_code) {
case 1: // repeat
handle_repeat_key();
return;
case 2: // set one-shot shift
set_oneshot_mods(MOD_LSFT);
return;
}
}
/**
* @brief Handles magic/repeat key press
*
* @param keycode Keycode registered by matrix press, per keymap
* @return false if keycode isn't a registered magic key
*/
bool perform_magic()
{
// Do nothing if key buffer is empty
if (!key_buffer_size)
return false;
// Look for chain matching our buffer in the trie.
trie_search_result_t res = {0, 0};
if (find_longest_chain(&trie, &res, 0, 1)) {
handle_result(&trie, &res);
return true;
}
return false;
}
/**
* @brief Process handler for context_magic feature.
*
* @param keycode Keycode registered by matrix press, per keymap
* @param record keyrecord_t structure
* @return true Continue processing keycodes, and send to host
* @return false Stop processing keycodes, and don't send to host
*/
bool process_context_magic(uint16_t keycode, keyrecord_t *record)
{
uprintf("Process_context_magic for keycode: %d", keycode);
#if MAGIC_IDLE_TIMEOUT > 0
magic_timer = timer_read32();
#endif
uint8_t mods = get_mods();
#ifndef NO_ACTION_ONESHOT
mods |= get_oneshot_mods();
#endif
if (!record->event.pressed)
return true;
// keycode verification and extraction
if (!process_check(&keycode, record, &mods))
return true;
// keycode buffer check
switch (keycode) {
case US_MAG1 ... US_MAG4:
// convert to special 0x01nn code
keycode = 0x0100 + keycode - US_MAG1;
break;
case KC_A ... KC_0:
// process normally
break;
case KC_MINUS ...KC_SLASH:
// treat " (shifted ') as a word boundary
if (keycode == KC_QUOTE && (mods & MOD_MASK_SHIFT) != 0)
keycode = KC_SPC;
break;
case S(KC_MINUS) ...S(KC_SLASH):
// treat " (shifted ') as a word boundary
if (S(KC_QUOTE))
keycode = KC_SPC;
break;
case KC_BSPC:
// remove last character from the buffer
reset_buffer();
return true;
default:
// set word boundary if some other non-alpha key is pressed
keycode = KC_SPC;
}
// append `keycode` to buffer
enqueue_keycode(keycode);
uprintf(" translated keycode: %d", keycode);
// perform magic action if this is one of our registered keycodes
if (perform_magic())
return false;
return true;
}

View file

@ -0,0 +1,45 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "action.h"
//////////////////////////////////////////////////////////////////
// Public API
bool process_context_magic(uint16_t keycode, keyrecord_t *record);
#if MAGIC_IDLE_TIMEOUT > 0
void context_magic_task(void);
#else
static inline void context_magic_task(void) {}
#endif
//////////////////////////////////////////////////////////////////
// Internal
typedef void (*trie_fallback)(void);
typedef struct
{
int data_size;
const uint16_t *data;
int completions_size;
const uint8_t *completions;
} trie_t;
typedef struct
{
uint8_t func_code;
uint8_t num_backspaces;
int completion_offset;
} trie_search_result_t;
// trie_t *get_trie(uint16_t keycode);
bool process_check(uint16_t *keycode, keyrecord_t *record, uint8_t *mods);
void enqueue_keycode(uint16_t keycode);
void reset_buffer(void);
void dequeue_keycodes(uint8_t num);
bool find_longest_chain(trie_t *trie, trie_search_result_t *res, int offset, int depth);
void record_send_key(uint16_t keycode);
void repeat_key_fallback(void);
void handle_repeat_key(void);
void handle_result(trie_t *trie, trie_search_result_t *res);
bool perform_magic(void);

View file

@ -1,181 +0,0 @@
#include "magic_sturdy/general.h"
#include "../__init__.h"
int mag1_key_count = 0;
int mag2_key_count = 0;
int mag3_key_count = 0;
int mag4_key_count = 0;
int last_key_pressed_time = 0;
int prev_key_time;
int prev_keys_queue[PREV_KEYS_QUEUE_SIZE] = {KC_NO};
deferred_token magic_timeout_token = INVALID_DEFERRED_TOKEN;
uint32_t enqueue_space(uint32_t trigger_time, void *cb_arg) {
enqueue(KC_SPC);
return 0;
}
void refresh_token(void) {
if (magic_timeout_token != INVALID_DEFERRED_TOKEN)
cancel_deferred_exec(magic_timeout_token);
magic_timeout_token = defer_exec(MAGIC_KEY_TIMEOUT, enqueue_space, NULL);
}
void record_send_string(const char *str) {
for (int i = 0; str[i] != '\0'; i++) {
if (str[i] == 8) dequeue(); // dequeue when sending backspace
else if (65 <= str[i] && str[i] <= 90) enqueue(str[i] - 61);
else if (97 <= str[i] && str[i] <= 122) enqueue(str[i] - 93);
}
SEND_STRING(str);
}
void enqueue(int keycode) {
for (int i = 0; i < PREV_KEYS_QUEUE_SIZE - 1; i += 1)
prev_keys_queue[i] = prev_keys_queue[i + 1];
prev_keys_queue[PREV_KEYS_QUEUE_SIZE - 1] = keycode;
}
void dequeue(void) {
set_last_keycode(prev_keys_queue[PREV_KEYS_QUEUE_SIZE - 1]);
for (int i = PREV_KEYS_QUEUE_SIZE - 1; i > 0; i -= 1)
prev_keys_queue[i] = prev_keys_queue[i - 1];
prev_keys_queue[0] = KC_NO;
}
void print_queue(void) {
uprintf("queue: ");
for (int i = 0; i < PREV_KEYS_QUEUE_SIZE - 1; i += 1)
uprintf("%d, ", prev_keys_queue[i]);
uprintf("\n");
}
uint16_t normalize_keycode(uint16_t keycode) {
if (IS_QK_MOD_TAP(keycode)) return QK_MOD_TAP_GET_TAP_KEYCODE(keycode);
if (IS_QK_LAYER_TAP(keycode)) return QK_LAYER_TAP_GET_TAP_KEYCODE(keycode);
return keycode;
}
bool remember_last_key_user(uint16_t keycode, keyrecord_t* record, uint8_t* mods) {
keycode = normalize_keycode(keycode);
switch (keycode) {
case KC_BSPC:
case KC_LEFT:
dequeue();
return false;
}
if (
(*mods & MOD_MASK_CTRL) &&
((keycode == KC_BSPC && record->tap.count) || (keycode == KC_BSPC))
) keycode = KC_SPC;
switch (keycode) {
case KC_ENT:
case KC_TAB:
case KC_BSPC:
case LCTL(KC_BSPC):
case KC_LEFT:
case KC_DQUO:
case KC_LPRN:
case KC_SPC:
keycode = KC_SPC;
break;
case KC_A ... KC_Z:
if ((*mods & ~(MOD_MASK_SHIFT)) == 0) {
*mods &= ~MOD_MASK_SHIFT;
}
break;
case US_MAG1:
case US_MAG2:
case US_MAG3:
case US_MAG4:
return false;
}
enqueue(keycode);
print_queue();
return true;
}
bool sturdy_pr(uint16_t keycode, keyrecord_t *record, bool *return_value) {
*return_value = false;
prev_key_time = last_key_pressed_time;
if (record->event.pressed) {
last_key_pressed_time = timer_read();
refresh_token();
if (keycode != US_MAG1) mag1_key_count = 0;
else mag1_key_count += 1;
if (keycode != US_MAG2) mag2_key_count = 0;
else mag2_key_count += 1;
if (keycode != US_MAG4) mag3_key_count = 0;
else mag3_key_count += 1;
if (keycode != US_MAG4) mag4_key_count = 0;
else mag4_key_count += 1;
}
switch (keycode) {
case US_MAG1:
if (record->event.pressed)
process_magic_key_1();
return true;
case US_MAG2:
if (record->event.pressed)
process_magic_key_2();
return true;
case US_MAG3:
if (record->event.pressed)
process_magic_key_3();
return true;
case US_MAG4:
if (record->event.pressed)
process_magic_key_4();
return true;
// case KC_B:
// case KC_Z:
// case KC_F:
// return process_double_tap(keycode, record);
}
return false;
}
// bool process_double_tap(uint16_t keycode, keyrecord_t *record) {
// if (
// !record->event.pressed ||
// highest_layer != STURDY ||
// queue(-2) != keycode ||
// timer_elapsed(prev_key_time) > (get_tapping_term(keycode, NULL) + 50)
// ) return false;
// dequeue();
// process_magic_key();
// return true;
// }

View file

@ -1,32 +0,0 @@
// Originally from QKekos
#pragma once
#include "../__init__.h"
#define PREV_KEYS_QUEUE_SIZE 10
#define MAGIC_KEY_TIMEOUT 2000
bool sturdy_pr(uint16_t keycode, keyrecord_t *record, bool *return_value);
bool remember_last_key_user(uint16_t keycode, keyrecord_t* record, uint8_t* mods);
bool process_double_tap(uint16_t keycode, keyrecord_t *record);
void record_send_string(const char *str);
void enqueue(int keycode);
void dequeue(void);
void print_queue(void);
uint32_t enqueue_space(uint32_t trigger_time, void *cb_arg);
void refresh_token(void);
extern int prev_keys_queue[];
extern int mag1_key_count;
extern int mag2_key_count;
extern int mag3_key_count;
extern int mag4_key_count;
extern int last_key_pressed_time;
#define queue(i) prev_keys_queue[PREV_KEYS_QUEUE_SIZE + i]
#define record_case(symbol, keycode) \
case symbol: \
enqueue(keycode); \
continue

View file

@ -1,361 +0,0 @@
#include "../__init__.h"
#include "keycodes.h"
#include "keymap_us.h"
#include "magic_sturdy/__init__.h"
#include "magic_sturdy/general.h"
void process_magic_key_1(void) {
if (mag1_key_count >= 2) {
switch (queue(-5)) {
quintuple_magic_case(KC_J, KC_U, KC_D, KC_G, KC_E, "\bment");
}
switch (queue(-4)) {
quadruple_magic_case(KC_SPC, KC_A, KC_L, KC_R, "eady");
quadruple_magic_switch(KC_J,
triple_magic_switch(KC_U,
double_magic_case(KC_D, KC_G, "ment");
double_magic_case(KC_S, KC_T, "ment");
);
);
}
switch (queue(-3)) {
triple_magic_case(KC_A, KC_B, KC_O, "ve");
default: record_send_string("n"); return;
}
}
switch (queue(-5)) {
quintuple_magic_switch(KC_B,
quadruple_magic_case(KC_A, KC_L, KC_L, KC_I, "st");
quadruple_magic_case(KC_L, KC_I, KC_V, KC_I, "on");
);
}
switch (queue(-4)) {
quadruple_magic_case(KC_A, KC_B, KC_E, KC_T, "ment");
quadruple_magic_case(KC_V, KC_I, KC_L, KC_I, "on");
}
switch (queue(-3)) {
triple_magic_switch(KC_SPC,
double_magic_switch(KC_A,
magic_case(KC_L, "r");
magic_case(KC_N, "other");
);
double_magic_switch(KC_E,
magic_case(KC_D, "ge");
magic_case(KC_X, "ample");
);
double_magic_switch(KC_T,
magic_case(KC_A, "ke");
magic_case(KC_I, "me");
);
double_magic_case(KC_M, KC_A, "ke");
double_magic_case(KC_U, KC_S, "e");
double_magic_case(KC_I, KC_N, "form");
);
triple_magic_switch(KC_B,
double_magic_switch(KC_E,
magic_case(KC_T, "ween");
magic_case(KC_L, "ieve");
);
double_magic_case(KC_U, KC_D, "ge");
);
triple_magic_switch(KC_V,
double_magic_switch(KC_A,
magic_case(KC_R, "ious");
);
double_magic_case(KC_I, KC_D, "eo");
);
triple_magic_switch(KC_R,
double_magic_case(KC_I, KC_D, "ge");
double_magic_case(KC_E, KC_D, "ge");
);
triple_magic_switch(KC_S,
double_magic_case(KC_Y, KC_S, "tem");
);
triple_magic_switch(KC_G,
double_magic_case(KC_E, KC_N, "ious");
);
triple_magic_switch(KC_D,
double_magic_case(KC_I, KC_F, "feren");
double_magic_case(KC_E, KC_F, "inite");
);
triple_magic_switch(KC_L,
double_magic_case(KC_E, KC_D, "ge");
double_magic_case(KC_L, KC_I, "on");
);
triple_magic_switch(KC_A,
double_magic_case(KC_N, KC_Y, "way");
);
triple_magic_case(KC_J, KC_U, KC_D, "ge");
triple_magic_case(KC_O, KC_B, KC_V, "ious");
triple_magic_case(KC_H, KC_I, KC_L, "arious");
}
switch (queue(-2)) {
double_magic_switch(KC_SPC,
magic_case(KC_T, "han");
magic_case(KC_L, "ittle");
);
double_magic_switch(KC_P,
magic_case(KC_L, "y");
magic_case(KC_A, "ge");
magic_case(KC_E, "ople");
magic_case(KC_R, "evious");
);
double_magic_switch(KC_D,
magic_case(KC_A, "y");
magic_case(KC_R, "y");
);
double_magic_case(KC_W, KC_A, "y");
double_magic_case(KC_B, KC_E, "en");
double_magic_case(KC_L, KC_I, "st");
double_magic_case(KC_V, KC_I, "sion");
double_magic_case(KC_A, KC_B, "ility");
double_magic_case(KC_I, KC_B, "ility");
}
switch (queue(-1)) {
magic_case(KC_SPC, "the");
magic_case(KC_V, "er");
magic_case(KC_S, "k");
magic_case(KC_X, "es");
magic_case(KC_M, "ent");
magic_case(KC_T, "ment");
magic_case(KC_K, "s");
magic_case(KC_L, "k");
magic_case(KC_R, "l");
magic_case(KC_J, "ust");
magic_case(KC_C, "y");
magic_case(KC_D, "y");
magic_case(KC_G, "y");
magic_case(KC_P, "y");
magic_case(KC_Y, "p");
magic_case(KC_W, "hich");
magic_case(KC_Q, "uestion");
magic_case(KC_B, "efore");
magic_case(KC_F, "irst");
magic_case(KC_Z, "one");
// US_MAG1
magic_case(KC_N, "ion");
magic_case(KC_H, "owever");
magic_case(KC_U, "e");
magic_case(KC_E, "u");
// KC_QUOT
magic_case(KC_O, "a");
magic_case(KC_A, "bo");
// KC_QUES
magic_case(KC_COMM, get_last_mods() & MOD_MASK_SHIFT ? "=" : " but");
magic_case(KC_I, "on");
magic_case(KC_DOT, get_last_mods() & MOD_MASK_SHIFT ? "=" : "\\");
magic_case(KC_MINS, ">");
}
}
void process_magic_key_2(void) {
switch (queue(-5)) {
quintuple_magic_switch(KC_SPC,
quadruple_magic_case(KC_M, KC_A, KC_K, KC_E, "\bing");
quadruple_magic_case(KC_T, KC_A, KC_K, KC_E, "\bing");
);
}
switch (queue(-4)) {
quadruple_magic_switch(KC_SPC,
triple_magic_case(KC_A, KC_L, KC_R, "ight");
triple_magic_case(KC_R, KC_E, KC_V, "iew");
triple_magic_switch(KC_U,
double_magic_switch(KC_N,
magic_case(KC_F, "ollow");
magic_case(KC_P, "roblem");
);
);
);
}
switch (queue(-3)) {
triple_magic_switch(KC_SPC,
double_magic_case(KC_O, KC_X, "ygen");
double_magic_case(KC_S, KC_C, "hool");
double_magic_case(KC_P, KC_S, "ych");
);
triple_magic_switch(KC_A,
double_magic_case(KC_B, KC_O, "ut");
double_magic_case(KC_N, KC_Y, "where");
);
triple_magic_case(KC_R, KC_S, KC_C, "hool");
triple_magic_case(KC_I, KC_T, KC_Y, "\bies");
}
switch (queue(-2)) {
double_magic_switch(KC_SPC,
magic_case(KC_V, "iew");
magic_case(KC_S, "hould");
// KC_X, "er"
magic_case(KC_M, "ight");
magic_case(KC_T, "hrough");
// KC_K, "now"
magic_case(KC_L, "ight");
magic_case(KC_R, "ight");
// KC_J, "oin"
magic_case(KC_C, "ould");
magic_case(KC_D, "on't");
magic_case(KC_G, "eneral");
magic_case(KC_P, "roblem");
// KC_Y, "ou"
// KC_W, "ould"
magic_case(KC_B, "ecause");
magic_case(KC_F, "ollow");
magic_case(KC_Z, "ero");
// KC_MAG1
magic_case(KC_N, "umber");
magic_case(KC_H, "owever");
magic_case(KC_U, "pdate");
magic_case(KC_E, "nough");
// KC_QUOT
magic_case(KC_O, "ften");
// KC_A, "nd"
// KC_QUES, " " OSS
// KC_COMM, " and"
magic_case(KC_I, "ncrease");
// KC_DOT, " " OSS
);
double_magic_case(KC_F, KC_R, "om");
double_magic_case(KC_A, KC_U, "ght");
double_magic_case(KC_O, KC_U, "ght");
}
switch (queue(-1)) {
magic_case(KC_SPC, "for");
magic_case(KC_A, "nd");
magic_case(KC_X, "er");
magic_case(KC_K, "now");
magic_case(KC_I, "ng");
magic_case(KC_Y, "ou");
magic_case(KC_Q, "uick");
magic_case(KC_J, "oin");
magic_case(KC_W, "ould");
magic_case(KC_C, "k");
magic_case(KC_N, "f");
magic_case(KC_H, "n");
magic_case(KC_COMMA, " and");
case KC_DOT:
case KC_QUES:
case KC_EXLM:
case KC_COLN:
case KC_SCLN:
unregister_weak_mods(MOD_MASK_CSAG);
send_char(' ');
add_oneshot_mods(MOD_MASK_SHIFT);
enqueue(KC_SPC);
return;
default: tap_code16(queue(-1)); enqueue(queue(-1));
}
}
void process_magic_key_3(void) {
switch (queue(-1)) {
magic_case(KC_V, "isit");
magic_case(KC_S, "mall");
// KC_X
magic_case(KC_M, "anage");
magic_case(KC_T, "hough");
magic_case(KC_K, "new");
magic_case(KC_L, "ocation");
magic_case(KC_R, "ecommend");
magic_case(KC_J, "udge");
magic_case(KC_C, "onsider");
magic_case(KC_D, "evelop");
magic_case(KC_G, "overn");
magic_case(KC_P, "ower");
// KC_Y
magic_case(KC_W, "here");
magic_case(KC_Q, "mlativ");
magic_case(KC_B, "ecome");
magic_case(KC_F, "ound");
// KC_Z
// KC_MAG1
magic_case(KC_N, "eighbor");
magic_case(KC_H, "appen");
magic_case(KC_U, "pgrade");
magic_case(KC_E, "'ll");
// KC_QUOT
magic_case(KC_O, "ther");
magic_case(KC_A, "fter");
// KC_QUES
magic_case(KC_COMM, " however");
magic_case(KC_I, "'ll");
magic_case(KC_DOT, "org");
default: return;
}
}
void process_magic_key_4(void) {
switch (queue(-1)) {
magic_case(KC_B, "etween");
magic_case(KC_N, "ation");
magic_case(KC_U, "nivers");
magic_case(KC_O, "ption");
magic_case(KC_A, "gainst");
magic_case(KC_P, "rogram");
magic_case(KC_I, "'m");
magic_case(KC_T, "echnology");
magic_case(KC_C, "rowd");
magic_case(KC_W, "orld");
magic_case(KC_S, "ervice");
magic_case(KC_E, "'re");
magic_case(KC_Q, "uiet");
magic_case(KC_DOT, "com");
magic_case(KC_COMM, " since");
default: return;
}
}

View file

@ -1,7 +0,0 @@
#pragma once
#include "../__init__.h"
void process_magic_key_1(void);
void process_magic_key_2(void);
void process_magic_key_3(void);
void process_magic_key_4(void);

View file

@ -0,0 +1,82 @@
#include "utils.h"
#include "send_string.h"
#include "quantum.h"
// Note: we bit-pack in "reverse" order to optimize loading
#define PGM_LOADBIT(mem, pos) ((pgm_read_byte(&((mem)[(pos) / 8])) >> ((pos) % 8)) & 0x01)
const char unshifted_keycode_to_ascii_lut[53] PROGMEM = {
// KC_A KC_B KC_C KC_D
'a', 'b', 'c', 'd',
// KC_E KC_F KC_G KC_H KC_I KC_J KC_K KC_L
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
// KC_M KC_N KC_O KC_P KC_Q KC_R KC_S KC_T
'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
// KC_U KC_V KC_W KC_X KC_Y KC_Z KC_1 KC_2
'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
// KC_3 KC_4 KC_5 KC_6 KC_7 KC_8 KC_9 KC_0
'3', '4', '5', '6', '7', '8', '9', '0',
// KC_ENTR KC_ESC KC_BSPC KC_TAB KC_SPC KC_MINS KC_EQL KC_LBRC
' ', ' ', ' ', ' ', ' ', '-', '=', '[',
// KC_RBRC KC_BSLS KC_NUHS KC_SCLN KC_QUOT KC_GRV KC_COMM KC_DOT
']', '\\', ' ', ';', '\'', '`', ',', '.',
// KC_SLSH
'/'
};
const char shifted_keycode_to_ascii_lut[53] PROGMEM = {
// KC_A KC_B KC_C KC_D
'A', 'B', 'C', 'D',
// KC_E KC_F KC_G KC_H KC_I KC_J KC_K KC_L
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
// KC_M KC_N KC_O KC_P KC_Q KC_R KC_S KC_T
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
// KC_U KC_V KC_W KC_X KC_Y KC_Z KC_EXLM KC_AT
'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
// KC_HASH KC_DLR KC_PERC KC_CIRC KC_AMPR KC_ASTR KC_LPRN KC_RPRN
'#', '$', '%', '^', '&', '*', '(', ')',
// KC_ENTR KC_ESC KC_BSPC KC_TAB KC_SPC KC_UNDS KC_PLUS KC_LCBR
' ', ' ', ' ', ' ', ' ', '_', '+', '{',
// KC_RCBR KC_PIPE KC_NUHS KC_COLN KC_DQUO KC_GRV KC_LABK KC_RABK
'}', '|', ' ', ':', '"', '~', '<', '>',
// KC_QUES
'?'
};
////////////////////////////////////////////////////////////////////////////////
char keycode_to_char(uint16_t keycode)
{
const bool shifted = keycode & QK_LSFT;
keycode &= 0xFF;
if (keycode >= KC_A && keycode <= KC_SLASH) {
keycode -= KC_A;
return shifted ? pgm_read_byte(&shifted_keycode_to_ascii_lut[keycode]) :
pgm_read_byte(&unshifted_keycode_to_ascii_lut[keycode]);
}
return ' ';
}
////////////////////////////////////////////////////////////////////////////////
uint16_t char_to_keycode(char c)
{
uint16_t k = pgm_read_byte(&ascii_to_keycode_lut[(uint8_t)c]);
bool is_shifted = PGM_LOADBIT(ascii_to_shift_lut, (uint8_t)c);
if (is_shifted)
k = S(k);
return k;
}
////////////////////////////////////////////////////////////////////////////////
// removes mod or layer info from a keycode
uint16_t normalize_keycode(uint16_t keycode)
{
if (IS_QK_MOD_TAP(keycode))
return QK_MOD_TAP_GET_TAP_KEYCODE(keycode);
if (IS_QK_LAYER_TAP(keycode))
return QK_LAYER_TAP_GET_TAP_KEYCODE(keycode);
return keycode;
}
////////////////////////////////////////////////////////////////////////////////
void multi_tap(uint16_t keycode, int count)
{
for (int i = 0; i < count; ++i)
tap_code16(keycode);
}

View file

@ -0,0 +1,8 @@
#pragma once
#include <stdint.h>
uint16_t char_to_keycode(char c);
char keycode_to_char(uint16_t keycode);
uint16_t normalize_keycode(uint16_t keycode);
void multi_tap(uint16_t keycode, int count);

View file

@ -26,18 +26,8 @@
#pragma once #pragma once
// Autocorrection dictionary with longest match semantics: // Autocorrection dictionary with longest match semantics:
// Autocorrection dictionary (25 entries): // Autocorrection dictionary (59 entries):
// c👆 -> cy
// p👆 -> py
// d👆 -> dy
// y👆 -> yp
// g👆 -> gy
// 👍 -> ↻ // 👍 -> ↻
// i👍 -> ing
// a👍 -> and
// :👆 -> the
// :👍 -> for
// .👍 -> .:⇑
// j👆 -> just // j👆 -> just
// j👆👆 -> justment // j👆👆 -> justment
// 👆👆 -> 👆n // 👆👆 -> 👆n
@ -52,28 +42,87 @@
// :i👍m -> I'm // :i👍m -> I'm
// :i👍d -> I'd // :i👍d -> I'd
// :i👍l -> I'll // :i👍l -> I'll
// :👆 -> the
// v👆 -> ver
// s👆 -> sk
// x👆 -> es
// m👆 -> ment
// t👆 -> tment
// k👆 -> ks
// l👆 -> lk
// r👆 -> rl
// c👆 -> cy
// p👆 -> py
// d👆 -> dy
// y👆 -> yp
// g👆 -> gy
// w👆 -> which
// q👆 -> question
// b👆 -> before
// f👆 -> first
// z👆 -> zone
// n👆 -> nion
// h👆 -> however
// u👆 -> eu
// e👆 -> ue
// o👆 -> oa
// a👆 -> abo
// ,👆 -> ,:but
// i👆 -> ion
// .👆 -> .\ [escape]
// :👍 -> for
// a👍 -> and
// x👍 -> xer
// k👍 -> know
// i👍 -> ing
// y👍 -> you
// q👍 -> quick
// w👍 -> would
// c👍 -> ck
// n👍 -> nf
// h👍 -> hn
// ,👍 -> ,:and
// .👍 -> .:⇑
// ?👍 -> ?:⇑
// ;👍 -> ;:⇑
// !👍 -> !:⇑
#define MAGICKEY_MIN_LENGTH 1 // "👍" #define MAGICKEY_MIN_LENGTH 1 // "👍"
#define MAGICKEY_MAX_LENGTH 5 // "jud👆👆" #define MAGICKEY_MAX_LENGTH 5 // "jud👆👆"
#define DICTIONARY_SIZE 134 #define DICTIONARY_SIZE 270
#define COMPLETIONS_SIZE 64 #define COMPLETIONS_SIZE 161
#define MAGICKEY_COUNT 4 #define MAGICKEY_COUNT 4
static const uint16_t magickey_data[DICTIONARY_SIZE] PROGMEM = { static const uint16_t magickey_data[DICTIONARY_SIZE] PROGMEM = {
0x4007, 0x0011, 0x000F, 0x001F, 0x0010, 0x0025, 0x0015, 0x002B, 0x0017, 0x0030, 0x0102, 0x0035, 0x0100, 0x0039, 0x0101, 0x006B, 0x4007, 0x0011, 0x000F, 0x001F, 0x0010, 0x0025, 0x0015, 0x002B, 0x0017, 0x0030, 0x0102, 0x0035, 0x0100, 0x0039, 0x0101, 0x00C3,
0x0000, 0x4102, 0x0016, 0x0101, 0x001A, 0x0000, 0x0007, 0x0000, 0x8000, 0x0000, 0x000C, 0x002C, 0x0000, 0x8000, 0x0003, 0x0101, 0x0000, 0x4102, 0x0016, 0x0101, 0x001A, 0x0000, 0x0007, 0x0000, 0x8000, 0x0000, 0x000C, 0x002C, 0x0000, 0x8000, 0x0003, 0x0101,
0x000C, 0x002C, 0x0000, 0x8000, 0x0006, 0x0101, 0x000C, 0x002C, 0x0000, 0x8000, 0x000A, 0x0102, 0x0007, 0x0000, 0x8000, 0x000D, 0x000C, 0x002C, 0x0000, 0x8000, 0x0006, 0x0101, 0x000C, 0x002C, 0x0000, 0x8000, 0x000A, 0x0102, 0x0007, 0x0000, 0x8000, 0x000D,
0x0102, 0x0007, 0x0000, 0x8000, 0x0010, 0x0007, 0x0000, 0x8000, 0x0015, 0x402C, 0x004A, 0x0006, 0x004C, 0x0007, 0x004E, 0x000A, 0x0102, 0x0007, 0x0000, 0x8000, 0x0010, 0x0007, 0x0000, 0x8000, 0x0015, 0x4036, 0x0076, 0x0037, 0x0078, 0x002C, 0x007A, 0x0004,
0x0055, 0x000D, 0x0057, 0x0013, 0x0059, 0x001C, 0x005B, 0x0100, 0x005D, 0x0000, 0x8000, 0x001C, 0x8000, 0x0020, 0xC000, 0x0020, 0x007C, 0x0005, 0x007E, 0x0006, 0x0080, 0x0007, 0x0082, 0x0008, 0x0089, 0x0009, 0x008B, 0x000A, 0x008D, 0x000B, 0x008F, 0x000C,
0x0018, 0x000D, 0x0000, 0x8000, 0x0022, 0x8000, 0x0020, 0x8000, 0x0025, 0x8000, 0x0020, 0x8000, 0x0029, 0xC000, 0x002B, 0x4007, 0x0091, 0x000D, 0x0093, 0x000E, 0x0095, 0x000F, 0x0097, 0x0010, 0x0099, 0x0011, 0x009B, 0x0012, 0x009D, 0x0013, 0x009F, 0x0014,
0x0064, 0x000D, 0x0069, 0x0000, 0x0018, 0x000D, 0x0000, 0x8001, 0x0010, 0x8000, 0x0010, 0xC800, 0x002D, 0x4037, 0x0078, 0x002C, 0x00A1, 0x0015, 0x00A3, 0x0016, 0x00A5, 0x0017, 0x00A7, 0x0018, 0x00A9, 0x0019, 0x00AB, 0x001A, 0x00AD, 0x001B, 0x00AF, 0x001C,
0x007A, 0x0004, 0x007C, 0x000C, 0x007E, 0x000D, 0x0084, 0x0000, 0x9000, 0x002E, 0x8000, 0x0030, 0x8000, 0x0034, 0xC000, 0x0037, 0x00B1, 0x001D, 0x00B3, 0x0100, 0x00B5, 0x0000, 0x8000, 0x001C, 0x8000, 0x0021, 0x8000, 0x0023, 0x8000, 0x0027, 0x8000, 0x002A,
0x002C, 0x0000, 0x8001, 0x003A, 0x8000, 0x003C 0x8000, 0x0030, 0xC000, 0x0030, 0x0018, 0x000D, 0x0000, 0x8000, 0x0032, 0x8001, 0x0035, 0x8000, 0x0038, 0x8000, 0x0030, 0x8000,
0x003D, 0x8000, 0x0044, 0x8000, 0x0047, 0x8000, 0x004B, 0x8000, 0x004D, 0x8000, 0x004F, 0x8000, 0x0053, 0x8000, 0x0057, 0x8000,
0x0030, 0x8000, 0x0059, 0x8000, 0x0061, 0x8000, 0x004D, 0x8000, 0x0010, 0x8001, 0x0063, 0x8000, 0x000D, 0x8000, 0x0066, 0x8001,
0x006B, 0x8000, 0x006E, 0x8000, 0x0070, 0xC000, 0x0074, 0x4007, 0x00BC, 0x000D, 0x00C1, 0x0000, 0x0018, 0x000D, 0x0000, 0x8001,
0x0010, 0x8000, 0x0010, 0xC800, 0x0076, 0x421E, 0x00E8, 0x0036, 0x00EA, 0x0037, 0x00EC, 0x002C, 0x00EE, 0x0033, 0x00F0, 0x0238,
0x00F2, 0x0004, 0x00F4, 0x0006, 0x00F6, 0x000B, 0x00F8, 0x000C, 0x00FA, 0x000D, 0x0100, 0x000E, 0x0102, 0x0011, 0x0104, 0x0014,
0x0106, 0x001A, 0x0108, 0x001B, 0x010A, 0x001C, 0x010C, 0x0000, 0x9000, 0x0077, 0x8000, 0x0079, 0x9000, 0x0077, 0x8000, 0x007E,
0x9000, 0x0077, 0x9000, 0x0077, 0x8000, 0x0082, 0x8000, 0x004D, 0x8000, 0x0074, 0xC000, 0x0085, 0x002C, 0x0000, 0x8001, 0x0088,
0x8000, 0x008A, 0x8000, 0x008E, 0x8000, 0x0092, 0x8000, 0x0094, 0x8000, 0x0099, 0x8000, 0x000D, 0x8000, 0x009E
}; };
static const uint8_t magickey_completions_data[COMPLETIONS_SIZE] PROGMEM = { static const uint8_t magickey_completions_data[COMPLETIONS_SIZE] PROGMEM = {
0x65, 0x64, 0x00, 0x27, 0x64, 0x00, 0x27, 0x6C, 0x6C, 0x00, 0x27, 0x6D, 0x00, 0x65, 0x72, 0x00, 0x65, 0x64, 0x00, 0x27, 0x64, 0x00, 0x27, 0x6C, 0x6C, 0x00, 0x27, 0x6D, 0x00, 0x65, 0x72, 0x00,
0x6D, 0x65, 0x6E, 0x74, 0x00, 0x65, 0x76, 0x65, 0x6C, 0x6F, 0x70, 0x00, 0x74, 0x68, 0x65, 0x00, 0x6D, 0x65, 0x6E, 0x74, 0x00, 0x65, 0x76, 0x65, 0x6C, 0x6F, 0x70, 0x00, 0x20, 0x62, 0x75, 0x74,
0x79, 0x00, 0x67, 0x65, 0x00, 0x75, 0x73, 0x74, 0x00, 0x70, 0x00, 0x6E, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x5C, 0x00, 0x74, 0x68, 0x65, 0x00, 0x62, 0x6F, 0x00, 0x65, 0x66, 0x6F, 0x72, 0x65, 0x00,
0x66, 0x6F, 0x72, 0x00, 0x6E, 0x64, 0x00, 0x6E, 0x67, 0x00, 0x49, 0x00, 0x6F, 0x69, 0x6E, 0x00 0x79, 0x00, 0x67, 0x65, 0x00, 0x75, 0x65, 0x00, 0x69, 0x72, 0x73, 0x74, 0x00, 0x6F, 0x77, 0x65,
0x76, 0x65, 0x72, 0x00, 0x6F, 0x6E, 0x00, 0x75, 0x73, 0x74, 0x00, 0x73, 0x00, 0x6B, 0x00, 0x65,
0x6E, 0x74, 0x00, 0x69, 0x6F, 0x6E, 0x00, 0x61, 0x00, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6F, 0x6E,
0x00, 0x6C, 0x00, 0x65, 0x75, 0x00, 0x68, 0x69, 0x63, 0x68, 0x00, 0x65, 0x73, 0x00, 0x70, 0x00,
0x6F, 0x6E, 0x65, 0x00, 0x6E, 0x00, 0x00, 0x20, 0x00, 0x20, 0x61, 0x6E, 0x64, 0x00, 0x66, 0x6F,
0x72, 0x00, 0x6E, 0x64, 0x00, 0x6E, 0x67, 0x00, 0x49, 0x00, 0x6F, 0x69, 0x6E, 0x00, 0x6E, 0x6F,
0x77, 0x00, 0x66, 0x00, 0x75, 0x69, 0x63, 0x6B, 0x00, 0x6F, 0x75, 0x6C, 0x64, 0x00, 0x6F, 0x75,
0x00
}; };

View file

@ -1,17 +1,7 @@
# 👆👍★✪ # 👆👍★✪
# ↻⇑ # ↻⇑
c👆 ⇒ cy
p👆 ⇒ py
d👆 ⇒ dy
y👆 ⇒ yp
g👆 ⇒ gy
👍 ⇒ ↻ 👍 ⇒ ↻
i👍 ⇒ ing
a👍 ⇒ and
:👆 ⇒ the
:👍 ⇒ for
.👍 ⇒ .:⇑
j👆 ⇒ just j👆 ⇒ just
j👆👆 ⇒ justment j👆👆 ⇒ justment
@ -28,3 +18,63 @@ d★d ⇒ developed
:i👍m ⇒ I'm :i👍m ⇒ I'm
:i👍d ⇒ I'd :i👍d ⇒ I'd
:i👍l ⇒ I'll :i👍l ⇒ I'll
# final
:👆 ⇒ the
v👆 ⇒ ver
s👆 ⇒ sk
x👆 ⇒ es
m👆 ⇒ ment
t👆 ⇒ tment
k👆 ⇒ ks
l👆 ⇒ lk
r👆 ⇒ rl
j👆 ⇒ just
c👆 ⇒ cy
p👆 ⇒ py
d👆 ⇒ dy
y👆 ⇒ yp
g👆 ⇒ gy
w👆 ⇒ which
q👆 ⇒ question
b👆 ⇒ before
f👆 ⇒ first
z👆 ⇒ zone
👆👆 ⇒ 👆n
n👆 ⇒ nion
h👆 ⇒ however
u👆 ⇒ eu
e👆 ⇒ ue
o👆 ⇒ oa
a👆 ⇒ abo
,👆 ⇒ ,:but
i👆 ⇒ ion
.👆 ⇒ .\
:👍 ⇒ for
a👍 ⇒ and
x👍 ⇒ xer
k👍 ⇒ know
i👍 ⇒ ing
y👍 ⇒ you
q👍 ⇒ quick
j👍 ⇒ join
w👍 ⇒ would
c👍 ⇒ ck
n👍 ⇒ nf
h👍 ⇒ hn
,👍 ⇒ ,:and
.👍 ⇒ .:⇑
?👍 ⇒ ?:⇑
:👍 ⇒ ::⇑
;👍 ⇒ ;:⇑
!👍 ⇒ !:⇑

View file

@ -1,11 +1,11 @@
# Set any rules.mk overrides for your specific keymap here. # Set any rules.mk overrides for your specific keymap here.
# See rules at https://docs.qmk.fm/#/config_options?id=the-rulesmk-file # See rules at https://docs.qmk.fm/#/config_options?id=the-rulesmk-file
CONSOLE_ENABLE = no CONSOLE_ENABLE = yes
COMMAND_ENABLE = no COMMAND_ENABLE = no
SPACE_CADET_ENABLE = no SPACE_CADET_ENABLE = no
COMBO_ENABLE = yes COMBO_ENABLE = yes
REPEAT_KEY_ENABLE = yes REPEAT_KEY_ENABLE = yes
DEFERRED_EXEC_ENABLE = yes DEFERRED_EXEC_ENABLE = yes
SRC += magic_sturdy/general.c SRC += magic_sturdy/context_magic.c
SRC += magic_sturdy/magic_keys.c SRC += magic_sturdy/utils.c

View file

@ -3,7 +3,7 @@
#include <string.h> #include <string.h>
#include "trie2.h" #include "trie2.h"
#include "stack.h" #include "stack.h"
#include "util.h" #include "util2.h"
#define KEY_BUFFER_MAX_LENGTH MAGICKEY_MAX_LENGTH #define KEY_BUFFER_MAX_LENGTH MAGICKEY_MAX_LENGTH
#define TDATA(i) trie->dict[i] #define TDATA(i) trie->dict[i]
@ -80,9 +80,9 @@ bool search_trie2(const trie2_t *trie, int offset, trie2_visitor_t *v)
search_buffer_t *search = (search_buffer_t*)v->cb_data; search_buffer_t *search = (search_buffer_t*)v->cb_data;
uint16_t code = TDATA(offset); uint16_t code = TDATA(offset);
assert(code); assert(code);
// MATCH node if bit 16 is set // MATCH node if bit 15 is set
if (code & 0x8000) { if (code & 0x8000) {
// If bit 15 is also set, there's a child node after the completion string // If bit 14 is also set, there's a child node after the completion string
if ((code & 0x4000) && search_trie2(trie, offset+2, v)) if ((code & 0x4000) && search_trie2(trie, offset+2, v))
return true; return true;
// If no better match found deeper, this is the result! // If no better match found deeper, this is the result!
@ -94,7 +94,7 @@ bool search_trie2(const trie2_t *trie, int offset, trie2_visitor_t *v)
// Found a match so return true! // Found a match so return true!
return true; return true;
} }
// BRANCH node if bit 15 is set // BRANCH node if bit 14 is set
if (code & 0x4000) { if (code & 0x4000) {
if ((v->stack.size+1) > search->size) if ((v->stack.size+1) > search->size)
return false; return false;
@ -104,7 +104,7 @@ bool search_trie2(const trie2_t *trie, int offset, trie2_visitor_t *v)
for (; code; offset += 2, code = TDATA(offset)) { for (; code; offset += 2, code = TDATA(offset)) {
const char c = keycode_to_char(code); const char c = keycode_to_char(code);
if (cur_char == c) { if (cur_char == c) {
// Get 16bit offset to child node // Get 15bit offset to child node
const int child_offset = TDATA(offset+1); const int child_offset = TDATA(offset+1);
// Traverse down child node // Traverse down child node
stack_push(&v->stack, c); stack_push(&v->stack, c);

View file

@ -1,5 +1,5 @@
#include "keycodes.h" #include "keycodes-copy.h"
#include "util.h" #include "util2.h"
#define QK_LSFT 0x0200 #define QK_LSFT 0x0200
#define pgm_read_byte(address_short) *((uint8_t*)(address_short)) #define pgm_read_byte(address_short) *((uint8_t*)(address_short))