Last active
September 27, 2024 01:47
-
-
Save jyn514/f3d6542391de9f18cc4d0a74c9b1d8bc to your computer and use it in GitHub Desktop.
input arbitrary unicode characters using right-alt + hex codes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/config.c b/src/config.c | |
index de3be1c..d482e7c 100644 | |
--- a/src/config.c | |
+++ b/src/config.c | |
@@ -17,6 +17,7 @@ | |
#include <unistd.h> | |
#include <libgen.h> | |
+#include "config.h" | |
#include "keyd.h" | |
#include "ini.h" | |
#include "keys.h" | |
@@ -68,6 +69,8 @@ static struct { | |
{ "macro2", NULL, OP_MACRO2, { ARG_TIMEOUT, ARG_TIMEOUT, ARG_MACRO } }, | |
{ "setlayout", NULL, OP_LAYOUT, { ARG_LAYOUT } }, | |
+ { "unicode", NULL, OP_UNICODE, {} }, | |
+ | |
/* Experimental */ | |
{ "scrollt", NULL, OP_SCROLL_TOGGLE, {ARG_SENSITIVITY} }, | |
{ "scroll", NULL, OP_SCROLL, {ARG_SENSITIVITY} }, | |
@@ -482,17 +485,6 @@ exit: | |
int parse_macro_expression(const char *s, struct macro *macro) | |
{ | |
uint8_t code, mods; | |
- | |
- #define ADD_ENTRY(t, d) do { \ | |
- if (macro->sz >= ARRAY_SIZE(macro->entries)) { \ | |
- err("maximum macro size (%d) exceeded", ARRAY_SIZE(macro->entries)); \ | |
- return 1; \ | |
- } \ | |
- macro->entries[macro->sz].type = t; \ | |
- macro->entries[macro->sz].data = d; \ | |
- macro->sz++; \ | |
- } while(0) | |
- | |
size_t len = strlen(s); | |
char buf[1024]; | |
@@ -1030,4 +1022,3 @@ int config_add_entry(struct config *config, const char *exp) | |
return set_layer_entry(config, layer, keyname, &d); | |
} | |
- | |
diff --git a/src/config.h b/src/config.h | |
index 30c8ec9..47abdbe 100644 | |
--- a/src/config.h | |
+++ b/src/config.h | |
@@ -7,7 +7,9 @@ | |
#define CONFIG_H | |
#include <limits.h> | |
+#include <stdint.h> | |
#include "macro.h" | |
+#include "keys.h" | |
#define MAX_LAYER_NAME_LEN 64 | |
#define MAX_DESCRIPTOR_ARGS 3 | |
@@ -47,6 +49,7 @@ enum op { | |
OP_MACRO2, | |
OP_COMMAND, | |
OP_TIMEOUT, | |
+ OP_UNICODE, | |
/* Experimental */ | |
OP_SCROLL_TOGGLE, | |
diff --git a/src/keyboard.c b/src/keyboard.c | |
index b21d6df..fac39c4 100644 | |
--- a/src/keyboard.c | |
+++ b/src/keyboard.c | |
@@ -4,9 +4,13 @@ | |
* © 2019 Raheman Vaiya (see also: LICENSE). | |
*/ | |
+#include "config.h" | |
+#include "keyboard.h" | |
#include "keyd.h" | |
+#include "keys.h" | |
static long process_event(struct keyboard *kbd, uint8_t code, int pressed, long time); | |
+static int resolve_chord(struct keyboard *kbd); | |
/* | |
* Here be tiny dragons. | |
@@ -79,7 +83,14 @@ static void send_key(struct keyboard *kbd, uint8_t code, uint8_t pressed) | |
if (kbd->keystate[code] != pressed) { | |
kbd->keystate[code] = pressed; | |
- kbd->output.send_key(code, pressed); | |
+ if (key_match(kbd->unicode_state, code)) { | |
+ if (pressed && kbd->unicode_len < sizeof kbd->pending_unicode) { | |
+ dbg2("enqueue unicode %s", KEY_NAME(code)); | |
+ kbd->pending_unicode[kbd->unicode_len++] = code; | |
+ } | |
+ } else { | |
+ kbd->output.send_key(code, pressed); | |
+ } | |
} | |
} | |
@@ -173,6 +184,19 @@ static void execute_macro(struct keyboard *kbd, int dl, const struct macro *macr | |
} | |
} | |
+int insert_unicode(struct keyboard *kbd, int dl, uint32_t codepoint) { | |
+ struct macro macro_def, *macro = ¯o_def; | |
+ int xcode; | |
+ | |
+ if ((xcode = unicode_lookup_index(codepoint)) > 0) { | |
+ macro_def.sz = 0; | |
+ MACRO_ADD_ENTRY(MACRO_UNICODE, xcode); | |
+ execute_macro(kbd, dl, macro); | |
+ return 0; | |
+ } | |
+ return -1; | |
+} | |
+ | |
static void lookup_descriptor(struct keyboard *kbd, uint8_t code, | |
struct descriptor *d, int *dl) | |
{ | |
@@ -726,6 +750,25 @@ static long process_descriptor(struct keyboard *kbd, uint8_t code, | |
schedule_timeout(kbd, kbd->pending_key.expire); | |
} | |
+ break; | |
+ case OP_UNICODE: ; | |
+ struct layer *layer = &kbd->config.layers[dl]; | |
+ | |
+ dbg("op unicode %s (layer %s)", pressed ? "down" : "up", layer->name); | |
+ | |
+ if (pressed) { | |
+ /* don't allow reentrant modifier */ | |
+ if (kbd->unicode_state == UNICODE_DISABLED) { | |
+ kbd->unicode_state = UNICODE_HEX; | |
+ kbd->unicode_len = 0; | |
+ } | |
+ } else { | |
+ /* Resolve the unicode sequence. */ | |
+ kbd->unicode_state = UNICODE_DISABLED; | |
+ uint32_t codepoint = parse_unicode_hex(kbd->pending_unicode, kbd->unicode_len); | |
+ dbg("input unicode seq U+%x", codepoint); | |
+ insert_unicode(kbd, dl, codepoint); | |
+ } | |
break; | |
case OP_COMMAND: | |
if (pressed) { | |
diff --git a/src/keyboard.h b/src/keyboard.h | |
index dc94075..2fad0a9 100644 | |
--- a/src/keyboard.h | |
+++ b/src/keyboard.h | |
@@ -49,6 +49,9 @@ struct keyboard { | |
uint8_t last_pressed_output_code; | |
uint8_t last_pressed_code; | |
+ uint8_t pending_unicode[7]; | |
+ size_t unicode_len; | |
+ enum unicode_state unicode_state; | |
uint8_t oneshot_latch; | |
@@ -56,7 +59,6 @@ struct keyboard { | |
struct macro *active_macro; | |
int active_macro_layer; | |
- int overload_last_layer_code; | |
long macro_timeout; | |
long oneshot_timeout; | |
diff --git a/src/keys.c b/src/keys.c | |
index 52f22ee..cc7ec1b 100644 | |
--- a/src/keys.c | |
+++ b/src/keys.c | |
@@ -4,6 +4,7 @@ | |
* © 2019 Raheman Vaiya (see also: LICENSE). | |
*/ | |
#include <stdint.h> | |
+#include <stdio.h> | |
#include <string.h> | |
#include "keys.h" | |
@@ -411,3 +412,77 @@ int parse_key_sequence(const char *s, uint8_t *codep, uint8_t *modsp) | |
return -1; | |
} | |
+uint8_t parse_digit(uint8_t digit) { | |
+ switch (digit) { | |
+ case KEYD_KP0: | |
+ return 0; | |
+ case KEYD_KP1: | |
+ return 1; | |
+ case KEYD_KP2: | |
+ return 2; | |
+ case KEYD_KP3: | |
+ return 3; | |
+ case KEYD_KP4: | |
+ return 4; | |
+ case KEYD_KP5: | |
+ return 5; | |
+ case KEYD_KP6: | |
+ return 6; | |
+ case KEYD_KP7: | |
+ return 7; | |
+ case KEYD_KP8: | |
+ return 8; | |
+ case KEYD_KP9: | |
+ return 9; | |
+ /* gunzip -c /etc/console-setup/cached_UTF-8_del.kmap.gz | |
+ | rg 'keycode.*?Hex_[A-F]' -o | sort -k6 */ | |
+ case KEYD_NUMLOCK: | |
+ return 10; | |
+ case KEYD_KPSLASH: | |
+ return 11; | |
+ case KEYD_KPASTERISK: | |
+ return 12; | |
+ case KEYD_KPMINUS: | |
+ return 13; | |
+ case KEYD_KPPLUS: | |
+ return 14; | |
+ case KEYD_KPENTER: | |
+ return 15; | |
+ default: | |
+ return -1; | |
+ } | |
+} | |
+ | |
+int key_match(enum unicode_state state, uint8_t code) { | |
+ uint8_t digit; | |
+ switch(state) { | |
+ case UNICODE_DISABLED: | |
+ return 0; | |
+ case UNICODE_HEX: | |
+ return parse_digit(code) != (uint8_t)-1; | |
+ case UNICODE_DECIMAL: | |
+ digit = parse_digit(code); | |
+ return digit != (uint8_t)-1 && digit < 10; | |
+ default: | |
+ fprintf(stderr, "unreachable: unknown unicode state %d\n", state); | |
+ exit(-1); | |
+ } | |
+} | |
+ | |
+uint32_t parse_unicode(uint8_t *keys, size_t n, uint8_t radix) { | |
+ uint32_t codepoint = 0; | |
+ | |
+ for (size_t i = 0; i < n; i++) { | |
+ codepoint = codepoint*radix + parse_digit(keys[i]); | |
+ } | |
+ | |
+ return codepoint; | |
+} | |
+ | |
+uint32_t parse_unicode_hex(uint8_t *keys, size_t n) { | |
+ return parse_unicode(keys, n, 16); | |
+} | |
+ | |
+uint32_t parse_unicode_decimal(uint8_t *keys, size_t n) { | |
+ return parse_unicode(keys, n, 10); | |
+} | |
diff --git a/src/keys.h b/src/keys.h | |
index d7a09e7..656f860 100644 | |
--- a/src/keys.h | |
+++ b/src/keys.h | |
@@ -290,8 +290,13 @@ struct modifier { | |
#define KEY_NAME(code) (keycode_table[code].name ? keycode_table[code].name : "UNKNOWN") | |
+enum unicode_state { UNICODE_DISABLED, UNICODE_DECIMAL, UNICODE_HEX }; | |
+ | |
int parse_modset(const char *s, uint8_t *mods); | |
int parse_key_sequence(const char *s, uint8_t *code, uint8_t *mods); | |
+uint32_t parse_unicode_hex(uint8_t *keys, size_t n); | |
+uint32_t parse_unicode_decimal(uint8_t *keys, size_t n); | |
+int key_match(enum unicode_state state, uint8_t code); | |
extern const struct modifier modifiers[MAX_MOD]; | |
extern const struct keycode_table_ent keycode_table[256]; | |
diff --git a/src/macro.c b/src/macro.c | |
index 13f743e..c83130e 100644 | |
--- a/src/macro.c | |
+++ b/src/macro.c | |
@@ -1,4 +1,6 @@ | |
#include "keyd.h" | |
+#include "unicode.h" | |
+#include <stdint.h> | |
/* | |
* Parses expressions of the form: C-t hello enter. | |
@@ -9,23 +11,13 @@ int macro_parse(char *s, struct macro *macro) | |
{ | |
char *tok; | |
- #define ADD_ENTRY(t, d) do { \ | |
- if (macro->sz >= ARRAY_SIZE(macro->entries)) { \ | |
- err("maximum macro size (%d) exceeded", ARRAY_SIZE(macro->entries)); \ | |
- return -1; \ | |
- } \ | |
- macro->entries[macro->sz].type = t; \ | |
- macro->entries[macro->sz].data = d; \ | |
- macro->sz++; \ | |
- } while(0) | |
- | |
macro->sz = 0; | |
for (tok = strtok(s, " "); tok; tok = strtok(NULL, " ")) { | |
uint8_t code, mods; | |
size_t len = strlen(tok); | |
if (!parse_key_sequence(tok, &code, &mods)) { | |
- ADD_ENTRY(MACRO_KEYSEQUENCE, (mods << 8) | code); | |
+ MACRO_ADD_ENTRY(MACRO_KEYSEQUENCE, (mods << 8) | code); | |
} else if (strchr(tok, '+')) { | |
char *saveptr; | |
char *key; | |
@@ -34,18 +26,18 @@ int macro_parse(char *s, struct macro *macro) | |
size_t len = strlen(key); | |
if (is_timeval(key)) | |
- ADD_ENTRY(MACRO_TIMEOUT, atoi(key)); | |
+ MACRO_ADD_ENTRY(MACRO_TIMEOUT, atoi(key)); | |
else if (!parse_key_sequence(key, &code, &mods)) | |
- ADD_ENTRY(MACRO_HOLD, code); | |
+ MACRO_ADD_ENTRY(MACRO_HOLD, code); | |
else { | |
err("%s is not a valid key", key); | |
return -1; | |
} | |
} | |
- ADD_ENTRY(MACRO_RELEASE, 0); | |
+ MACRO_ADD_ENTRY(MACRO_RELEASE, 0); | |
} else if (is_timeval(tok)) { | |
- ADD_ENTRY(MACRO_TIMEOUT, atoi(tok)); | |
+ MACRO_ADD_ENTRY(MACRO_TIMEOUT, atoi(tok)); | |
} else { | |
uint32_t codepoint; | |
int chrsz; | |
@@ -60,17 +52,17 @@ int macro_parse(char *s, struct macro *macro) | |
const char *shiftname = keycode_table[i].shifted_name; | |
if (name && name[0] == tok[0] && name[1] == 0) { | |
- ADD_ENTRY(MACRO_KEYSEQUENCE, i); | |
+ MACRO_ADD_ENTRY(MACRO_KEYSEQUENCE, i); | |
break; | |
} | |
if (shiftname && shiftname[0] == tok[0] && shiftname[1] == 0) { | |
- ADD_ENTRY(MACRO_KEYSEQUENCE, (MOD_SHIFT << 8) | i); | |
+ MACRO_ADD_ENTRY(MACRO_KEYSEQUENCE, (MOD_SHIFT << 8) | i); | |
break; | |
} | |
} | |
} else if ((xcode = unicode_lookup_index(codepoint)) > 0) | |
- ADD_ENTRY(MACRO_UNICODE, xcode); | |
+ MACRO_ADD_ENTRY(MACRO_UNICODE, xcode); | |
tok += chrsz; | |
} | |
@@ -78,8 +70,6 @@ int macro_parse(char *s, struct macro *macro) | |
} | |
return 0; | |
- | |
- #undef ADD_ENTRY | |
} | |
void macro_execute(void (*output)(uint8_t, uint8_t), | |
diff --git a/src/macro.h b/src/macro.h | |
index 14c6f8d..4320145 100644 | |
--- a/src/macro.h | |
+++ b/src/macro.h | |
@@ -32,4 +32,15 @@ void macro_execute(void (*output)(uint8_t, uint8_t), | |
size_t timeout); | |
int macro_parse(char *s, struct macro *macro); | |
+ | |
+#define MACRO_ADD_ENTRY(t, d) do { \ | |
+ if (macro->sz >= ARRAY_SIZE(macro->entries)) { \ | |
+ err("maximum macro size (%d) exceeded", ARRAY_SIZE(macro->entries)); \ | |
+ return -1; \ | |
+ } \ | |
+ macro->entries[macro->sz].type = t; \ | |
+ macro->entries[macro->sz].data = d; \ | |
+ macro->sz++; \ | |
+} while(0) | |
+ | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
to run this:
git apply
)make compose && make && sudo make install && sudo systemctl daemon-reload && sudo systemctl start keyd
ln -s /usr/local/share/keyd/keyd.compose ~/.XCompose