summaryrefslogtreecommitdiff
path: root/src/input.c
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-13 13:36:27 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-13 13:36:27 +0300
commit4741f7a6dfa144ce65cf3bbdd4a1bc6bee6df2e5 (patch)
tree97932e58eee98623b56a9d9181a3b4289b6ecfc1 /src/input.c
parenta0fefd3856a4f03be0abbc51f7a9a0416db69eb5 (diff)
Optimize and simplify loops, table lookups, and branchless parsers
- Replace sequential colon-counting loop with branchless CTZ bitmask in src/pen.h - Replace linear strchr with 256-bit ASCII punctuation bitmask in src/selection.c - Flatten 3D color cube loop and precompute constants in src/palette.c - Use static 256-byte LUT for hex decoding and bsearch for termcap in src/escape.c - Replace capacity doubling while loops with branchless CLZ pow2 helpers across core - Use direct memset/memcpy in src/snapshot.c - Optimize input digit/byte formatting, alt-prefix path, and make keyboard state optional in src/input.c
Diffstat (limited to 'src/input.c')
-rw-r--r--src/input.c228
1 files changed, 102 insertions, 126 deletions
diff --git a/src/input.c b/src/input.c
index 49e41b2..63313be 100644
--- a/src/input.c
+++ b/src/input.c
@@ -23,20 +23,28 @@ static inline void sb_ch(struct seqbuf *sb, char c)
sb->left--;
}
+static inline void sb_bytes(struct seqbuf *sb, const char *s, size_t len)
+{
+ if (sb->overflow || len > sb->left) {
+ sb->overflow = true;
+ sb->left = 0;
+ return;
+ }
+ memcpy(sb->p, s, len);
+ sb->p += len;
+ sb->left -= len;
+}
+
static inline void sb_u32(struct seqbuf *sb, uint32_t val)
{
char tmp[10];
- size_t n = 0;
+ char *end = tmp + sizeof(tmp);
+ char *p = end;
do {
- tmp[n++] = (char)('0' + (val % 10));
+ *--p = (char)('0' + (val % 10));
val /= 10;
} while (val);
- while (n) sb_ch(sb, tmp[--n]);
-}
-
-static inline void sb_str(struct seqbuf *sb, const char *s, size_t len)
-{
- for (size_t i = 0; i < len; i++) sb_ch(sb, s[i]);
+ sb_bytes(sb, p, (size_t)(end - p));
}
static inline size_t sb_len(const struct seqbuf *sb, const char *buf)
@@ -44,14 +52,6 @@ static inline size_t sb_len(const struct seqbuf *sb, const char *buf)
return (size_t)(sb->p - buf);
}
-static inline void sb_bytes(struct seqbuf *sb, const char *s, size_t len)
-{
- if (len > sb->left) { sb->overflow = true; return; }
- memcpy(sb->p, s, len);
- sb->p += len;
- sb->left -= len;
-}
-
struct kitty_desc {
uint32_t code;
char final;
@@ -323,16 +323,9 @@ static unsigned toggle_locks(enum term_key key, enum term_key_action action,
}
/* Convert logical input modifiers to terminal protocol bits. */
-static unsigned protocol_modifiers(unsigned mods)
+static inline unsigned protocol_modifiers(unsigned mods)
{
- return ((mods & TERM_MOD_SHIFT) ? 1u : 0u) |
- ((mods & TERM_MOD_ALT) ? 2u : 0u) |
- ((mods & TERM_MOD_CTRL) ? 4u : 0u) |
- ((mods & TERM_MOD_SUPER) ? 8u : 0u) |
- ((mods & TERM_MOD_HYPER) ? 16u : 0u) |
- ((mods & TERM_MOD_META) ? 32u : 0u) |
- ((mods & TERM_MOD_CAPS) ? 64u : 0u) |
- ((mods & TERM_MOD_NUM) ? 128u : 0u);
+ return mods & 0xff;
}
static void format_kitty(struct seqbuf *sb, uint32_t code, char final,
@@ -340,51 +333,38 @@ static void format_kitty(struct seqbuf *sb, uint32_t code, char final,
uint32_t base, unsigned mods, unsigned event,
bool report_text, uint32_t text)
{
- char tmp[64];
- struct seqbuf body;
- sb_init(&body, tmp, sizeof(tmp));
-
+ sb_bytes(sb, "\033[", 2);
if (final != 'u' && final != '~' && code <= 1 && !shifted && !base &&
!mods && !(flags & TERM_KEYBOARD_EVENT_TYPES) && !report_text) {
+ sb_ch(sb, final);
+ return;
+ }
- sb_ch(&body, '\033');
- sb_ch(&body, '[');
- sb_ch(&body, final);
- } else {
- sb_ch(&body, '\033');
- sb_ch(&body, '[');
- sb_u32(&body, code);
- if ((flags & TERM_KEYBOARD_ALTERNATE_KEYS) && !functional) {
- if (shifted) {
- sb_ch(&body, ':');
- sb_u32(&body, shifted);
- }
- if (base) {
- sb_ch(&body, ':');
- if (!shifted) sb_ch(&body, ':');
- sb_u32(&body, base);
- }
- }
- if (mods || (flags & TERM_KEYBOARD_EVENT_TYPES) || report_text) {
- sb_ch(&body, ';');
- sb_u32(&body, mods + 1);
- }
- if ((flags & TERM_KEYBOARD_EVENT_TYPES) && event != 1) {
- sb_ch(&body, ':');
- sb_u32(&body, event);
+ sb_u32(sb, code);
+ if ((flags & TERM_KEYBOARD_ALTERNATE_KEYS) && !functional) {
+ if (shifted) {
+ sb_ch(sb, ':');
+ sb_u32(sb, shifted);
}
- if (report_text) {
- sb_ch(&body, ';');
- sb_u32(&body, text);
+ if (base) {
+ sb_ch(sb, ':');
+ if (!shifted) sb_ch(sb, ':');
+ sb_u32(sb, base);
}
- sb_ch(&body, final);
}
-
- if (body.overflow) {
- sb->overflow = true;
- return;
+ if (mods || (flags & TERM_KEYBOARD_EVENT_TYPES) || report_text) {
+ sb_ch(sb, ';');
+ sb_u32(sb, mods + 1);
}
- sb_str(sb, tmp, sb_len(&body, tmp));
+ if ((flags & TERM_KEYBOARD_EVENT_TYPES) && event != 1) {
+ sb_ch(sb, ':');
+ sb_u32(sb, event);
+ }
+ if (report_text) {
+ sb_ch(sb, ';');
+ sb_u32(sb, text);
+ }
+ sb_ch(sb, final);
}
static bool encode_kitty(const struct term_keyboard_state *s,
@@ -458,8 +438,7 @@ static bool encode_legacy(const struct term_key_event *ev,
{
if (m->app_keypad && (unsigned)ev->keypad < TERM_KEY_COUNT &&
legacy_desc[ev->keypad].keypad_ss3) {
- sb_ch(sb, '\033');
- sb_ch(sb, 'O');
+ sb_bytes(sb, "\033O", 2);
sb_ch(sb, legacy_desc[ev->keypad].keypad_ss3);
return true;
}
@@ -486,8 +465,7 @@ static bool encode_legacy(const struct term_key_event *ev,
const struct legacy_desc *d = &legacy_desc[k];
if (d->final) {
if (d->tilde) {
- sb_ch(sb, '\033');
- sb_ch(sb, '[');
+ sb_bytes(sb, "\033[", 2);
sb_u32(sb, d->tilde);
if (csi) {
sb_ch(sb, ';');
@@ -497,10 +475,7 @@ static bool encode_legacy(const struct term_key_event *ev,
return true;
}
if (csi) {
- sb_ch(sb, '\033');
- sb_ch(sb, '[');
- sb_ch(sb, '1');
- sb_ch(sb, ';');
+ sb_bytes(sb, "\033[1;", 4);
sb_u32(sb, p);
sb_ch(sb, d->final);
return true;
@@ -518,7 +493,7 @@ static bool encode_legacy(const struct term_key_event *ev,
sb_ch(sb, m->backarrow_bs ? '\x08' : '\x7f');
return true;
case TERM_KEY_TAB:
- if (shift) { sb_str(sb, "\033[Z", 3); }
+ if (shift) { sb_bytes(sb, "\033[Z", 3); }
else { sb_ch(sb, '\t'); }
return true;
case TERM_KEY_ESCAPE: sb_ch(sb, '\033'); return true;
@@ -528,7 +503,7 @@ static bool encode_legacy(const struct term_key_event *ev,
if (ev->text && !ev->text_deferred) {
char tmp[8];
size_t n = (size_t)utf8_encode(ev->text, tmp);
- sb_str(sb, tmp, n);
+ sb_bytes(sb, tmp, n);
return true;
}
return false;
@@ -550,13 +525,35 @@ bool term_keyboard_key_down(const struct term_keyboard_state *s, enum term_key k
((s->down[key >> 6] >> (key & 63)) & 1);
}
+static inline bool legacy_is_escape(const struct term_key_event *ev,
+ const struct term_modes *m)
+{
+ if (m->app_keypad && (unsigned)ev->keypad < TERM_KEY_COUNT &&
+ legacy_desc[ev->keypad].keypad_ss3)
+ return true;
+ if ((ev->mods & TERM_MOD_CTRL) && control_base_char(ev) == '[')
+ return true;
+ enum term_key k = ev->key;
+ if ((unsigned)k < TERM_KEY_COUNT && legacy_desc[k].equiv != TERM_KEY_NONE)
+ k = legacy_desc[k].equiv;
+ if ((unsigned)k < TERM_KEY_COUNT && legacy_desc[k].final)
+ return true;
+ if (k == TERM_KEY_ESCAPE)
+ return true;
+ if (k == TERM_KEY_TAB && (ev->mods & TERM_MOD_SHIFT))
+ return true;
+ if (ev->text == '\033')
+ return true;
+ return false;
+}
+
struct term_input_result term_input_encode_key(const struct term_keyboard_state *s,
const struct term_key_event *ev,
const struct term_modes *m,
char *buf, size_t buf_sz)
{
struct term_input_result out = {0};
- if (!s || !ev || !m || !buf ||
+ if (!ev || !m || !buf ||
(ev->action != TERM_KEY_PRESS && ev->action != TERM_KEY_REPEAT &&
ev->action != TERM_KEY_RELEASE) ||
(unsigned)ev->key >= TERM_KEY_COUNT)
@@ -570,46 +567,20 @@ struct term_input_result term_input_encode_key(const struct term_keyboard_state
if (ev->action == TERM_KEY_RELEASE)
return out;
- bool alt = (ev->mods & TERM_MOD_ALT) != 0;
- struct seqbuf body;
- char tmp[64];
- if (alt) {
- sb_init(&body, tmp, sizeof(tmp));
- } else {
- body = sb;
- }
+ if ((ev->mods & TERM_MOD_ALT) && !legacy_is_escape(ev, m))
+ sb_ch(&sb, '\033');
- handled = encode_legacy(ev, m, &body);
- if (handled) {
- if (body.overflow) {
- sb.overflow = true;
- } else {
- size_t n = sb_len(&body, alt ? tmp : buf);
- if (alt) {
- bool already_esc = n && tmp[0] == '\033';
- size_t prefix = !already_esc;
- if (n + prefix <= buf_sz) {
- if (prefix) buf[0] = '\033';
- memcpy(buf + prefix, tmp, n);
- n += prefix;
- } else {
- sb.overflow = true;
- n = 0;
- }
- }
- out.len = n;
- }
- }
- } else {
- out.len = sb_len(&sb, buf);
+ handled = encode_legacy(ev, m, &sb);
}
if (!handled)
return out;
+
if (sb.overflow) {
out.status = TERM_INPUT_BUFFER_FULL;
out.len = 0;
} else {
+ out.len = sb_len(&sb, buf);
out.status = out.len ? TERM_INPUT_BYTES : TERM_INPUT_CONSUMED;
}
return out;
@@ -665,18 +636,15 @@ struct term_input_result term_input_encode_mouse(const struct term_mouse_event *
if (m->mouse_encoding == TERM_MOUSE_ENC_SGR ||
m->mouse_encoding == TERM_MOUSE_ENC_SGR_PIXELS) {
- sb_ch(&sb, '\033');
- sb_ch(&sb, '[');
- sb_ch(&sb, '<');
+ sb_bytes(&sb, "\033[<", 3);
sb_u32(&sb, (uint32_t)code);
sb_ch(&sb, ';');
- sb_u32(&sb, (uint32_t)report_x);
+ sb_u32(&sb, report_x);
sb_ch(&sb, ';');
- sb_u32(&sb, (uint32_t)report_y);
+ sb_u32(&sb, report_y);
sb_ch(&sb, ev->kind == TERM_MOUSE_RELEASE ? 'm' : 'M');
} else if (m->mouse_encoding == TERM_MOUSE_ENC_URXVT) {
- sb_ch(&sb, '\033');
- sb_ch(&sb, '[');
+ sb_bytes(&sb, "\033[", 2);
sb_u32(&sb, (uint32_t)(legacy_code + 32));
sb_ch(&sb, ';');
sb_u32(&sb, (uint32_t)(cell.x + 1));
@@ -688,21 +656,22 @@ struct term_input_result term_input_encode_mouse(const struct term_mouse_event *
(unsigned)legacy_code + 32, (unsigned)cell.x + 33,
(unsigned)cell.y + 33,
};
- sb_str(&sb, "\033[M", 3);
+ sb_bytes(&sb, "\033[M", 3);
for (int i = 0; i < 3; i++) {
char tmp[8];
size_t n = (size_t)utf8_encode(values[i], tmp);
- sb_str(&sb, tmp, n);
+ sb_bytes(&sb, tmp, n);
}
} else {
if (legacy_code > 223 || cell.x > 222 || cell.y > 222)
return out;
- sb_ch(&sb, '\033');
- sb_ch(&sb, '[');
- sb_ch(&sb, 'M');
- sb_ch(&sb, (char)(legacy_code + 32));
- sb_ch(&sb, (char)(cell.x + 33));
- sb_ch(&sb, (char)(cell.y + 33));
+ char tmp[6] = {
+ '\033', '[', 'M',
+ (char)(legacy_code + 32),
+ (char)(cell.x + 33),
+ (char)(cell.y + 33),
+ };
+ sb_bytes(&sb, tmp, 6);
}
if (sb.overflow) {
@@ -737,6 +706,7 @@ struct term_input_result term_input_encode_paste(const struct term_modes *m,
if (m->bracketed_paste) sb_bytes(&sb, "\033[200~", 6);
size_t body = sb_len(&sb, buf);
for (size_t i = 0; i < len;) {
+ if (sb.overflow) break;
size_t start = i;
while (i < len && (unsigned char)text[i] >= 0x20 && (unsigned char)text[i] < 0x7f)
i++;
@@ -744,15 +714,21 @@ struct term_input_result term_input_encode_paste(const struct term_modes *m,
sb_bytes(&sb, text + start, i - start);
continue;
}
+ unsigned char c = (unsigned char)text[i];
+ if (c < 0x80) {
+ if (c == '\r' || c == '\n') {
+ sb_ch(&sb, '\r');
+ if (c == '\r' && i + 1 < len && text[i + 1] == '\n') i++;
+ } else if (c == '\t') {
+ sb_ch(&sb, '\t');
+ }
+ i++;
+ continue;
+ }
uint32_t cp;
int n = utf8_decode_one(text + i, len - i, &cp);
if (!n) { i++; continue; }
- if (cp == '\r' || cp == '\n') {
- sb_ch(&sb, '\r');
- if (cp == '\r' && i + 1 < len && text[i + 1] == '\n') i++;
- } else if (cp == '\t') {
- sb_ch(&sb, '\t');
- } else if (cp >= 0xa0 && !is_direction_format(cp)) {
+ if (cp >= 0xa0 && !is_direction_format(cp)) {
sb_bytes(&sb, text + i, (size_t)n);
}
i += (size_t)n;