osc: add support for URxvt extension to include alpha in OSC-11

There are two variants, either using a ‘[percent]’ prefix for legacy
colors, or using ‘rgba:r/g/b/a’ instead of ‘rgb:r/g/b’.

Closes #436
This commit is contained in:
Daniel Eklöf 2021-04-05 22:05:50 +02:00
parent 0af7916fcc
commit ed41bffc28
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
2 changed files with 105 additions and 24 deletions

View file

@ -27,6 +27,11 @@
## Unreleased ## Unreleased
### Added ### Added
* Support for URxvt OSC-11 extension to set background alpha
(https://codeberg.org/dnkl/foot/issues/436).
### Changed ### Changed
### Deprecated ### Deprecated
### Removed ### Removed

124
osc.c
View file

@ -261,8 +261,30 @@ osc_flash(struct terminal *term)
} }
static bool static bool
parse_legacy_color(const char *string, uint32_t *color) parse_legacy_color(const char *string, uint32_t *color, bool *_have_alpha,
uint16_t *_alpha)
{ {
bool have_alpha = false;
uint16_t alpha = 0xffff;
if (string[0] == '[') {
/* e.g. \E]11;[0.5]#00ff00 */
const char *start = &string[1];
const char *end = strchr(string, ']');
if (end == NULL)
return false;
char *_end;
double percent = strtod(start, &_end);
if (_end != end)
return false;
have_alpha = true;
alpha = 0xffff * percent;
string = end + 1;
}
if (string[0] != '#') if (string[0] != '#')
return false; return false;
@ -299,31 +321,53 @@ parse_legacy_color(const char *string, uint32_t *color)
uint8_t g = 256 * (rgb[1] / 65536.); uint8_t g = 256 * (rgb[1] / 65536.);
uint8_t b = 256 * (rgb[2] / 65536.); uint8_t b = 256 * (rgb[2] / 65536.);
LOG_DBG("legacy: %02x%02x%02x", r, g, b); LOG_DBG("legacy: %02x%02x%02x (alpha=%04x)", r, g, b,
have_alpha ? alpha : 0xffff);
*color = r << 16 | g << 8 | b; *color = r << 16 | g << 8 | b;
if (_have_alpha != NULL)
*_have_alpha = have_alpha;
if (_alpha != NULL)
*_alpha = alpha;
return true; return true;
} }
static bool static bool
parse_rgb(const char *string, uint32_t *color) parse_rgb(const char *string, uint32_t *color, bool *_have_alpha,
uint16_t *_alpha)
{ {
size_t len = strlen(string); size_t len = strlen(string);
bool have_alpha = len >= 4 && strncmp(string, "rgba", 4) == 0;
/* Verify we have the minimum required length (for "rgb:x/x/x") */ /* Verify we have the minimum required length (for "") */
if (len < 3 /* 'rgb' */ + 1 /* ':' */ + 2 /* '/' */ + 3 * 1 /* 3 * 'x' */) if (have_alpha) {
return false; /* rgba:x/x/x/x */
if (len < 4 /* 'rgba' */ + 1 /* ':' */ + 3 /* '/' */ + 4 * 1 /* 4 * 'x' */)
return false;
} else {
/* rgb:x/x/x */
if (len < 3 /* 'rgb' */ + 1 /* ':' */ + 2 /* '/' */ + 3 * 1 /* 3 * 'x' */)
return false;
}
/* Verify prefix is "rgb:" */ /* Verify prefix is “rgb:” or “rgba:” */
if (string[0] != 'r' || string[1] != 'g' || string[2] != 'b' || string[3] != ':') if (have_alpha) {
return false; if (strncmp(string, "rgba:", 5) != 0)
return false;
string += 5;
len -= 5;
} else {
if (strncmp(string, "rgb:", 4) != 0)
return false;
string += 4;
len -= 4;
}
string += 4; int rgb[4];
len -= 4; int digits[4];
int rgb[3]; for (size_t i = 0; i < (have_alpha ? 4 : 3); i++) {
int digits[3];
for (size_t i = 0; i < 3; i++) {
for (rgb[i] = 0, digits[i] = 0; for (rgb[i] = 0, digits[i] = 0;
len > 0 && *string != '/'; len > 0 && *string != '/';
len--, string++, digits[i]++) len--, string++, digits[i]++)
@ -338,7 +382,7 @@ parse_rgb(const char *string, uint32_t *color)
c >= 'a' && c <= 'f' ? c - 'a' + 10 : c - 'A' + 10; c >= 'a' && c <= 'f' ? c - 'a' + 10 : c - 'A' + 10;
} }
if (i >= 2) if (i >= (have_alpha ? 3 : 2))
break; break;
if (len == 0 || *string != '/') if (len == 0 || *string != '/')
@ -351,7 +395,20 @@ parse_rgb(const char *string, uint32_t *color)
uint8_t g = 256 * (rgb[1] / (double)(1 << (4 * digits[1]))); uint8_t g = 256 * (rgb[1] / (double)(1 << (4 * digits[1])));
uint8_t b = 256 * (rgb[2] / (double)(1 << (4 * digits[2]))); uint8_t b = 256 * (rgb[2] / (double)(1 << (4 * digits[2])));
LOG_DBG("rgb: %02x%02x%02x", r, g, b); uint16_t alpha;
if (have_alpha)
alpha = 65536 * (rgb[3] / (double)(1 << (4 * digits[3])));
if (have_alpha)
LOG_DBG("rgba: %02x%02x%02x (alpha=%04x)", r, g, b, alpha);
else
LOG_DBG("rgb: %02x%02x%02x", r, g, b);
if (_have_alpha != NULL)
*_have_alpha = have_alpha;
if (_alpha != NULL)
*_alpha = alpha;
*color = r << 16 | g << 8 | b; *color = r << 16 | g << 8 | b;
return true; return true;
} }
@ -595,9 +652,9 @@ osc_dispatch(struct terminal *term)
else { else {
uint32_t color; uint32_t color;
bool color_is_valid = s_color[0] == '#' bool color_is_valid = s_color[0] == '#' || s_color[0] == '['
? parse_legacy_color(s_color, &color) ? parse_legacy_color(s_color, &color, NULL, NULL)
: parse_rgb(s_color, &color); : parse_rgb(s_color, &color, NULL, NULL);
if (!color_is_valid) if (!color_is_valid)
continue; continue;
@ -647,15 +704,29 @@ osc_dispatch(struct terminal *term)
} }
uint32_t color; uint32_t color;
if (string[0] == '#' ? !parse_legacy_color(string, &color) : !parse_rgb(string, &color)) bool have_alpha = false;
uint16_t alpha = 0xffff;
if (string[0] == '#' || string[0] == '['
? !parse_legacy_color(string, &color, &have_alpha, &alpha)
: !parse_rgb(string, &color, &have_alpha, &alpha))
{
break; break;
}
LOG_DBG("change color definition for %s to %06x", LOG_DBG("change color definition for %s to %06x",
param == 10 ? "foreground" : "background", color); param == 10 ? "foreground" : "background", color);
switch (param) { switch (param) {
case 10: term->colors.fg = color; break; case 10:
case 11: term->colors.bg = color; break; term->colors.fg = color;
break;
case 11:
term->colors.bg = color;
if (have_alpha)
term->colors.alpha = alpha;
break;
} }
term_damage_view(term); term_damage_view(term);
@ -678,8 +749,13 @@ osc_dispatch(struct terminal *term)
} }
uint32_t color; uint32_t color;
if (string[0] == '#' ? !parse_legacy_color(string, &color) : !parse_rgb(string, &color))
if (string[0] == '#' || string[0] == '['
? !parse_legacy_color(string, &color, NULL, NULL)
: !parse_rgb(string, &color, NULL, NULL))
{
break; break;
}
LOG_DBG("change cursor color to %06x", color); LOG_DBG("change cursor color to %06x", color);