1
0
Fork 0

[Keymap] Drashna update for post Q2 merge (#17241)

This commit is contained in:
Drashna Jael're 2022-05-30 22:02:55 -07:00 committed by GitHub
parent b554e4b612
commit cda343acbe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
59 changed files with 672 additions and 769 deletions

View file

@ -0,0 +1,5 @@
#if __has_include("../qmk_secrets/autocorrection_data.h")
# include "../qmk_secrets/autocorrection_data.h"
#else
# include "autocorrect_data_default.h"
#endif

View file

@ -87,6 +87,9 @@ void suspend_power_down_user(void) {
__attribute__((weak)) void suspend_wakeup_init_keymap(void) {}
void suspend_wakeup_init_user(void) {
#ifdef OLED_ENABLE
oled_timer_reset();
#endif
suspend_wakeup_init_keymap();
}

View file

@ -23,6 +23,3 @@ void matrix_init_unicode(void);
#ifdef SPLIT_KEYBOARD
void matrix_slave_scan_keymap(void);
#endif
#ifdef CAPS_WORD_ENABLE
# include "keyrecords/caps_word.h"
#endif

View file

@ -37,9 +37,9 @@
// # define WPM_LAUNCH_CONTROL
// # define WPM_ALLOW_COUNT_REGRESSOIN
// # define WPM_UNFILTERED
# define WPM_SAMPLE_SECONDS 6
# define WPM_SAMPLE_SECONDS 10
# define WPM_SAMPLE_PERIODS 50
# define WPM_ESTIMATED_WORD_SIZE 6
# define WPM_ESTIMATED_WORD_SIZE 5
#endif
#ifdef AUDIO_ENABLE
@ -291,6 +291,12 @@
# ifndef OLED_BRIGHTNESS
# define OLED_BRIGHTNESS 50
# endif
# undef OLED_UPDATE_INTERVAL
# define OLED_UPDATE_INTERVAL 100
# if !defined(STM32F4XX)
# undef OLED_UPDATE_INTERVAL
# define OLED_UPDATE_INTERVAL 75
# endif
#endif
#define ENABLE_COMPILE_KEYCODE
#define BOTH_SHIFTS_TURNS_ON_CAPS_WORD

View file

@ -10,6 +10,13 @@
# pragma GCC push_options
# pragma GCC optimize("O0")
# include "autocorrection_data.h"
# ifndef AUTOCORRECTION_MIN_LENGTH
# define AUTOCORRECTION_MIN_LENGTH AUTOCORRECT_MIN_LENGTH
# endif
# ifndef AUTOCORRECTION_MAX_LENGTH
# define AUTOCORRECTION_MAX_LENGTH AUTOCORRECT_MAX_LENGTH
# endif
# define autocorrection_data autocorrect_data
# if AUTOCORRECTION_MIN_LENGTH < 4
# error Minimum Length is too short and may cause overflows
# endif
@ -17,6 +24,138 @@
# error Dictionary size excees maximum size permitted
# endif
static uint8_t typo_buffer[AUTOCORRECT_MAX_LENGTH] = {KC_SPC};
static uint8_t typo_buffer_size = 1;
/**
* @brief function for querying the enabled state of autocorrect
*
* @return true if enabled
* @return false if disabled
*/
bool autocorrect_is_enabled(void) {
return userspace_config.autocorrection;
}
/**
* @brief Enables autocorrect and saves state to eeprom
*
*/
void autocorrect_enable(void) {
userspace_config.autocorrection = true;
eeconfig_update_user(userspace_config.raw);
}
/**
* @brief Disables autocorrect and saves state to eeprom
*
*/
void autocorrect_disable(void) {
userspace_config.autocorrection = false;
typo_buffer_size = 0;
eeconfig_update_user(userspace_config.raw);
}
/**
* @brief Toggles autocorrect's status and save state to eeprom
*
*/
void autocorrect_toggle(void) {
userspace_config.autocorrection = !userspace_config.autocorrection;
typo_buffer_size = 0;
eeconfig_update_user(userspace_config.raw);
}
/**
* @brief handler for determining if autocorrect should process keypress
*
* @param keycode Keycode registered by matrix press, per keymap
* @param record keyrecord_t structure
* @param typo_buffer_size passed along to allow resetting of autocorrect buffer
* @param mods allow processing of mod status
* @return true Allow autocorection
* @return false Stop processing and escape from autocorrect.
*/
__attribute__((weak)) bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, 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_ONE_SHOT_LAYER_MAX:
case QK_LAYER_TAP_TOGGLE ... 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 &= 0xFF; // 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;
# endif
case QK_MOD_TAP ... QK_MOD_TAP_MAX:
// Exclude hold keycode
if (!record->tap.count) {
return false;
}
*keycode &= 0xFF;
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
if (*keycode >= 0x56F0 || !record->tap.count) {
return false;
}
*keycode &= 0xFF;
break;
#else
// Exclude if disabled
return false;
#endif
}
// Disable autocorrect while a mod other than shift is active.
if ((*mods & ~MOD_MASK_SHIFT) != 0) {
*typo_buffer_size = 0;
return false;
}
return true;
}
/**
* @brief handling for when autocorrection has been triggered
*
* @param backspaces number of characters to remove
* @param str pointer to PROGMEM string to replace mistyped seletion with
* @return true apply correction
* @return false user handled replacement
*/
__attribute__((weak)) bool apply_autocorrect(uint8_t backspaces, const char *str) {
return true;
}
/**
* @brief Process handler for autocorrect feature
*
@ -25,131 +164,124 @@
* @return true Continue processing keycodes, and send to host
* @return false Stop processing keycodes, and don't send to host
*/
bool process_autocorrection(uint16_t keycode, keyrecord_t* record) {
static uint8_t typo_buffer[AUTOCORRECTION_MAX_LENGTH] = {KC_SPC};
static uint8_t typo_buffer_size = 1;
bool process_autocorrection(uint16_t keycode, keyrecord_t *record) {
uint8_t mods = get_mods();
#ifndef NO_ACTION_ONESHOT
mods |= get_oneshot_mods();
#endif
if (keycode == AUTO_CTN) {
if (record->event.pressed) {
typo_buffer_size = 0;
userspace_config.autocorrection ^= 1;
eeconfig_update_user(userspace_config.raw);
if ((keycode >= AUTOCORRECT_ON && keycode <= AUTOCORRECT_TOGGLE) && record->event.pressed) {
if (keycode == AUTOCORRECT_ON) {
autocorrect_enable();
} else if (keycode == AUTOCORRECT_OFF) {
autocorrect_disable();
} else if (keycode == AUTOCORRECT_TOGGLE) {
autocorrect_toggle();
} else {
return true;
}
return false;
}
if (!userspace_config.autocorrection) {
if (!autocorrect_is_enabled()) {
typo_buffer_size = 0;
return true;
}
switch (keycode) {
case KC_LSFT:
case KC_RSFT:
return true;
# ifndef NO_ACTION_TAPPING
case QK_MOD_TAP ... QK_MOD_TAP_MAX:
if (((keycode >> 8) & 0xF) == MOD_LSFT) {
return true;
}
# ifndef NO_ACTION_LAYER
case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
# endif
if (record->event.pressed || !record->tap.count) {
return true;
}
keycode &= 0xFF;
break;
# endif
# ifdef SWAP_HANDS_ENABLE
case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:
if (keycode >= 0x56F0 || record->event.pressed || !record->tap.count) {
return true;
}
keycode &= 0xFF;
break;
# endif
# ifndef NO_ACTION_ONESHOT
case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX:
if ((keycode & 0xF) == MOD_LSFT) {
return true;
}
# endif
default:
// Disable autocorrection while a mod other than shift is active.
if (((get_mods() | get_oneshot_mods()) & ~MOD_MASK_SHIFT) != 0) {
typo_buffer_size = 0;
return true;
}
if (!record->event.pressed) {
return true;
}
if (!record->event.pressed) {
return true;
}
// Subtract buffer for Backspace key, reset for other non-alpha.
if (!(KC_A <= keycode && keycode <= KC_Z)) {
if (keycode == KC_BSPC) {
// autocorrect keycode verification and extraction
if (!process_autocorrect_user(&keycode, record, &typo_buffer_size, &mods)) {
return true;
}
// keycode buffer check
switch (keycode) {
case KC_A ... KC_Z:
// process normally
break;
case KC_1 ... KC_0:
case KC_TAB ... KC_SEMICOLON:
case KC_GRAVE ... KC_SLASH:
// Set a word boundary if space, period, digit, etc. is pressed.
keycode = KC_SPC;
break;
case KC_ENTER:
// Behave more conservatively for the enter key. Reset, so that enter
// can't be used on a word ending.
typo_buffer_size = 0;
keycode = KC_SPC;
break;
case KC_BSPC:
// Remove last character from the buffer.
if (typo_buffer_size > 0) {
--typo_buffer_size;
}
return true;
} else if (KC_1 <= keycode && keycode <= KC_SLSH && keycode != KC_ESC) {
// Set a word boundary if space, period, digit, etc. is pressed.
// Behave more conservatively for the enter key. Reset, so that enter
// can't be used on a word ending.
if (keycode == KC_ENT || (keycode == KC_MINUS && (get_mods() | get_oneshot_mods()) & MOD_MASK_SHIFT)) {
typo_buffer_size = 0;
case KC_QUOTE:
// Treat " (shifted ') as a word boundary.
if ((mods & MOD_MASK_SHIFT) != 0) {
keycode = KC_SPC;
}
keycode = KC_SPC;
} else {
break;
default:
// Clear state if some other non-alpha key is pressed.
typo_buffer_size = 0;
return true;
}
}
// Rotate oldest character if buffer is full.
if (typo_buffer_size >= AUTOCORRECTION_MAX_LENGTH) {
memmove(typo_buffer, typo_buffer + 1, AUTOCORRECTION_MAX_LENGTH - 1);
typo_buffer_size = AUTOCORRECTION_MAX_LENGTH - 1;
if (typo_buffer_size >= AUTOCORRECT_MAX_LENGTH) {
memmove(typo_buffer, typo_buffer + 1, AUTOCORRECT_MAX_LENGTH - 1);
typo_buffer_size = AUTOCORRECT_MAX_LENGTH - 1;
}
// Append `keycode` to buffer.
typo_buffer[typo_buffer_size++] = keycode;
// Return if buffer is smaller than the shortest word.
if (typo_buffer_size < AUTOCORRECTION_MIN_LENGTH) {
if (typo_buffer_size < AUTOCORRECT_MIN_LENGTH) {
return true;
}
// Check for typo in buffer using a trie stored in `autocorrection_data`.
// Check for typo in buffer using a trie stored in `autocorrect_data`.
uint16_t state = 0;
uint8_t code = pgm_read_byte(autocorrection_data + state);
for (uint8_t i = typo_buffer_size - 1; i >= 0; --i) {
uint8_t code = pgm_read_byte(autocorrect_data + state);
for (int8_t i = typo_buffer_size - 1; i >= 0; --i) {
uint8_t const key_i = typo_buffer[i];
if (code & 64) { // Check for match in node with multiple children.
if (code & 64) { // Check for match in node with multiple children.
code &= 63;
for (; code != key_i; code = pgm_read_byte(autocorrection_data + (state += 3))) {
for (; code != key_i; code = pgm_read_byte(autocorrect_data + (state += 3))) {
if (!code) return true;
}
// Follow link to child node.
state = (pgm_read_byte(autocorrection_data + state + 1) | pgm_read_byte(autocorrection_data + state + 2) << 8);
state = (pgm_read_byte(autocorrect_data + state + 1) | pgm_read_byte(autocorrect_data + state + 2) << 8);
// Check for match in node with single child.
} else if (code != key_i) {
return true;
} else if (!(code = pgm_read_byte(autocorrection_data + (++state)))) {
} else if (!(code = pgm_read_byte(autocorrect_data + (++state)))) {
++state;
}
code = pgm_read_byte(autocorrection_data + state);
// Stop if `state` becomes an invalid index. This should not normally
// happen, it is a safeguard in case of a bug, data corruption, etc.
if (state >= DICTIONARY_SIZE) {
return true;
}
if (code & 128) { // A typo was found! Apply autocorrection.
const uint8_t backspaces = code & 63;
for (uint8_t i = 0; i < backspaces; ++i) {
tap_code(KC_BSPC);
code = pgm_read_byte(autocorrect_data + state);
if (code & 128) { // A typo was found! Apply autocorrect.
const uint8_t backspaces = (code & 63) + !record->event.pressed;
if (apply_autocorrect(backspaces, (char const *)(autocorrect_data + state + 1))) {
for (uint8_t i = 0; i < backspaces; ++i) {
tap_code(KC_BSPC);
}
send_string_P((char const *)(autocorrect_data + state + 1));
}
send_string_P((char const*)(autocorrection_data + state + 1));
if (keycode == KC_SPC) {
typo_buffer[0] = KC_SPC;
@ -166,5 +298,7 @@ bool process_autocorrection(uint16_t keycode, keyrecord_t* record) {
# pragma GCC pop_options
#else
# pragma message "Warning!!! Autocorrect is not corretly setup!"
bool process_autocorrection(uint16_t keycode, keyrecord_t* record) { return true; }
bool process_autocorrection(uint16_t keycode, keyrecord_t* record) {
return true;
}
#endif

View file

@ -7,4 +7,11 @@
#include "drashna.h"
bool process_autocorrection(uint16_t keycode, keyrecord_t* record);
bool process_autocorrection(uint16_t keycode, keyrecord_t *record);
bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods);
bool apply_autocorrect(uint8_t backspaces, const char *str);
bool autocorrect_is_enabled(void);
void autocorrect_enable(void);
void autocorrect_disable(void);
void autocorrect_toggle(void);

View file

@ -1,4 +1,4 @@
# Copyright 2021 Google LLC
# Copyright 2021-2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -29,6 +29,7 @@ Each line of the dict file defines one typo and its correction with the syntax
Example:
:thier -> their
dosen't -> doesn't
fitler -> filter
lenght -> length
ouput -> output
@ -42,7 +43,7 @@ https://getreuer.info/posts/keyboards/autocorrection
import sys
import textwrap
from typing import Any, Dict, List, Tuple
from typing import Any, Dict, Iterator, List, Tuple
try:
from english_words import english_words_lower_alpha_set as CORRECT_WORDS
@ -51,85 +52,67 @@ except ImportError:
'correctly spelled word. To check for this, install the english_words '
'package and rerun this script:\n\n pip install english_words\n')
# Use a minimal word list as a fallback.
CORRECT_WORDS = ('information', 'available', 'international', 'language',
'loosest', 'reference', 'wealthier', 'entertainment',
'association', 'provides', 'technology', 'statehood')
CORRECT_WORDS = ('apparent', 'association', 'available', 'classification',
'effect', 'entertainment', 'fantastic', 'information',
'integrate', 'international', 'language', 'loosest',
'manual', 'nothing', 'provides', 'reference', 'statehood',
'technology', 'virtually', 'wealthier', 'wonderful')
KC_A = 4
KC_SPC = 0x2c
KC_QUOT = 0x34
TYPO_CHARS = dict(
[
("'", KC_QUOT),
(':', KC_SPC), # "Word break" character.
] +
# Characters a-z.
[(chr(c), c + KC_A - ord('a')) for c in range(ord('a'), ord('z') + 1)]
)
def parse_file(file_name: str) -> List[Tuple[str, str]]:
"""Parses autocorrections dictionary file.
Each line of the file defines one typo and its correction with the syntax
"typo -> correction". Blank lines or lines starting with '#' are ignored. The
function validates that typos only have characters a-z and that typos are not
substrings of other typos, otherwise the longer typo would never trigger.
function validates that typos only have characters in TYPO_CHARS, that
typos are not substrings of other typos, and checking that typos don't trigger
on CORRECT_WORDS.
Args:
file_name: String, path of the autocorrections dictionary.
Returns:
List of (typo, correction) tuples.
"""
correct_words = ('information', 'available', 'international', 'language', 'loosest', 'reference', 'wealthier', 'entertainment', 'association', 'provides', 'technology', 'statehood')
autocorrections = []
typos = set()
line_number = 0
for line in open(file_name, 'rt'):
line_number += 1
line = line.strip()
if line and line[0] != '#':
# Parse syntax "typo -> correction", using strip to ignore indenting.
tokens = [token.strip() for token in line.split('->', 1)]
if len(tokens) != 2 or not tokens[0]:
print(f'Error:{line_number}: Invalid syntax: "{line}"')
for line_number, typo, correction in parse_file_lines(file_name):
if typo in typos:
print(f'Warning:{line_number}: Ignoring duplicate typo: "{typo}"')
continue
# Check that `typo` is valid.
if not(all([c in TYPO_CHARS for c in typo])):
print(f'Error:{line_number}: Typo "{typo}" has '
'characters other than ' + ''.join(TYPO_CHARS.keys()))
sys.exit(1)
for other_typo in typos:
if typo in other_typo or other_typo in typo:
print(f'Error:{line_number}: Typos may not be substrings of one '
f'another, otherwise the longer typo would never trigger: '
f'"{typo}" vs. "{other_typo}".')
sys.exit(1)
if len(typo) < 5:
print(f'Warning:{line_number}: It is suggested that typos are at '
f'least 5 characters long to avoid false triggers: "{typo}"')
typo, correction = tokens
typo = typo.lower() # Force typos to lowercase.
typo = typo.replace(' ', ':')
check_typo_against_dictionary(typo, line_number, correct_words)
if typo in typos:
print(f'Warning:{line_number}: Ignoring duplicate typo: "{typo}"')
continue
# Check that `typo` is valid.
if not(all([ord('a') <= ord(c) <= ord('z') or c == ':' for c in typo])):
print(f'Error:{line_number}: Typo "{typo}" has '
'characters other than a-z and :.')
sys.exit(1)
for other_typo in typos:
if typo in other_typo or other_typo in typo:
print(f'Error:{line_number}: Typos may not be substrings of one '
f'another, otherwise the longer typo would never trigger: '
f'"{typo}" vs. "{other_typo}".')
sys.exit(1)
if len(typo) < 5:
print(f'Warning:{line_number}: It is suggested that typos are at '
f'least 5 characters long to avoid false triggers: "{typo}"')
if typo.startswith(':') and typo.endswith(':'):
if typo[1:-1] in CORRECT_WORDS:
print(f'Warning:{line_number}: Typo "{typo}" is a correctly spelled '
'dictionary word.')
elif typo.startswith(':') and not typo.endswith(':'):
for word in CORRECT_WORDS:
if word.startswith(typo[1:]):
print(f'Warning:{line_number}: Typo "{typo}" would falsely trigger '
f'on correctly spelled word "{word}".')
elif not typo.startswith(':') and typo.endswith(':'):
for word in CORRECT_WORDS:
if word.endswith(typo[:-1]):
print(f'Warning:{line_number}: Typo "{typo}" would falsely trigger '
f'on correctly spelled word "{word}".')
elif not typo.startswith(':') and not typo.endswith(':'):
for word in CORRECT_WORDS:
if typo in word:
print(f'Warning:{line_number}: Typo "{typo}" would falsely trigger '
f'on correctly spelled word "{word}".')
autocorrections.append((typo, correction))
typos.add(typo)
autocorrections.append((typo, correction))
typos.add(typo)
return autocorrections
@ -152,6 +135,47 @@ def make_trie(autocorrections: List[Tuple[str, str]]) -> Dict[str, Any]:
return trie
def parse_file_lines(file_name: str) -> Iterator[Tuple[int, str, str]]:
"""Parses lines read from `file_name` into typo-correction pairs."""
line_number = 0
for line in open(file_name, 'rt'):
line_number += 1
line = line.strip()
if line and line[0] != '#':
# Parse syntax "typo -> correction", using strip to ignore indenting.
tokens = [token.strip() for token in line.split('->', 1)]
if len(tokens) != 2 or not tokens[0]:
print(f'Error:{line_number}: Invalid syntax: "{line}"')
sys.exit(1)
typo, correction = tokens
typo = typo.lower() # Force typos to lowercase.
typo = typo.replace(' ', ':')
yield line_number, typo, correction
def check_typo_against_dictionary(typo: str, line_number: int, correct_words) -> None:
"""Checks `typo` against English dictionary words."""
if typo.startswith(':') and typo.endswith(':'):
if typo[1:-1] in correct_words:
print(f'Warning:{line_number}: Typo "{typo}" is a correctly spelled dictionary word.')
elif typo.startswith(':') and not typo.endswith(':'):
for word in correct_words:
if word.startswith(typo[1:]):
print(f'Warning:{line_number}: Typo "{typo}" would falsely trigger on correctly spelled word "{word}".')
elif not typo.startswith(':') and typo.endswith(':'):
for word in correct_words:
if word.endswith(typo[:-1]):
print(f'Warning:{line_number}: Typo "{typo}" would falsely trigger on correctly spelled word "{word}".')
elif not typo.startswith(':') and not typo.endswith(':'):
for word in correct_words:
if typo in word:
print(f'Warning:{line_number}: Typo "{typo}" would falsely trigger on correctly spelled word "{word}".')
def serialize_trie(autocorrections: List[Tuple[str, str]],
trie: Dict[str, Any]) -> List[int]:
"""Serializes trie and correction data in a form readable by the C code.
@ -165,7 +189,7 @@ def serialize_trie(autocorrections: List[Tuple[str, str]],
table = []
# Traverse trie in depth first order.
def traverse(trie_node):
def traverse(trie_node: Dict[str, Any]) -> Dict[str, Any]:
if 'LEAF' in trie_node: # Handle a leaf trie node.
typo, correction = trie_node['LEAF']
word_boundary_ending = typo[-1] == ':'
@ -200,37 +224,35 @@ def serialize_trie(autocorrections: List[Tuple[str, str]],
traverse(trie)
def serialize(e):
def kc_code(c):
if ord('a') <= ord(c) <= ord('z'):
return ord(c) - ord('a') + KC_A
elif c == ':':
return KC_SPC
else:
raise ValueError(f'Invalid character: {c}')
encode_link = lambda link: [link['byte_offset'] & 255,
link['byte_offset'] >> 8]
def serialize(e: Dict[str, Any]) -> List[int]:
if not e['links']: # Handle a leaf table entry.
return e['data']
elif len(e['links']) == 1: # Handle a chain table entry.
return list(map(kc_code, e['chars'])) + [0] #+ encode_link(e['links'][0]))
return [TYPO_CHARS[c] for c in e['chars']] + [0]
else: # Handle a branch table entry.
data = []
for c, link in zip(e['chars'], e['links']):
data += [kc_code(c) | (0 if data else 64)] + encode_link(link)
data += [TYPO_CHARS[c] | (0 if data else 64)] + encode_link(link)
return data + [0]
byte_offset = 0
for e in table: # To encode links, first compute byte offset of each entry.
e['byte_offset'] = byte_offset
byte_offset += len(serialize(e))
assert 0 <= byte_offset <= 0xffff
return [b for e in table for b in serialize(e)] # Serialize final table.
def encode_link(link: Dict[str, Any]) -> List[int]:
"""Encodes a node link as two bytes."""
byte_offset = link['byte_offset']
if not (0 <= byte_offset <= 0xffff):
print('Error: The autocorrection table is too large, a node link exceeds '
'64KB limit. Try reducing the autocorrection dict to fewer entries.')
sys.exit(1)
return [byte_offset & 255, byte_offset >> 8]
def write_generated_code(autocorrections: List[Tuple[str, str]],
data: List[int],
file_name: str) -> None:
@ -242,7 +264,10 @@ def write_generated_code(autocorrections: List[Tuple[str, str]],
file_name: String, path of the output C file.
"""
assert all(0 <= b <= 255 for b in data)
typo_len = lambda e: len(e[0])
def typo_len(e: Tuple[str, str]) -> int:
return len(e[0])
min_typo = min(autocorrections, key=typo_len)[0]
max_typo = max(autocorrections, key=typo_len)[0]
generated_code = ''.join([
@ -252,9 +277,8 @@ def write_generated_code(autocorrections: List[Tuple[str, str]],
for typo, correction in autocorrections)),
f'\n#define AUTOCORRECTION_MIN_LENGTH {len(min_typo)} // "{min_typo}"\n',
f'#define AUTOCORRECTION_MAX_LENGTH {len(max_typo)} // "{max_typo}"\n\n',
f'#define DICTIONARY_SIZE {len(data)}\n\n',
textwrap.fill('static const uint8_t autocorrection_data[DICTIONARY_SIZE] PROGMEM = {%s};' % (
', '.join(map(str, data))), width=120, subsequent_indent=' '),
textwrap.fill('static const uint8_t autocorrection_data[%d] PROGMEM = {%s};' % (
len(data), ', '.join(map(str, data))), width=80, subsequent_indent=' '),
'\n\n'])
with open(file_name, 'wt') as f:

View file

@ -1,139 +0,0 @@
#include "caps_word.h"
static bool caps_word_active = false;
#if CAPS_WORD_IDLE_TIMEOUT > 0
# if CAPS_WORD_IDLE_TIMEOUT < 100 || CAPS_WORD_IDLE_TIMEOUT > 30000
// Constrain timeout to a sensible range. With the 16-bit timer, the longest
// representable timeout is 32768 ms, rounded here to 30000 ms = half a minute.
# error "caps_word: CAPS_WORD_IDLE_TIMEOUT must be between 100 and 30000 ms"
# endif
static uint16_t idle_timer = 0;
void caps_word_task(void) {
if (caps_word_active && timer_expired(timer_read(), idle_timer)) {
caps_word_set(false);
}
}
#endif // CAPS_WORD_IDLE_TIMEOUT > 0
bool process_caps_word(uint16_t keycode, keyrecord_t* record) {
#ifndef NO_ACTION_ONESHOT
const uint8_t mods = get_mods() | get_oneshot_mods();
#else
const uint8_t mods = get_mods();
#endif // NO_ACTION_ONESHOT
if (!caps_word_active) {
// Pressing both shift keys at the same time enables caps word.
if ((mods & MOD_MASK_SHIFT) == MOD_MASK_SHIFT) {
caps_word_set(true); // Activate Caps Word.
return false;
}
return true;
} else {
#if CAPS_WORD_IDLE_TIMEOUT > 0
idle_timer = record->event.time + CAPS_WORD_IDLE_TIMEOUT;
#endif // CAPS_WORD_IDLE_TIMEOUT > 0
}
if (!record->event.pressed) {
return true;
}
if (!(mods & ~MOD_MASK_SHIFT)) {
switch (keycode) {
// Ignore MO, TO, TG, TT, and OSL layer switch keys.
case QK_MOMENTARY ... QK_MOMENTARY_MAX:
case QK_TO ... QK_TO_MAX:
case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:
case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:
return true;
#ifndef NO_ACTION_TAPPING
case QK_MOD_TAP ... QK_MOD_TAP_MAX:
if (record->tap.count == 0) {
// Deactivate if a mod becomes active through holding a mod-tap key.
caps_word_set(false);
return true;
}
keycode &= 0xff;
break;
# ifndef NO_ACTION_LAYER
case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
# endif // NO_ACTION_LAYER
if (record->tap.count == 0) {
return true;
}
keycode &= 0xff;
break;
#endif // NO_ACTION_TAPPING
#ifdef SWAP_HANDS_ENABLE
case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:
if (keycode > 0x56F0 || record->tap.count == 0) {
return true;
}
keycode &= 0xff;
break;
#endif // SWAP_HANDS_ENABLE
}
if (caps_word_press_user(keycode)) {
return true;
}
}
caps_word_set(false); // Deactivate Caps Word.
return true;
}
void caps_word_set(bool active) {
if (active != caps_word_active) {
if (active) {
clear_mods();
#ifndef NO_ACTION_ONESHOT
clear_oneshot_mods();
#endif // NO_ACTION_ONESHOT
#if CAPS_WORD_IDLE_TIMEOUT > 0
idle_timer = timer_read() + CAPS_WORD_IDLE_TIMEOUT;
#endif // CAPS_WORD_IDLE_TIMEOUT > 0
} else if ((get_weak_mods() & MOD_BIT(KC_LSFT)) != 0) {
// If the weak shift mod is still on, turn it off and send an update to
// the host computer.
del_weak_mods(MOD_BIT(KC_LSFT));
send_keyboard_report();
}
caps_word_active = active;
caps_word_set_user(active);
}
}
bool caps_word_get(void) {
return caps_word_active;
}
__attribute__((weak)) void caps_word_set_user(bool active) {}
__attribute__((weak)) bool caps_word_press_user(uint16_t keycode) {
switch (keycode) {
// Keycodes that continue Caps Word, with shift applied.
case KC_A ... KC_Z:
add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to the next key.
return true;
// Keycodes that continue Caps Word, without shifting.
case KC_1 ... KC_0:
case KC_BSPC:
case KC_MINS:
case KC_UNDS:
return true;
default:
return false; // Deactivate Caps Word.
}
}

View file

@ -1,85 +0,0 @@
// Copyright 2021 Google LLC.
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "drashna.h"
// Call this function from `process_record_user()` to implement Caps Word.
bool process_caps_word(uint16_t keycode, keyrecord_t* record);
// If CAPS_WORD_IDLE_TIMEOUT is set, call `caps_word_task()` from
// `matrix_scan_user()` as described above.
//
// If CAPS_WORD_IDLE_TIMEOUT isn't set, calling this function has no effect (but
// will still compile).
#if CAPS_WORD_IDLE_TIMEOUT > 0
void caps_word_task(void);
#else
static inline void caps_word_task(void) {}
#endif
// Activates or deactivates Caps Word. For instance activate Caps Word with a
// combo by defining a `COMBO_ACTION` that calls `caps_word_set(true)`:
//
// void process_combo_event(uint16_t combo_index, bool pressed) {
// switch(combo_index) {
// case CAPS_COMBO:
// if (pressed) {
// caps_word_set(true); // Activate Caps Word.
// }
// break;
//
// // Other combos...
// }
// }
void caps_word_set(bool active);
// Returns whether Caps Word is currently active.
bool caps_word_get(void);
// An optional callback that gets called when Caps Word turns on or off. This is
// useful to represent the current Caps Word state, e.g. by setting an LED or
// playing a sound. In your keymap, define
//
// void caps_word_set_user(bool active) {
// if (active) {
// // Do something when Caps Word activates.
// } else {
// // Do something when Caps Word deactivates.
// }
// }
void caps_word_set_user(bool active);
// An optional callback which is called on every key press while Caps Word is
// active. When the key should be shifted (that is, a letter key), the callback
// should call `add_weak_mods(MOD_BIT(KC_LSFT))` to shift the key. The callback
// also determines whether the key should continue Caps Word. Returning true
// continues the current "word", while returning false is "word breaking" and
// deactivates Caps Word. The default callback is
//
// bool caps_word_press_user(uint16_t keycode) {
// switch (keycode) {
// // Keycodes that continue Caps Word, with shift applied.
// case KC_A ... KC_Z:
// add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to the next key.
// return true;
//
// // Keycodes that continue Caps Word, without shifting.
// case KC_1 ... KC_0:
// case KC_BSPC:
// case KC_MINS:
// case KC_UNDS:
// return true;
//
// default:
// return false; // Deactivate Caps Word.
// }
// }
//
// To customize, copy the above function into your keymap and add/remove
// keycodes to the above cases.
//
// NOTE: Outside of this callback, you can use `caps_word_set(false)` to
// deactivate Caps Word.
bool caps_word_press_user(uint16_t keycode);

View file

@ -5,14 +5,9 @@ Keycodes are defined in the `process_record.h` file and need to be included in t
A bunch of macros are present and are only included on boards that are not the Ergodox EZ or Orthodox, as they are not needed for those boards.
* `KC_MAKE` - outputs `qmk compile -kb (keyboard) -km (keymap)` and enter, to start compiling the currenct keyboard. This uses generated variables to always use the current keyboard and keymap. Will work with any keyboard and any keymap.
* If you are holding shift, it will use `qmk flash` instead of `qmk compile`.
* If `MAKE_BOOTLOADER` is defined, it will always use `qmk flash` instead of `qmk compile`.
* `DEFAULT_LAYER_1` ... `DEFAULT_LAYER_4` - This sets layer 0-3 as the default layer, and writes that to eeprom, and plays a chime.
* `VRSN`, outputs the keyboard, keymap, commit and date info. Eg:
* `handwired/tractyl_manuform/5x6_right/f411/drashna @ 0.15.9-162-g087d08, Built on: 2021-12-19-21:10:26`
* `KC_DIABLO_CLEAR` - clears the diablo tapdance status.
* `KC_CCCV` - Copy on hold, paste on tap.
* `KEYLOCK` - This unloads the host driver, and prevents any data from being sent to the host. Hitting it again loads the driver, back.
* `REBOOT` - Uses watchdog timer on AVR, and `NVIC_SystemReset()` on ChibiOS to reset the board, without jumping to the bootloader.
* `EEP_RST` - Overrides the default behavior, disables EEPROM (which will trigger a reset on init), and reboots the keyboard as per `REBOOT` keycode.

View file

@ -3,9 +3,6 @@
#include "drashna.h"
#include "version.h"
#ifdef CAPS_WORD_ENABLE
# include "caps_word.h"
#endif
#ifdef AUTOCORRECTION_ENABLE
# include "autocorrection/autocorrection.h"
#endif
@ -37,6 +34,15 @@ __attribute__((weak)) bool process_record_secrets(uint16_t keycode, keyrecord_t
* @return false Stop process keycode and do not send to host
*/
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
#ifdef ENCODER_ENABLE // some debouncing for weird issues
if (IS_ENCODEREVENT(record->event)) {
static bool ignore_first = true;
if (ignore_first) {
ignore_first = false;
return false;
}
}
#endif
// If console is enabled, it will print the matrix position and status of each key pressed
#ifdef KEYLOGGER_ENABLE
uprintf("KL: kc: 0x%04X, col: %2u, row: %2u, pressed: %b, time: %5u, int: %b, count: %u\n", keycode, record->event.key.col, record->event.key.row, record->event.pressed, record->event.time, record->tap.interrupted, record->tap.count);
@ -58,9 +64,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
#if defined(CUSTOM_POINTING_DEVICE)
&& process_record_pointing(keycode, record)
#endif
#ifdef CAPS_WORD_ENABLE
&& process_caps_word(keycode, record)
#endif
#ifdef AUTOCORRECTION_ENABLE
&& process_autocorrection(keycode, record)
#endif
@ -87,33 +90,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
}
break;
case KC_MAKE: // Compiles the firmware, and adds the flash command based on keyboard bootloader
if (!record->event.pressed) {
#ifndef MAKE_BOOTLOADER
uint8_t temp_mod = mod_config(get_mods());
uint8_t temp_osm = mod_config(get_oneshot_mods());
clear_mods();
clear_oneshot_mods();
#endif
send_string_with_delay_P(PSTR("qmk"), TAP_CODE_DELAY);
#ifndef MAKE_BOOTLOADER
if ((temp_mod | temp_osm) & MOD_MASK_SHIFT)
#endif
{
send_string_with_delay_P(PSTR(" flash "), TAP_CODE_DELAY);
#ifndef MAKE_BOOTLOADER
} else {
send_string_with_delay_P(PSTR(" compile "), TAP_CODE_DELAY);
#endif
}
send_string_with_delay_P(PSTR("-kb " QMK_KEYBOARD " -km " QMK_KEYMAP), TAP_CODE_DELAY);
#ifdef CONVERT_TO_PROTON_C
send_string_with_delay_P(PSTR(" -e CTPC=yes"), TAP_CODE_DELAY);
#endif
send_string_with_delay_P(PSTR(SS_TAP(X_ENTER)), TAP_CODE_DELAY);
}
break;
case VRSN: // Prints firmware version
if (record->event.pressed) {
send_string_with_delay_P(PSTR(QMK_KEYBOARD "/" QMK_KEYMAP " @ " QMK_VERSION ", Built on: " QMK_BUILDDATE), TAP_CODE_DELAY);
@ -148,13 +124,19 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
dprintf("rgblight layer change [EEPROM]: %u\n", userspace_config.rgb_layer_change);
eeconfig_update_user(userspace_config.raw);
if (userspace_config.rgb_layer_change) {
# if defined(CUSTOM_RGBLIGHT) && defined(CUSTOM_RGB_MATRIX)
# if defined(CUSTOM_RGB_MATRIX)
rgb_matrix_set_flags(LED_FLAG_UNDERGLOW | LED_FLAG_KEYLIGHT | LED_FLAG_INDICATOR);
# if defined(CUSTOM_RGBLIGHT)
rgblight_enable_noeeprom();
# endif
# endif
layer_state_set(layer_state); // This is needed to immediately set the layer color (looks better)
# if defined(CUSTOM_RGBLIGHT) && defined(CUSTOM_RGB_MATRIX)
layer_state_set(layer_state); // This is needed to immediately set the layer color (looks better)
# if defined(CUSTOM_RGB_MATRIX)
} else {
rgb_matrix_set_flags(LED_FLAG_ALL);
# if defined(CUSTOM_RGBLIGHT)
rgblight_disable_noeeprom();
# endif
# endif
}
}
@ -218,23 +200,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
}
break;
}
case EEP_RST:
if (record->event.pressed) {
eeconfig_disable();
shutdown_user();
#ifdef __AVR__
wdt_enable(WDTO_250MS);
#else
NVIC_SystemReset();
#endif
}
return false;
case REBOOT:
if (record->event.pressed) {
software_reset();
}
return false;
}
}
return true;
}

View file

@ -21,7 +21,6 @@ enum userspace_custom_keycodes {
KC_DVORAK, // Sets default layer to DVORAK
LAST_DEFAULT_LAYER_KEYCODE = KC_DVORAK, // Sets default layer to WORKMAN
KC_DIABLO_CLEAR, // Clears all Diablo Timers
KC_MAKE, // Run keyboard's customized make command
KC_RGB_T, // Toggles RGB Layer Indication mode
RGB_IDL, // RGB Idling animations
KC_SECRET_1, // test1
@ -46,8 +45,9 @@ enum userspace_custom_keycodes {
KC_AUSSIE,
KC_ZALGO,
KC_ACCEL,
AUTO_CTN, // Toggle Autocorrect status
REBOOT,
AUTOCORRECT_ON,
AUTOCORRECT_OFF,
AUTOCORRECT_TOGGLE,
NEW_SAFE_RANGE // use "NEWPLACEHOLDER for keymap specific codes
};
@ -95,9 +95,6 @@ bool process_record_unicode(uint16_t keycode, keyrecord_t *record);
# endif
#endif
#define KC_RESET RESET
#define KC_RST KC_RESET
#ifdef SWAP_HANDS_ENABLE
# define KC_C1R3 SH_T(KC_TAB)
#elif defined(DRASHNA_LP)
@ -130,7 +127,7 @@ bool process_record_unicode(uint16_t keycode, keyrecord_t *record);
#define MG_NKRO MAGIC_TOGGLE_NKRO
#define AUTO_CTN AUTOCORRECT_TOGGLE
/*
Custom Keycodes for Diablo 3 layer
But since TD() doesn't work when tap dance is disabled

View file

@ -24,14 +24,12 @@ secrets.h
Here is the magic. This handles including the "secrets", and adding the custom macros to send them.
```c
#include "drashna.h" // replace with your keymap's "h" file, or whatever file stores the keycodes
#include QMK_KEYBOARD_H
#if (__has_include("secrets.h") && !defined(NO_SECRETS))
#include "secrets.h"
#else
// `PROGMEM const char secret[][x]` may work better, but it takes up more space in the firmware
// And I'm not familiar enough to know which is better or why...
static const char * const secret[] = {
static const char * const secrets[] = {
"test1",
"test2",
"test3",
@ -43,9 +41,10 @@ static const char * const secret[] = {
bool process_record_secrets(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
case KC_SECRET_1 ... KC_SECRET_5: // Secrets! Externally defined strings, not stored in repo
if (!record->event.pressed) {
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
send_string_with_delay(secret[keycode - KC_SECRET_1], MACRO_TIMER);
if (record->event.pressed) {
clear_mods();
clear_oneshot_mods();
send_string_with_delay(secrets[keycode - KC_SECRET_1], MACRO_TIMER);
}
return false;
break;
@ -59,7 +58,7 @@ bool process_record_secrets(uint16_t keycode, keyrecord_t *record) {
Now, for the actual secrets! The file needs to look like
```c
static const char * secrets[] = {
static const char * secrets[] = {
"secret1",
"secret2",
"secret3",
@ -96,7 +95,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
Here, you want your `/users/<name>/rules.mk` file to "detect" the existence of the `secrets.c` file, and only add it if the file exists.
Additionally, to ensure that it's not added or processed in any way, it checks to see if `NO_SECRETS` is set. This way, if you run `make keyboard:name NO_SECRETS=yes`, it will remove the feature altogether.
Additionally, to ensure that it's not added or processed in any way, it checks to see if `NO_SECRETS` is set. This way, if you run `qmk compile -kb keyboard -km name -e NO_SECRETS=yes`, it will remove the feature altogether.
```make
ifneq ($(strip $(NO_SECRETS)), yes)

View file

@ -4,9 +4,10 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "drashna.h"
#include "unicode.h"
#include "process_unicode_common.h"
uint16_t typing_mode = KC_NOMODE;
uint8_t typing_mode = UCTM_NO_MODE;
/**
* @brief Registers the unicode keystrokes based on desired unicode
@ -242,10 +243,10 @@ bool process_record_unicode(uint16_t keycode, keyrecord_t *record) {
break;
case KC_NOMODE ... KC_ZALGO:
if (record->event.pressed) {
if (typing_mode != keycode) {
typing_mode = keycode;
if (typing_mode != keycode - KC_NOMODE) {
typing_mode = keycode - KC_NOMODE;
} else {
typing_mode = KC_NOMODE;
typing_mode = UCTM_NO_MODE;
}
}
break;
@ -259,19 +260,19 @@ bool process_record_unicode(uint16_t keycode, keyrecord_t *record) {
keycode &= 0xFF;
}
if (typing_mode == KC_WIDE) {
if (typing_mode == UCTM_WIDE) {
if (((KC_A <= keycode) && (keycode <= KC_0)) || keycode == KC_SPACE) {
return process_record_glyph_replacement(keycode, record, unicode_range_translator_wide);
}
} else if (typing_mode == KC_SCRIPT) {
} else if (typing_mode == UCTM_SCRIPT) {
if (((KC_A <= keycode) && (keycode <= KC_0)) || keycode == KC_SPACE) {
return process_record_glyph_replacement(keycode, record, unicode_range_translator_script);
}
} else if (typing_mode == KC_BLOCKS) {
} else if (typing_mode == UCTM_BLOCKS) {
if (((KC_A <= keycode) && (keycode <= KC_0)) || keycode == KC_SPACE) {
return process_record_glyph_replacement(keycode, record, unicode_range_translator_boxes);
}
} else if (typing_mode == KC_REGIONAL) {
} else if (typing_mode == UCTM_REGIONAL) {
if (((KC_A <= keycode) && (keycode <= KC_0)) || keycode == KC_SPACE) {
if (!process_record_glyph_replacement(keycode, record, unicode_range_translator_regional)) {
wait_us(500);
@ -279,9 +280,9 @@ bool process_record_unicode(uint16_t keycode, keyrecord_t *record) {
return false;
}
}
} else if (typing_mode == KC_AUSSIE) {
} else if (typing_mode == UCTM_AUSSIE) {
return process_record_aussie(keycode, record);
} else if (typing_mode == KC_ZALGO) {
} else if (typing_mode == UCTM_ZALGO) {
return process_record_zalgo(keycode, record);
}
return true;

View file

@ -0,0 +1,16 @@
// Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
enum unicode_typing_mode {
UCTM_NO_MODE,
UCTM_WIDE,
UCTM_SCRIPT,
UCTM_BLOCKS,
UCTM_REGIONAL,
UCTM_AUSSIE,
UCTM_ZALGO,
};
extern uint8_t typing_mode;

View file

@ -18,19 +18,23 @@
#include "drashna.h"
#ifdef UNICODE_COMMON_ENABLE
# include "process_unicode_common.h"
# include "keyrecords/unicode.h"
#endif
#ifdef AUDIO_CLICKY
# include "process_clicky.h"
#endif
#if defined(AUTOCORRECTION_ENABLE)
# include "keyrecords/autocorrection/autocorrection.h"
#endif
# ifdef AUDIO_CLICKY
# include "process_clicky.h"
# endif
#include <string.h>
extern bool host_driver_disabled;
uint32_t oled_timer = 0;
char keylog_str[OLED_KEYLOGGER_LENGTH] = {0};
static uint16_t log_timer = 0;
uint32_t oled_timer = 0;
char keylog_str[OLED_KEYLOGGER_LENGTH] = {0};
static uint16_t log_timer = 0;
#ifdef OLED_DISPLAY_VERBOSE
static const char PROGMEM display_border[3] = {0x0, 0xFF, 0x0};
static const char PROGMEM display_border[3] = {0x0, 0xFF, 0x0};
#endif
deferred_token kittoken;
@ -100,12 +104,15 @@ void add_keylog(uint16_t keycode, keyrecord_t *record) {
*/
bool process_record_user_oled(uint16_t keycode, keyrecord_t *record) {
if (record->event.pressed) {
oled_timer = timer_read32();
oled_timer_reset();
add_keylog(keycode, record);
}
return true;
}
void oled_timer_reset(void) {
oled_timer = timer_read32();
}
/**
* @brief Renders keylogger buffer to oled
*
@ -262,9 +269,8 @@ void render_layer_state(void) {
}
};
// clang-format on
uint8_t layer_is[4] = { 0, 4, 4, 4};
uint8_t layer_is[4] = {0, 4, 4, 4};
if (layer_state_is(_ADJUST)) {
layer_is[0] = 3;
} else if (layer_state_is(_RAISE)) {
@ -280,7 +286,6 @@ void render_layer_state(void) {
layer_is[2] = 5;
}
oled_set_cursor(1, 2);
oled_write_raw_P(tri_layer_image[layer_is[0]][0], sizeof(tri_layer_image[0][0]));
oled_set_cursor(5, 2);
@ -322,7 +327,7 @@ void render_layer_state(void) {
*
* @param led_usb_state Current keyboard led state
*/
void render_keylock_status(uint8_t led_usb_state) {
void render_keylock_status(led_t led_usb_state) {
#if defined(OLED_DISPLAY_VERBOSE)
oled_set_cursor(1, 6);
#endif
@ -330,12 +335,12 @@ void render_keylock_status(uint8_t led_usb_state) {
#if !defined(OLED_DISPLAY_VERBOSE)
oled_write_P(PSTR(" "), false);
#endif
oled_write_P(PSTR(OLED_RENDER_LOCK_NUML), led_usb_state & (1 << USB_LED_NUM_LOCK));
oled_write_P(PSTR(OLED_RENDER_LOCK_NUML), led_usb_state.num_lock);
oled_write_P(PSTR(" "), false);
oled_write_P(PSTR(OLED_RENDER_LOCK_CAPS), led_usb_state & (1 << USB_LED_CAPS_LOCK));
oled_write_P(PSTR(OLED_RENDER_LOCK_CAPS), led_usb_state.caps_lock);
#if defined(OLED_DISPLAY_VERBOSE)
oled_write_P(PSTR(" "), false);
oled_write_P(PSTR(OLED_RENDER_LOCK_SCLK), led_usb_state & (1 << USB_LED_SCROLL_LOCK));
oled_write_P(PSTR(OLED_RENDER_LOCK_SCLK), led_usb_state.scroll_lock);
#endif
}
@ -417,15 +422,14 @@ void render_bootmagic_status(void) {
oled_write_P(logo[0][0], !is_bootmagic_on);
}
#ifndef OLED_DISPLAY_VERBOSE
oled_write_P(PSTR(" "), false);
oled_write_P(logo[1][1], is_bootmagic_on);
oled_write_P(logo[0][1], !is_bootmagic_on);
#endif
oled_write_P(PSTR(" "), false);
oled_write_P(PSTR(OLED_RENDER_BOOTMAGIC_NKRO), keymap_config.nkro);
oled_write_P(PSTR(" "), false);
#ifdef AUTOCORRECTION_ENABLE
oled_write_P(PSTR("CRCT"), userspace_config.autocorrection);
#if defined(AUTOCORRECTION_ENABLE) || defined(AUTOCORRECT_ENABLE)
oled_write_P(PSTR("CRCT"), autocorrect_is_enabled());
oled_write_P(PSTR(" "), false);
#else
oled_write_P(PSTR(OLED_RENDER_BOOTMAGIC_NOGUI), keymap_config.no_gui);
@ -439,7 +443,7 @@ void render_bootmagic_status(void) {
}
#endif
oled_write_P(PSTR(" "), false);
oled_write_P(PSTR(OLED_RENDER_BOOTMAGIC_ONESHOT), !is_oneshot_enabled());
oled_write_P(PSTR(OLED_RENDER_BOOTMAGIC_ONESHOT), is_oneshot_enabled());
#ifdef SWAP_HANDS_ENABLE
oled_write_P(PSTR(" "), false);
oled_write_P(PSTR(OLED_RENDER_BOOTMAGIC_SWAP), swap_hands);
@ -461,7 +465,7 @@ void render_user_status(void) {
l_is_clicky_on = user_state.audio_clicky_enable;
# endif
# else
is_audio_on = is_audio_on();
is_audio_on = is_audio_on();
# ifdef AUDIO_CLICKY
l_is_clicky_on = is_clicky_on();
# endif
@ -501,7 +505,7 @@ void render_user_status(void) {
static const char PROGMEM cat_mode[2][3] = {{0xF8, 0xF9, 0}, {0xF6, 0xF7, 0}};
oled_write_P(cat_mode[0], host_driver_disabled);
#if defined(UNICODE_COMMON_ENABLE)
static const char PROGMEM uc_mod_status[5][3] = {{0xEC, 0xED, 0}, {0x20, 0x20, 0}, {0x20, 0x20, 0}, {0x20, 0x20, 0}, {0xEA, 0xEB, 0}};
static const char PROGMEM uc_mod_status[5][3] = {{0xEC, 0xED, 0}, {0x20, 0x20, 0}, {0x20, 0x20, 0}, {0x20, 0x20, 0}, {0xEA, 0xEB, 0}};
oled_write_P(uc_mod_status[get_unicode_input_mode()], false);
#endif
if (userspace_config.nuke_switch) {
@ -549,9 +553,9 @@ void render_wpm_graph(uint8_t max_lines_graph, uint8_t vertical_offset) {
uint8_t currwpm = get_current_wpm();
float max_wpm = OLED_WPM_GRAPH_MAX_WPM;
if (timer_elapsed(timer) > OLED_WPM_GRAPH_REFRESH_INTERVAL) { // check if it's been long enough before refreshing graph
x = (max_lines_graph - 1) - ((currwpm / max_wpm) * (max_lines_graph - 1)); // main calculation to plot graph line
for (uint8_t i = 0; i <= OLED_WPM_GRAPH_GRAPH_LINE_THICKNESS - 1; i++) { // first draw actual value line
if (timer_elapsed(timer) > OLED_WPM_GRAPH_REFRESH_INTERVAL) { // check if it's been long enough before refreshing graph
x = (max_lines_graph - 1) - ((currwpm / max_wpm) * (max_lines_graph - 1)); // main calculation to plot graph line
for (uint8_t i = 0; i <= OLED_WPM_GRAPH_GRAPH_LINE_THICKNESS - 1; i++) { // first draw actual value line
oled_write_pixel(3, x + i + vertical_offset, true);
}
# ifdef OLED_WPM_GRAPH_VERTICAL_LINE
@ -577,20 +581,11 @@ void render_wpm_graph(uint8_t max_lines_graph, uint8_t vertical_offset) {
}
}
# endif
oled_pan(false); // then move the entire graph one pixel to the right
static const char PROGMEM display_border[3] = {0x0, 0xFF, 0x0};
for (uint8_t i = 0; i < 7; i++) {
oled_set_cursor(0, i + 8);
oled_write_raw_P(display_border, sizeof(display_border));
oled_set_cursor(21, i + 8);
oled_write_raw_P(display_border, sizeof(display_border));
}
static const char PROGMEM footer_image[] = {0, 3, 4, 8, 16, 32, 64, 128, 128, 128, 128, 128, 128, 128, 192, 224, 240, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 240, 224, 192, 128, 128, 128, 128, 128, 128, 128, 64, 32, 16, 8, 4, 3, 0};
oled_set_cursor(0, 15);
oled_write_raw_P(footer_image, sizeof(footer_image));
timer = timer_read(); // refresh the timer for the next iteration
# include <math.h>
uint8_t y_start = ceil(vertical_offset / 8);
uint8_t y_length = y_start + ceil(max_lines_graph / 8);
oled_pan_section(false, y_start, y_length, 3, 125); // then move the entire graph one pixel to the right
timer = timer_read(); // refresh the timer for the next iteration
}
#endif
}
@ -617,13 +612,13 @@ __attribute__((weak)) void oled_driver_render_logo_right(void) {
// WPM-responsive animation stuff here
#define OLED_SLEEP_FRAMES 2
#define OLED_SLEEP_SPEED 10 // below this wpm value your animation will idle
#define OLED_SLEEP_SPEED 10 // below this wpm value your animation will idle
#define OLED_WAKE_FRAMES 2 // uncomment if >1
#define OLED_WAKE_SPEED OLED_SLEEP_SPEED // below this wpm value your animation will idle
#define OLED_WAKE_FRAMES 2 // uncomment if >1
#define OLED_WAKE_SPEED OLED_SLEEP_SPEED // below this wpm value your animation will idle
#define OLED_KAKI_FRAMES 3
#define OLED_KAKI_SPEED 40 // above this wpm value typing animation to triggere
#define OLED_KAKI_SPEED 40 // above this wpm value typing animation to triggere
#define OLED_RTOGI_FRAMES 2
//#define OLED_LTOGI_FRAMES 2
@ -781,25 +776,13 @@ void oled_driver_render_logo_left(void) {
# if (defined(KEYBOARD_bastardkb_charybdis) || defined(KEYBOARD_handwired_tractyl_manuform)) && defined(POINTING_DEVICE_ENABLE)
render_pointing_dpi_status(charybdis_get_pointer_sniping_enabled() ? charybdis_get_pointer_sniping_dpi() : charybdis_get_pointer_default_dpi(), 1);
// credit and thanks to jaspertandy on discord for these images
static const char PROGMEM mouse_logo[3][2][16] = {
// mouse icon
{
{ 0, 0, 0, 252, 2, 2, 2, 58, 2, 2, 2, 252, 0, 0, 0, 0 },
{ 0, 0, 63, 96, 64, 64, 64, 64, 64, 64, 64, 96, 63, 0, 0, 0 }
},
// crosshair icon
{
{ 128, 240, 136, 228, 146, 138, 202, 127, 202, 138, 146, 228, 136, 240, 128, 0 },
{ 0, 7, 8, 19, 36, 40, 41, 127, 41, 40, 36, 19, 8, 7, 0, 0 }
},
// dragscroll icon
{
{ 0, 0, 112, 136, 156, 2, 15, 1, 15, 2, 140, 68, 56, 0, 0, 0 },
{ 0, 0, 2, 6, 15, 28, 60, 124, 60, 28, 15, 6, 2, 0, 0, 0 }
}
};
// credit and thanks to jaspertandy on discord for these images
static const char PROGMEM mouse_logo[3][2][16] = {// mouse icon
{{0, 0, 0, 252, 2, 2, 2, 58, 2, 2, 2, 252, 0, 0, 0, 0}, {0, 0, 63, 96, 64, 64, 64, 64, 64, 64, 64, 96, 63, 0, 0, 0}},
// crosshair icon
{{128, 240, 136, 228, 146, 138, 202, 127, 202, 138, 146, 228, 136, 240, 128, 0}, {0, 7, 8, 19, 36, 40, 41, 127, 41, 40, 36, 19, 8, 7, 0, 0}},
// dragscroll icon
{{0, 0, 112, 136, 156, 2, 15, 1, 15, 2, 140, 68, 56, 0, 0, 0}, {0, 0, 2, 6, 15, 28, 60, 124, 60, 28, 15, 6, 2, 0, 0, 0}}};
uint8_t image_index = 0;
# ifdef OLED_DISPLAY_TEST
@ -847,7 +830,7 @@ void render_status_right(void) {
#if !defined(OLED_DISPLAY_VERBOSE) && defined(WPM_ENABLE) && !defined(CONVERT_TO_PROTON_C)
render_wpm(2);
#endif
render_keylock_status(host_keyboard_leds());
render_keylock_status(host_keyboard_led_state());
}
void render_status_left(void) {
@ -860,9 +843,11 @@ void render_status_left(void) {
render_keylogger_status();
}
__attribute__((weak)) void oled_render_large_display(void) {}
__attribute__((weak)) void oled_render_large_display(bool side) {}
__attribute__((weak)) oled_rotation_t oled_init_keymap(oled_rotation_t rotation) { return rotation; }
__attribute__((weak)) oled_rotation_t oled_init_keymap(oled_rotation_t rotation) {
return rotation;
}
oled_rotation_t oled_init_user(oled_rotation_t rotation) {
if (is_keyboard_master()) {
@ -876,10 +861,11 @@ oled_rotation_t oled_init_user(oled_rotation_t rotation) {
return oled_init_keymap(rotation);
}
__attribute__((weak)) bool oled_task_keymap(void) { return true; }
__attribute__((weak)) bool oled_task_keymap(void) {
return true;
}
bool oled_task_user(void) {
if (is_keyboard_master()) {
#ifndef OLED_DISPLAY_TEST
if (timer_elapsed32(oled_timer) > 60000) {
@ -896,29 +882,29 @@ bool oled_task_user(void) {
return false;
}
#if defined(OLED_DISPLAY_128X128)
oled_set_cursor(0, 7);
oled_render_large_display();
#endif
#if defined(OLED_DISPLAY_VERBOSE)
static const char PROGMEM header_image[] = {
0, 192, 32, 16, 8, 4, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 7, 15, 31, 63, 127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, 63, 31, 15, 7, 3, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 8, 16, 32, 192, 0,
// 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 7, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 7, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255, 0
};
static const char PROGMEM footer_image[] = {0, 3, 4, 8, 16, 32, 64, 128, 128, 128, 128, 128, 128, 128, 192, 224, 240, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 240, 224, 192, 128, 128, 128, 128, 128, 128, 128, 64, 32, 16, 8, 4, 3, 0};
oled_set_cursor(0, 0);
oled_write_raw_P(header_image, sizeof(header_image));
oled_set_cursor(0, 1);
#endif
#ifndef OLED_DISPLAY_TEST
if (is_keyboard_left()) {
#endif
render_status_left();
#if defined(OLED_DISPLAY_128X128)
oled_set_cursor(0, 7);
oled_render_large_display(true);
#endif
#ifndef OLED_DISPLAY_TEST
} else {
render_status_right();
# if defined(OLED_DISPLAY_128X128)
oled_set_cursor(0, 7);
oled_render_large_display(false);
# endif
}
#endif
@ -936,6 +922,7 @@ bool oled_task_user(void) {
oled_write_raw_P(display_border, sizeof(display_border));
}
static const char PROGMEM footer_image[] = {0, 3, 4, 8, 16, 32, 64, 128, 128, 128, 128, 128, 128, 128, 192, 224, 240, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 240, 224, 192, 128, 128, 128, 128, 128, 128, 128, 64, 32, 16, 8, 4, 3, 0};
oled_set_cursor(0, num_of_rows);
oled_write_raw_P(footer_image, sizeof(footer_image));
#endif

View file

@ -23,11 +23,11 @@ extern deferred_token kittoken;
void oled_driver_render_logo(void);
bool process_record_user_oled(uint16_t keycode, keyrecord_t *record);
oled_rotation_t oled_init_keymap(oled_rotation_t rotation);
extern uint32_t oled_timer;
void oled_timer_reset(void);
void render_keylogger_status(void);
void render_default_layer_state(void);
void render_layer_state(void);
void render_keylock_status(uint8_t led_usb_state);
void render_keylock_status(led_t led_usb_state);
void render_matrix_scan_rate(uint8_t padding);
void render_mod_status(uint8_t modifiers);
void render_bootmagic_status(void);
@ -37,9 +37,12 @@ void render_wpm(uint8_t padding);
void render_pointing_dpi_status(uint16_t cpi, uint8_t padding);
void oled_driver_render_logo_left(void);
void oled_driver_render_logo_right(void);
void oled_render_large_display(void);
void oled_render_large_display(bool side);
void render_wpm_graph(uint8_t max_lines_graph, uint8_t vertical_offset);
void oled_pan_section(bool left, uint16_t y_start, uint16_t y_end, uint16_t x_start, uint16_t x_end);
#if defined(OLED_DISPLAY_128X128) || defined(OLED_DISPLAY_128X64)
# define OLED_DISPLAY_VERBOSE

View file

@ -521,6 +521,25 @@ void oled_pan(bool left) {
oled_dirty = OLED_ALL_BLOCKS_MASK;
}
void oled_pan_section(bool left, uint16_t y_start, uint16_t y_end, uint16_t x_start, uint16_t x_end) {
uint16_t i = 0;
for (uint16_t y = y_start; y < y_end; y++) {
if (left) {
for (uint16_t x = x_start; x < x_end - 1; x++) {
i = y * OLED_DISPLAY_WIDTH + x;
oled_buffer[i] = oled_buffer[i + 1];
oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE));
}
} else {
for (uint16_t x = x_end - 1; x > 0; x--) {
i = y * OLED_DISPLAY_WIDTH + x;
oled_buffer[i] = oled_buffer[i - 1];
oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE));
}
}
}
}
oled_buffer_reader_t oled_read_raw(uint16_t start_index) {
if (start_index > OLED_MATRIX_SIZE) start_index = OLED_MATRIX_SIZE;
oled_buffer_reader_t ret_reader;

View file

@ -29,7 +29,7 @@ report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) {
if (x != 0 && y != 0) {
mouse_timer = timer_read();
#ifdef OLED_ENABLE
oled_timer = timer_read32();
oled_timer_reset();
#endif
if (timer_elapsed(mouse_debounce_timer) > TAP_CHECK) {
if (enable_acceleration) {
@ -94,8 +94,10 @@ bool process_record_pointing(uint16_t keycode, keyrecord_t* record) {
record->event.pressed ? mouse_keycode_tracker++ : mouse_keycode_tracker--;
mouse_timer = timer_read();
break;
#if 0
case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX:
break;
#endif
case QK_MOD_TAP ... QK_MOD_TAP_MAX:
if (record->event.pressed || !record->tap.count) {
break;

View file

@ -57,6 +57,9 @@ void keyboard_post_init_rgb_matrix(void) {
rgb_matrix_mode_noeeprom(RGB_MATRIX_REST_MODE);
}
#endif
if (userspace_config.rgb_layer_change) {
rgb_matrix_set_flags(LED_FLAG_UNDERGLOW | LED_FLAG_KEYLIGHT | LED_FLAG_INDICATOR);
}
}
bool process_record_user_rgb_matrix(uint16_t keycode, keyrecord_t *record) {

View file

@ -13,8 +13,9 @@ GRAVE_ESC_ENABLE = no
# DEBUG_MATRIX_SCAN_RATE_ENABLE = api
ifneq ($(strip $(NO_SECRETS)), yes)
ifneq ("$(wildcard $(USER_PATH)/keyrecords/secrets.c)","")
SRC += $(USER_PATH)/keyrecords/secrets.c
ifneq ("$(wildcard $(USER_PATH)/../../../qmk_secrets/secrets.c)","")
SRC += $(USER_PATH)/../../../qmk_secrets/secrets.c
SECURE_ENABLE = yes
endif
ifeq ($(strip $(NO_SECRETS)), lite)
OPT_DEFS += -DNO_SECRETS
@ -102,6 +103,7 @@ ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes)
ifeq ($(strip $(CUSTOM_POINTING_DEVICE)), yes)
SRC += $(USER_PATH)/pointing/pointing.c
OPT_DEFS += -DCUSTOM_POINTING_DEVICE
OPT_DEFS += -DMOUSE_EXT_REPORT
endif
endif
@ -119,9 +121,3 @@ ifeq ($(strip $(AUTOCORRECTION_ENABLE)), yes)
SRC += $(USER_PATH)/keyrecords/autocorrection/autocorrection.c
OPT_DEFS += -DAUTOCORRECTION_ENABLE
endif
CAPS_WORD_ENABLE ?= no
ifeq ($(strip $(CAPS_WORD_ENABLE)), yes)
SRC += $(USER_PATH)/keyrecords/caps_word.c
OPT_DEFS += -DCAPS_WORD_ENABLE
endif

View file

@ -11,6 +11,7 @@
#ifdef UNICODE_COMMON_ENABLE
# include "process_unicode_common.h"
extern unicode_config_t unicode_config;
# include "keyrecords/unicode.h"
#endif
#ifdef AUDIO_ENABLE
# include "audio.h"
@ -97,6 +98,7 @@ void user_transport_update(void) {
#endif
#ifdef UNICODE_COMMON_ENABLE
user_state.unicode_mode = unicode_config.input_mode;
user_state.unicode_typing_mode = typing_mode;
#endif
#ifdef SWAP_HANDS_ENABLE
user_state.swap_hands = swap_hands;
@ -110,6 +112,7 @@ void user_transport_update(void) {
user_state.raw = transport_user_state;
#ifdef UNICODE_COMMON_ENABLE
unicode_config.input_mode = user_state.unicode_mode;
typing_mode = user_state.unicode_typing_mode;
#endif
#if defined(CUSTOM_POINTING_DEVICE)
tap_toggling = user_state.tap_toggling;

View file

@ -12,12 +12,13 @@ extern char keylog_str[OLED_KEYLOGGER_LENGTH];
typedef union {
uint32_t raw;
struct {
bool audio_enable :1;
bool audio_clicky_enable :1;
bool tap_toggling :1;
uint8_t unicode_mode :3;
bool swap_hands :1;
bool host_driver_disabled :1;
bool audio_enable :1;
bool audio_clicky_enable :1;
bool tap_toggling :1;
uint8_t unicode_mode :3;
bool swap_hands :1;
bool host_driver_disabled :1;
uint8_t unicode_typing_mode :3;
};
} user_runtime_config_t;

View file

@ -25,21 +25,6 @@ __attribute__((weak)) bool process_record_keymap(uint16_t keycode, keyrecord_t *
// And use "NEWPLACEHOLDER" for new safe range
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
case KC_MAKE:
if (!record->event.pressed) {
SEND_STRING("make " QMK_KEYBOARD ":" QMK_KEYMAP
#if (defined(BOOTLOADER_DFU) || defined(BOOTLOADER_LUFA_DFU) || defined(BOOTLOADER_QMK_DFU))
":dfu"
#elif defined(BOOTLOADER_HALFKAY)
":teensy"
#elif defined(BOOTLOADER_CATERINA)
":avrdude"
#endif
SS_TAP(X_ENTER));
}
return false;
break;
case VRSN:
if (record->event.pressed) {
SEND_STRING(QMK_KEYBOARD "/" QMK_KEYMAP " @ " QMK_VERSION);

View file

@ -12,7 +12,7 @@
enum custom_keycodes {
VRSN = SAFE_RANGE, // can always be here
KC_MAKE,
KC_RESET,
QK_MAKE,
QK_BOOT,
NEWPLACEHOLDER // use "NEWPLACEHOLDER for keymap specific codes
};