mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-04 04:06:06 -05:00
osc: implement "change color" commands
This implements OSC 4, 10, 11 - change <color>/foreground/background, and their corresponding 'query' variant (which was already implemented for OSC 10/11). It also implements OSC 104, 110, 111 - reset <color>/foreground/background. Set corresponding terminfo entries to signal this support to clients.
This commit is contained in:
parent
c1903f5522
commit
52ece3592c
2 changed files with 205 additions and 39 deletions
|
|
@ -13,6 +13,7 @@ foot-direct|foot with direct color indexing,
|
|||
foot+base|foot base fragment,
|
||||
am,
|
||||
bce,
|
||||
ccc,
|
||||
km,
|
||||
mir,
|
||||
msgr,
|
||||
|
|
@ -61,6 +62,7 @@ foot+base|foot base fragment,
|
|||
il1=\E[L,
|
||||
ind=\n,
|
||||
indn=\E[%p1%dS,
|
||||
initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\,
|
||||
invis=\E[8m,
|
||||
is2=\E[!p\E[?3;4l\E[4l\E>,
|
||||
kDC3=\E[3;3~,
|
||||
|
|
|
|||
242
osc.c
242
osc.c
|
|
@ -14,36 +14,6 @@
|
|||
|
||||
#define UNHANDLED() LOG_ERR("unhandled: OSC: %.*s", (int)term->vt.osc.idx, term->vt.osc.data)
|
||||
|
||||
static void
|
||||
osc_query(struct terminal *term, unsigned param)
|
||||
{
|
||||
switch (param) {
|
||||
case 10:
|
||||
case 11: {
|
||||
uint32_t color = param == 10 ? term->colors.fg : term->colors.bg;
|
||||
uint8_t r = (color >> 16) & 0xff;
|
||||
uint8_t g = (color >> 8) & 0xff;
|
||||
uint8_t b = (color >> 0) & 0xff;
|
||||
|
||||
/*
|
||||
* Reply in XParseColor format
|
||||
* E.g. for color 0xdcdccc we reply "\e]10;rgb:dc/dc/cc\e\\"
|
||||
*/
|
||||
char reply[32];
|
||||
snprintf(
|
||||
reply, sizeof(reply), "\033]%u;rgb:%02x/%02x/%02x\033\\",
|
||||
param, r, g, b);
|
||||
|
||||
vt_to_slave(term, reply, strlen(reply));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
UNHANDLED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
osc_to_clipboard(struct terminal *term, const char *target,
|
||||
const char *base64_data)
|
||||
|
|
@ -210,17 +180,69 @@ osc_flash(struct terminal *term)
|
|||
term_flash(term, 50);
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_rgb(const char *string, uint32_t *color)
|
||||
{
|
||||
size_t len = strlen(string);
|
||||
|
||||
/* Verify we have the minimum required length (for "rgb:x/x/x") */
|
||||
if (len < 3 /* 'rgb' */ + 1 /* ':' */ + 2 /* '/' */ + 3 * 1 /* 3 * 'x' */)
|
||||
return false;
|
||||
|
||||
/* Verify prefix is "rgb:" */
|
||||
if (string[0] != 'r' || string[1] != 'g' || string[2] != 'b' || string[3] != ':')
|
||||
return false;
|
||||
|
||||
string += 4;
|
||||
len -= 4;
|
||||
|
||||
int rgb[3];
|
||||
int digits[3];
|
||||
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
for (rgb[i] = 0, digits[i] = 0;
|
||||
len > 0 && *string != '/';
|
||||
len--, string++, digits[i]++)
|
||||
{
|
||||
char c = *string;
|
||||
rgb[i] <<= 4;
|
||||
|
||||
if (!isxdigit(c))
|
||||
rgb[i] |= 0;
|
||||
else
|
||||
rgb[i] |= c >= '0' && c <= '9' ? c - '0' :
|
||||
c >= 'a' && c <= 'f' ? c - 'a' + 10 : c - 'A' + 10;
|
||||
}
|
||||
|
||||
if (i >= 2)
|
||||
break;
|
||||
|
||||
if (len == 0 || *string != '/')
|
||||
return false;
|
||||
string++; len--;
|
||||
}
|
||||
|
||||
/* Re-scale to 8-bit */
|
||||
uint8_t r = 255 * (rgb[0] / (double)((1 << (4 * digits[0])) - 1));
|
||||
uint8_t g = 255 * (rgb[1] / (double)((1 << (4 * digits[1])) - 1));
|
||||
uint8_t b = 255 * (rgb[2] / (double)((1 << (4 * digits[2])) - 1));
|
||||
|
||||
LOG_DBG("rgb: %02x%02x%02x", r, g, b);
|
||||
*color = r << 16 | g << 8 | b;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
osc_dispatch(struct terminal *term)
|
||||
{
|
||||
unsigned param = 0;
|
||||
int data_ofs = 0;
|
||||
|
||||
for (size_t i = 0; i < term->vt.osc.idx; i++) {
|
||||
for (size_t i = 0; i < term->vt.osc.idx; i++, data_ofs++) {
|
||||
char c = term->vt.osc.data[i];
|
||||
|
||||
if (c == ';') {
|
||||
data_ofs = i + 1;
|
||||
data_ofs++;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -238,16 +260,108 @@ osc_dispatch(struct terminal *term)
|
|||
|
||||
char *string = (char *)&term->vt.osc.data[data_ofs];
|
||||
|
||||
if (strlen(string) == 1 && string[0] == '?') {
|
||||
osc_query(term, param);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (param) {
|
||||
case 0: term_set_window_title(term, string); break; /* icon + title */
|
||||
case 1: break; /* icon */
|
||||
case 2: term_set_window_title(term, string); break; /* title */
|
||||
|
||||
case 4: {
|
||||
/* Set color<idx> */
|
||||
|
||||
/* First param - the color index */
|
||||
unsigned idx = 0;
|
||||
for (; *string != '\0' && *string != ';'; string++) {
|
||||
char c = *string;
|
||||
idx *= 10;
|
||||
idx += c - '0';
|
||||
}
|
||||
|
||||
if (idx >= 256)
|
||||
break;
|
||||
|
||||
/* Next follows the color specification. For now, we only support rgb:x/y/z */
|
||||
|
||||
if (*string == '\0') {
|
||||
/* No color specification */
|
||||
break;
|
||||
}
|
||||
|
||||
assert(*string == ';');
|
||||
string++;
|
||||
|
||||
/* Client queried for current value */
|
||||
if (strlen(string) == 1 && string[0] == '?') {
|
||||
uint32_t color =
|
||||
(idx >= 0 && idx < 8) ? term->colors.regular[idx] :
|
||||
(idx >= 8 && idx < 16) ? term->colors.bright[idx] :
|
||||
term->colors.colors256[idx];
|
||||
|
||||
uint8_t r = (color >> 16) & 0xff;
|
||||
uint8_t g = (color >> 8) & 0xff;
|
||||
uint8_t b = (color >> 0) & 0xff;
|
||||
|
||||
char reply[32];
|
||||
snprintf(reply, sizeof(reply), "\033]4;%u;rgb:%02x/%02x/%02x\033\\",
|
||||
idx, r, g, b);
|
||||
vt_to_slave(term, reply, strlen(reply));
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t color;
|
||||
if (!parse_rgb(string, &color))
|
||||
break;
|
||||
|
||||
if (idx >= 0 && idx < 8)
|
||||
term->colors.regular[idx] = color;
|
||||
else if (idx >= 8 && idx < 16)
|
||||
term->colors.bright[idx - 8] = color;
|
||||
else
|
||||
term->colors.colors256[idx] = color;
|
||||
|
||||
render_refresh(term);
|
||||
break;
|
||||
}
|
||||
|
||||
case 10:
|
||||
case 11: {
|
||||
/* Set default foreground/background color */
|
||||
|
||||
/* Client queried for current value */
|
||||
if (strlen(string) == 1 && string[0] == '?') {
|
||||
uint32_t color = param == 10 ? term->colors.fg : term->colors.bg;
|
||||
uint8_t r = (color >> 16) & 0xff;
|
||||
uint8_t g = (color >> 8) & 0xff;
|
||||
uint8_t b = (color >> 0) & 0xff;
|
||||
|
||||
/*
|
||||
* Reply in XParseColor format
|
||||
* E.g. for color 0xdcdccc we reply "\e]10;rgb:dc/dc/cc\e\\"
|
||||
*/
|
||||
char reply[32];
|
||||
snprintf(
|
||||
reply, sizeof(reply), "\033]%u;rgb:%02x/%02x/%02x\033\\",
|
||||
param, r, g, b);
|
||||
|
||||
vt_to_slave(term, reply, strlen(reply));
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t color;
|
||||
if (!parse_rgb(string, &color))
|
||||
break;
|
||||
|
||||
switch (param) {
|
||||
case 10: term->colors.fg = color; break;
|
||||
case 11: term->colors.bg = color; break;
|
||||
}
|
||||
|
||||
render_refresh(term);
|
||||
break;
|
||||
}
|
||||
|
||||
case 12: /* Set cursor color */
|
||||
break;
|
||||
|
||||
case 30: /* Set tab title */
|
||||
break;
|
||||
|
||||
|
|
@ -255,9 +369,59 @@ osc_dispatch(struct terminal *term)
|
|||
osc_selection(term, string);
|
||||
break;
|
||||
|
||||
case 104: /* Reset Color Number 'c' */
|
||||
case 104: {
|
||||
/* Reset Color Number 'c' (whole table if no parameter) */
|
||||
|
||||
if (strlen(string) == 0) {
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
term->colors.regular[i] = term->colors.default_regular[i];
|
||||
term->colors.bright[i] = term->colors.default_bright[i];
|
||||
}
|
||||
for (size_t i = 16; i < 256; i++)
|
||||
term->colors.colors256[i] = term->colors.default_colors256[i];
|
||||
} else {
|
||||
unsigned idx = 0;
|
||||
|
||||
for (; *string != '\0'; string++) {
|
||||
char c = *string;
|
||||
if (c == ';') {
|
||||
if (idx >= 0 && idx < 8)
|
||||
term->colors.regular[idx] = term->colors.default_regular[idx];
|
||||
else if (idx >= 8 && idx < 16)
|
||||
term->colors.bright[idx] = term->colors.default_bright[idx];
|
||||
else if (idx < 256)
|
||||
term->colors.colors256[idx] = term->colors.default_colors256[idx];
|
||||
idx = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
idx *= 10;
|
||||
idx += c - '0';
|
||||
}
|
||||
|
||||
if (idx >= 0 && idx < 8)
|
||||
term->colors.regular[idx] = term->colors.default_regular[idx];
|
||||
else if (idx >= 8 && idx < 16)
|
||||
term->colors.bright[idx] = term->colors.default_bright[idx];
|
||||
else if (idx < 256)
|
||||
term->colors.colors256[idx] = term->colors.default_colors256[idx];
|
||||
}
|
||||
|
||||
render_refresh(term);
|
||||
break;
|
||||
}
|
||||
|
||||
case 105: /* Reset Special Color Number 'c' */
|
||||
case 112: /* Reset text cursor color */
|
||||
break;
|
||||
|
||||
case 110: /* Reset default text foreground color */
|
||||
term->colors.fg = term->colors.default_fg;
|
||||
render_refresh(term);
|
||||
break;
|
||||
|
||||
case 111: /* Reset default text backgroundc color */
|
||||
term->colors.bg = term->colors.default_bg;
|
||||
render_refresh(term);
|
||||
break;
|
||||
|
||||
case 555:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue