From 5c99e8013b3e8fb68e55250be550e856636ccf7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 14 Jul 2020 16:41:57 +0200 Subject: [PATCH 1/8] term: rename COMB_CHARS_LO,HI -> CELL_COMB_CHARS_LO,HI --- render.c | 6 +++--- selection.c | 5 +++-- terminal.h | 4 ++-- vt.c | 12 ++++++------ 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/render.c b/render.c index 99ca03e3..d2a278ff 100644 --- a/render.c +++ b/render.c @@ -408,10 +408,10 @@ render_cell(struct terminal *term, pixman_image_t *pix, if (cell->wc != 0) { wchar_t base = cell->wc; - if (base >= COMB_CHARS_LO && - base < (COMB_CHARS_LO + term->composed_count)) + if (base >= CELL_COMB_CHARS_LO && + base < (CELL_COMB_CHARS_LO + term->composed_count)) { - composed = &term->composed[base - COMB_CHARS_LO]; + composed = &term->composed[base - CELL_COMB_CHARS_LO]; base = composed->base; } diff --git a/selection.c b/selection.c index cd3780a7..8dc83906 100644 --- a/selection.c +++ b/selection.c @@ -321,8 +321,9 @@ extract_one(struct terminal *term, struct row *row, struct cell *cell, assert(ctx->idx + 1 <= ctx->size); - if (cell->wc >= COMB_CHARS_LO && cell->wc < (COMB_CHARS_LO + term->composed_count)) { - const struct composed *composed = &term->composed[cell->wc - COMB_CHARS_LO]; + if (cell->wc >= CELL_COMB_CHARS_LO && + cell->wc < (CELL_COMB_CHARS_LO + term->composed_count)) { + const struct composed *composed = &term->composed[cell->wc - CELL_COMB_CHARS_LO]; ctx->buf[ctx->idx++] = composed->base; diff --git a/terminal.h b/terminal.h index 4d1cceff..f32f86dc 100644 --- a/terminal.h +++ b/terminal.h @@ -48,6 +48,8 @@ struct attributes { }; static_assert(sizeof(struct attributes) == 8, "bad size"); +#define CELL_COMB_CHARS_LO 0x40000000ul +#define CELL_COMB_CHARS_HI 0x400ffffful struct cell { wchar_t wc; struct attributes attrs; @@ -216,8 +218,6 @@ struct terminal { struct grid alt; struct grid *grid; - #define COMB_CHARS_LO 0x40000000ul - #define COMB_CHARS_HI 0x400ffffful size_t composed_count; struct composed *composed; diff --git a/vt.c b/vt.c index 8d9d6cfa..618611c6 100644 --- a/vt.c +++ b/vt.c @@ -550,9 +550,9 @@ action_utf8_print(struct terminal *term, wchar_t wc) } const struct composed *composed = - (base >= COMB_CHARS_LO && - base < (COMB_CHARS_LO + term->composed_count)) - ? &term->composed[base - COMB_CHARS_LO] + (base >= CELL_COMB_CHARS_LO && + base < (CELL_COMB_CHARS_LO + term->composed_count)) + ? &term->composed[base - CELL_COMB_CHARS_LO] : NULL; if (composed != NULL) @@ -635,7 +635,7 @@ action_utf8_print(struct terminal *term, wchar_t wc) if (cc->combining[wanted_count - 1] != wc) continue; - term_print(term, COMB_CHARS_LO + i, base_width); + term_print(term, CELL_COMB_CHARS_LO + i, base_width); return; } @@ -648,12 +648,12 @@ action_utf8_print(struct terminal *term, wchar_t wc) new_cc.combining[i] = composed->combining[i]; new_cc.combining[wanted_count - 1] = wc; - if (term->composed_count < COMB_CHARS_HI) { + if (term->composed_count < CELL_COMB_CHARS_HI) { term->composed_count++; term->composed = realloc(term->composed, term->composed_count * sizeof(term->composed[0])); term->composed[term->composed_count - 1] = new_cc; - term_print(term, COMB_CHARS_LO + term->composed_count - 1, base_width); + term_print(term, CELL_COMB_CHARS_LO + term->composed_count - 1, base_width); return; } else { /* We reached our maximum number of allowed composed From df2927e088ceb2119264d1a970c22a450f00005b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 14 Jul 2020 16:49:11 +0200 Subject: [PATCH 2/8] term: print: write special value CELL_MULT_COL_SPACER to extra cells When printing a multi-column character, write CELL_MULT_COL_SPACER instead of '0' to both padding cells (when character doesn't fit at the end of the line), and to the cells following the actual character. --- render.c | 2 +- selection.c | 8 ++++---- terminal.c | 21 +++++++++------------ terminal.h | 2 ++ 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/render.c b/render.c index d2a278ff..a049ca05 100644 --- a/render.c +++ b/render.c @@ -438,7 +438,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, if (has_cursor && term->cursor_style == CURSOR_BLOCK && term->kbd_focus) draw_cursor(term, cell, font, pix, &fg, &bg, x, y, cell_cols); - if (cell->wc == 0 || cell->attrs.conceal) + if (cell->wc == 0 || cell->wc == CELL_MULT_COL_SPACER || cell->attrs.conceal) goto draw_cursor; pixman_image_t *clr_pix = pixman_image_create_solid_fill(&fg); diff --git a/selection.c b/selection.c index 8dc83906..d5f8721c 100644 --- a/selection.c +++ b/selection.c @@ -134,9 +134,9 @@ foreach_selected_normal( c <= (r == end_row ? end_col : term->cols - 1); c++) { + if (row->cells[c].wc == CELL_MULT_COL_SPACER) + continue; cb(term, row, &row->cells[c], c, data); - c += max(1, wcwidth(row->cells[c].wc)) - 1; - assert(c < term->cols); } start_col = 0; @@ -168,9 +168,9 @@ foreach_selected_block( assert(row != NULL); for (int c = top_left.col; c <= bottom_right.col; c++) { + if (row->cells[c].wc == CELL_MULT_COL_SPACER) + continue; cb(term, row, &row->cells[c], c, data); - c += max(1, wcwidth(row->cells[c].wc)) - 1; - assert(c < term->cols); } } } diff --git a/terminal.c b/terminal.c index eda311da..2a232de3 100644 --- a/terminal.c +++ b/terminal.c @@ -2397,18 +2397,15 @@ term_print(struct terminal *term, wchar_t wc, int width) term->grid->cursor.point.col + width > term->cols) { /* Multi-column character that doesn't fit on current line - - * force a line wrap */ - term->grid->cursor.lcf = 1; + * pad with spacers */ + for (size_t i = term->grid->cursor.point.col; i < term->cols; i++) { + struct cell *cell = &term->grid->cur_row->cells[i]; + cell->wc = CELL_MULT_COL_SPACER; + cell->attrs.clean = 0; + } - /* - * TODO: should we insert place holder values in the remaining - * cells? This would allow e.g. text extraction to simply - * skip these, instead of trying to recognize a sequence of - * empty cells at the end of the line followed by a - * multi-column character... - * - * Might also make text reflow easier, or even more correct. - */ + /* And force a line-wrap */ + term->grid->cursor.lcf = 1; } print_linewrap(term); @@ -2431,7 +2428,7 @@ term_print(struct terminal *term, wchar_t wc, int width) term->grid->cursor.point.col++; struct cell *cell = &row->cells[term->grid->cursor.point.col]; - cell->wc = 0; + cell->wc = CELL_MULT_COL_SPACER; cell->attrs.clean = 0; } diff --git a/terminal.h b/terminal.h index f32f86dc..68259160 100644 --- a/terminal.h +++ b/terminal.h @@ -50,6 +50,8 @@ static_assert(sizeof(struct attributes) == 8, "bad size"); #define CELL_COMB_CHARS_LO 0x40000000ul #define CELL_COMB_CHARS_HI 0x400ffffful +#define CELL_MULT_COL_SPACER 0x40100000ul + struct cell { wchar_t wc; struct attributes attrs; From 6d7aba3ea07b9a7a584dcb027fa8f0f26c5c4fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 14 Jul 2020 17:03:20 +0200 Subject: [PATCH 3/8] term: print: linewrap + insert *before* inserting SPACERS Otherwise we end up overwriting the character in the last column --- terminal.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/terminal.c b/terminal.c index 2a232de3..e9bb0812 100644 --- a/terminal.c +++ b/terminal.c @@ -2393,6 +2393,9 @@ term_print(struct terminal *term, wchar_t wc, int width) if (unlikely(width <= 0)) return; + print_linewrap(term); + print_insert(term, width); + if (unlikely(width > 1) && term->grid->cursor.point.col + width > term->cols) { @@ -2406,11 +2409,9 @@ term_print(struct terminal *term, wchar_t wc, int width) /* And force a line-wrap */ term->grid->cursor.lcf = 1; + print_linewrap(term); } - print_linewrap(term); - print_insert(term, width); - sixel_overwrite_at_cursor(term, width); /* *Must* get current cell *after* linewrap+insert */ From 9ea42ef226cdc92c09470a522516a2209b986281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 14 Jul 2020 17:04:59 +0200 Subject: [PATCH 4/8] grid: reflow: handle multi column character spacers Ignore *old* cells containing spacers. Pad new grid with spacers if a multi-column character doesn't fit at the end of a line. Insert spacers after a multi-column character. --- grid.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/grid.c b/grid.c index d2905a93..3a595b61 100644 --- a/grid.c +++ b/grid.c @@ -222,12 +222,22 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols, /* Multi-column characters are never cut in half */ assert(c + width <= old_cols); - for (int i = 0; i < empty_count + width; i++) { + for (int i = 0; i < empty_count + 1; i++) { const struct cell *old_cell = &old_row->cells[c - empty_count + i]; + if (old_cell->wc == CELL_MULT_COL_SPACER) + continue; + /* Out of columns on current row in new grid? */ - if (new_col_idx + max(1, wcwidth(old_cell->wc)) > new_cols) + if (new_col_idx + max(1, wcwidth(old_cell->wc)) > new_cols) { + /* Pad to end-of-line with spacers, then line-wrap */ + for (;new_col_idx < new_cols; new_col_idx++) { + new_row->cells[new_col_idx] = (struct cell){}; + new_row->cells[new_col_idx].wc = CELL_MULT_COL_SPACER; + new_row->cells[new_col_idx].attrs.clean = 1; + } line_wrap(); + } assert(new_row != NULL); assert(new_col_idx >= 0); @@ -247,6 +257,16 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols, } } new_col_idx++; + + /* For multi-column characters, insert spacers in the + * subsequent cells */ + for (size_t i = 0; i < width - 1; i++) { + assert(new_col_idx < new_cols); + new_row->cells[new_col_idx] = (struct cell){}; + new_row->cells[new_col_idx].wc = CELL_MULT_COL_SPACER; + new_row->cells[new_col_idx].attrs.clean = 1; + new_col_idx++; + } } c += width - 1; From a2af13a1260b70e59a465debc890d40cd7103964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 14 Jul 2020 17:06:04 +0200 Subject: [PATCH 5/8] selection: no need to try to detect multi-column chars at the end of the line This is handled by the generic foreach() functions, which now simply skips spacer cells. --- selection.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/selection.c b/selection.c index d5f8721c..f460607a 100644 --- a/selection.c +++ b/selection.c @@ -273,22 +273,7 @@ extract_one(struct terminal *term, struct row *row, struct cell *cell, /* New row - determine if we should insert a newline or not */ if (term->selection.kind == SELECTION_NORMAL) { - int width = max(1, wcwidth(cell->wc)); - - if (width > 1) { - /* Heuristict to handle force-wrapped multi-column - * characters */ - - /* - * TODO: maybe we should print a placeholder value to - * the empty cells at the end of the line when - * force-wrapping? Then extract() could simply skip - * those cells - */ - ctx->empty_count -= min(width, ctx->empty_count); - } - - else if (ctx->last_row->linebreak || + if (ctx->last_row->linebreak || ctx->empty_count > 0 || cell->wc == 0) { From f962fb236ecfb76e01589865022817d9bf9133b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 14 Jul 2020 20:24:52 +0200 Subject: [PATCH 6/8] term: copy current VT attributes to multi-column spacer cells And refactor: break out spacer-writing code to a function, since we do exactly the same thing in two places. --- terminal.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/terminal.c b/terminal.c index e9bb0812..78c72ada 100644 --- a/terminal.c +++ b/terminal.c @@ -2387,6 +2387,17 @@ print_insert(struct terminal *term, int width) row->cells[i].attrs.clean = 0; } +static void +print_spacer(struct terminal *term, int col) +{ + struct row *row = term->grid->cur_row; + struct cell *cell = &row->cells[col]; + + cell->wc = CELL_MULT_COL_SPACER; + cell->attrs = term->vt.attrs; + cell->attrs.clean = 0; +} + void term_print(struct terminal *term, wchar_t wc, int width) { @@ -2401,11 +2412,8 @@ term_print(struct terminal *term, wchar_t wc, int width) { /* Multi-column character that doesn't fit on current line - * pad with spacers */ - for (size_t i = term->grid->cursor.point.col; i < term->cols; i++) { - struct cell *cell = &term->grid->cur_row->cells[i]; - cell->wc = CELL_MULT_COL_SPACER; - cell->attrs.clean = 0; - } + for (size_t i = term->grid->cursor.point.col; i < term->cols; i++) + print_spacer(term, i); /* And force a line-wrap */ term->grid->cursor.lcf = 1; @@ -2427,10 +2435,7 @@ term_print(struct terminal *term, wchar_t wc, int width) /* Advance cursor the 'additional' columns while dirty:ing the cells */ for (int i = 1; i < width && term->grid->cursor.point.col < term->cols - 1; i++) { term->grid->cursor.point.col++; - - struct cell *cell = &row->cells[term->grid->cursor.point.col]; - cell->wc = CELL_MULT_COL_SPACER; - cell->attrs.clean = 0; + print_spacer(term, term->grid->cursor.point.col); } /* Advance cursor */ From b0798ad0bed7dac45c167040644df6599cc390f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 14 Jul 2020 20:29:59 +0200 Subject: [PATCH 7/8] grid: reflow: use macro print_spacer() to insert multi-column character spacers --- grid.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/grid.c b/grid.c index 3a595b61..4ccb27ac 100644 --- a/grid.c +++ b/grid.c @@ -183,6 +183,13 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols, } \ } while(0) +#define print_spacer() \ + do { \ + new_row->cells[new_col_idx].wc = CELL_MULT_COL_SPACER; \ + new_row->cells[new_col_idx].attrs = old_cell->attrs; \ + new_row->cells[new_col_idx].attrs.clean = 1; \ + } while (0) + /* * Keep track of empty cells. If the old line ends with a * string of empty cells, we don't need to, nor do we want to, @@ -231,11 +238,8 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols, /* Out of columns on current row in new grid? */ if (new_col_idx + max(1, wcwidth(old_cell->wc)) > new_cols) { /* Pad to end-of-line with spacers, then line-wrap */ - for (;new_col_idx < new_cols; new_col_idx++) { - new_row->cells[new_col_idx] = (struct cell){}; - new_row->cells[new_col_idx].wc = CELL_MULT_COL_SPACER; - new_row->cells[new_col_idx].attrs.clean = 1; - } + for (;new_col_idx < new_cols; new_col_idx++) + print_spacer(); line_wrap(); } @@ -262,9 +266,7 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols, * subsequent cells */ for (size_t i = 0; i < width - 1; i++) { assert(new_col_idx < new_cols); - new_row->cells[new_col_idx] = (struct cell){}; - new_row->cells[new_col_idx].wc = CELL_MULT_COL_SPACER; - new_row->cells[new_col_idx].attrs.clean = 1; + print_spacer(); new_col_idx++; } } @@ -278,6 +280,7 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols, line_wrap(); } +#undef print_spacer #undef line_wrap } From 2fa2c9fad4c401b174e084ee560defba7c6ca49e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 15 Jul 2020 08:04:51 +0200 Subject: [PATCH 8/8] term: print: don't pad with spacers if auto-margin is disabled --- terminal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminal.c b/terminal.c index 78c72ada..40bfbc90 100644 --- a/terminal.c +++ b/terminal.c @@ -2407,7 +2407,7 @@ term_print(struct terminal *term, wchar_t wc, int width) print_linewrap(term); print_insert(term, width); - if (unlikely(width > 1) && + if (unlikely(width > 1) && likely(term->auto_margin) && term->grid->cursor.point.col + width > term->cols) { /* Multi-column character that doesn't fit on current line -