Skip to content

Instantly share code, notes, and snippets.

@jyn514
Last active September 27, 2024 01:47
Show Gist options
  • Save jyn514/f3d6542391de9f18cc4d0a74c9b1d8bc to your computer and use it in GitHub Desktop.
Save jyn514/f3d6542391de9f18cc4d0a74c9b1d8bc to your computer and use it in GitHub Desktop.
input arbitrary unicode characters using right-alt + hex codes
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 = &macro_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
@jyn514
Copy link
Author

jyn514 commented Sep 26, 2024

to run this:

  1. checkout rvaiya/keyd@393d341
  2. apply this patch (with e.g. git apply)
  3. make compose && make && sudo make install && sudo systemctl daemon-reload && sudo systemctl start keyd
  4. ln -s /usr/local/share/keyd/keyd.compose ~/.XCompose
echo '[ids]
*
[main]
rightalt = unicode()' > /etc/keyd/default.conf
  1. hold right-alt and type in numbers on the numpad. either "real" alt-gr or a normal alt key mapped to Alt_R will work.

@jyn514
Copy link
Author

jyn514 commented Sep 26, 2024

also oops the hex keys above 9 are wrong lol https://tech.lgbt/@jyn/113203652071743894

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment