mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -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,
|
foot+base|foot base fragment,
|
||||||
am,
|
am,
|
||||||
bce,
|
bce,
|
||||||
|
ccc,
|
||||||
km,
|
km,
|
||||||
mir,
|
mir,
|
||||||
msgr,
|
msgr,
|
||||||
|
|
@ -61,6 +62,7 @@ foot+base|foot base fragment,
|
||||||
il1=\E[L,
|
il1=\E[L,
|
||||||
ind=\n,
|
ind=\n,
|
||||||
indn=\E[%p1%dS,
|
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,
|
invis=\E[8m,
|
||||||
is2=\E[!p\E[?3;4l\E[4l\E>,
|
is2=\E[!p\E[?3;4l\E[4l\E>,
|
||||||
kDC3=\E[3;3~,
|
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)
|
#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
|
static void
|
||||||
osc_to_clipboard(struct terminal *term, const char *target,
|
osc_to_clipboard(struct terminal *term, const char *target,
|
||||||
const char *base64_data)
|
const char *base64_data)
|
||||||
|
|
@ -210,17 +180,69 @@ osc_flash(struct terminal *term)
|
||||||
term_flash(term, 50);
|
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
|
void
|
||||||
osc_dispatch(struct terminal *term)
|
osc_dispatch(struct terminal *term)
|
||||||
{
|
{
|
||||||
unsigned param = 0;
|
unsigned param = 0;
|
||||||
int data_ofs = 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];
|
char c = term->vt.osc.data[i];
|
||||||
|
|
||||||
if (c == ';') {
|
if (c == ';') {
|
||||||
data_ofs = i + 1;
|
data_ofs++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,16 +260,108 @@ osc_dispatch(struct terminal *term)
|
||||||
|
|
||||||
char *string = (char *)&term->vt.osc.data[data_ofs];
|
char *string = (char *)&term->vt.osc.data[data_ofs];
|
||||||
|
|
||||||
if (strlen(string) == 1 && string[0] == '?') {
|
|
||||||
osc_query(term, param);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (param) {
|
switch (param) {
|
||||||
case 0: term_set_window_title(term, string); break; /* icon + title */
|
case 0: term_set_window_title(term, string); break; /* icon + title */
|
||||||
case 1: break; /* icon */
|
case 1: break; /* icon */
|
||||||
case 2: term_set_window_title(term, string); break; /* title */
|
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 */
|
case 30: /* Set tab title */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -255,9 +369,59 @@ osc_dispatch(struct terminal *term)
|
||||||
osc_selection(term, string);
|
osc_selection(term, string);
|
||||||
break;
|
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 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;
|
break;
|
||||||
|
|
||||||
case 555:
|
case 555:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue