mirror of
https://github.com/qmk/qmk_firmware
synced 2024-12-22 08:26:21 +00:00
Unicode, Unicodemap and UCIS refactor (#21659)
This commit is contained in:
parent
95681b8ff4
commit
70e34e491c
2
Makefile
2
Makefile
@ -328,7 +328,7 @@ define PARSE_TEST
|
||||
ifeq ($$(TEST_NAME),all)
|
||||
MATCHED_TESTS := $$(TEST_LIST)
|
||||
else
|
||||
MATCHED_TESTS := $$(foreach TEST, $$(TEST_LIST),$$(if $$(findstring $$(TEST_NAME), $$(notdir $$(TEST))), $$(TEST),))
|
||||
MATCHED_TESTS := $$(foreach TEST, $$(TEST_LIST),$$(if $$(findstring x$$(TEST_NAME)x, x$$(notdir $$(TEST))x), $$(TEST),))
|
||||
endif
|
||||
$$(foreach TEST,$$(MATCHED_TESTS),$$(eval $$(call BUILD_TEST,$$(TEST),$$(TEST_TARGET))))
|
||||
endef
|
||||
|
@ -784,13 +784,15 @@ endif
|
||||
ifeq ($(strip $(UCIS_ENABLE)), yes)
|
||||
OPT_DEFS += -DUCIS_ENABLE
|
||||
UNICODE_COMMON := yes
|
||||
SRC += $(QUANTUM_DIR)/process_keycode/process_ucis.c
|
||||
SRC += $(QUANTUM_DIR)/process_keycode/process_ucis.c \
|
||||
$(QUANTUM_DIR)/unicode/ucis.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(UNICODEMAP_ENABLE)), yes)
|
||||
OPT_DEFS += -DUNICODEMAP_ENABLE
|
||||
UNICODE_COMMON := yes
|
||||
SRC += $(QUANTUM_DIR)/process_keycode/process_unicodemap.c
|
||||
SRC += $(QUANTUM_DIR)/process_keycode/process_unicodemap.c \
|
||||
$(QUANTUM_DIR)/unicode/unicodemap.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(UNICODE_ENABLE)), yes)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,7 @@ void dance_key_one (tap_dance_state_t *state, void *user_data) {
|
||||
tap_code(KC_ENTER);
|
||||
reset_tap_dance (state);
|
||||
} else if (state->count == 2) {
|
||||
cycle_unicode_input_mode(+1);
|
||||
unicode_input_mode_step();
|
||||
reset_tap_dance (state);
|
||||
}
|
||||
}
|
||||
|
@ -15,110 +15,30 @@
|
||||
*/
|
||||
|
||||
#include "process_ucis.h"
|
||||
#include "unicode.h"
|
||||
#include "keycode.h"
|
||||
#include "wait.h"
|
||||
|
||||
ucis_state_t ucis_state;
|
||||
|
||||
void ucis_start(void) {
|
||||
ucis_state.count = 0;
|
||||
ucis_state.in_progress = true;
|
||||
|
||||
ucis_start_user();
|
||||
}
|
||||
|
||||
__attribute__((weak)) void ucis_start_user(void) {
|
||||
register_unicode(0x2328); // ⌨
|
||||
}
|
||||
|
||||
__attribute__((weak)) void ucis_success(uint8_t symbol_index) {}
|
||||
|
||||
static bool is_uni_seq(char *seq) {
|
||||
uint8_t i;
|
||||
for (i = 0; seq[i]; i++) {
|
||||
uint16_t keycode;
|
||||
if ('1' <= seq[i] && seq[i] <= '0') {
|
||||
keycode = seq[i] - '1' + KC_1;
|
||||
} else {
|
||||
keycode = seq[i] - 'a' + KC_A;
|
||||
}
|
||||
if (i > ucis_state.count || ucis_state.codes[i] != keycode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return ucis_state.codes[i] == KC_ENTER || ucis_state.codes[i] == KC_SPACE;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void ucis_symbol_fallback(void) {
|
||||
for (uint8_t i = 0; i < ucis_state.count - 1; i++) {
|
||||
tap_code(ucis_state.codes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((weak)) void ucis_cancel(void) {}
|
||||
|
||||
void register_ucis(const uint32_t *code_points) {
|
||||
for (int i = 0; i < UCIS_MAX_CODE_POINTS && code_points[i]; i++) {
|
||||
register_unicode(code_points[i]);
|
||||
}
|
||||
}
|
||||
#include "ucis.h"
|
||||
#include "keycodes.h"
|
||||
|
||||
bool process_ucis(uint16_t keycode, keyrecord_t *record) {
|
||||
if (!ucis_state.in_progress || !record->event.pressed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool special = keycode == KC_SPACE || keycode == KC_ENTER || keycode == KC_ESCAPE || keycode == KC_BACKSPACE;
|
||||
if (ucis_state.count >= UCIS_MAX_SYMBOL_LENGTH && !special) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ucis_state.codes[ucis_state.count] = keycode;
|
||||
ucis_state.count++;
|
||||
|
||||
switch (keycode) {
|
||||
case KC_BACKSPACE:
|
||||
if (ucis_state.count >= 2) {
|
||||
ucis_state.count -= 2;
|
||||
return true;
|
||||
} else {
|
||||
ucis_state.count--;
|
||||
return false;
|
||||
}
|
||||
|
||||
case KC_SPACE:
|
||||
case KC_ENTER:
|
||||
case KC_ESCAPE:
|
||||
for (uint8_t i = 0; i < ucis_state.count; i++) {
|
||||
tap_code(KC_BACKSPACE);
|
||||
}
|
||||
|
||||
if (keycode == KC_ESCAPE) {
|
||||
ucis_state.in_progress = false;
|
||||
ucis_cancel();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t i;
|
||||
bool symbol_found = false;
|
||||
for (i = 0; ucis_symbol_table[i].symbol; i++) {
|
||||
if (is_uni_seq(ucis_symbol_table[i].symbol)) {
|
||||
symbol_found = true;
|
||||
register_ucis(ucis_symbol_table[i].code_points);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (symbol_found) {
|
||||
ucis_success(i);
|
||||
} else {
|
||||
ucis_symbol_fallback();
|
||||
}
|
||||
|
||||
ucis_state.in_progress = false;
|
||||
if (ucis_active() && record->event.pressed) {
|
||||
bool special = keycode == KC_SPACE || keycode == KC_ENTER || keycode == KC_ESCAPE || keycode == KC_BACKSPACE;
|
||||
if (ucis_count() >= UCIS_MAX_INPUT_LENGTH && !special) {
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
return true;
|
||||
if (!ucis_add(keycode)) {
|
||||
switch (keycode) {
|
||||
case KC_BACKSPACE:
|
||||
return ucis_remove_last();
|
||||
case KC_ESCAPE:
|
||||
ucis_cancel();
|
||||
return false;
|
||||
case KC_SPACE:
|
||||
case KC_ENTER:
|
||||
ucis_finish();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -18,48 +18,6 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "action.h"
|
||||
|
||||
#ifndef UCIS_MAX_SYMBOL_LENGTH
|
||||
# define UCIS_MAX_SYMBOL_LENGTH 32
|
||||
#endif
|
||||
#ifndef UCIS_MAX_CODE_POINTS
|
||||
# define UCIS_MAX_CODE_POINTS 3
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
char * symbol;
|
||||
uint32_t code_points[UCIS_MAX_CODE_POINTS];
|
||||
} ucis_symbol_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t count;
|
||||
uint16_t codes[UCIS_MAX_SYMBOL_LENGTH];
|
||||
bool in_progress : 1;
|
||||
} ucis_state_t;
|
||||
|
||||
extern ucis_state_t ucis_state;
|
||||
|
||||
// clang-format off
|
||||
|
||||
#define UCIS_TABLE(...) \
|
||||
{ \
|
||||
__VA_ARGS__, \
|
||||
{ NULL, {} } \
|
||||
}
|
||||
#define UCIS_SYM(name, ...) \
|
||||
{ name, {__VA_ARGS__} }
|
||||
|
||||
// clang-format on
|
||||
|
||||
extern const ucis_symbol_t ucis_symbol_table[];
|
||||
|
||||
void ucis_start(void);
|
||||
void ucis_start_user(void);
|
||||
void ucis_symbol_fallback(void);
|
||||
void ucis_success(uint8_t symbol_index);
|
||||
|
||||
void register_ucis(const uint32_t *code_points);
|
||||
|
||||
bool process_ucis(uint16_t keycode, keyrecord_t *record);
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "process_unicode.h"
|
||||
#include "unicode.h"
|
||||
#include "keycodes.h"
|
||||
#include "quantum_keycodes.h"
|
||||
|
||||
bool process_unicode(uint16_t keycode, keyrecord_t *record) {
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "action.h"
|
||||
|
||||
bool process_unicode(uint16_t keycode, keyrecord_t *record);
|
||||
|
@ -17,7 +17,8 @@
|
||||
#include "process_unicode_common.h"
|
||||
#include "unicode.h"
|
||||
#include "action_util.h"
|
||||
#include "keycode.h"
|
||||
#include "keycodes.h"
|
||||
#include "modifiers.h"
|
||||
|
||||
#if defined(UNICODE_ENABLE)
|
||||
# include "process_unicode.h"
|
||||
@ -32,10 +33,18 @@ bool process_unicode_common(uint16_t keycode, keyrecord_t *record) {
|
||||
bool shifted = get_mods() & MOD_MASK_SHIFT;
|
||||
switch (keycode) {
|
||||
case QK_UNICODE_MODE_NEXT:
|
||||
cycle_unicode_input_mode(shifted ? -1 : +1);
|
||||
if (shifted) {
|
||||
unicode_input_mode_step_reverse();
|
||||
} else {
|
||||
unicode_input_mode_step();
|
||||
}
|
||||
break;
|
||||
case QK_UNICODE_MODE_PREVIOUS:
|
||||
cycle_unicode_input_mode(shifted ? +1 : -1);
|
||||
if (shifted) {
|
||||
unicode_input_mode_step();
|
||||
} else {
|
||||
unicode_input_mode_step_reverse();
|
||||
}
|
||||
break;
|
||||
case QK_UNICODE_MODE_MACOS:
|
||||
set_unicode_input_mode(UNICODE_MODE_MACOS);
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "action.h"
|
||||
|
||||
bool process_unicode_common(uint16_t keycode, keyrecord_t *record);
|
||||
|
@ -15,41 +15,12 @@
|
||||
*/
|
||||
|
||||
#include "process_unicodemap.h"
|
||||
#include "unicode.h"
|
||||
#include "quantum_keycodes.h"
|
||||
#include "keycode.h"
|
||||
#include "action_util.h"
|
||||
#include "host.h"
|
||||
|
||||
__attribute__((weak)) uint16_t unicodemap_index(uint16_t keycode) {
|
||||
if (keycode >= QK_UNICODEMAP_PAIR) {
|
||||
// Keycode is a pair: extract index based on Shift / Caps Lock state
|
||||
uint16_t index;
|
||||
|
||||
uint8_t mods = get_mods() | get_weak_mods();
|
||||
#ifndef NO_ACTION_ONESHOT
|
||||
mods |= get_oneshot_mods();
|
||||
#endif
|
||||
|
||||
bool shift = mods & MOD_MASK_SHIFT;
|
||||
bool caps = host_keyboard_led_state().caps_lock;
|
||||
if (shift ^ caps) {
|
||||
index = QK_UNICODEMAP_PAIR_GET_SHIFTED_INDEX(keycode);
|
||||
} else {
|
||||
index = QK_UNICODEMAP_PAIR_GET_UNSHIFTED_INDEX(keycode);
|
||||
}
|
||||
|
||||
return index;
|
||||
} else {
|
||||
// Keycode is a regular index
|
||||
return QK_UNICODEMAP_GET_INDEX(keycode);
|
||||
}
|
||||
}
|
||||
#include "unicodemap.h"
|
||||
#include "keycodes.h"
|
||||
|
||||
bool process_unicodemap(uint16_t keycode, keyrecord_t *record) {
|
||||
if (keycode >= QK_UNICODEMAP && keycode <= QK_UNICODEMAP_PAIR_MAX && record->event.pressed) {
|
||||
uint32_t code_point = pgm_read_dword(unicode_map + unicodemap_index(keycode));
|
||||
register_unicode(code_point);
|
||||
register_unicodemap(unicodemap_index(keycode));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -18,11 +18,6 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "action.h"
|
||||
#include "progmem.h"
|
||||
|
||||
extern const uint32_t unicode_map[] PROGMEM;
|
||||
|
||||
uint16_t unicodemap_index(uint16_t keycode);
|
||||
bool process_unicodemap(uint16_t keycode, keyrecord_t *record);
|
||||
bool process_unicodemap(uint16_t keycode, keyrecord_t *record);
|
||||
|
@ -68,10 +68,6 @@
|
||||
# include "process_unicode_common.h"
|
||||
#endif
|
||||
|
||||
#ifdef UNICODE_ENABLE
|
||||
# include "process_unicode.h"
|
||||
#endif
|
||||
|
||||
#ifdef VELOCIKEY_ENABLE
|
||||
# include "velocikey.h"
|
||||
#endif
|
||||
|
@ -97,16 +97,16 @@ extern layer_state_t layer_state;
|
||||
# include "leader.h"
|
||||
#endif
|
||||
|
||||
#ifdef UNICODE_COMMON_ENABLE
|
||||
# include "unicode.h"
|
||||
#endif
|
||||
|
||||
#ifdef UCIS_ENABLE
|
||||
# include "process_ucis.h"
|
||||
# include "ucis.h"
|
||||
#endif
|
||||
|
||||
#ifdef UNICODEMAP_ENABLE
|
||||
# include "process_unicodemap.h"
|
||||
#endif
|
||||
|
||||
#ifdef UNICODE_COMMON_ENABLE
|
||||
# include "unicode.h"
|
||||
# include "unicodemap.h"
|
||||
#endif
|
||||
|
||||
#ifdef KEY_OVERRIDE_ENABLE
|
||||
|
@ -55,4 +55,4 @@
|
||||
#define GUI_TOG QK_MAGIC_TOGGLE_GUI
|
||||
|
||||
#define X(i) UM(i)
|
||||
#define XP(i, j) UM(i, j)
|
||||
#define XP(i, j) UP(i, j)
|
||||
|
96
quantum/unicode/ucis.c
Normal file
96
quantum/unicode/ucis.c
Normal file
@ -0,0 +1,96 @@
|
||||
// Copyright 2023 QMK
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "ucis.h"
|
||||
#include "unicode.h"
|
||||
#include "action.h"
|
||||
|
||||
uint8_t count = 0;
|
||||
bool active = false;
|
||||
char input[UCIS_MAX_INPUT_LENGTH] = {0};
|
||||
|
||||
void ucis_start(void) {
|
||||
count = 0;
|
||||
active = true;
|
||||
|
||||
register_unicode(0x2328); // ⌨
|
||||
}
|
||||
|
||||
bool ucis_active(void) {
|
||||
return active;
|
||||
}
|
||||
|
||||
uint8_t ucis_count(void) {
|
||||
return count;
|
||||
}
|
||||
|
||||
static char keycode_to_char(uint16_t keycode) {
|
||||
if (keycode >= KC_A && keycode <= KC_Z) {
|
||||
return 'a' + (keycode - KC_A);
|
||||
} else if (keycode >= KC_1 && keycode <= KC_9) {
|
||||
return '1' + (keycode - KC_1);
|
||||
} else if (keycode == KC_0) {
|
||||
return '0';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ucis_add(uint16_t keycode) {
|
||||
char c = keycode_to_char(keycode);
|
||||
if (c) {
|
||||
input[count++] = c;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ucis_remove_last(void) {
|
||||
if (count) {
|
||||
count--;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool match_mnemonic(char *mnemonic) {
|
||||
for (uint8_t i = 0; input[i]; i++) {
|
||||
if (i > count || input[i] != mnemonic[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ucis_finish(void) {
|
||||
uint8_t i = 0;
|
||||
bool found = false;
|
||||
for (; ucis_symbol_table[i].mnemonic; i++) {
|
||||
if (match_mnemonic(ucis_symbol_table[i].mnemonic)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
for (uint8_t j = 0; j <= count; j++) {
|
||||
tap_code(KC_BACKSPACE);
|
||||
}
|
||||
register_ucis(i);
|
||||
}
|
||||
|
||||
active = false;
|
||||
}
|
||||
|
||||
void ucis_cancel(void) {
|
||||
count = 0;
|
||||
active = false;
|
||||
}
|
||||
|
||||
void register_ucis(uint8_t index) {
|
||||
const uint32_t *code_points = ucis_symbol_table[index].code_points;
|
||||
|
||||
for (int i = 0; i < UCIS_MAX_CODE_POINTS && code_points[i]; i++) {
|
||||
register_unicode(code_points[i]);
|
||||
}
|
||||
}
|
97
quantum/unicode/ucis.h
Normal file
97
quantum/unicode/ucis.h
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright 2023 QMK
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \defgroup ucis UCIS
|
||||
* \{
|
||||
*/
|
||||
|
||||
#ifndef UCIS_MAX_INPUT_LENGTH
|
||||
# define UCIS_MAX_INPUT_LENGTH 32
|
||||
#endif
|
||||
|
||||
#ifndef UCIS_MAX_CODE_POINTS
|
||||
# define UCIS_MAX_CODE_POINTS 3
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
char* mnemonic;
|
||||
uint32_t code_points[UCIS_MAX_CODE_POINTS];
|
||||
} ucis_symbol_t;
|
||||
|
||||
// clang-format off
|
||||
|
||||
#define UCIS_TABLE(...) { \
|
||||
__VA_ARGS__, \
|
||||
{ NULL, {} } \
|
||||
}
|
||||
|
||||
#define UCIS_SYM(name, ...) { \
|
||||
.mnemonic = name, \
|
||||
.code_points = {__VA_ARGS__} \
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
||||
extern const ucis_symbol_t ucis_symbol_table[];
|
||||
|
||||
/**
|
||||
* \brief Begin the input sequence.
|
||||
*/
|
||||
void ucis_start(void);
|
||||
|
||||
/**
|
||||
* \brief Whether UCIS is currently active.
|
||||
*
|
||||
* \return `true` if UCIS is active.
|
||||
*/
|
||||
bool ucis_active(void);
|
||||
|
||||
/**
|
||||
* \brief Get the number of characters in the input sequence buffer.
|
||||
*
|
||||
* \return The current input sequence buffer length.
|
||||
*/
|
||||
uint8_t ucis_count(void);
|
||||
|
||||
/**
|
||||
* \brief Add the given keycode to the input sequence buffer.
|
||||
*
|
||||
* \param keycode The keycode to add. Must be between `KC_A` and `KC_Z`, or `KC_1` and `KC_0`.
|
||||
*
|
||||
* \return `true` if the keycode was added.
|
||||
*/
|
||||
bool ucis_add(uint16_t keycode);
|
||||
|
||||
/**
|
||||
* \brief Remove the last character from the input sequence.
|
||||
*
|
||||
* \return `true` if the sequence was not empty.
|
||||
*/
|
||||
bool ucis_remove_last(void);
|
||||
|
||||
/**
|
||||
* Mark the input sequence as complete, and attempt to match.
|
||||
*/
|
||||
void ucis_finish(void);
|
||||
|
||||
/**
|
||||
* \brief Cancel the input sequence.
|
||||
*/
|
||||
void ucis_cancel(void);
|
||||
|
||||
/**
|
||||
* Send the code point(s) for the given UCIS index.
|
||||
*
|
||||
* \param index The index into the UCIS symbol table.
|
||||
*/
|
||||
void register_ucis(uint8_t index);
|
||||
|
||||
/** \} */
|
@ -73,16 +73,8 @@ static int8_t selected_count = ARRAY_SIZE(selected);
|
||||
static int8_t selected_index;
|
||||
#endif
|
||||
|
||||
/** \brief unicode input mode set at user level
|
||||
*
|
||||
* Run user code on unicode input mode change
|
||||
*/
|
||||
__attribute__((weak)) void unicode_input_mode_set_user(uint8_t input_mode) {}
|
||||
|
||||
/** \brief unicode input mode set at keyboard level
|
||||
*
|
||||
* Run keyboard code on unicode input mode change
|
||||
*/
|
||||
__attribute__((weak)) void unicode_input_mode_set_kb(uint8_t input_mode) {
|
||||
unicode_input_mode_set_user(input_mode);
|
||||
}
|
||||
@ -172,6 +164,10 @@ uint8_t get_unicode_input_mode(void) {
|
||||
return unicode_config.input_mode;
|
||||
}
|
||||
|
||||
static void persist_unicode_input_mode(void) {
|
||||
eeprom_update_byte(EECONFIG_UNICODEMODE, unicode_config.input_mode);
|
||||
}
|
||||
|
||||
void set_unicode_input_mode(uint8_t mode) {
|
||||
unicode_config.input_mode = mode;
|
||||
persist_unicode_input_mode();
|
||||
@ -182,26 +178,34 @@ void set_unicode_input_mode(uint8_t mode) {
|
||||
dprintf("Unicode input mode set to: %u\n", unicode_config.input_mode);
|
||||
}
|
||||
|
||||
void cycle_unicode_input_mode(int8_t offset) {
|
||||
static void cycle_unicode_input_mode(int8_t offset) {
|
||||
#if UNICODE_SELECTED_MODES != -1
|
||||
selected_index = (selected_index + offset) % selected_count;
|
||||
if (selected_index < 0) {
|
||||
selected_index += selected_count;
|
||||
}
|
||||
|
||||
unicode_config.input_mode = selected[selected_index];
|
||||
|
||||
# if UNICODE_CYCLE_PERSIST
|
||||
persist_unicode_input_mode();
|
||||
# endif
|
||||
|
||||
# ifdef AUDIO_ENABLE
|
||||
unicode_play_song(unicode_config.input_mode);
|
||||
# endif
|
||||
|
||||
unicode_input_mode_set_kb(unicode_config.input_mode);
|
||||
dprintf("Unicode input mode cycle to: %u\n", unicode_config.input_mode);
|
||||
#endif
|
||||
}
|
||||
|
||||
void persist_unicode_input_mode(void) {
|
||||
eeprom_update_byte(EECONFIG_UNICODEMODE, unicode_config.input_mode);
|
||||
void unicode_input_mode_step(void) {
|
||||
cycle_unicode_input_mode(1);
|
||||
}
|
||||
|
||||
void unicode_input_mode_step_reverse(void) {
|
||||
cycle_unicode_input_mode(-1);
|
||||
}
|
||||
|
||||
__attribute__((weak)) void unicode_input_start(void) {
|
||||
|
@ -19,6 +19,13 @@
|
||||
#include <stdint.h>
|
||||
#include "unicode_keycodes.h"
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \defgroup unicode Unicode
|
||||
* \{
|
||||
*/
|
||||
|
||||
typedef union {
|
||||
uint8_t raw;
|
||||
struct {
|
||||
@ -41,21 +48,87 @@ enum unicode_input_modes {
|
||||
UNICODE_MODE_COUNT // Number of available input modes (always leave at the end)
|
||||
};
|
||||
|
||||
void unicode_input_mode_init(void);
|
||||
uint8_t get_unicode_input_mode(void);
|
||||
void set_unicode_input_mode(uint8_t mode);
|
||||
void cycle_unicode_input_mode(int8_t offset);
|
||||
void persist_unicode_input_mode(void);
|
||||
void unicode_input_mode_init(void);
|
||||
|
||||
/**
|
||||
* \brief Get the current Unicode input mode.
|
||||
*
|
||||
* \return The currently active Unicode input mode.
|
||||
*/
|
||||
uint8_t get_unicode_input_mode(void);
|
||||
|
||||
/**
|
||||
* \brief Set the Unicode input mode.
|
||||
*
|
||||
* \param mode The input mode to set.
|
||||
*/
|
||||
void set_unicode_input_mode(uint8_t mode);
|
||||
|
||||
/**
|
||||
* \brief Change to the next Unicode input mode.
|
||||
*/
|
||||
void unicode_input_mode_step(void);
|
||||
|
||||
/**
|
||||
* \brief Change to the previous Unicode input mode.
|
||||
*/
|
||||
void unicode_input_mode_step_reverse(void);
|
||||
|
||||
/**
|
||||
* \brief User-level callback, invoked when the input mode is changed.
|
||||
*
|
||||
* \param input_mode The new input mode.
|
||||
*/
|
||||
void unicode_input_mode_set_user(uint8_t input_mode);
|
||||
|
||||
/**
|
||||
* \brief Keyboard-level callback, invoked when the input mode is changed.
|
||||
*
|
||||
* \param input_mode The new input mode.
|
||||
*/
|
||||
void unicode_input_mode_set_kb(uint8_t input_mode);
|
||||
|
||||
/**
|
||||
* \brief Begin the Unicode input sequence. The exact behavior depends on the currently selected input mode.
|
||||
*/
|
||||
void unicode_input_start(void);
|
||||
|
||||
/**
|
||||
* \brief Complete the Unicode input sequence. The exact behavior depends on the currently selected input mode.
|
||||
*/
|
||||
void unicode_input_finish(void);
|
||||
|
||||
/**
|
||||
* \brief Cancel the Unicode input sequence. The exact behavior depends on the currently selected input mode.
|
||||
*/
|
||||
void unicode_input_cancel(void);
|
||||
|
||||
/**
|
||||
* \brief Send a 16-bit hex number.
|
||||
*
|
||||
* \param hex The number to send.
|
||||
*/
|
||||
void register_hex(uint16_t hex);
|
||||
|
||||
/**
|
||||
* \brief Send a 32-bit hex number.
|
||||
*
|
||||
* \param hex The number to send.
|
||||
*/
|
||||
void register_hex32(uint32_t hex);
|
||||
|
||||
/**
|
||||
* \brief Input a single Unicode character. A surrogate pair will be sent if required by the input mode.
|
||||
*
|
||||
* \param code_point The code point of the character to send.
|
||||
*/
|
||||
void register_unicode(uint32_t code_point);
|
||||
|
||||
/**
|
||||
* \brief Send a string containing Unicode characters.
|
||||
*
|
||||
* \param str The string to send.
|
||||
*/
|
||||
void send_unicode_string(const char *str);
|
||||
|
||||
/** \} */
|
||||
|
43
quantum/unicode/unicodemap.c
Normal file
43
quantum/unicode/unicodemap.c
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2023 QMK
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "unicodemap.h"
|
||||
#include "unicode.h"
|
||||
#include "keycodes.h"
|
||||
#include "quantum_keycodes.h"
|
||||
#include "modifiers.h"
|
||||
#include "host.h"
|
||||
#include "action_util.h"
|
||||
|
||||
uint8_t unicodemap_index(uint16_t keycode) {
|
||||
if (keycode >= QK_UNICODEMAP_PAIR) {
|
||||
// Keycode is a pair: extract index based on Shift / Caps Lock state
|
||||
uint16_t index;
|
||||
|
||||
uint8_t mods = get_mods() | get_weak_mods();
|
||||
#ifndef NO_ACTION_ONESHOT
|
||||
mods |= get_oneshot_mods();
|
||||
#endif
|
||||
|
||||
bool shift = mods & MOD_MASK_SHIFT;
|
||||
bool caps = host_keyboard_led_state().caps_lock;
|
||||
if (shift ^ caps) {
|
||||
index = QK_UNICODEMAP_PAIR_GET_SHIFTED_INDEX(keycode);
|
||||
} else {
|
||||
index = QK_UNICODEMAP_PAIR_GET_UNSHIFTED_INDEX(keycode);
|
||||
}
|
||||
|
||||
return index;
|
||||
} else {
|
||||
// Keycode is a regular index
|
||||
return QK_UNICODEMAP_GET_INDEX(keycode);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t unicodemap_get_code_point(uint8_t index) {
|
||||
return pgm_read_dword(unicode_map + index);
|
||||
}
|
||||
|
||||
void register_unicodemap(uint8_t index) {
|
||||
register_unicode(unicodemap_get_code_point(index));
|
||||
}
|
43
quantum/unicode/unicodemap.h
Normal file
43
quantum/unicode/unicodemap.h
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2023 QMK
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "progmem.h"
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \defgroup unicodemap Unicode Map
|
||||
* \{
|
||||
*/
|
||||
|
||||
extern const uint32_t unicode_map[] PROGMEM;
|
||||
|
||||
/**
|
||||
* \brief Get the index into the `unicode_map` array for the given keycode, respecting shift state for pair keycodes.
|
||||
*
|
||||
* \param keycode The Unicode Map keycode to get the index of.
|
||||
*
|
||||
* \return An index into the `unicode_map` array.
|
||||
*/
|
||||
uint8_t unicodemap_index(uint16_t keycode);
|
||||
|
||||
/**
|
||||
* \brief Get the code point for the given index in the `unicode_map` array.
|
||||
*
|
||||
* \param index The index into the `unicode_map` array.
|
||||
*
|
||||
* \return A Unicode code point value.
|
||||
*/
|
||||
uint32_t unicodemap_get_code_point(uint8_t index);
|
||||
|
||||
/**
|
||||
* \brief Send the code point for the given index in the `unicode_map` array.
|
||||
*
|
||||
* \param index The index into the `unicode_map` array.
|
||||
*/
|
||||
void register_unicodemap(uint8_t index);
|
||||
|
||||
/** \} */
|
@ -60,7 +60,10 @@ void TestDriver::send_extra(report_extra_t* report) {
|
||||
namespace internal {
|
||||
void expect_unicode_code_point(TestDriver& driver, uint32_t code_point) {
|
||||
testing::InSequence seq;
|
||||
EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_U));
|
||||
EXPECT_REPORT(driver, (KC_LEFT_CTRL, KC_LEFT_SHIFT));
|
||||
EXPECT_REPORT(driver, (KC_LEFT_CTRL, KC_LEFT_SHIFT, KC_U));
|
||||
EXPECT_REPORT(driver, (KC_LEFT_CTRL, KC_LEFT_SHIFT));
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
|
||||
bool print_zero = false;
|
||||
for (int i = 7; i >= 0; --i) {
|
||||
@ -71,10 +74,12 @@ void expect_unicode_code_point(TestDriver& driver, uint32_t code_point) {
|
||||
const uint8_t digit = (code_point >> (i * 4)) & 0xf;
|
||||
if (digit || print_zero) {
|
||||
EXPECT_REPORT(driver, (hex_digit_to_keycode(digit)));
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
print_zero = true;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_REPORT(driver, (KC_SPC));
|
||||
EXPECT_REPORT(driver, (KC_SPACE));
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
}
|
||||
} // namespace internal
|
||||
|
8
tests/unicode/config.h
Normal file
8
tests/unicode/config.h
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright 2023 QMK
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "test_common.h"
|
||||
|
||||
#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX, UNICODE_MODE_MACOS
|
5
tests/unicode/test.mk
Normal file
5
tests/unicode/test.mk
Normal file
@ -0,0 +1,5 @@
|
||||
# --------------------------------------------------------------------------------
|
||||
# Keep this file, even if it is empty, as a marker that this folder contains tests
|
||||
# --------------------------------------------------------------------------------
|
||||
|
||||
UNICODE_COMMON = yes
|
86
tests/unicode/test_unicode.cpp
Normal file
86
tests/unicode/test_unicode.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright 2023 QMK
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "keyboard_report_util.hpp"
|
||||
#include "keycode.h"
|
||||
#include "test_common.hpp"
|
||||
#include "test_keymap_key.hpp"
|
||||
|
||||
using testing::_;
|
||||
|
||||
class Unicode : public TestFixture {};
|
||||
|
||||
TEST_F(Unicode, sends_bmp_unicode_sequence) {
|
||||
TestDriver driver;
|
||||
|
||||
set_unicode_input_mode(UNICODE_MODE_LINUX);
|
||||
|
||||
EXPECT_UNICODE(driver, 0x03A8); // Ψ
|
||||
register_unicode(0x03A8);
|
||||
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(Unicode, sends_smp_unicode_sequence) {
|
||||
TestDriver driver;
|
||||
|
||||
set_unicode_input_mode(UNICODE_MODE_LINUX);
|
||||
|
||||
EXPECT_UNICODE(driver, 0x1F9D9); // 🧙
|
||||
register_unicode(0x1F9D9);
|
||||
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(Unicode, sends_surrogate_pair_for_macos) {
|
||||
TestDriver driver;
|
||||
|
||||
set_unicode_input_mode(UNICODE_MODE_MACOS);
|
||||
|
||||
// EXPECT_UNICODE() assumes Linux input mode
|
||||
{
|
||||
testing::InSequence s;
|
||||
|
||||
// Alt+D83EDDD9 🧙
|
||||
EXPECT_REPORT(driver, (KC_LEFT_ALT));
|
||||
EXPECT_REPORT(driver, (KC_D, KC_LEFT_ALT));
|
||||
EXPECT_REPORT(driver, (KC_LEFT_ALT));
|
||||
EXPECT_REPORT(driver, (KC_8, KC_LEFT_ALT));
|
||||
EXPECT_REPORT(driver, (KC_LEFT_ALT));
|
||||
EXPECT_REPORT(driver, (KC_3, KC_LEFT_ALT));
|
||||
EXPECT_REPORT(driver, (KC_LEFT_ALT));
|
||||
EXPECT_REPORT(driver, (KC_E, KC_LEFT_ALT));
|
||||
EXPECT_REPORT(driver, (KC_LEFT_ALT));
|
||||
EXPECT_REPORT(driver, (KC_D, KC_LEFT_ALT));
|
||||
EXPECT_REPORT(driver, (KC_LEFT_ALT));
|
||||
EXPECT_REPORT(driver, (KC_D, KC_LEFT_ALT));
|
||||
EXPECT_REPORT(driver, (KC_LEFT_ALT));
|
||||
EXPECT_REPORT(driver, (KC_D, KC_LEFT_ALT));
|
||||
EXPECT_REPORT(driver, (KC_LEFT_ALT));
|
||||
EXPECT_REPORT(driver, (KC_9, KC_LEFT_ALT));
|
||||
EXPECT_REPORT(driver, (KC_LEFT_ALT));
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
}
|
||||
|
||||
register_unicode(0x1F9D9);
|
||||
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(Unicode, sends_unicode_string) {
|
||||
TestDriver driver;
|
||||
|
||||
set_unicode_input_mode(UNICODE_MODE_LINUX);
|
||||
|
||||
{
|
||||
testing::InSequence s;
|
||||
|
||||
EXPECT_UNICODE(driver, 0xFF31);
|
||||
EXPECT_UNICODE(driver, 0xFF2D);
|
||||
EXPECT_UNICODE(driver, 0xFF2B);
|
||||
EXPECT_UNICODE(driver, 0xFF01);
|
||||
}
|
||||
send_unicode_string("QMK!");
|
||||
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
8
tests/unicode/unicode_basic/config.h
Normal file
8
tests/unicode/unicode_basic/config.h
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright 2023 QMK
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "test_common.h"
|
||||
|
||||
#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX, UNICODE_MODE_MACOS
|
5
tests/unicode/unicode_basic/test.mk
Normal file
5
tests/unicode/unicode_basic/test.mk
Normal file
@ -0,0 +1,5 @@
|
||||
# --------------------------------------------------------------------------------
|
||||
# Keep this file, even if it is empty, as a marker that this folder contains tests
|
||||
# --------------------------------------------------------------------------------
|
||||
|
||||
UNICODE_ENABLE = yes
|
26
tests/unicode/unicode_basic/test_unicode_basic.cpp
Normal file
26
tests/unicode/unicode_basic/test_unicode_basic.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2023 QMK
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "keyboard_report_util.hpp"
|
||||
#include "keycode.h"
|
||||
#include "test_common.hpp"
|
||||
#include "test_keymap_key.hpp"
|
||||
|
||||
using testing::_;
|
||||
|
||||
class UnicodeBasic : public TestFixture {};
|
||||
|
||||
TEST_F(UnicodeBasic, sends_unicode_sequence) {
|
||||
TestDriver driver;
|
||||
|
||||
set_unicode_input_mode(UNICODE_MODE_LINUX);
|
||||
|
||||
auto key_uc = KeymapKey(0, 0, 0, UC(0x03A8)); // Ψ
|
||||
|
||||
set_keymap({key_uc});
|
||||
|
||||
EXPECT_UNICODE(driver, 0x03A8);
|
||||
tap_key(key_uc);
|
||||
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
8
tests/unicode/unicode_map/config.h
Normal file
8
tests/unicode/unicode_map/config.h
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright 2023 QMK
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "test_common.h"
|
||||
|
||||
#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX
|
5
tests/unicode/unicode_map/test.mk
Normal file
5
tests/unicode/unicode_map/test.mk
Normal file
@ -0,0 +1,5 @@
|
||||
# --------------------------------------------------------------------------------
|
||||
# Keep this file, even if it is empty, as a marker that this folder contains tests
|
||||
# --------------------------------------------------------------------------------
|
||||
|
||||
UNICODEMAP_ENABLE = yes
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user