diff options
Diffstat (limited to 'src/osc/dispatch.c')
| -rw-r--r-- | src/osc/dispatch.c | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/src/osc/dispatch.c b/src/osc/dispatch.c new file mode 100644 index 0000000..4cf986e --- /dev/null +++ b/src/osc/dispatch.c @@ -0,0 +1,227 @@ +#include "osc.h" +#include "../internal.h" +#include "../palette.h" +#include "../screen.h" +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#define OSC_HEAP_INIT 4096 +#define OSC_HEAP_KEEP (1u << 20) + +size_t term_osc_room(const struct term *t) +{ + size_t cap = t->osc_heap ? t->osc_heap_cap : sizeof(t->osc_buf); + return cap - 1 - (size_t)t->osc_len; +} + +char *term_osc_tail(struct term *t) +{ + return (t->osc_heap ? t->osc_heap : t->osc_buf) + t->osc_len; +} + +void term_osc_commit(struct term *t, size_t n) +{ + t->osc_len += (int)n; +} + +char *term_osc_data(struct term *t) +{ + return t->osc_heap ? t->osc_heap : t->osc_buf; +} + +bool term_osc_grow(struct term *t) +{ + if (!t->osc_heap) { + char *p = malloc(OSC_HEAP_INIT); + if (!p) return false; + memcpy(p, t->osc_buf, (size_t)t->osc_len); + t->osc_heap = p; + t->osc_heap_cap = OSC_HEAP_INIT; + return true; + } + if (t->osc_heap_cap >= TERM_OSC_MAX) return false; + size_t cap = t->osc_heap_cap * 2; + if (cap > TERM_OSC_MAX) cap = TERM_OSC_MAX; + char *p = realloc(t->osc_heap, cap); + if (!p) return false; + t->osc_heap = p; + t->osc_heap_cap = cap; + return true; +} + +void term_osc_cleanup(struct term *t) +{ + free(t->osc_heap); + t->osc_heap = NULL; + t->osc_heap_cap = 0; + free(t->dcs_buf); + t->dcs_buf = NULL; + t->dcs_cap = 0; + term_osc8_cleanup(t); + free(t->cwd); + t->cwd = NULL; + free(t->pointer_shape); + t->pointer_shape = NULL; +} + +/* Reply to a color query. An OSC 4 reply names the index it answers for; + * index is negative for OSC 10, 11, and 12. */ +static void send_color_query_reply(struct term *t, int code, int index, uint32_t argb) +{ + char resp[64], prefix[16] = ""; + uint8_t r = (argb >> 16) & 0xff, g = (argb >> 8) & 0xff, b = argb & 0xff; + if (index >= 0) + snprintf(prefix, sizeof(prefix), "%d;", index); + snprintf(resp, sizeof(resp), + "\x1b]%d;%srgb:%02x%02x/%02x%02x/%02x%02x\x1b\\", + code, prefix, r, r, g, g, b, b); + send_response(t, resp); +} + +static void osc_color_set_or_query(struct term *t, int code, const char *params, int slot) +{ + if (!strcmp(params, "?")) { + send_color_query_reply(t, code, -1, t->palette.colors[slot]); + return; + } + uint32_t argb; + if (term_color_parse(params, &argb)) + term_palette_set(&t->palette, slot, argb); +} + +static long osc_next_index(const char **pp) +{ + char *end; + long idx = strtol(*pp, &end, 10); + if (end == *pp || (*end != ';' && *end != '\0')) + return -1; + *pp = end; + if (**pp == ';') (*pp)++; + return idx; +} + +static void osc4_handle(struct term *t, const char *params) +{ + const char *p = params; + while (*p) { + long idx = osc_next_index(&p); + if (idx < 0) + return; + + const char *spec = p; + const char *spec_end = spec; + while (*spec_end && *spec_end != ';') + spec_end++; + size_t spec_len = (size_t)(spec_end - spec); + if (spec_len == 1 && spec[0] == '?') { + if (idx <= 255) + send_color_query_reply(t, 4, (int)idx, t->palette.colors[idx]); + } else { + char buf[128]; + if (idx <= 255 && spec_len < sizeof(buf)) { + memcpy(buf, spec, spec_len); + buf[spec_len] = '\0'; + uint32_t argb; + if (term_color_parse(buf, &argb)) + term_palette_set(&t->palette, (int)idx, argb); + } + } + p = spec_end; + if (*p == ';') p++; + } +} + +static void osc104_handle(struct term *t, const char *params) +{ + if (!*params) { + for (int i = 0; i < 256; i++) + term_palette_reset(&t->palette, i); + return; + } + const char *p = params; + while (*p) { + long idx = osc_next_index(&p); + if (idx < 0) + return; + if (idx <= 255) + term_palette_reset(&t->palette, (int)idx); + } +} + +void term_osc_finish(struct term *t) +{ + if (!term_str_was_truncated(t)) { + char *buf = term_osc_data(t); + buf[t->osc_len] = '\0'; + + if (!t->alternate && t->osc_len >= 5 && !strncmp(buf, "133;", 4) && + (buf[5] == ';' || buf[5] == '\0')) { + if (buf[4] == 'A') { + t->prompt_y = t->cursor_y; + t->has_prompt = true; + } + if (buf[4] == 'C' || buf[4] == 'D') { + t->prompt_y = -1; + t->has_prompt = false; + } + } + + char *rest; + long code = strtol(buf, &rest, 10); + bool no_code = rest == buf; + bool legacy_title = no_code && !strchr(buf, ';'); + if (no_code) { + code = legacy_title || *buf == ';' ? 0 : -1; + rest = buf; + } + if (code >= 0 && (legacy_title || *rest == ';' || *rest == '\0' || code <= 2)) { + const char *params = *rest == ';' ? rest + 1 : rest; + switch (code) { + case 0: case 1: case 2: + if (t->cb.title) + t->cb.title(t->cb.ud, params); + break; + case 4: + osc4_handle(t, params); + break; + case 8: + term_osc8(t, params); + break; + case 10: + osc_color_set_or_query(t, 10, params, TERM_PALETTE_FG); + break; + case 11: + osc_color_set_or_query(t, 11, params, TERM_PALETTE_BG); + break; + case 12: + osc_color_set_or_query(t, 12, params, TERM_PALETTE_CURSOR); + break; + case 52: + term_osc52(t, params); + break; + case 104: + osc104_handle(t, params); + break; + case 110: + term_palette_reset(&t->palette, TERM_PALETTE_FG); + break; + case 111: + term_palette_reset(&t->palette, TERM_PALETTE_BG); + break; + case 112: + term_palette_reset(&t->palette, TERM_PALETTE_CURSOR); + break; + default: + term_osc_misc_dispatch(t, code, params); + break; + } + } + } + t->osc_len = 0; + if (t->osc_heap && t->osc_heap_cap > OSC_HEAP_KEEP) { + free(t->osc_heap); + t->osc_heap = NULL; + t->osc_heap_cap = 0; + } +} |
