Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 127 additions & 17 deletions src/tsm/tsm-vte.c
Original file line number Diff line number Diff line change
Expand Up @@ -1220,9 +1220,45 @@ static void do_esc(struct tsm_vte *vte, uint32_t data)
}
}

static void csi_attribute(struct tsm_vte *vte)
static void palette_rgb(struct tsm_vte *vte, int code, uint8_t *cr,
uint8_t *cg, uint8_t *cb)
{
*cr = vte->palette[code][0];
*cg = vte->palette[code][1];
*cb = vte->palette[code][2];
}

static void cube_rgb(int code, uint8_t *cr, uint8_t *cg, uint8_t *cb)
{
static const uint8_t bval[6] = { 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff };
code -= 16;
*cb = bval[code % 6];
code /= 6;
*cg = bval[code % 6];
code /= 6;
*cr = bval[code % 6];
}

static void greyscale_rgb(int code, uint8_t *cr, uint8_t *cg, uint8_t *cb)
{
uint8_t lightness = (code - 232) * 10 + 8;
*cr = *cg = *cb = lightness;
}

static void lookup_color(struct tsm_vte *vte, int color, uint8_t *cr,
uint8_t *cg, uint8_t *cb)
{
if (color < 16) {
palette_rgb(vte, color, cr, cg, cb);
} else if (color < 232) {
cube_rgb(color, cr, cg, cb);
} else {
greyscale_rgb(color, cr, cg, cb);
}
}

static void csi_attribute(struct tsm_vte *vte)
{
unsigned int i, code, val;
uint8_t cr, cg, cb;

Expand Down Expand Up @@ -1390,19 +1426,8 @@ static void csi_attribute(struct tsm_vte *vte)
if (code < 16) {
//no change
cb = cg = cr = 0;
} else if (code < 232) {
code -= 16;
cb = bval[code % 6];
code /= 6;
cg = bval[code % 6];
code /= 6;
cr = bval[code % 6];
code = -1;
} else {
code = (code - 232) * 10 + 8;
cr = code;
cg = code;
cb = code;
lookup_color(vte, code, &cr, &cg, &cb);
code = -1;
}
i += 2;
Expand Down Expand Up @@ -2059,12 +2084,97 @@ static void do_osc_collect(struct tsm_vte *vte, uint32_t val) {
vte->osc_len += len;
}

static void do_osc_end(struct tsm_vte *vte) {
if (!vte->osc_cb) {
return;
static void vte_write_xcolor(struct tsm_vte *vte, char *code,
const char *end_seq,
uint8_t r, uint8_t g, uint8_t b)
{
char buf[32];
snprintf(buf, sizeof(buf), "\e]%s;rgb:%02x%02x/%02x%02x/%02x%02x%s",
code, r, r, g, g, b, b, end_seq);
vte_write(vte, buf, strlen(buf));
}

static void do_osc_4(struct tsm_vte *vte, const char *data, const char *end_seq)
Comment thread
malsyned marked this conversation as resolved.
{
char buf[32];
uint8_t cr, cg, cb;
const char *c = data;

while (*c) {
// parse color code
unsigned int code = 0;
while (*c >= '0' && *c <= '9') {
code = code * 10 + (*c++ - '0');
}
if (*c++ != ';') {
break;
}

// parse value field
if (*c == '?') {
c++;
// handle rgb value query
snprintf(buf, sizeof(buf), "4;%u", code);
lookup_color(vte, code, &cr, &cg, &cb);
vte_write_xcolor(vte, buf, end_seq, cr, cg, cb);
} else {
// parsing of new rgb value to assign to code would go
// here. For now, skip past field contents.
while (*c && *c != ';') {
c++;
}
}
if (*c++ != ';') {
break;
}
}
}

static bool do_osc_internal(struct tsm_vte *vte, const char *end_seq)
{
// OSC 4, 10, and 11 query or change the RGB values of color numbers in
// the foreground/background (SGR 38 & 48), 3-bit (SGR 30-37 & 40-47),
// 4-bit (SGR 90-97 & 100-107), and 256-color (SGR 38;5 & 48;5) indexed
// color tables. We don't support changing the tables, but we do support
// querying them.
if (!strncmp(vte->osc_arg, "4;", 2)) {
do_osc_4(vte, vte->osc_arg + 2, end_seq);
return true;
}
if (!strncmp(vte->osc_arg, "10;?", 4)) {
vte_write_xcolor(vte, "10", end_seq,
vte->def_attr.fr, vte->def_attr.fg,
vte->def_attr.fb);
return true;
}
if (!strncmp(vte->osc_arg, "11;?", 4)) {
vte_write_xcolor(vte, "11", end_seq,
vte->def_attr.br, vte->def_attr.bg,
vte->def_attr.bb);
return true;
}
return false;
}

/* XTerm OSC responses use the same sequence terminator used in the query */
static const char *osc_end_seq(uint32_t end)
{
if (end == 0x07) {
return "\x07";
}
return "\x1b\x5c";
}

static void do_osc_end(struct tsm_vte *vte, uint32_t val) {
const char *end_seq = osc_end_seq(val);
vte->osc_arg[vte->osc_len] = 0;

if (do_osc_internal(vte, end_seq)) {
return;
}
if (!vte->osc_cb) {
return;
}
vte->osc_cb(vte, vte->osc_arg, vte->osc_len, vte->osc_data);
}

Expand Down Expand Up @@ -2115,7 +2225,7 @@ static void do_action(struct tsm_vte *vte, uint32_t data, int action)
do_osc_collect(vte, data);
break;
case ACTION_OSC_END:
do_osc_end(vte);
do_osc_end(vte, data);
break;
default:
llog_warning(vte, "invalid action %d", action);
Expand Down
127 changes: 127 additions & 0 deletions test/test_vte.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,15 +164,140 @@ START_TEST(test_vte_custom_palette)
}
END_TEST

static bool write_cb_called = false;

static void checking_write_cb(struct tsm_vte *vte, const char *u8, size_t len, void *data)
{
write_cb_called = true;

ck_assert_ptr_ne(vte, NULL);
ck_assert_ptr_ne(u8, NULL);
ck_assert_uint_gt(len, 0);

ck_assert_mem_eq(u8, data, len);
}

static void checked_vte_input(struct tsm_vte *vte, const char *u8, size_t len)
{
write_cb_called = false;
tsm_vte_input(vte, u8, len);
ck_assert(write_cb_called);
}

START_TEST(test_vte_osc_query)
{
struct tsm_screen *screen;
struct tsm_vte *vte;
int r;
char expected_output[32];
const char *input;

r = tsm_screen_new(&screen, log_cb, NULL);
ck_assert_int_eq(r, 0);

r = tsm_vte_new(&vte, screen, checking_write_cb, expected_output, NULL, NULL);
ck_assert_int_eq(r, 0);

r = tsm_vte_set_custom_palette(vte, test_palette);
ck_assert_int_eq(r, 0);

r = tsm_vte_set_palette(vte, "custom");
ck_assert_int_eq(r, 0);

// query foreground, end_seq = BEL
input = "\e]10;?\x07";
strcpy(expected_output, "\e]10;rgb:1010/2222/3434\x07");
checked_vte_input(vte, input, strlen(input));

// query foreground, end_seq = ST
input = "\e]10;?\e\\";
strcpy(expected_output, "\e]10;rgb:1010/2222/3434\e\\");
checked_vte_input(vte, input, strlen(input));

// ignore additional parameters after foreground query
input = "\e]10;?;11;?\x07";
strcpy(expected_output, "\e]10;rgb:1010/2222/3434\x07");
checked_vte_input(vte, input, strlen(input));

// query background
input = "\e]11;?\x07";
strcpy(expected_output, "\e]11;rgb:1111/2323/3535\x07");
checked_vte_input(vte, input, strlen(input));

// ignore additional parameters after background query
input = "\e]11;?;12;?\x07";
strcpy(expected_output, "\e]11;rgb:1111/2323/3535\x07");
checked_vte_input(vte, input, strlen(input));
}
END_TEST

unsigned char write_buffer[512];
unsigned char *write_buffer_p = write_buffer;

static void storing_write_cb(struct tsm_vte *vte, const char *u8, size_t len, void *data)
{
ck_assert_ptr_ne(vte, NULL);
ck_assert_ptr_ne(u8, NULL);

memcpy(write_buffer_p, u8, len);
write_buffer_p[len] = '\0';
write_buffer_p += len;
}

static void storing_write_cb_reset(void)
{
memset(write_buffer, 0, sizeof(write_buffer));
write_buffer_p = write_buffer;
}

START_TEST(test_vte_osc4)
{
struct tsm_screen *screen;
struct tsm_vte *vte;
int r;
const char *input;

r = tsm_screen_new(&screen, log_cb, NULL);
ck_assert_int_eq(r, 0);

r = tsm_vte_new(&vte, screen, storing_write_cb, NULL, NULL, NULL);
ck_assert_int_eq(r, 0);

r = tsm_vte_set_custom_palette(vte, test_palette);
ck_assert_int_eq(r, 0);

r = tsm_vte_set_palette(vte, "custom");
ck_assert_int_eq(r, 0);

// query palette entries
storing_write_cb_reset();
input = "\e]4;1;?;13;?;3;?\x07";
const char *expected =
"\e]4;1;rgb:0101/1313/2525\x07"
"\e]4;13;rgb:0d0d/1f1f/3131\x07"
"\e]4;3;rgb:0303/1515/2727\x07";
tsm_vte_input(vte, input, strlen(input));
ck_assert_mem_eq(write_buffer, expected, strlen(expected));
ck_assert_int_eq(write_buffer_p - write_buffer, strlen(expected));

// query cube & grayscale entries
storing_write_cb_reset();
input = "\e]4;110;?;254;?;\x07";
expected =
"\e]4;110;rgb:8787/afaf/d7d7\x07"
"\e]4;254;rgb:e4e4/e4e4/e4e4\x07";
tsm_vte_input(vte, input, strlen(input));
ck_assert_mem_eq(write_buffer, expected, strlen(expected));

// ignore color change requests, incomplete messages
storing_write_cb_reset();
input = "\e]4;1;rgb:1111/2222/3333;2\x07";
tsm_vte_input(vte, input, strlen(input));
ck_assert_ptr_eq(write_buffer, write_buffer_p);
storing_write_cb_reset();
}
END_TEST

START_TEST(test_vte_backspace_key)
{
struct tsm_screen *screen;
Expand Down Expand Up @@ -337,6 +462,8 @@ TEST_DEFINE_CASE(misc)
TEST(test_vte_init)
TEST(test_vte_null)
TEST(test_vte_custom_palette)
TEST(test_vte_osc_query)
TEST(test_vte_osc4)
TEST(test_vte_backspace_key)
TEST(test_vte_get_flags)
TEST(test_vte_decrqm_no_reset)
Expand Down
Loading