From dd6fc99ae1badb7963271ed171beea8ef0e39601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 1 Jul 2024 17:40:45 +0200 Subject: [PATCH] csi: implement XTPUSHCOLORS+XTPOPCOLORS+XTREPORTCOLORS The documentation of these sequences are vague and lacking, as is often the case with XTerm invented control sequences. I've tried to replicate what XTerm does (as of xterm-392). The stack represents *stashed/stored* palettes. The currently active palette is *not* stored on the stack. The stack is dynamically allocated, and starts out with zero elements. Now, XTerm has a somewhat weird definition of "pushing" and "popping" in this context, and the documentation is somewhat misleading. What a push does is this: it stores the current palette to the stack at the specified slot. If the specified slot number (Pm) is 0, the slot used is the current slot index incremented by 1. The "current" slot index is then set to the specified slot (which is current slot + 1 if Pm == 0). Thus, "push" (i.e. when Pm == 0 is used) means store to the "next" slot. This is true even if the current slot index points into the middle of stack. Pop works in a similar way. The palette is restored from the specified slot index. If the specified slot number is 0, we use the current slot index. The "current" slot index is then set to the specified slot - 1 (current slot - 1 if Pm == 0). XTREPORTCOLORS return the current slot index, and the number of palettes stored on the stack, on the format CSI ? ; # Q When XTPUSHCOLORS grows the stack with more than one element (i.e. via a 'CSI N # P' sequence), make sure *all* new slots are initialized (to the current color palette). This avoids uninitialized slots, that could then be popped with XTPOPCOLORS. Closes #856 --- csi.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ terminal.c | 10 +++++++ terminal.h | 29 ++++++++++++++------- 3 files changed, 105 insertions(+), 10 deletions(-) diff --git a/csi.c b/csi.c index 2d0f3c62..42f19a91 100644 --- a/csi.c +++ b/csi.c @@ -2048,6 +2048,82 @@ csi_dispatch(struct terminal *term, uint8_t final) break; /* private[0] == ‘$’ */ } + case '#': { + switch (final) { + case 'P': { /* XTPUSHCOLORS */ + int slot = vt_param_get(term, 0, 0); + + /* Pm == 0, "push" (what xterm does is take take the + *current* slot + 1, even if that's in the middle of the + stack, and overwrites whatever is already in that + slot) */ + if (slot == 0) + slot = term->color_stack.idx + 1; + + if (term->color_stack.size < slot) { + const size_t new_size = slot; + term->color_stack.stack = xrealloc( + term->color_stack.stack, + new_size * sizeof(term->color_stack.stack[0])); + + /* Initialize new slots (except the selected slot, + which is done below) */ + xassert(new_size > 0); + for (size_t i = term->color_stack.size; i < new_size - 1; i++) { + memcpy(&term->color_stack.stack[i], &term->colors, + sizeof(term->colors)); + } + term->color_stack.size = new_size; + } + + xassert(slot > 0); + xassert(slot <= term->color_stack.size); + term->color_stack.idx = slot; + memcpy(&term->color_stack.stack[slot - 1], &term->colors, + sizeof(term->colors)); + break; + } + + case 'Q': { /* XTPOPCOLORS */ + int slot = vt_param_get(term, 0, 0); + + /* Pm == 0, "pop" (what xterm does is copy colors from the + *current* slot, *and* decrease the current slot index, + even if that's in the middle of the stack) */ + if (slot == 0) + slot = term->color_stack.idx; + + if (slot > 0 && slot <= term->color_stack.size) { + memcpy(&term->colors, &term->color_stack.stack[slot - 1], + sizeof(term->colors)); + term->color_stack.idx = slot - 1; + + /* TODO: we _could_ iterate all cells and only dirty + those that are affected by the palette change... */ + term_damage_view(term); + } else if (slot == 0) { + LOG_ERR("XTPOPCOLORS: cannot pop beyond the first element"); + } else { + LOG_ERR( + "XTPOPCOLORS: invalid color slot: %d " + "(stack has %zu slots, current slot is %zu)", + vt_param_get(term, 0, 0), + term->color_stack.size, term->color_stack.idx); + } + break; + } + + case 'R': { /* XTREPORTCOLORS */ + char reply[64]; + int n = xsnprintf(reply, sizeof(reply), "\033[?%zu;%zu#Q", + term->color_stack.idx, term->color_stack.size); + term_to_slave(term, reply, n); + break; + } + } + break; /* private[0] == '#' */ + } + case 0x243f: /* ?$ */ switch (final) { case 'p': { diff --git a/terminal.c b/terminal.c index caa8aa5a..ca3d2bc1 100644 --- a/terminal.c +++ b/terminal.c @@ -1227,6 +1227,11 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, .selection_bg = conf->colors.selection_bg, .use_custom_selection = conf->colors.use_custom.selection, }, + .color_stack = { + .stack = NULL, + .size = 0, + .idx = 0, + }, .origin = ORIGIN_ABSOLUTE, .cursor_style = conf->cursor.style, .cursor_blink = { @@ -1823,6 +1828,7 @@ term_destroy(struct terminal *term) free(term->foot_exe); free(term->cwd); free(term->mouse_user_cursor); + free(term->color_stack.stack); int ret = EXIT_SUCCESS; @@ -2040,6 +2046,10 @@ term_reset(struct terminal *term, bool hard) term->colors.use_custom_selection = term->conf->colors.use_custom.selection; memcpy(term->colors.table, term->conf->colors.table, sizeof(term->colors.table)); + free(term->color_stack.stack); + term->color_stack.stack = NULL; + term->color_stack.size = 0; + term->color_stack.idx = 0; term->origin = ORIGIN_ABSOLUTE; term->normal.cursor.lcf = false; term->alt.cursor.lcf = false; diff --git a/terminal.h b/terminal.h index 9fce2396..4d628661 100644 --- a/terminal.h +++ b/terminal.h @@ -393,6 +393,19 @@ struct url { }; typedef tll(struct url) url_list_t; + +struct colors { + uint32_t fg; + uint32_t bg; + uint32_t table[256]; + uint16_t alpha; + uint32_t cursor_fg; /* Text color */ + uint32_t cursor_bg; /* cursor color */ + uint32_t selection_fg; + uint32_t selection_bg; + bool use_custom_selection; +}; + struct terminal { struct fdm *fdm; struct reaper *reaper; @@ -563,17 +576,13 @@ struct terminal { int cell_width; /* pixels per cell, x-wise */ int cell_height; /* pixels per cell, y-wise */ + struct colors colors; + struct { - uint32_t fg; - uint32_t bg; - uint32_t table[256]; - uint16_t alpha; - uint32_t cursor_fg; /* Text color */ - uint32_t cursor_bg; /* cursor color */ - uint32_t selection_fg; - uint32_t selection_bg; - bool use_custom_selection; - } colors; + struct colors *stack; + size_t idx; + size_t size; + } color_stack; enum cursor_style cursor_style; struct {