2020-01-12 11:55:22 +01:00
|
|
|
#include "dcs.h"
|
2022-01-02 18:28:40 +01:00
|
|
|
#include <string.h>
|
2020-01-12 11:55:22 +01:00
|
|
|
|
|
|
|
|
#define LOG_MODULE "dcs"
|
|
|
|
|
#define LOG_ENABLE_DBG 0
|
|
|
|
|
#include "log.h"
|
2022-01-02 18:28:40 +01:00
|
|
|
#include "foot-terminfo.h"
|
2020-02-21 21:53:23 +01:00
|
|
|
#include "sixel.h"
|
2022-01-02 18:28:40 +01:00
|
|
|
#include "util.h"
|
2020-01-12 11:55:22 +01:00
|
|
|
#include "vt.h"
|
2022-01-02 18:28:40 +01:00
|
|
|
#include "xmalloc.h"
|
2024-07-01 16:25:45 +02:00
|
|
|
#include "xsnprintf.h"
|
2020-01-12 11:55:22 +01:00
|
|
|
|
2022-03-19 20:13:49 +01:00
|
|
|
static bool
|
|
|
|
|
ensure_size(struct terminal *term, size_t required_size)
|
|
|
|
|
{
|
|
|
|
|
if (required_size <= term->vt.dcs.size)
|
|
|
|
|
return true;
|
|
|
|
|
|
2022-03-19 20:22:59 +01:00
|
|
|
uint8_t *new_data = realloc(term->vt.dcs.data, required_size);
|
2022-03-19 20:13:49 +01:00
|
|
|
if (new_data == NULL) {
|
|
|
|
|
LOG_ERRNO("failed to increase size of DCS buffer");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
term->vt.dcs.data = new_data;
|
2022-03-19 20:22:59 +01:00
|
|
|
term->vt.dcs.size = required_size;
|
2022-03-19 20:13:49 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-02 18:28:40 +01:00
|
|
|
/* Decode hex-encoded string *inline*. NULL terminates */
|
|
|
|
|
static char *
|
|
|
|
|
hex_decode(const char *s, size_t len)
|
|
|
|
|
{
|
|
|
|
|
if (len % 2)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
char *hex = xmalloc(len / 2 + 1);
|
|
|
|
|
char *o = hex;
|
|
|
|
|
|
|
|
|
|
/* TODO: error checking */
|
|
|
|
|
for (size_t i = 0; i < len; i += 2) {
|
|
|
|
|
uint8_t nib1 = hex2nibble(*s); s++;
|
|
|
|
|
uint8_t nib2 = hex2nibble(*s); s++;
|
|
|
|
|
|
|
|
|
|
if (nib1 == HEX_DIGIT_INVALID || nib2 == HEX_DIGIT_INVALID)
|
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
*o = nib1 << 4 | nib2; o++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*o = '\0';
|
|
|
|
|
return hex;
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
free(hex);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UNITTEST
|
|
|
|
|
{
|
|
|
|
|
/* Verify table is sorted */
|
2022-01-13 13:37:44 +01:00
|
|
|
const char *p = terminfo_capabilities;
|
|
|
|
|
size_t left = sizeof(terminfo_capabilities);
|
|
|
|
|
|
|
|
|
|
const char *last_cap = NULL;
|
|
|
|
|
|
|
|
|
|
while (left > 0) {
|
|
|
|
|
const char *cap = p;
|
|
|
|
|
const char *val = cap + strlen(cap) + 1;
|
|
|
|
|
|
|
|
|
|
size_t size = strlen(cap) + 1 + strlen(val) + 1;;
|
|
|
|
|
xassert(size <= left);
|
|
|
|
|
p += size;
|
|
|
|
|
left -= size;
|
|
|
|
|
|
|
|
|
|
if (last_cap != NULL)
|
|
|
|
|
xassert(strcmp(last_cap, cap) < 0);
|
|
|
|
|
|
|
|
|
|
last_cap = cap;
|
2022-01-02 18:28:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-13 13:37:44 +01:00
|
|
|
static bool
|
|
|
|
|
lookup_capability(const char *name, const char **value)
|
2022-01-02 18:28:40 +01:00
|
|
|
{
|
2022-01-13 13:37:44 +01:00
|
|
|
const char *p = terminfo_capabilities;
|
|
|
|
|
size_t left = sizeof(terminfo_capabilities);
|
|
|
|
|
|
|
|
|
|
while (left > 0) {
|
|
|
|
|
const char *cap = p;
|
|
|
|
|
const char *val = cap + strlen(cap) + 1;
|
|
|
|
|
|
|
|
|
|
size_t size = strlen(cap) + 1 + strlen(val) + 1;;
|
|
|
|
|
xassert(size <= left);
|
|
|
|
|
p += size;
|
|
|
|
|
left -= size;
|
|
|
|
|
|
|
|
|
|
int r = strcmp(cap, name);
|
|
|
|
|
if (r == 0) {
|
|
|
|
|
*value = val;
|
|
|
|
|
return true;
|
|
|
|
|
} else if (r > 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-01-02 18:28:40 +01:00
|
|
|
|
2022-01-13 13:37:44 +01:00
|
|
|
*value = NULL;
|
|
|
|
|
return false;
|
2022-01-02 18:28:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
xtgettcap_reply(struct terminal *term, const char *hex_cap_name, size_t len)
|
|
|
|
|
{
|
|
|
|
|
char *name = hex_decode(hex_cap_name, len);
|
2023-09-25 16:37:32 +02:00
|
|
|
if (name == NULL) {
|
|
|
|
|
LOG_WARN("XTGETTCAP: invalid hex encoding, ignoring capability");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-01-02 18:28:40 +01:00
|
|
|
|
2022-01-13 13:37:44 +01:00
|
|
|
const char *value;
|
|
|
|
|
bool valid_capability = lookup_capability(name, &value);
|
|
|
|
|
xassert(!valid_capability || value != NULL);
|
2022-01-02 18:28:40 +01:00
|
|
|
|
|
|
|
|
LOG_DBG("XTGETTCAP: cap=%s (%.*s), value=%s",
|
|
|
|
|
name, (int)len, hex_cap_name,
|
2022-01-13 13:37:44 +01:00
|
|
|
valid_capability ? value : "<invalid>");
|
2022-01-02 18:28:40 +01:00
|
|
|
|
2022-01-13 13:37:44 +01:00
|
|
|
if (!valid_capability)
|
2022-01-02 18:28:40 +01:00
|
|
|
goto err;
|
|
|
|
|
|
2022-01-13 13:37:44 +01:00
|
|
|
if (value[0] == '\0') {
|
2022-01-04 22:01:36 +01:00
|
|
|
/* Boolean */
|
|
|
|
|
term_to_slave(term, "\033P1+r", 5);
|
|
|
|
|
term_to_slave(term, hex_cap_name, len);
|
|
|
|
|
term_to_slave(term, "\033\\", 2);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-02 18:28:40 +01:00
|
|
|
/*
|
|
|
|
|
* Reply format:
|
|
|
|
|
* \EP 1 + r cap=value \E\\
|
2024-02-06 12:36:45 +01:00
|
|
|
* Where 'cap' and 'value are hex encoded ascii strings
|
2022-01-02 18:28:40 +01:00
|
|
|
*/
|
|
|
|
|
char *reply = xmalloc(
|
|
|
|
|
5 + /* DCS 1 + r (\EP1+r) */
|
|
|
|
|
len + /* capability name, hex encoded */
|
2024-02-06 12:36:45 +01:00
|
|
|
1 + /* '=' */
|
2022-01-13 13:37:44 +01:00
|
|
|
strlen(value) * 2 + /* capability value, hex encoded */
|
2022-01-02 18:28:40 +01:00
|
|
|
2 + /* ST (\E\\) */
|
|
|
|
|
1);
|
|
|
|
|
|
|
|
|
|
int idx = sprintf(reply, "\033P1+r%.*s=", (int)len, hex_cap_name);
|
|
|
|
|
|
2022-01-13 13:37:44 +01:00
|
|
|
for (const char *c = value; *c != '\0'; c++) {
|
2022-01-02 18:28:40 +01:00
|
|
|
uint8_t nib1 = (uint8_t)*c >> 4;
|
|
|
|
|
uint8_t nib2 = (uint8_t)*c & 0xf;
|
|
|
|
|
|
|
|
|
|
reply[idx] = nib1 >= 0xa ? 'A' + nib1 - 0xa : '0' + nib1; idx++;
|
|
|
|
|
reply[idx] = nib2 >= 0xa ? 'A' + nib2 - 0xa : '0' + nib2; idx++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reply[idx] = '\033'; idx++;
|
|
|
|
|
reply[idx] = '\\'; idx++;
|
|
|
|
|
term_to_slave(term, reply, idx);
|
|
|
|
|
|
|
|
|
|
free(reply);
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
term_to_slave(term, "\033P0+r", 5);
|
|
|
|
|
term_to_slave(term, hex_cap_name, len);
|
|
|
|
|
term_to_slave(term, "\033\\", 2);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
free(name);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-19 20:22:59 +01:00
|
|
|
static void
|
|
|
|
|
xtgettcap_put(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
struct vt *vt = &term->vt;
|
|
|
|
|
|
|
|
|
|
/* Grow buffer expontentially */
|
|
|
|
|
if (vt->dcs.idx >= vt->dcs.size) {
|
|
|
|
|
size_t new_size = vt->dcs.size * 2;
|
|
|
|
|
if (new_size == 0)
|
|
|
|
|
new_size = 128;
|
|
|
|
|
|
|
|
|
|
if (!ensure_size(term, new_size))
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vt->dcs.data[vt->dcs.idx++] = c;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-02 18:28:40 +01:00
|
|
|
static void
|
|
|
|
|
xtgettcap_unhook(struct terminal *term)
|
|
|
|
|
{
|
|
|
|
|
size_t left = term->vt.dcs.idx;
|
|
|
|
|
|
|
|
|
|
const char *const end = (const char *)&term->vt.dcs.data[left];
|
|
|
|
|
const char *p = (const char *)term->vt.dcs.data;
|
|
|
|
|
|
2024-04-20 08:19:58 +02:00
|
|
|
if (p == NULL) {
|
|
|
|
|
/* Request is empty; send an error reply, without any capabilities */
|
|
|
|
|
term_to_slave(term, "\033P0+r\033\\", 7);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-02 18:28:40 +01:00
|
|
|
while (true) {
|
|
|
|
|
const char *sep = memchr(p, ';', left);
|
|
|
|
|
size_t cap_len;
|
|
|
|
|
|
|
|
|
|
if (sep == NULL) {
|
|
|
|
|
/* Last capability */
|
|
|
|
|
cap_len = end - p;
|
|
|
|
|
} else {
|
|
|
|
|
cap_len = sep - p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xtgettcap_reply(term, p, cap_len);
|
|
|
|
|
|
|
|
|
|
left -= cap_len + 1;
|
|
|
|
|
p += cap_len + 1;
|
|
|
|
|
|
|
|
|
|
if (sep == NULL)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-14 14:09:28 +01:00
|
|
|
static void NOINLINE
|
|
|
|
|
append_sgr_attr_n(char **reply, size_t *len, const char *attr, size_t n)
|
|
|
|
|
{
|
|
|
|
|
size_t new_len = *len + n + 1;
|
|
|
|
|
*reply = xrealloc(*reply, new_len);
|
|
|
|
|
memcpy(&(*reply)[*len], attr, n);
|
|
|
|
|
(*reply)[new_len - 1] = ';';
|
|
|
|
|
*len = new_len;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-13 15:36:27 +01:00
|
|
|
static void
|
2022-03-19 20:13:49 +01:00
|
|
|
decrqss_put(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
/* Largest request we support is two bytes */
|
|
|
|
|
if (!ensure_size(term, 2))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
struct vt *vt = &term->vt;
|
2024-04-10 05:44:33 +02:00
|
|
|
if (vt->dcs.idx >= 2)
|
2022-03-19 20:13:49 +01:00
|
|
|
return;
|
|
|
|
|
vt->dcs.data[vt->dcs.idx++] = c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
decrqss_unhook(struct terminal *term)
|
2022-01-13 15:36:27 +01:00
|
|
|
{
|
|
|
|
|
const uint8_t *query = term->vt.dcs.data;
|
|
|
|
|
const size_t n = term->vt.dcs.idx;
|
|
|
|
|
|
2022-01-13 15:46:29 +01:00
|
|
|
/*
|
|
|
|
|
* A note on the Ps parameter in the reply: many DEC manual
|
|
|
|
|
* instances (e.g. https://vt100.net/docs/vt510-rm/DECRPSS) claim
|
2024-02-06 12:36:45 +01:00
|
|
|
* that 0 means "request is valid", and 1 means "request is
|
|
|
|
|
* invalid".
|
2022-01-13 15:46:29 +01:00
|
|
|
*
|
|
|
|
|
* However, this appears to be a typo; actual hardware inverts the
|
|
|
|
|
* response (as does XTerm and mlterm):
|
|
|
|
|
* https://github.com/hackerb9/vt340test/issues/13
|
|
|
|
|
*/
|
|
|
|
|
|
2022-03-02 19:05:51 +00:00
|
|
|
if (n == 1 && query[0] == 'r') {
|
2022-01-13 15:36:27 +01:00
|
|
|
/* DECSTBM - Set Top and Bottom Margins */
|
|
|
|
|
char reply[64];
|
2024-07-01 16:25:45 +02:00
|
|
|
int len = xsnprintf(reply, sizeof(reply), "\033P1$r%d;%dr\033\\",
|
|
|
|
|
term->scroll_region.start + 1,
|
|
|
|
|
term->scroll_region.end);
|
2022-01-13 15:36:27 +01:00
|
|
|
term_to_slave(term, reply, len);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-02 19:05:51 +00:00
|
|
|
else if (n == 1 && query[0] == 'm') {
|
2022-01-13 15:36:27 +01:00
|
|
|
/* SGR - Set Graphic Rendition */
|
|
|
|
|
char *reply = NULL;
|
|
|
|
|
size_t len = 0;
|
|
|
|
|
|
2022-01-14 14:09:28 +01:00
|
|
|
#define append_sgr_attr(num_as_str) \
|
|
|
|
|
append_sgr_attr_n(&reply, &len, num_as_str, sizeof(num_as_str) - 1)
|
2022-01-13 15:36:27 +01:00
|
|
|
|
|
|
|
|
/* Always present, both in the example from the VT510 manual
|
|
|
|
|
* (https://vt100.net/docs/vt510-rm/DECRPSS), and in XTerm and
|
|
|
|
|
* mlterm */
|
2022-01-14 14:09:28 +01:00
|
|
|
append_sgr_attr("0");
|
2022-01-13 15:36:27 +01:00
|
|
|
|
|
|
|
|
struct attributes *a = &term->vt.attrs;
|
|
|
|
|
if (a->bold)
|
2022-01-14 14:09:28 +01:00
|
|
|
append_sgr_attr("1");
|
2022-01-13 15:36:27 +01:00
|
|
|
if (a->dim)
|
2022-01-14 14:09:28 +01:00
|
|
|
append_sgr_attr("2");
|
2022-01-13 15:36:27 +01:00
|
|
|
if (a->italic)
|
2022-01-14 14:09:28 +01:00
|
|
|
append_sgr_attr("3");
|
2024-07-01 16:25:45 +02:00
|
|
|
if (a->underline) {
|
|
|
|
|
if (term->vt.curly.style > CURLY_SINGLE) {
|
|
|
|
|
char value[4];
|
|
|
|
|
int val_len =
|
|
|
|
|
xsnprintf(value, sizeof(value), "4:%d", term->vt.curly.style);
|
|
|
|
|
append_sgr_attr_n(&reply, &len, value, val_len);
|
|
|
|
|
} else
|
|
|
|
|
append_sgr_attr("4");
|
|
|
|
|
}
|
2022-01-13 15:36:27 +01:00
|
|
|
if (a->blink)
|
2022-01-14 14:09:28 +01:00
|
|
|
append_sgr_attr("5");
|
2022-01-13 15:36:27 +01:00
|
|
|
if (a->reverse)
|
2022-01-14 14:09:28 +01:00
|
|
|
append_sgr_attr("7");
|
2022-01-13 15:36:27 +01:00
|
|
|
if (a->conceal)
|
2022-01-14 14:09:28 +01:00
|
|
|
append_sgr_attr("8");
|
2022-01-13 15:36:27 +01:00
|
|
|
if (a->strikethrough)
|
2022-01-14 14:09:28 +01:00
|
|
|
append_sgr_attr("9");
|
2022-01-13 15:36:27 +01:00
|
|
|
|
|
|
|
|
switch (a->fg_src) {
|
|
|
|
|
case COLOR_DEFAULT:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case COLOR_BASE16: {
|
|
|
|
|
char value[4];
|
2024-07-01 16:25:45 +02:00
|
|
|
int val_len = xsnprintf(
|
2022-01-13 15:36:27 +01:00
|
|
|
value, sizeof(value), "%u",
|
|
|
|
|
a->fg >= 8 ? a->fg - 8 + 90 : a->fg + 30);
|
2022-01-14 14:09:28 +01:00
|
|
|
append_sgr_attr_n(&reply, &len, value, val_len);
|
2022-01-13 15:36:27 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case COLOR_BASE256: {
|
|
|
|
|
char value[16];
|
2024-07-01 16:25:45 +02:00
|
|
|
int val_len = xsnprintf(value, sizeof(value), "38:5:%u", a->fg);
|
2022-01-14 14:09:28 +01:00
|
|
|
append_sgr_attr_n(&reply, &len, value, val_len);
|
2022-01-13 15:36:27 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case COLOR_RGB: {
|
|
|
|
|
uint8_t r = a->fg >> 16;
|
|
|
|
|
uint8_t g = a->fg >> 8;
|
|
|
|
|
uint8_t b = a->fg >> 0;
|
|
|
|
|
|
|
|
|
|
char value[32];
|
2024-07-01 16:25:45 +02:00
|
|
|
int val_len = xsnprintf(
|
2022-01-13 15:36:27 +01:00
|
|
|
value, sizeof(value), "38:2::%hhu:%hhu:%hhu", r, g, b);
|
2022-01-14 14:09:28 +01:00
|
|
|
append_sgr_attr_n(&reply, &len, value, val_len);
|
2022-01-13 15:36:27 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (a->bg_src) {
|
|
|
|
|
case COLOR_DEFAULT:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case COLOR_BASE16: {
|
|
|
|
|
char value[4];
|
2024-07-01 16:25:45 +02:00
|
|
|
int val_len = xsnprintf(
|
2022-01-13 15:36:27 +01:00
|
|
|
value, sizeof(value), "%u",
|
|
|
|
|
a->bg >= 8 ? a->bg - 8 + 100 : a->bg + 40);
|
2022-01-14 14:09:28 +01:00
|
|
|
append_sgr_attr_n(&reply, &len, value, val_len);
|
2022-01-13 15:36:27 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case COLOR_BASE256: {
|
|
|
|
|
char value[16];
|
2024-07-01 16:25:45 +02:00
|
|
|
int val_len = xsnprintf(value, sizeof(value), "48:5:%u", a->bg);
|
2022-01-14 14:09:28 +01:00
|
|
|
append_sgr_attr_n(&reply, &len, value, val_len);
|
2022-01-13 15:36:27 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case COLOR_RGB: {
|
|
|
|
|
uint8_t r = a->bg >> 16;
|
|
|
|
|
uint8_t g = a->bg >> 8;
|
|
|
|
|
uint8_t b = a->bg >> 0;
|
|
|
|
|
|
|
|
|
|
char value[32];
|
2024-07-01 16:25:45 +02:00
|
|
|
int val_len = xsnprintf(
|
2022-01-13 15:36:27 +01:00
|
|
|
value, sizeof(value), "48:2::%hhu:%hhu:%hhu", r, g, b);
|
2022-01-14 14:09:28 +01:00
|
|
|
append_sgr_attr_n(&reply, &len, value, val_len);
|
2022-01-13 15:36:27 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-01 16:25:45 +02:00
|
|
|
switch (term->vt.curly.color_src) {
|
|
|
|
|
case COLOR_DEFAULT:
|
|
|
|
|
case COLOR_BASE16:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case COLOR_BASE256: {
|
|
|
|
|
char value[16];
|
|
|
|
|
int val_len = xsnprintf(
|
|
|
|
|
value, sizeof(value), "58:5:%u", term->vt.curly.color);
|
|
|
|
|
append_sgr_attr_n(&reply, &len, value, val_len);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case COLOR_RGB: {
|
|
|
|
|
uint8_t r = term->vt.curly.color >> 16;
|
|
|
|
|
uint8_t g = term->vt.curly.color >> 8;
|
|
|
|
|
uint8_t b = term->vt.curly.color >> 0;
|
|
|
|
|
|
|
|
|
|
char value[32];
|
|
|
|
|
int val_len = xsnprintf(
|
|
|
|
|
value, sizeof(value), "58:2::%hhu:%hhu:%hhu", r, g, b);
|
|
|
|
|
append_sgr_attr_n(&reply, &len, value, val_len);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-14 14:09:28 +01:00
|
|
|
#undef append_sgr_attr_n
|
2022-01-13 15:36:27 +01:00
|
|
|
|
|
|
|
|
reply[len - 1] = 'm';
|
|
|
|
|
|
|
|
|
|
term_to_slave(term, "\033P1$r", 5);
|
|
|
|
|
term_to_slave(term, reply, len);
|
|
|
|
|
term_to_slave(term, "\033\\", 2);
|
|
|
|
|
free(reply);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-02 19:05:51 +00:00
|
|
|
else if (n == 2 && memcmp(query, " q", 2) == 0) {
|
2022-01-13 15:36:27 +01:00
|
|
|
/* DECSCUSR - Set Cursor Style */
|
|
|
|
|
int mode;
|
|
|
|
|
|
|
|
|
|
switch (term->cursor_style) {
|
|
|
|
|
case CURSOR_BLOCK: mode = 2; break;
|
|
|
|
|
case CURSOR_UNDERLINE: mode = 4; break;
|
|
|
|
|
case CURSOR_BEAM: mode = 6; break;
|
2022-01-13 15:43:22 +01:00
|
|
|
default: BUG("invalid cursor style"); break;
|
2022-01-13 15:36:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (term->cursor_blink.deccsusr)
|
|
|
|
|
mode--;
|
|
|
|
|
|
|
|
|
|
char reply[16];
|
2024-07-01 16:25:45 +02:00
|
|
|
int len = xsnprintf(reply, sizeof(reply), "\033P1$r%d q\033\\", mode);
|
2022-01-13 15:36:27 +01:00
|
|
|
term_to_slave(term, reply, len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else {
|
2022-03-02 19:05:51 +00:00
|
|
|
static const char err[] = "\033P0$r\033\\";
|
2022-01-13 15:36:27 +01:00
|
|
|
term_to_slave(term, err, sizeof(err) - 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-12 11:55:22 +01:00
|
|
|
void
|
|
|
|
|
dcs_hook(struct terminal *term, uint8_t final)
|
|
|
|
|
{
|
vt: don’t ignore extra private/intermediate characters
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.
2020-12-16 14:30:49 +01:00
|
|
|
LOG_DBG("hook: %c (intermediate(s): %.2s, param=%d)", final,
|
|
|
|
|
(const char *)&term->vt.private, vt_param_get(term, 0, 0));
|
2020-01-12 11:55:22 +01:00
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(term->vt.dcs.data == NULL);
|
|
|
|
|
xassert(term->vt.dcs.size == 0);
|
|
|
|
|
xassert(term->vt.dcs.put_handler == NULL);
|
|
|
|
|
xassert(term->vt.dcs.unhook_handler == NULL);
|
2020-01-12 11:55:22 +01:00
|
|
|
|
vt: don’t ignore extra private/intermediate characters
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.
2020-12-16 14:30:49 +01:00
|
|
|
switch (term->vt.private) {
|
2020-02-21 21:53:23 +01:00
|
|
|
case 0:
|
|
|
|
|
switch (final) {
|
2021-03-09 17:23:55 +01:00
|
|
|
case 'q': {
|
2022-02-27 16:29:35 +01:00
|
|
|
if (!term->conf->tweak.sixel) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-03-09 17:23:55 +01:00
|
|
|
int p1 = vt_param_get(term, 0, 0);
|
|
|
|
|
int p2 = vt_param_get(term, 1,0);
|
|
|
|
|
int p3 = vt_param_get(term, 2, 0);
|
|
|
|
|
|
sixel: special case parsing of images with an aspect ratio of 1:1
Images with an aspect ratio of 1:1 are by far the most common (though
not the default).
It makes a lot of sense, performance wise, to special case
them.
Specifically, the sixel_add() function benefits greatly from this, as
it is the inner most, most heavily executed function when parsing a
sixel image.
sixel_add_many() also benefits, since allows us to drop a
multiplication. Since sixel_add_many() always called first (no other
call sites call sixel_add() directly), this has a noticeable effect on
performance.
Another thing that helps (though not as much), and not specifically
with AR 1:1 images, is special casing DECGRI a bit.
Up until now, it simply updated the current sixel parameter value. The
problem is that the default parameter value is 0. But, a value of 0
should be treated as 1. By adding a special ‘repeat_count’ variable to
the sixel struct, we can initialize it to ‘1’ when we see DECGRI, and
then simply overwrite it as the parameter value gets updated. This
allows us to drop an if..else when emitting the sixel.
2023-06-27 14:25:55 +02:00
|
|
|
term->vt.dcs.put_handler = sixel_init(term, p1, p2, p3);
|
2020-02-21 21:53:23 +01:00
|
|
|
term->vt.dcs.unhook_handler = &sixel_unhook;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-03-09 17:23:55 +01:00
|
|
|
}
|
2020-02-21 21:53:23 +01:00
|
|
|
break;
|
|
|
|
|
|
2022-01-13 15:36:27 +01:00
|
|
|
case '$':
|
|
|
|
|
switch (final) {
|
|
|
|
|
case 'q':
|
2022-03-19 20:13:49 +01:00
|
|
|
term->vt.dcs.put_handler = &decrqss_put;
|
|
|
|
|
term->vt.dcs.unhook_handler = &decrqss_unhook;
|
2022-01-13 15:36:27 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2020-01-12 11:55:22 +01:00
|
|
|
case '=':
|
|
|
|
|
switch (final) {
|
|
|
|
|
case 's':
|
2022-03-19 20:04:11 +01:00
|
|
|
/* BSU/ESU: https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec */
|
2020-01-12 11:55:22 +01:00
|
|
|
switch (vt_param_get(term, 0, 0)) {
|
2022-03-19 20:04:11 +01:00
|
|
|
case 1:
|
|
|
|
|
term->vt.dcs.unhook_handler = &term_enable_app_sync_updates;
|
|
|
|
|
return;
|
|
|
|
|
case 2:
|
|
|
|
|
term->vt.dcs.unhook_handler = &term_disable_app_sync_updates;
|
|
|
|
|
return;
|
2020-01-12 11:55:22 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2022-01-02 18:28:40 +01:00
|
|
|
|
|
|
|
|
case '+':
|
|
|
|
|
switch (final) {
|
|
|
|
|
case 'q': /* XTGETTCAP */
|
2022-03-19 20:22:59 +01:00
|
|
|
term->vt.dcs.put_handler = &xtgettcap_put;
|
2022-01-02 18:28:40 +01:00
|
|
|
term->vt.dcs.unhook_handler = &xtgettcap_unhook;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2020-01-12 11:55:22 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
dcs_put(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
2022-01-02 18:28:40 +01:00
|
|
|
/* LOG_DBG("PUT: %c", c); */
|
|
|
|
|
|
2020-02-21 21:53:23 +01:00
|
|
|
if (term->vt.dcs.put_handler != NULL)
|
|
|
|
|
term->vt.dcs.put_handler(term, c);
|
2020-01-12 11:55:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
2020-02-21 21:53:23 +01:00
|
|
|
term->vt.dcs.put_handler = NULL;
|
2020-01-12 11:55:22 +01:00
|
|
|
|
|
|
|
|
free(term->vt.dcs.data);
|
|
|
|
|
term->vt.dcs.data = NULL;
|
|
|
|
|
term->vt.dcs.size = 0;
|
|
|
|
|
term->vt.dcs.idx = 0;
|
|
|
|
|
}
|