mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
Take ‘\E(#0’ for example - this is *not* the same as ‘\E(0’.
Up until now, foot has however treated them as the same escape,
because the handler for ‘\E(0’ didn’t verify there weren’t any _other_
private characters present.
Fix this by turning the ‘private’ array into a single 4-byte
integer. This allows us to match *all* privates with a single
comparison.
Private characters are added to the LSB first, and MSB last. This
means we can check for single privates in pretty much the same way as
before:
switch (term->vt.private) {
case ‘?’:
...
break;
}
Checking for two (or more) is much uglier, but foot only supports
a *single* escape with two privates, and no escapes with three or
more:
switch (term->vt.private) {
case 0x243f: /* ‘?$’ */
...
break;
}
The ‘clear’ action remains simple (and fast), with a single write
operation.
Collecting privates is potentially _slightly_ more complex than
before; we now need mask and compare, instead of simply comparing,
when checking how many privates we already have.
We _could_ add a counter, which would make collecting privates easier,
but this would add an additional write to the ‘clean’ action which is
really bad since it’s in the hot path.
112 lines
2.7 KiB
C
112 lines
2.7 KiB
C
#include "dcs.h"
|
|
|
|
#define LOG_MODULE "dcs"
|
|
#define LOG_ENABLE_DBG 0
|
|
#include "log.h"
|
|
#include "sixel.h"
|
|
#include "vt.h"
|
|
|
|
static void
|
|
bsu(struct terminal *term)
|
|
{
|
|
/* https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec */
|
|
|
|
LOG_DBG("untested: BSU - Begin Synchronized Update (params: %.*s)",
|
|
(int)term->vt.dcs.idx, term->vt.dcs.data);
|
|
|
|
term_enable_app_sync_updates(term);
|
|
}
|
|
|
|
static void
|
|
esu(struct terminal *term)
|
|
{
|
|
/* https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec */
|
|
|
|
LOG_DBG("untested: ESU - End Synchronized Update (params: %.*s)",
|
|
(int)term->vt.dcs.idx, term->vt.dcs.data);
|
|
|
|
term_disable_app_sync_updates(term);
|
|
}
|
|
|
|
void
|
|
dcs_hook(struct terminal *term, uint8_t final)
|
|
{
|
|
LOG_DBG("hook: %c (intermediate(s): %.2s, param=%d)", final,
|
|
(const char *)&term->vt.private, vt_param_get(term, 0, 0));
|
|
|
|
assert(term->vt.dcs.data == NULL);
|
|
assert(term->vt.dcs.size == 0);
|
|
assert(term->vt.dcs.put_handler == NULL);
|
|
assert(term->vt.dcs.unhook_handler == NULL);
|
|
|
|
switch (term->vt.private) {
|
|
case 0:
|
|
switch (final) {
|
|
case 'q':
|
|
sixel_init(term);
|
|
term->vt.dcs.put_handler = &sixel_put;
|
|
term->vt.dcs.unhook_handler = &sixel_unhook;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case '=':
|
|
switch (final) {
|
|
case 's':
|
|
switch (vt_param_get(term, 0, 0)) {
|
|
case 1: term->vt.dcs.unhook_handler = &bsu; return;
|
|
case 2: term->vt.dcs.unhook_handler = &esu; return;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool
|
|
ensure_size(struct terminal *term, size_t required_size)
|
|
{
|
|
if (required_size <= term->vt.dcs.size)
|
|
return true;
|
|
|
|
size_t new_size = (required_size + 127) / 128 * 128;
|
|
assert(new_size > 0);
|
|
|
|
uint8_t *new_data = realloc(term->vt.dcs.data, new_size);
|
|
if (new_data == NULL) {
|
|
LOG_ERRNO("failed to increase size of DCS buffer");
|
|
return false;
|
|
}
|
|
|
|
term->vt.dcs.data = new_data;
|
|
term->vt.dcs.size = new_size;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
dcs_put(struct terminal *term, uint8_t c)
|
|
{
|
|
LOG_DBG("PUT: %c", c);
|
|
if (term->vt.dcs.put_handler != NULL)
|
|
term->vt.dcs.put_handler(term, c);
|
|
else {
|
|
if (!ensure_size(term, term->vt.dcs.idx + 1))
|
|
return;
|
|
term->vt.dcs.data[term->vt.dcs.idx++] = c;
|
|
}
|
|
}
|
|
|
|
void
|
|
dcs_unhook(struct terminal *term)
|
|
{
|
|
if (term->vt.dcs.unhook_handler != NULL)
|
|
term->vt.dcs.unhook_handler(term);
|
|
|
|
term->vt.dcs.unhook_handler = NULL;
|
|
term->vt.dcs.put_handler = NULL;
|
|
|
|
free(term->vt.dcs.data);
|
|
term->vt.dcs.data = NULL;
|
|
term->vt.dcs.size = 0;
|
|
term->vt.dcs.idx = 0;
|
|
}
|