From ea851962c1f4f1d30e350de3f4aa7bfbea135dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 14:34:17 +0100 Subject: [PATCH 01/24] term: add term_put_char() This function prints a single, non-double width, character to the grid. It handles OSC-8 hyperlinks, but does not: * update the cursor location * erase sixels --- terminal.c | 36 ++++++++++++++++++++++++++++++++++++ terminal.h | 1 + 2 files changed, 37 insertions(+) diff --git a/terminal.c b/terminal.c index d9d66bac..6119f68c 100644 --- a/terminal.c +++ b/terminal.c @@ -3498,6 +3498,42 @@ print_spacer(struct terminal *term, int col, int remaining) cell->attrs = term->vt.attrs; } +/* + * Puts a character on the grid. Coordinates are in screen coordinates + * (i.e. ‘cursor’ coordinates). + * + * Does NOT: + * - update the cursor + * - linewrap + * - erase sixels + * + * Limitiations: + * - double width characters not supported + */ +void +term_put_char(struct terminal *term, int r, int c, wchar_t wc) +{ + struct row *row = grid_row(term->grid, r); + row->dirty = true; + + struct cell *cell = &row->cells[c]; + cell->wc = wc; + cell->attrs = term->vt.attrs; + + if (unlikely(term->vt.osc8.uri != NULL)) { + grid_row_uri_range_put(row, c, term->vt.osc8.uri, term->vt.osc8.id); + + switch (term->conf->url.osc8_underline) { + case OSC8_UNDERLINE_ALWAYS: + cell->attrs.url = true; + break; + + case OSC8_UNDERLINE_URL_MODE: + break; + } + } +} + void term_print(struct terminal *term, char32_t wc, int width) { diff --git a/terminal.h b/terminal.h index ec2ff963..8abc1c8e 100644 --- a/terminal.h +++ b/terminal.h @@ -797,6 +797,7 @@ void term_cursor_up(struct terminal *term, int count); void term_cursor_down(struct terminal *term, int count); void term_cursor_blink_update(struct terminal *term); +void term_put_char(struct terminal *term, int r, int c, char32_t wc); void term_print(struct terminal *term, char32_t wc, int width); void term_scroll(struct terminal *term, int rows); From e6c372b14fa76799ea3c3ff89258a1b905cc9ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 14:35:18 +0100 Subject: [PATCH 02/24] term: print: spacers may be printed all the way up to the last column --- terminal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminal.c b/terminal.c index 6119f68c..f814de97 100644 --- a/terminal.c +++ b/terminal.c @@ -3602,7 +3602,7 @@ term_print(struct terminal *term, char32_t wc, int width) grid_row_uri_range_erase(row, col, col + width - 1); /* Advance cursor the 'additional' columns while dirty:ing the cells */ - for (int i = 1; i < width && col < term->cols - 1; i++) { + for (int i = 1; i < width && col < term->cols; i++) { col++; print_spacer(term, col, width - i); } From b4dbfb58b883d4bae5ffb794895cbdcf39e5bb0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 14:35:38 +0100 Subject: [PATCH 03/24] grid: remove prototype for non-existing function --- grid.h | 1 - 1 file changed, 1 deletion(-) diff --git a/grid.h b/grid.h index 0664409c..8ea5200b 100644 --- a/grid.h +++ b/grid.h @@ -86,7 +86,6 @@ grid_row_in_view(struct grid *grid, int row_no) void grid_row_uri_range_put( struct row *row, int col, const char *uri, uint64_t id); -void grid_row_uri_range_add(struct row *row, struct row_uri_range range); void grid_row_uri_range_erase(struct row *row, int start, int end); static inline void From b30b8a294498ce765698dd40df4a750f8debe689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 15:00:13 +0100 Subject: [PATCH 04/24] put_char fixup --- terminal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/terminal.c b/terminal.c index f814de97..bdbcd9bd 100644 --- a/terminal.c +++ b/terminal.c @@ -3506,6 +3506,7 @@ print_spacer(struct terminal *term, int col, int remaining) * - update the cursor * - linewrap * - erase sixels + * - erase URIs (but it _does_ emit them if one is active) * * Limitiations: * - double width characters not supported From 926d88fd3035407057c70a9995bf61e0c47fbe40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 15:00:27 +0100 Subject: [PATCH 05/24] csi: implement rectangular edit escapes * DECCARA - change attributes in rectangular area * DECRARA - reverse attributes in rectangular area * DECCRA - copy rectangular area * DECFRA - fill rectangular area * DECERA - erase rectangular area Not implemented: * DECSERA - selective erase rectangular area --- csi.c | 237 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) diff --git a/csi.c b/csi.c index 78fdf4ef..a5987968 100644 --- a/csi.c +++ b/csi.c @@ -1752,6 +1752,243 @@ csi_dispatch(struct terminal *term, uint8_t final) break; /* private[0] == '=' */ } + case '$': { + switch (final) { + case 'r': { /* DECCARA */ + int rel_top_row = vt_param_get(term, 0, 1) - 1; + int left_col = vt_param_get(term, 1, 1) - 1; + int rel_bottom_row = vt_param_get(term, 2, term->rows) - 1; + int right_col = vt_param_get(term, 3, term->cols) - 1; + + int top_row = term_row_rel_to_abs(term, rel_top_row); + int bottom_row = term_row_rel_to_abs(term, rel_bottom_row); + + if (unlikely(top_row > bottom_row || left_col > right_col)) + break; + + for (int r = top_row; r <= bottom_row; r++) { + struct row *row = grid_row(term->grid, r); + + for (int c = left_col; c <= right_col; c++) { + struct attributes *a = &row->cells[c].attrs; + + for (size_t i = 4; i < term->vt.params.idx; i++) { + const int param = term->vt.params.v[i].value; + + /* DECCARA only supports a sub-set of SGR parameters */ + switch (param) { + case 0: + a->bold = false; + a->underline = false; + a->blink = false; + a->reverse = false; + break; + + case 1: a->bold = true; break; + case 4: a->underline = true; break; + case 5: a->blink = true; break; + case 7: a->reverse = true; break; + + case 22: a->bold = false; break; + case 24: a->underline = false; break; + case 25: a->blink = false; break; + case 27: a->reverse = false; break; + } + } + } + } + break; + } + + case 't': { /* DECRARA */ + int rel_top_row = vt_param_get(term, 0, 1) - 1; + int left_col = vt_param_get(term, 1, 1) - 1; + int rel_bottom_row = vt_param_get(term, 2, term->rows) - 1; + int right_col = vt_param_get(term, 3, term->cols) - 1; + + int top_row = term_row_rel_to_abs(term, rel_top_row); + int bottom_row = term_row_rel_to_abs(term, rel_bottom_row); + + if (unlikely(top_row > bottom_row || left_col > right_col)) + break; + + for (int r = top_row; r <= bottom_row; r++) { + struct row *row = grid_row(term->grid, r); + + for (int c = left_col; c <= right_col; c++) { + struct attributes *a = &row->cells[c].attrs; + + for (size_t i = 4; i < term->vt.params.idx; i++) { + const int param = term->vt.params.v[i].value; + + /* DECCARA only supports a sub-set of SGR parameters */ + switch (param) { + case 0: + a->bold = !a->bold; + a->underline = !a->underline; + a->blink = !a->blink; + a->reverse = !a->reverse; + break; + + case 1: a->bold = !a->bold; break; + case 4: a->underline = !a->underline; break; + case 5: a->blink = !a->blink; break; + case 7: a->reverse = !a->reverse; break; + } + } + } + } + break; + } + + case 'v': { /* DECCRA */ + int src_rel_top_row = vt_param_get(term, 0, 1) - 1; + int src_left_col = vt_param_get(term, 1, 1) - 1; + int src_rel_bottom_row = vt_param_get(term, 2, term->rows) - 1; + int src_right_col = vt_param_get(term, 3, term->cols) - 1; + int src_page = vt_param_get(term, 4, 1); + + int dst_rel_top_row = vt_param_get(term, 5, 1) - 1; + int dst_left_col = vt_param_get(term, 6, 1) - 1; + int dst_page = vt_param_get(term, 7, 1); + + if (unlikely(src_page != 1 || dst_page != 1)) { + /* We don’t support “pages” */ + break; + } + + int dst_rel_bottom_row = + dst_rel_top_row + (src_rel_bottom_row - src_rel_top_row); + int dst_right_col = min( + dst_left_col + (src_right_col - src_left_col), + term->cols - 1); + + /* Source and destination boxes, absolute coordinates */ + int src_top_row = term_row_rel_to_abs(term, src_rel_top_row); + int src_bottom_row = term_row_rel_to_abs(term, src_rel_bottom_row); + int dst_top_row = term_row_rel_to_abs(term, dst_rel_top_row); + int dst_bottom_row = term_row_rel_to_abs(term, dst_rel_bottom_row); + + if (unlikely(src_top_row > src_bottom_row || + src_left_col > src_right_col)) + break; + + /* Target area outside the screen is clipped */ + const size_t row_count = min(src_bottom_row - src_top_row, + dst_bottom_row - dst_top_row) + 1; + const size_t cell_count = min(src_right_col - src_left_col, + dst_right_col - dst_left_col) + 1; + + sixel_overwrite_by_rectangle( + term, dst_top_row, dst_left_col, row_count, cell_count); + + /* + * Copy source area + * + * Note: since source and destination may overlap, we need + * to copy out the entire source region first, and _then_ + * write the destination. I.e. this is similar to how + * memmove() behaves, but adapted to our row/cell + * structure. + */ + struct cell **copy = xmalloc(row_count * sizeof(copy[0])); + for (int r = 0; r < row_count; r++) { + copy[r] = xmalloc(cell_count * sizeof(copy[r][0])); + + const struct row *row = grid_row(term->grid, src_top_row + r); + const struct cell *cell = &row->cells[src_left_col]; + memcpy(copy[r], cell, cell_count * sizeof(copy[r][0])); + } + + /* Paste into destination area */ + for (int r = 0; r < row_count; r++) { + struct row *row = grid_row(term->grid, dst_top_row + r); + row->dirty = true; + + struct cell *cell = &row->cells[dst_left_col]; + memcpy(cell, copy[r], cell_count * sizeof(copy[r][0])); + free(copy[r]); + + for (int c = 0; c < cell_count; c++, cell++) + cell->attrs.clean = 0; + + if (unlikely(row->extra != NULL)) { + /* TODO: technically, we should copy the source URIs... */ + grid_row_uri_range_erase(row, dst_left_col, dst_right_col); + } + } + free(copy); + break; + } + + case 'x': { /* DECFRA */ + const char c = vt_param_get(term, 0, 0); + if (likely((c >= 32 && c < 126) || + (c >= 160 && c <= 255))) + { + int rel_top_row = vt_param_get(term, 1, 1) - 1; + int left_col = vt_param_get(term, 2, 1) - 1; + int rel_bottom_row = vt_param_get(term, 3, term->rows) - 1; + int right_col = vt_param_get(term, 4, term->cols) - 1; + + int top_row = term_row_rel_to_abs(term, rel_top_row); + int bottom_row = term_row_rel_to_abs(term, rel_bottom_row); + + if (unlikely(top_row > bottom_row || left_col > right_col)) + break; + + /* Erase the entire region at once (MUCH cheaper than + * doing it row by row, or even character by + * character). */ + sixel_overwrite_by_rectangle( + term, + top_row, left_col, + bottom_row - top_row + 1, right_col - left_col + 1); + + for (int r = top_row; r <= bottom_row; r++) { + struct row *row = grid_row(term->grid, r); + + if (unlikely(row->extra != NULL)) + grid_row_uri_range_erase(row, left_col, right_col); + + for (int col = left_col; col <= right_col; col++) + term_put_char(term, r, col, (wchar_t)c); + } + } + break; + } + + case 'z': { /* DECERA */ + int rel_top_row = vt_param_get(term, 0, 1) - 1; + int left_col = vt_param_get(term, 1, 1) - 1; + int rel_bottom_row = vt_param_get(term, 2, term->rows) - 1; + int right_col = vt_param_get(term, 3, term->cols) - 1; + + int top_row = term_row_rel_to_abs(term, rel_top_row); + int bottom_row = term_row_rel_to_abs(term, rel_bottom_row); + + if (unlikely(top_row > bottom_row || left_col > right_col)) + break; + + /* + * Note: term_erase() _also_ erases sixels, but since + * we’re forced to erase one row at a time, erasing the + * entire sixel here is more efficient. + */ + sixel_overwrite_by_rectangle( + term, + top_row, left_col, + bottom_row - top_row + 1, right_col - left_col + 1); + + for (int r = top_row; r <= bottom_row; r++) + term_erase(term, r, left_col, r, right_col); + break; + } + } + + break; /* private[0] == ‘$’ */ + } + case 0x243f: /* ?$ */ switch (final) { case 'p': { From 95293f142a1288eedf4d07b09ecb4036ff1db295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 15:03:55 +0100 Subject: [PATCH 06/24] =?UTF-8?q?csi:=20add=20=E2=80=9828=E2=80=99=20(rect?= =?UTF-8?q?angular=20edit)=20to=20primary=20DA=20response?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- csi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csi.c b/csi.c index a5987968..48c3211b 100644 --- a/csi.c +++ b/csi.c @@ -743,10 +743,10 @@ csi_dispatch(struct terminal *term, uint8_t final) * Note: tertiary DA responds with "FOOT". */ if (term->conf->tweak.sixel) { - static const char reply[] = "\033[?62;4;22c"; + static const char reply[] = "\033[?62;4;22;28c"; term_to_slave(term, reply, sizeof(reply) - 1); } else { - static const char reply[] = "\033[?62;22c"; + static const char reply[] = "\033[?62;22;28c"; term_to_slave(term, reply, sizeof(reply) - 1); } break; From 4b4fe9d49335265b7287d457c78ce661235c1c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 15:05:07 +0100 Subject: [PATCH 07/24] changelog: rectangular edit functions --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ceab4d5..0d11f3ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -681,6 +681,9 @@ way of entering Unicode characters is with an IME ([#1116][1116]). * Support for `xdg_toplevel.wm_capabilities`, to adapt the client-side decoration buttons to the compositor capabilities ([#1061][1061]). +* Rectangular edit functions: `DECCARA`, `DECRARA`, `DECCRA`, `DECFRA` + and `DECERA`. + [1058]: https://codeberg.org/dnkl/foot/issues/1058 [1070]: https://codeberg.org/dnkl/foot/issues/1070 From df5dd94789291290489e7e875dd7f7862aa6dc35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 15:09:46 +0100 Subject: [PATCH 08/24] term: codespell: limitiations -> limitations --- terminal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminal.c b/terminal.c index bdbcd9bd..ad20c737 100644 --- a/terminal.c +++ b/terminal.c @@ -3508,7 +3508,7 @@ print_spacer(struct terminal *term, int col, int remaining) * - erase sixels * - erase URIs (but it _does_ emit them if one is active) * - * Limitiations: + * Limitations: * - double width characters not supported */ void From 1b66c6a3acd9ea80be0903d4860d3b06f6f108d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 15:53:46 +0100 Subject: [PATCH 09/24] csi: rectangular: add helper function params_to_rectangular_area() This functions reads the four top/left/bottom/right parameters, validates them, and converts relative row numbers to absolute. Returns true if the params are valid, otherwise false. --- csi.c | 152 +++++++++++++++++++++++++++------------------------------- 1 file changed, 71 insertions(+), 81 deletions(-) diff --git a/csi.c b/csi.c index 48c3211b..43a358a8 100644 --- a/csi.c +++ b/csi.c @@ -673,6 +673,23 @@ xtrestore(struct terminal *term, unsigned param) decset_decrst(term, param, enable); } +static bool +params_to_rectangular_area(const struct terminal *term, int first_idx, + int *top, int *left, int *bottom, int *right) +{ + int rel_top = vt_param_get(term, first_idx + 0, 1) - 1; + *left = vt_param_get(term, first_idx + 1, 1) - 1; + int rel_bottom = vt_param_get(term, first_idx + 2, term->rows) - 1; + *right = vt_param_get(term, first_idx + 3, term->cols) - 1; + + if (rel_top > rel_bottom || *left > *right) + return false; + + *top = term_row_rel_to_abs(term, rel_top); + *bottom = term_row_rel_to_abs(term, rel_bottom); + return true; +} + void csi_dispatch(struct terminal *term, uint8_t final) { @@ -1755,21 +1772,17 @@ csi_dispatch(struct terminal *term, uint8_t final) case '$': { switch (final) { case 'r': { /* DECCARA */ - int rel_top_row = vt_param_get(term, 0, 1) - 1; - int left_col = vt_param_get(term, 1, 1) - 1; - int rel_bottom_row = vt_param_get(term, 2, term->rows) - 1; - int right_col = vt_param_get(term, 3, term->cols) - 1; - - int top_row = term_row_rel_to_abs(term, rel_top_row); - int bottom_row = term_row_rel_to_abs(term, rel_bottom_row); - - if (unlikely(top_row > bottom_row || left_col > right_col)) + int top, left, bottom, right; + if (!params_to_rectangular_area( + term, 0, &top, &left, &bottom, &right)) + { break; + } - for (int r = top_row; r <= bottom_row; r++) { + for (int r = top; r <= bottom; r++) { struct row *row = grid_row(term->grid, r); - for (int c = left_col; c <= right_col; c++) { + for (int c = left; c <= right; c++) { struct attributes *a = &row->cells[c].attrs; for (size_t i = 4; i < term->vt.params.idx; i++) { @@ -1801,21 +1814,17 @@ csi_dispatch(struct terminal *term, uint8_t final) } case 't': { /* DECRARA */ - int rel_top_row = vt_param_get(term, 0, 1) - 1; - int left_col = vt_param_get(term, 1, 1) - 1; - int rel_bottom_row = vt_param_get(term, 2, term->rows) - 1; - int right_col = vt_param_get(term, 3, term->cols) - 1; - - int top_row = term_row_rel_to_abs(term, rel_top_row); - int bottom_row = term_row_rel_to_abs(term, rel_bottom_row); - - if (unlikely(top_row > bottom_row || left_col > right_col)) + int top, left, bottom, right; + if (!params_to_rectangular_area( + term, 0, &top, &left, &bottom, &right)) + { break; + } - for (int r = top_row; r <= bottom_row; r++) { + for (int r = top; r <= bottom; r++) { struct row *row = grid_row(term->grid, r); - for (int c = left_col; c <= right_col; c++) { + for (int c = left; c <= right; c++) { struct attributes *a = &row->cells[c].attrs; for (size_t i = 4; i < term->vt.params.idx; i++) { @@ -1842,14 +1851,17 @@ csi_dispatch(struct terminal *term, uint8_t final) } case 'v': { /* DECCRA */ - int src_rel_top_row = vt_param_get(term, 0, 1) - 1; - int src_left_col = vt_param_get(term, 1, 1) - 1; - int src_rel_bottom_row = vt_param_get(term, 2, term->rows) - 1; - int src_right_col = vt_param_get(term, 3, term->cols) - 1; + int src_top, src_left, src_bottom, src_right; + if (!params_to_rectangular_area( + term, 0, &src_top, &src_left, &src_bottom, &src_right)) + { + break; + } + int src_page = vt_param_get(term, 4, 1); - int dst_rel_top_row = vt_param_get(term, 5, 1) - 1; - int dst_left_col = vt_param_get(term, 6, 1) - 1; + int dst_rel_top = vt_param_get(term, 5, 1) - 1; + int dst_left = vt_param_get(term, 6, 1) - 1; int dst_page = vt_param_get(term, 7, 1); if (unlikely(src_page != 1 || dst_page != 1)) { @@ -1857,30 +1869,20 @@ csi_dispatch(struct terminal *term, uint8_t final) break; } - int dst_rel_bottom_row = - dst_rel_top_row + (src_rel_bottom_row - src_rel_top_row); - int dst_right_col = min( - dst_left_col + (src_right_col - src_left_col), - term->cols - 1); + int dst_rel_bottom = dst_rel_top + (src_bottom - src_top); + int dst_right = min(dst_left + (src_right - src_left), term->cols - 1); - /* Source and destination boxes, absolute coordinates */ - int src_top_row = term_row_rel_to_abs(term, src_rel_top_row); - int src_bottom_row = term_row_rel_to_abs(term, src_rel_bottom_row); - int dst_top_row = term_row_rel_to_abs(term, dst_rel_top_row); - int dst_bottom_row = term_row_rel_to_abs(term, dst_rel_bottom_row); - - if (unlikely(src_top_row > src_bottom_row || - src_left_col > src_right_col)) - break; + int dst_top = term_row_rel_to_abs(term, dst_rel_top); + int dst_bottom = term_row_rel_to_abs(term, dst_rel_bottom); /* Target area outside the screen is clipped */ - const size_t row_count = min(src_bottom_row - src_top_row, - dst_bottom_row - dst_top_row) + 1; - const size_t cell_count = min(src_right_col - src_left_col, - dst_right_col - dst_left_col) + 1; + const size_t row_count = min(src_bottom - src_top, + dst_bottom - dst_top) + 1; + const size_t cell_count = min(src_right - src_left, + dst_right - dst_left) + 1; sixel_overwrite_by_rectangle( - term, dst_top_row, dst_left_col, row_count, cell_count); + term, dst_top, dst_left, row_count, cell_count); /* * Copy source area @@ -1895,17 +1897,17 @@ csi_dispatch(struct terminal *term, uint8_t final) for (int r = 0; r < row_count; r++) { copy[r] = xmalloc(cell_count * sizeof(copy[r][0])); - const struct row *row = grid_row(term->grid, src_top_row + r); - const struct cell *cell = &row->cells[src_left_col]; + const struct row *row = grid_row(term->grid, src_top + r); + const struct cell *cell = &row->cells[src_left]; memcpy(copy[r], cell, cell_count * sizeof(copy[r][0])); } /* Paste into destination area */ for (int r = 0; r < row_count; r++) { - struct row *row = grid_row(term->grid, dst_top_row + r); + struct row *row = grid_row(term->grid, dst_top + r); row->dirty = true; - struct cell *cell = &row->cells[dst_left_col]; + struct cell *cell = &row->cells[dst_left]; memcpy(cell, copy[r], cell_count * sizeof(copy[r][0])); free(copy[r]); @@ -1914,7 +1916,7 @@ csi_dispatch(struct terminal *term, uint8_t final) if (unlikely(row->extra != NULL)) { /* TODO: technically, we should copy the source URIs... */ - grid_row_uri_range_erase(row, dst_left_col, dst_right_col); + grid_row_uri_range_erase(row, dst_left, dst_right); } } free(copy); @@ -1926,32 +1928,26 @@ csi_dispatch(struct terminal *term, uint8_t final) if (likely((c >= 32 && c < 126) || (c >= 160 && c <= 255))) { - int rel_top_row = vt_param_get(term, 1, 1) - 1; - int left_col = vt_param_get(term, 2, 1) - 1; - int rel_bottom_row = vt_param_get(term, 3, term->rows) - 1; - int right_col = vt_param_get(term, 4, term->cols) - 1; - - int top_row = term_row_rel_to_abs(term, rel_top_row); - int bottom_row = term_row_rel_to_abs(term, rel_bottom_row); - - if (unlikely(top_row > bottom_row || left_col > right_col)) + int top, left, bottom, right; + if (!params_to_rectangular_area( + term, 1, &top, &left, &bottom, &right)) + { break; + } /* Erase the entire region at once (MUCH cheaper than * doing it row by row, or even character by * character). */ sixel_overwrite_by_rectangle( - term, - top_row, left_col, - bottom_row - top_row + 1, right_col - left_col + 1); + term, top, left, bottom - top + 1, right - left + 1); - for (int r = top_row; r <= bottom_row; r++) { + for (int r = top; r <= bottom; r++) { struct row *row = grid_row(term->grid, r); if (unlikely(row->extra != NULL)) - grid_row_uri_range_erase(row, left_col, right_col); + grid_row_uri_range_erase(row, left, right); - for (int col = left_col; col <= right_col; col++) + for (int col = left; col <= right; col++) term_put_char(term, r, col, (wchar_t)c); } } @@ -1959,16 +1955,12 @@ csi_dispatch(struct terminal *term, uint8_t final) } case 'z': { /* DECERA */ - int rel_top_row = vt_param_get(term, 0, 1) - 1; - int left_col = vt_param_get(term, 1, 1) - 1; - int rel_bottom_row = vt_param_get(term, 2, term->rows) - 1; - int right_col = vt_param_get(term, 3, term->cols) - 1; - - int top_row = term_row_rel_to_abs(term, rel_top_row); - int bottom_row = term_row_rel_to_abs(term, rel_bottom_row); - - if (unlikely(top_row > bottom_row || left_col > right_col)) + int top, left, bottom, right; + if (!params_to_rectangular_area( + term, 0, &top, &left, &bottom, &right)) + { break; + } /* * Note: term_erase() _also_ erases sixels, but since @@ -1976,12 +1968,10 @@ csi_dispatch(struct terminal *term, uint8_t final) * entire sixel here is more efficient. */ sixel_overwrite_by_rectangle( - term, - top_row, left_col, - bottom_row - top_row + 1, right_col - left_col + 1); + term, top, left, bottom - top + 1, right - left + 1); - for (int r = top_row; r <= bottom_row; r++) - term_erase(term, r, left_col, r, right_col); + for (int r = top; r <= bottom; r++) + term_erase(term, r, left, r, right); break; } } From 189cfd717fd4670a97995ad9b8e08a2352c09099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 15:59:38 +0100 Subject: [PATCH 10/24] term: replace term_put_char() with term_fill() --- csi.c | 11 ++--------- terminal.c | 32 +++++++++++++++++++------------- terminal.h | 2 +- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/csi.c b/csi.c index 43a358a8..ac03825e 100644 --- a/csi.c +++ b/csi.c @@ -1941,15 +1941,8 @@ csi_dispatch(struct terminal *term, uint8_t final) sixel_overwrite_by_rectangle( term, top, left, bottom - top + 1, right - left + 1); - for (int r = top; r <= bottom; r++) { - struct row *row = grid_row(term->grid, r); - - if (unlikely(row->extra != NULL)) - grid_row_uri_range_erase(row, left, right); - - for (int col = left; col <= right; col++) - term_put_char(term, r, col, (wchar_t)c); - } + for (int r = top; r <= bottom; r++) + term_fill(term, r, left, c, right - left + 1); } break; } diff --git a/terminal.c b/terminal.c index ad20c737..8abe61e8 100644 --- a/terminal.c +++ b/terminal.c @@ -3506,33 +3506,39 @@ print_spacer(struct terminal *term, int col, int remaining) * - update the cursor * - linewrap * - erase sixels - * - erase URIs (but it _does_ emit them if one is active) * * Limitations: * - double width characters not supported */ void -term_put_char(struct terminal *term, int r, int c, wchar_t wc) +term_fill(struct terminal *term, int r, int c, char data, size_t count) { struct row *row = grid_row(term->grid, r); row->dirty = true; - struct cell *cell = &row->cells[c]; - cell->wc = wc; - cell->attrs = term->vt.attrs; + xassert(c + count <= term->cols); - if (unlikely(term->vt.osc8.uri != NULL)) { - grid_row_uri_range_put(row, c, term->vt.osc8.uri, term->vt.osc8.id); + const struct cell *last = &row->cells[c + count]; + for (struct cell *cell = &row->cells[c]; cell < last; cell++) { + cell->wc = data; + cell->attrs = term->vt.attrs; - switch (term->conf->url.osc8_underline) { - case OSC8_UNDERLINE_ALWAYS: - cell->attrs.url = true; - break; + if (unlikely(term->vt.osc8.uri != NULL)) { + grid_row_uri_range_put(row, c, term->vt.osc8.uri, term->vt.osc8.id); - case OSC8_UNDERLINE_URL_MODE: - break; + switch (term->conf->url.osc8_underline) { + case OSC8_UNDERLINE_ALWAYS: + cell->attrs.url = true; + break; + + case OSC8_UNDERLINE_URL_MODE: + break; + } } } + + if (unlikely(row->extra != NULL)) + grid_row_uri_range_erase(row, c, c + count - 1); } void diff --git a/terminal.h b/terminal.h index 8abc1c8e..f56ffdb0 100644 --- a/terminal.h +++ b/terminal.h @@ -797,8 +797,8 @@ void term_cursor_up(struct terminal *term, int count); void term_cursor_down(struct terminal *term, int count); void term_cursor_blink_update(struct terminal *term); -void term_put_char(struct terminal *term, int r, int c, char32_t wc); void term_print(struct terminal *term, char32_t wc, int width); +void term_fill(struct terminal *term, int row, int col, char c, size_t count); void term_scroll(struct terminal *term, int rows); void term_scroll_reverse(struct terminal *term, int rows); From b3a84ba71b021b484e0ddc023480eb6d9f885991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 16:05:18 +0100 Subject: [PATCH 11/24] term: modify term_fill() to optionally reset the SGR attributes --- csi.c | 2 +- terminal.c | 9 +++++++-- terminal.h | 3 ++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/csi.c b/csi.c index ac03825e..558c2dc2 100644 --- a/csi.c +++ b/csi.c @@ -1942,7 +1942,7 @@ csi_dispatch(struct terminal *term, uint8_t final) term, top, left, bottom - top + 1, right - left + 1); for (int r = top; r <= bottom; r++) - term_fill(term, r, left, c, right - left + 1); + term_fill(term, r, left, c, right - left + 1, true); } break; } diff --git a/terminal.c b/terminal.c index 8abe61e8..d826282b 100644 --- a/terminal.c +++ b/terminal.c @@ -3511,17 +3511,22 @@ print_spacer(struct terminal *term, int col, int remaining) * - double width characters not supported */ void -term_fill(struct terminal *term, int r, int c, char data, size_t count) +term_fill(struct terminal *term, int r, int c, char data, size_t count, + bool use_sgr_attrs) { struct row *row = grid_row(term->grid, r); row->dirty = true; xassert(c + count <= term->cols); + const struct attributes attrs = use_sgr_attrs + ? term->vt.attrs + : (struct attributes){0}; + const struct cell *last = &row->cells[c + count]; for (struct cell *cell = &row->cells[c]; cell < last; cell++) { cell->wc = data; - cell->attrs = term->vt.attrs; + cell->attrs = attrs; if (unlikely(term->vt.osc8.uri != NULL)) { grid_row_uri_range_put(row, c, term->vt.osc8.uri, term->vt.osc8.id); diff --git a/terminal.h b/terminal.h index f56ffdb0..c8a43c64 100644 --- a/terminal.h +++ b/terminal.h @@ -798,7 +798,8 @@ void term_cursor_down(struct terminal *term, int count); void term_cursor_blink_update(struct terminal *term); void term_print(struct terminal *term, char32_t wc, int width); -void term_fill(struct terminal *term, int row, int col, char c, size_t count); +void term_fill(struct terminal *term, int row, int col, char c, size_t count, + bool use_sgr_attrs); void term_scroll(struct terminal *term, int rows); void term_scroll_reverse(struct terminal *term, int rows); From 74a1fa9e00058589c30454a87b68527fd27ead9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 16:07:51 +0100 Subject: [PATCH 12/24] vt: update DECALN to use term_fill() --- vt.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/vt.c b/vt.c index caa53e62..beb26670 100644 --- a/vt.c +++ b/vt.c @@ -560,15 +560,9 @@ action_esc_dispatch(struct terminal *term, uint8_t final) case '#': switch (final) { - case '8': - for (int r = 0; r < term->rows; r++) { - struct row *row = grid_row(term->grid, r); - for (int c = 0; c < term->cols; c++) { - row->cells[c].wc = U'E'; - row->cells[c].attrs = (struct attributes){0}; - } - row->dirty = true; - } + case '8': /* DECALN */ + for (int r = 0; r < term->rows; r++) + term_fill(term, r, 0, 'E', term->cols, false); break; } break; /* private[0] == '#' */ From 1b13deff048fc10620b26ffe0220fae15549f9b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 16:08:42 +0100 Subject: [PATCH 13/24] =?UTF-8?q?term=5Ffill():=20make=20sure=20the=20fill?= =?UTF-8?q?ed=20cells=20have=20their=20=E2=80=98clean=E2=80=99=20bit=20res?= =?UTF-8?q?et?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terminal.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/terminal.c b/terminal.c index d826282b..acde951b 100644 --- a/terminal.c +++ b/terminal.c @@ -3519,10 +3519,12 @@ term_fill(struct terminal *term, int r, int c, char data, size_t count, xassert(c + count <= term->cols); - const struct attributes attrs = use_sgr_attrs + struct attributes attrs = use_sgr_attrs ? term->vt.attrs : (struct attributes){0}; + attrs.clean = 0; + const struct cell *last = &row->cells[c + count]; for (struct cell *cell = &row->cells[c]; cell < last; cell++) { cell->wc = data; From 23908d9277b3c41c294102993875cf485db689cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 16:13:05 +0100 Subject: [PATCH 14/24] =?UTF-8?q?term=5Ffill():=20change=20=E2=80=98charac?= =?UTF-8?q?ter=E2=80=99=20parameter=20from=20char=20->=20uint8=5Ft?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- csi.c | 37 +++++++++++++++++++------------------ terminal.c | 2 +- terminal.h | 2 +- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/csi.c b/csi.c index 558c2dc2..494ac957 100644 --- a/csi.c +++ b/csi.c @@ -1924,26 +1924,27 @@ csi_dispatch(struct terminal *term, uint8_t final) } case 'x': { /* DECFRA */ - const char c = vt_param_get(term, 0, 0); - if (likely((c >= 32 && c < 126) || - (c >= 160 && c <= 255))) + const uint8_t c = vt_param_get(term, 0, 0); + + if (unlikely(!((c >= 32 && c < 126) || c >= 160))) + break; + + int top, left, bottom, right; + if (!params_to_rectangular_area( + term, 1, &top, &left, &bottom, &right)) { - int top, left, bottom, right; - if (!params_to_rectangular_area( - term, 1, &top, &left, &bottom, &right)) - { - break; - } - - /* Erase the entire region at once (MUCH cheaper than - * doing it row by row, or even character by - * character). */ - sixel_overwrite_by_rectangle( - term, top, left, bottom - top + 1, right - left + 1); - - for (int r = top; r <= bottom; r++) - term_fill(term, r, left, c, right - left + 1, true); + break; } + + /* Erase the entire region at once (MUCH cheaper than + * doing it row by row, or even character by + * character). */ + sixel_overwrite_by_rectangle( + term, top, left, bottom - top + 1, right - left + 1); + + for (int r = top; r <= bottom; r++) + term_fill(term, r, left, c, right - left + 1, true); + break; } diff --git a/terminal.c b/terminal.c index acde951b..1ca581be 100644 --- a/terminal.c +++ b/terminal.c @@ -3511,7 +3511,7 @@ print_spacer(struct terminal *term, int col, int remaining) * - double width characters not supported */ void -term_fill(struct terminal *term, int r, int c, char data, size_t count, +term_fill(struct terminal *term, int r, int c, uint8_t data, size_t count, bool use_sgr_attrs) { struct row *row = grid_row(term->grid, r); diff --git a/terminal.h b/terminal.h index c8a43c64..0dd40c51 100644 --- a/terminal.h +++ b/terminal.h @@ -798,7 +798,7 @@ void term_cursor_down(struct terminal *term, int count); void term_cursor_blink_update(struct terminal *term); void term_print(struct terminal *term, char32_t wc, int width); -void term_fill(struct terminal *term, int row, int col, char c, size_t count, +void term_fill(struct terminal *term, int row, int col, uint8_t c, size_t count, bool use_sgr_attrs); void term_scroll(struct terminal *term, int rows); From f5c574cd94796561a9138a30eae0cbe724033644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 16:25:52 +0100 Subject: [PATCH 15/24] csi: DECCRA: no need for a counter here --- csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csi.c b/csi.c index 494ac957..b5d58f97 100644 --- a/csi.c +++ b/csi.c @@ -1911,7 +1911,7 @@ csi_dispatch(struct terminal *term, uint8_t final) memcpy(cell, copy[r], cell_count * sizeof(copy[r][0])); free(copy[r]); - for (int c = 0; c < cell_count; c++, cell++) + for (;cell < &row->cells[dst_left + cell_count]; cell++) cell->attrs.clean = 0; if (unlikely(row->extra != NULL)) { From d6c5bc3262594d265cf9a506e9cd4afb033e4f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 16:37:11 +0100 Subject: [PATCH 16/24] csi: DECRARA: fix comment: DECCARA -> DECRARA --- csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csi.c b/csi.c index b5d58f97..881a8ce1 100644 --- a/csi.c +++ b/csi.c @@ -1830,7 +1830,7 @@ csi_dispatch(struct terminal *term, uint8_t final) for (size_t i = 4; i < term->vt.params.idx; i++) { const int param = term->vt.params.v[i].value; - /* DECCARA only supports a sub-set of SGR parameters */ + /* DECRARA only supports a sub-set of SGR parameters */ switch (param) { case 0: a->bold = !a->bold; From 60c5d889ecd8b0496e181f587dc2c6368a209b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 16:42:53 +0100 Subject: [PATCH 17/24] vt: DECALN: erase sixels, reset margins, home the cursor https://vt100.net/docs/vt510-rm/DECALN.html: Notes on DECALN DECALN sets the margins to the extremes of the page, and moves the cursor to the home position. --- vt.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vt.c b/vt.c index beb26670..a8a0f0fb 100644 --- a/vt.c +++ b/vt.c @@ -18,6 +18,7 @@ #include "debug.h" #include "grid.h" #include "osc.h" +#include "sixel.h" #include "util.h" #include "xmalloc.h" @@ -561,8 +562,15 @@ action_esc_dispatch(struct terminal *term, uint8_t final) case '#': switch (final) { case '8': /* DECALN */ + sixel_overwrite_by_rectangle(term, 0, 0, term->rows, term->cols); + + term->scroll_region.start = 0; + term->scroll_region.end = term->rows; + for (int r = 0; r < term->rows; r++) term_fill(term, r, 0, 'E', term->cols, false); + + term_cursor_home(term); break; } break; /* private[0] == '#' */ From aac24bfa1b3779c1851bd4dcbfbf8266d16634dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 19:43:47 +0100 Subject: [PATCH 18/24] =?UTF-8?q?foot.info:=20add=20non-standard=20capabil?= =?UTF-8?q?ity=20=E2=80=98Rect=E2=80=99=20(used=20by=20tmux)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This tells tmux it can use DECFRA to erase rectangular regions. --- CHANGELOG.md | 1 + foot.info | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d11f3ba..9e48118b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -683,6 +683,7 @@ decoration buttons to the compositor capabilities ([#1061][1061]). * Rectangular edit functions: `DECCARA`, `DECRARA`, `DECCRA`, `DECFRA` and `DECERA`. +* `Rect` capability to terminfo. [1058]: https://codeberg.org/dnkl/foot/issues/1058 diff --git a/foot.info b/foot.info index 3bab3266..319d0781 100644 --- a/foot.info +++ b/foot.info @@ -38,6 +38,7 @@ PE=\E[201~, PS=\E[200~, RV=\E[>c, + Rect=\E[%p1%d;%p2%d;%p3%d;%p4%d;%p5%d$x, Se=\E[ q, Ss=\E[%p1%d q, Sync=\E[?2026%?%p1%{1}%-%tl%eh%;, From 6a01642a6fef11d5663c9dfcdf212826f7224f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 28 Dec 2021 17:14:50 +0100 Subject: [PATCH 19/24] csi: DECCARA+DECRARA: dirty cells --- csi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/csi.c b/csi.c index 881a8ce1..7a427f9e 100644 --- a/csi.c +++ b/csi.c @@ -1781,9 +1781,11 @@ csi_dispatch(struct terminal *term, uint8_t final) for (int r = top; r <= bottom; r++) { struct row *row = grid_row(term->grid, r); + row->dirty = true; for (int c = left; c <= right; c++) { struct attributes *a = &row->cells[c].attrs; + a->clean = 0; for (size_t i = 4; i < term->vt.params.idx; i++) { const int param = term->vt.params.v[i].value; @@ -1823,9 +1825,11 @@ csi_dispatch(struct terminal *term, uint8_t final) for (int r = top; r <= bottom; r++) { struct row *row = grid_row(term->grid, r); + row->dirty = true; for (int c = left; c <= right; c++) { struct attributes *a = &row->cells[c].attrs; + a->clean = 0; for (size_t i = 4; i < term->vt.params.idx; i++) { const int param = term->vt.params.v[i].value; From 8d7ab86182c1439e9aa4c436f0ceebe77c7d05a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 28 Dec 2021 17:15:12 +0100 Subject: [PATCH 20/24] term_fill(): no need to set attrs.clean = 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VT state’s attribute is always 0 --- terminal.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/terminal.c b/terminal.c index 1ca581be..2248bd02 100644 --- a/terminal.c +++ b/terminal.c @@ -3523,8 +3523,6 @@ term_fill(struct terminal *term, int r, int c, uint8_t data, size_t count, ? term->vt.attrs : (struct attributes){0}; - attrs.clean = 0; - const struct cell *last = &row->cells[c + count]; for (struct cell *cell = &row->cells[c]; cell < last; cell++) { cell->wc = data; From 6ff307b3b53c50902b185e0e7504ff5dcc0c0934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 1 Jan 2022 14:32:26 +0100 Subject: [PATCH 21/24] doc: ctlseq: DECCARA, DECRARA, DECCRA, DECFRA and DECERA --- doc/foot-ctlseqs.7.scd | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/foot-ctlseqs.7.scd b/doc/foot-ctlseqs.7.scd index 0d8d5d79..38742aa3 100644 --- a/doc/foot-ctlseqs.7.scd +++ b/doc/foot-ctlseqs.7.scd @@ -495,6 +495,31 @@ manipulation sequences. The generic format is: : VT320 : Request status of ECMA-48/ANSI mode. See the descriptions for SM/RM above for recognized _Ps_ values. +| \\E[ _Pt_ ; _Pl_ ; _Pb_ ; _Pr_ ; _Pm_ $ r +: DECCARA +: VT400 +: Change attributes in rectangular area. _Pt_, _Pl_, _Pb_ and _Pr_ + denotes the rectangle, _Pm_ denotes the SGR attributes. +| \\E[ _Pt_ ; _Pl_ ; _Pb_ ; _Pr_ ; _Pm_ $ t +: DECRARA +: VT400 +: Invert attributes in rectangular area. _Pt_, _Pl_, _Pb_ and _Pr_ + denotes the rectangle, _Pm_ denotes the SGR attributes. +| \\E[ _Pt_ ; _Pl_ ; _Pb_ ; _Pr_ ; _Pp_ ; _Pt_ ; _Pl_ ; _Pp_ $ v +: DECCRA +: VT400 +: Copy rectangular area. _Pt_, _Pl_, _Pb_ and _Pr_ denotes the + rectangle, _Pt_ and _Pl_ denotes the target location. +| \\E[ _Pc_ ; _Pt_ ; _Pl_ ; _Pb_ ; _Pr_ $ x +: DECFRA +: VT420 +: Fill rectangular area. _Pc_ is the character to use, _Pt_, _Pl_, + _Pb_ and _Pr_ denotes the rectangle. +| \\E[ _Pt_ ; _Pl_ ; _Pb_ ; _Pr_ $ z +: DECERA +: VT400 +: Erase rectangular area. _Pt_, _Pl_, _Pb_ and _Pr_ denotes the + rectangle. | \\E[ _Ps_ T : SD : VT420 From cbf55ccacf694058ce85a324243ded9cf64bb36c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Mar 2024 16:28:59 +0100 Subject: [PATCH 22/24] changeloge: move DECERA to the 'unreleased' section --- CHANGELOG.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e48118b..2f0214a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,9 @@ * Support for OSC-176, _"Set App-ID"_ (https://gist.github.com/delthas/d451e2cc1573bb2364839849c7117239). * Support for `DECRQM` queries with ANSI/ECMA-48 modes (`CSI Ps $ p`). +* Rectangular edit functions: `DECCARA`, `DECRARA`, `DECCRA`, `DECFRA` + and `DECERA`. +* `Rect` capability to terminfo. [1348]: https://codeberg.org/dnkl/foot/issues/1348 @@ -681,10 +684,6 @@ way of entering Unicode characters is with an IME ([#1116][1116]). * Support for `xdg_toplevel.wm_capabilities`, to adapt the client-side decoration buttons to the compositor capabilities ([#1061][1061]). -* Rectangular edit functions: `DECCARA`, `DECRARA`, `DECCRA`, `DECFRA` - and `DECERA`. -* `Rect` capability to terminfo. - [1058]: https://codeberg.org/dnkl/foot/issues/1058 [1070]: https://codeberg.org/dnkl/foot/issues/1070 From 4ea4e5da4eb6dca888fdadb76f293c843d719f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Mar 2024 16:29:39 +0100 Subject: [PATCH 23/24] changelog: rectangular functions: add bug ref --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f0214a0..4712a299 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,10 +63,11 @@ (https://gist.github.com/delthas/d451e2cc1573bb2364839849c7117239). * Support for `DECRQM` queries with ANSI/ECMA-48 modes (`CSI Ps $ p`). * Rectangular edit functions: `DECCARA`, `DECRARA`, `DECCRA`, `DECFRA` - and `DECERA`. + and `DECERA` ([#1633][1633]). * `Rect` capability to terminfo. [1348]: https://codeberg.org/dnkl/foot/issues/1348 +[1633]: https://codeberg.org/dnkl/foot/issues/1633 ### Changed From e2b3eb91ddfb8690d45d4300f0f41cbb1137f22e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 10 Mar 2024 17:37:11 +0100 Subject: [PATCH 24/24] csi: params_to_rectangular_area(): ensure left/right is within bounds --- csi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/csi.c b/csi.c index 7a427f9e..d03ae40d 100644 --- a/csi.c +++ b/csi.c @@ -678,15 +678,16 @@ params_to_rectangular_area(const struct terminal *term, int first_idx, int *top, int *left, int *bottom, int *right) { int rel_top = vt_param_get(term, first_idx + 0, 1) - 1; - *left = vt_param_get(term, first_idx + 1, 1) - 1; + *left = min(vt_param_get(term, first_idx + 1, 1) - 1, term->cols - 1); int rel_bottom = vt_param_get(term, first_idx + 2, term->rows) - 1; - *right = vt_param_get(term, first_idx + 3, term->cols) - 1; + *right = min(vt_param_get(term, first_idx + 3, term->cols) - 1, term->cols - 1); if (rel_top > rel_bottom || *left > *right) return false; *top = term_row_rel_to_abs(term, rel_top); *bottom = term_row_rel_to_abs(term, rel_bottom); + return true; }