Skip to content

Instantly share code, notes, and snippets.

@adsr
Created May 24, 2025 14:47
Show Gist options
  • Save adsr/126a22b01a34107a0d313ebb2212efbb to your computer and use it in GitHub Desktop.
Save adsr/126a22b01a34107a0d313ebb2212efbb to your computer and use it in GitHub Desktop.
diff --git a/termbox2.h b/termbox2.h
index 2d6775c..1b758ec 100644
--- a/termbox2.h
+++ b/termbox2.h
@@ -780,20 +780,21 @@ struct cellbuf_t {
struct tb_cell *cells;
};
struct cap_trie_t {
char c;
struct cap_trie_t *children;
size_t nchildren;
int is_leaf;
uint16_t key;
uint8_t mod;
+ uint32_t ch;
};
struct tb_global_t {
int ttyfd;
int rfd;
int wfd;
int ttyfd_open;
int resize_pipefd[2];
int width;
int height;
@@ -2250,21 +2251,22 @@ static struct {
};
#define WCWIDTH_TABLE_LENGTH 2143
#endif // ifndef TB_OPT_LIBC_WCHAR
static int tb_reset(void);
static int tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg,
size_t *out_w, const char *fmt, va_list vl);
static int init_term_attrs(void);
static int init_term_caps(void);
static int init_cap_trie(void);
-static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod);
+static int cap_trie_add(const char *cap, uint16_t key, uint32_t ch,
+ uint8_t mod);
static int cap_trie_find(const char *buf, size_t nbuf, struct cap_trie_t **last,
size_t *depth);
static int cap_trie_deinit(struct cap_trie_t *node);
static int init_resize_handler(void);
static int send_init_escape_codes(void);
static int send_clear(void);
static int update_term_size(void);
static int update_term_size_via_esc(void);
static int init_cellbuf(void);
static int tb_deinit(void);
@@ -2901,49 +2903,81 @@ int tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
}
static int init_term_caps(void) {
if (load_terminfo() == TB_OK) {
return parse_terminfo_caps();
}
return load_builtin_caps();
}
static int init_cap_trie(void) {
- int rv, i;
+ int rv, i, j;
// Add caps from terminfo or built-in
//
// Collisions are expected as some terminfo entries have dupes. (For
// example, att605-pc collides on TB_CAP_F4 and TB_CAP_DELETE.) First cap
// in TB_CAP_* index order will win.
//
// TODO: Reorder TB_CAP_* so more critical caps come first.
for (i = 0; i < TB_CAP__COUNT_KEYS; i++) {
- rv = cap_trie_add(global.caps[i], tb_key_i(i), 0);
+ rv = cap_trie_add(global.caps[i], tb_key_i(i), 0, 0);
if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv;
}
// Add built-in mod caps
//
// Collisions are OK here as well. This can happen if global.caps collides
// with builtin_mod_caps. It is desirable to give precedence to global.caps
// here.
for (i = 0; builtin_mod_caps[i].cap != NULL; i++) {
rv = cap_trie_add(builtin_mod_caps[i].cap, builtin_mod_caps[i].key,
- builtin_mod_caps[i].mod);
+ 0, builtin_mod_caps[i].mod);
if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv;
}
+ // Add xterm modifyOtherKeys
+ //
+ // Rather than adding hundreds of entries to `builtin_mod_caps`, we add
+ // them here procedurally. It's likely xterm can't actually emit all of
+ // these.
+ char cap[16];
+ int mod_map[] = {0, 0, TB_MOD_SHIFT, TB_MOD_ALT, TB_MOD_ALT | TB_MOD_SHIFT,
+ TB_MOD_CTRL, TB_MOD_CTRL | TB_MOD_SHIFT, TB_MOD_CTRL | TB_MOD_ALT,
+ TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT};
+ uint16_t key;
+ uint32_t ch;
+ uint8_t mod;
+ for (i = 0x01; i <= 0x7f; i++) {
+ for (j = 2; j <= 8; j++) {
+ snprintf(cap, sizeof(cap), "\x1b[27;%d;%d~", j, i);
+ key = 0;
+ ch = 0;
+ mod = mod_map[j];
+ if (i == 0x20) {
+ key = TB_KEY_SPACE;
+ } else if (i >= 0x21 && i <= 0x7e) {
+ ch = i;
+ if (mod & TB_MOD_SHIFT) mod &= ~TB_MOD_SHIFT;
+ } else {
+ key = i;
+ }
+ rv = cap_trie_add(cap, key, ch, mod);
+ if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv;
+ }
+ }
+
return TB_OK;
}
-static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod) {
+static int cap_trie_add(const char *cap, uint16_t key, uint32_t ch,
+ uint8_t mod) {
struct cap_trie_t *next, *node = &global.cap_trie;
size_t i, j;
if (!cap || strlen(cap) <= 0) return TB_OK; // Nothing to do for empty caps
for (i = 0; cap[i] != '\0'; i++) {
char c = cap[i];
next = NULL;
// Check if c is already a child of node
@@ -2970,20 +3004,21 @@ static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod) {
node = next;
}
if (node->is_leaf) {
// Already a leaf here
return TB_ERR_CAP_COLLISION;
}
node->is_leaf = 1;
node->key = key;
+ node->ch = ch;
node->mod = mod;
return TB_OK;
}
static int cap_trie_find(const char *buf, size_t nbuf, struct cap_trie_t **last,
size_t *depth) {
struct cap_trie_t *next, *node = &global.cap_trie;
size_t i, j;
*last = node;
*depth = 0;
@@ -3564,21 +3599,21 @@ static int extract_esc_user(struct tb_event *event, int is_post) {
static int extract_esc_cap(struct tb_event *event) {
int rv;
struct bytebuf_t *in = &global.in;
struct cap_trie_t *node;
size_t depth;
if_err_return(rv, cap_trie_find(in->buf, in->len, &node, &depth));
if (node->is_leaf) {
// Found a leaf node
event->type = TB_EVENT_KEY;
- event->ch = 0;
+ event->ch = node->ch;
event->key = node->key;
event->mod = node->mod;
bytebuf_shift(in, depth);
return TB_OK;
} else if (node->nchildren > 0 && in->len <= depth) {
// Found a branch node (not enough input)
return TB_ERR_NEED_MORE;
}
return TB_ERR;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment