From d70956da08c987af639fd8e03e669ca59c4c542b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 1 Jul 2019 12:23:38 +0200 Subject: [PATCH 01/50] wip: use a sliding window instead of memmove() to scroll Instead of memmoving a large amount of data on every scroll, use a sliding window. That is, each time we scroll, we offset origin. --- csi.c | 2 +- grid.c | 34 +++++++++++++++ grid.h | 4 ++ main.c | 66 ++++++++++++++++++++--------- terminal.c | 120 ++++++++++++++++++----------------------------------- terminal.h | 16 +++---- vt.c | 6 ++- 7 files changed, 140 insertions(+), 108 deletions(-) diff --git a/csi.c b/csi.c index 647d5052..9e3b8675 100644 --- a/csi.c +++ b/csi.c @@ -280,7 +280,7 @@ csi_dispatch(struct terminal *term, uint8_t final) i == term->vt.params.idx - 1 ? "" : ";"); } - c += snprintf(&log[c], sizeof(log) - c, "%c (%zu parameters)", + c += snprintf(&log[c], sizeof(log) - c, "%c (%d parameters)", final, term->vt.params.idx); LOG_DBG("%s", log); #endif diff --git a/grid.c b/grid.c index 67f6e57a..bb8d46eb 100644 --- a/grid.c +++ b/grid.c @@ -6,3 +6,37 @@ #define LOG_MODULE "grid" #define LOG_ENABLE_DBG 1 #include "log.h" + +struct cell * +grid_get_range(struct grid *grid, size_t start, size_t *length) +{ +#define min(x, y) ((x) < (y) ? (x) : (y)) + assert(*length <= grid->size); + + size_t real_start = (grid->offset + start) % grid->size; + assert(real_start < grid->size); + + *length = min(*length, grid->size - real_start); + assert(real_start + *length <= grid->size); + + return &grid->cells[real_start]; +#undef min +} + +void +grid_memset(struct grid *grid, size_t start, int c, size_t length) +{ + size_t left = length; + while (left > 0) { + size_t count = left; + struct cell *cells = grid_get_range(grid, start, &count); + + assert(count > 0); + assert(count <= left); + + memset(cells, c, count * sizeof(cells[0])); + + left -= count; + start += count; + } +} diff --git a/grid.h b/grid.h index 580ef343..3a5f3f4b 100644 --- a/grid.h +++ b/grid.h @@ -1,3 +1,7 @@ #pragma once +#include #include "terminal.h" + +struct cell *grid_get_range(struct grid *grid, size_t start, size_t *length); +void grid_memset(struct grid *grid, size_t start, int c, size_t length); diff --git a/main.c b/main.c index f4f9eaa7..a1e7f5ee 100644 --- a/main.c +++ b/main.c @@ -79,24 +79,42 @@ attrs_to_font(struct context *c, const struct attributes *attrs) static void grid_render_update(struct context *c, struct buffer *buf, const struct damage *dmg) { - LOG_DBG("damage: UPDATE: %d -> %d", - dmg->range.start, dmg->range.start + dmg->range.length); + LOG_DBG("damage: UPDATE: %d -> %d (offset = %d)", + (dmg->range.start - c->term.grid->offset) % c->term.grid->size, + (dmg->range.start - c->term.grid->offset) % c->term.grid->size + dmg->range.length, + c->term.grid->offset); + + int start = dmg->range.start; + int length = dmg->range.length; + + if (start < c->term.grid->offset) { + int end = start + length; + if (end >= c->term.grid->offset) { + start = c->term.grid->offset; + length = end - start; + } else + return; + } const int cols = c->term.cols; - for (int linear_cursor = dmg->range.start, - row = dmg->range.start / cols, - col = dmg->range.start % cols; - linear_cursor < dmg->range.start + dmg->range.length; + for (int linear_cursor = start, + row = ((start - c->term.grid->offset) % c->term.grid->size) / cols, + col = ((start - c->term.grid->offset) % c->term.grid->size) % cols; + linear_cursor < start + length; linear_cursor++, //col = (col + 1) % cols, col = col + 1 >= c->term.cols ? 0 : col + 1, row += col == 0 ? 1 : 0) { + //LOG_DBG("UPDATE: %d (%dx%d)", linear_cursor, row, col); - const struct cell *cell = &c->term.grid->cells[linear_cursor]; - bool has_cursor = c->term.cursor.linear == linear_cursor; + //assert(linear_cursor < c->term.grid->size); + const struct cell *cell = &c->term.grid->cells[linear_cursor % c->term.grid->size]; + + bool has_cursor = c->term.cursor.linear == linear_cursor - c->term.grid->offset; + //bool has_cursor = false; int x = col * c->term.cell_width; int y = row * c->term.cell_height; @@ -160,15 +178,19 @@ grid_render_update(struct context *c, struct buffer *buf, const struct damage *d wl_surface_damage_buffer( c->wl.surface, - 0, (dmg->range.start / cols) * c->term.cell_height, + 0, ((dmg->range.start - c->term.grid->offset) / cols) * c->term.cell_height, buf->width, (dmg->range.length + cols - 1) / cols * c->term.cell_height); } static void grid_render_erase(struct context *c, struct buffer *buf, const struct damage *dmg) { - LOG_DBG("damage: ERASE: %d -> %d", - dmg->range.start, dmg->range.start + dmg->range.length); + LOG_DBG("damage: ERASE: %d -> %d (offset = %d)", + (dmg->range.start - c->term.grid->offset) % c->term.grid->size, + (dmg->range.start - c->term.grid->offset) % c->term.grid->size + dmg->range.length, + c->term.grid->offset); + + assert(dmg->range.start >= c->term.grid->offset); cairo_set_source_rgba( buf->cairo, default_background.r, default_background.g, @@ -176,7 +198,7 @@ grid_render_erase(struct context *c, struct buffer *buf, const struct damage *dm const int cols = c->term.cols; - int start = dmg->range.start; + int start = (dmg->range.start - c->term.grid->offset) % c->term.grid->size; int left = dmg->range.length; int row = start / cols; @@ -238,17 +260,21 @@ grid_render_erase(struct context *c, struct buffer *buf, const struct damage *dm cairo_fill(buf->cairo); wl_surface_damage_buffer(c->wl.surface, x, y, width, height); } - +#if 0 /* Redraw cursor, if it's inside the erased range */ - if (c->term.cursor.linear >= dmg->range.start && - c->term.cursor.linear < dmg->range.start + dmg->range.length) + if (c->term.grid->offset + c->term.cursor.linear >= dmg->range.start && + c->term.grid->offset + c->term.cursor.linear < dmg->range.start + dmg->range.length) { grid_render_update( c, buf, &(struct damage){ .type = DAMAGE_UPDATE, - .range = {.start = c->term.cursor.linear, .length = 1}}); + .range = { + .start = (c->term.grid->offset + c->term.cursor.linear) % c->term.grid->size, + .length = 1} + }); } + #endif } static void @@ -284,7 +310,7 @@ grid_render_scroll(struct context *c, struct buffer *buf, struct damage erase = { .type = DAMAGE_ERASE, .range = { - .start = max(dmg->scroll.region.end - dmg->scroll.lines, + .start = c->term.grid->offset + max(dmg->scroll.region.end - dmg->scroll.lines, dmg->scroll.region.start) * cols, .length = min(dmg->scroll.region.end - dmg->scroll.region.start, dmg->scroll.lines) * cols, @@ -383,6 +409,8 @@ grid_render(struct context *c) tll_remove(c->term.grid->damage, it); } + c->term.grid->offset %= c->term.grid->size; + //cairo_surface_flush(buf->cairo_surface); wl_surface_attach(c->wl.surface, buf->wl_buf, 0, 0); @@ -450,8 +478,8 @@ resize(struct context *c, int width, int height) /* Update environment variables */ char cols_s[12], rows_s[12]; - sprintf(cols_s, "%u", c->term.cols); - sprintf(rows_s, "%u", c->term.rows); + sprintf(cols_s, "%d", c->term.cols); + sprintf(rows_s, "%d", c->term.rows); setenv("COLUMNS", cols_s, 1); setenv("LINES", rows_s, 1); diff --git a/terminal.c b/terminal.c index 482452c5..0bd38915 100644 --- a/terminal.c +++ b/terminal.c @@ -6,6 +6,7 @@ #define LOG_MODULE "terminal" #define LOG_ENABLE_DBG 1 #include "log.h" +#include "grid.h" #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) @@ -37,10 +38,6 @@ damage_merge_range(struct terminal *term, const struct damage *dmg) old->range.start = new_start; old->range.length = new_end - new_start; - assert(old->range.start >= 0); - assert(old->range.start < term->rows * term->cols); - assert(old->range.length >= 0); - assert(old->range.start + old->range.length <= term->rows * term->cols); return true; } @@ -53,14 +50,8 @@ term_damage_update_or_erase(struct terminal *term, enum damage_type damage_type, { struct damage dmg = { .type = damage_type, - .range = {.start = start, .length = length}, + .range = {.start = term->grid->offset + start, .length = length}, }; - - assert(dmg.range.start >= 0); - assert(dmg.range.start < term->rows * term->cols); - assert(dmg.range.length >= 0); - assert(dmg.range.start + dmg.range.length <= term->rows * term->cols); - if (damage_merge_range(term, &dmg)) return; @@ -70,12 +61,14 @@ term_damage_update_or_erase(struct terminal *term, enum damage_type damage_type, void term_damage_update(struct terminal *term, int start, int length) { + assert(start + length <= term->rows * term->cols); term_damage_update_or_erase(term, DAMAGE_UPDATE, start, length); } void term_damage_erase(struct terminal *term, int start, int length) { + assert(start + length <= term->rows * term->cols); term_damage_update_or_erase(term, DAMAGE_ERASE, start, length); } @@ -87,82 +80,30 @@ term_damage_all(struct terminal *term) term_damage_update(term, 0, term->rows * term->cols); } +#if 0 static void damage_adjust_after_scroll(struct terminal *term, enum damage_type damage_type, struct scroll_region region, int lines) { - const int adjustment - = lines * term->cols * (damage_type == DAMAGE_SCROLL_REVERSE ? -1 : 1); - - const int scroll_start = region.start * term->cols; - const int scroll_end = region.end * term->cols; - tll_foreach(term->grid->damage, it) { - int start = it->item.range.start; - int length = it->item.range.length; - int end = start + length; - - if (start < scroll_start && end > scroll_start) { - /* Start outside, end either inside or on the other side */ - struct damage outside = { - .type = it->item.type, - .range = {.start = start, .length = scroll_start - start}, - }; - - tll_push_back(term->grid->damage, outside); - start = scroll_start; - length = end - start; - - } - - if (start < scroll_end && end > scroll_end) { - /* End outside, start either inside or on the other side */ - struct damage outside = { - .type = it->item.type, - .range = {.start = scroll_end, .length = length - scroll_end}, - }; - - tll_push_back(term->grid->damage, outside); - end = scroll_end; - length = end - start; - } - - if (start >= scroll_start && end <= scroll_end) { - /* Completely inside scroll region */ - start -= adjustment; - it->item.range.start = start; - - if (start < scroll_start) { - /* Scrolled up outside scroll region */ - int new_length = length - (scroll_start - start); - assert(new_length < length); - - if (new_length <= 0) - tll_remove(term->grid->damage, it); - else { - it->item.range.start = scroll_start; - it->item.range.length = new_length; - } - } - - if (start + length > scroll_end) { - /* Scrolled down outside scroll region */ - if (start >= scroll_end) - tll_remove(term->grid->damage, it); - else { - it->item.range.start = start; - it->item.range.length = scroll_end - start; - } + if (it->item.range.start < term->grid->offset) { + int end = it->item.range.start + it->item.range.length; + if (end >= term->grid->offset) { + it->item.range.start = term->grid->offset; + it->item.range.length = end - it->item.range.start; + } else { + tll_remove(term->grid->damage, it); } } } } +#endif void term_damage_scroll(struct terminal *term, enum damage_type damage_type, struct scroll_region region, int lines) { - damage_adjust_after_scroll(term, damage_type, region, lines); + //damage_adjust_after_scroll(term, damage_type, region, lines); if (tll_length(term->grid->scroll_damage) > 0) { struct damage *dmg = &tll_back(term->grid->scroll_damage); @@ -185,8 +126,11 @@ term_damage_scroll(struct terminal *term, enum damage_type damage_type, void term_erase(struct terminal *term, int start, int end) { + LOG_DBG("erase: %d-%d", start, end); assert(end >= start); - memset(&term->grid->cells[start], 0, (end - start) * sizeof(term->grid->cells[0])); + assert(end <= term->rows * term->cols); + + grid_memset(term->grid, start, 0, end - start); term_damage_erase(term, start, end - start); } @@ -199,13 +143,10 @@ term_cursor_linear(const struct terminal *term, int row, int col) void term_cursor_to(struct terminal *term, int row, int col) { - assert(row >= 0); assert(row < term->rows); - assert(col >= 0); assert(col < term->cols); int new_linear = row * term->cols + col; - assert(new_linear >= 0); assert(new_linear < term->rows * term->cols); term_damage_update(term, term->cursor.linear, 1); @@ -248,11 +189,13 @@ term_cursor_down(struct terminal *term, int count) void term_scroll_partial(struct terminal *term, struct scroll_region region, int rows) { + LOG_DBG("scroll: %d rows", rows); if (rows >= region.end - region.start) { assert(false && "untested"); return; } +#if 0 int cell_dst = (region.start + 0) * term->cols; int cell_src = (region.start + rows) * term->cols; int cell_count = (region.end - region.start - rows) * term->cols; @@ -260,7 +203,7 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows LOG_DBG("moving %d lines from row %d to row %d", cell_count / term->cols, cell_src / term->cols, cell_dst / term->cols); - const size_t bytes = cell_count * sizeof(term->grid->cells[0]); + const int bytes = cell_count * sizeof(term->grid->cells[0]); memmove( &term->grid->cells[cell_dst], &term->grid->cells[cell_src], bytes); @@ -269,6 +212,15 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows rows * term->cols * sizeof(term->grid->cells[0])); term_damage_scroll(term, DAMAGE_SCROLL, region, rows); +#else + /* TODO */ + assert(region.start == 0 && region.end == term->rows); + assert(rows < term->rows); + + term->grid->offset += rows * term->cols; + grid_memset(term->grid, (region.end - rows) * term->cols, 0, rows * term->cols); + term_damage_scroll(term, DAMAGE_SCROLL, region, rows); +#endif } void @@ -286,6 +238,7 @@ term_scroll_reverse_partial(struct terminal *term, return; } +#if 0 int cell_dst = (region.start + rows) * term->cols; int cell_src = (region.start + 0) * term->cols; int cell_count = (region.end - region.start - rows) * term->cols; @@ -293,7 +246,7 @@ term_scroll_reverse_partial(struct terminal *term, LOG_DBG("moving %d lines from row %d to row %d", cell_count / term->cols, cell_src / term->cols, cell_dst / term->cols); - const size_t bytes = cell_count * sizeof(term->grid->cells[0]); + const int bytes = cell_count * sizeof(term->grid->cells[0]); memmove( &term->grid->cells[cell_dst], &term->grid->cells[cell_src], bytes); @@ -302,6 +255,15 @@ term_scroll_reverse_partial(struct terminal *term, rows * term->cols * sizeof(term->grid->cells[0])); term_damage_scroll(term, DAMAGE_SCROLL_REVERSE, region, rows); +#else + /* TODO */ + assert(false); + assert(region.start == 0 && region.end == 0); + assert(rows < term->rows); + + term->grid->offset -= rows * term->cols; + term_damage_scroll(term, DAMAGE_SCROLL_REVERSE, region, rows); +#endif } void diff --git a/terminal.h b/terminal.h index cceaa6b6..4efaab9f 100644 --- a/terminal.h +++ b/terminal.h @@ -62,8 +62,8 @@ struct damage { }; struct grid { - size_t size; - size_t offset; + int size; + int offset; struct cell *cells; @@ -73,7 +73,7 @@ struct grid { struct vt_subparams { unsigned value[16]; - size_t idx; + int idx; }; struct vt_param { @@ -85,20 +85,20 @@ struct vt { int state; /* enum state */ struct { struct vt_param v[16]; - size_t idx; + int idx; } params; struct { uint8_t data[2]; - size_t idx; + int idx; } intermediates; struct { uint8_t data[1024]; - size_t idx; + int idx; } osc; struct { uint8_t data[4]; - size_t idx; - size_t left; + int idx; + int left; } utf8; struct attributes attrs; bool dim; diff --git a/vt.c b/vt.c index c7e75a94..f52b6d0f 100644 --- a/vt.c +++ b/vt.c @@ -678,7 +678,11 @@ action(struct terminal *term, enum action action, uint8_t c) term_cursor_to(term, term->cursor.row + 1, 0); } - struct cell *cell = &term->grid->cells[term->cursor.linear]; + size_t cell_count = 1; + struct cell *cell = grid_get_range( + term->grid, term->cursor.linear, &cell_count); + assert(cell_count == 1); + term_damage_update(term, term->cursor.linear, 1); if (term->vt.utf8.idx > 0) { From b45b492f4149ce58f01bdec6cf648f30c87bba28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 1 Jul 2019 19:13:45 +0200 Subject: [PATCH 02/50] csi: DECSTBM: move cursor to top left corner of scrolling region --- csi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/csi.c b/csi.c index 9e3b8675..22efbeab 100644 --- a/csi.c +++ b/csi.c @@ -481,6 +481,8 @@ csi_dispatch(struct terminal *term, uint8_t final) term->scroll_region.start = start - 1; term->scroll_region.end = end; + term_cursor_to(term, (start - 1) * term->cols, 0); + LOG_INFO("scroll region: %d-%d", term->scroll_region.start, term->scroll_region.end); From 482f56b4a2f9a30a57b78188cd3faa5e21bbb588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 1 Jul 2019 19:18:52 +0200 Subject: [PATCH 03/50] grid: implement a memmove-sort-of function grid_memmove() moves cell data from one index to another, taking the grid offset into account. --- grid.c | 33 +++++++++++++++++++++++++++++++++ grid.h | 1 + 2 files changed, 34 insertions(+) diff --git a/grid.c b/grid.c index bb8d46eb..a21e0c88 100644 --- a/grid.c +++ b/grid.c @@ -40,3 +40,36 @@ grid_memset(struct grid *grid, size_t start, int c, size_t length) start += count; } } + +void +grid_memmove(struct grid *grid, size_t dst, size_t src, size_t length) +{ + size_t left = length; + size_t copy_idx = 0; + struct cell copy[left]; + + while (left > 0) { + size_t count = left; + struct cell *src_cells = grid_get_range(grid, src, &count); + + memcpy(©[copy_idx], src_cells, count * sizeof(copy[0])); + + left -= count; + src += count; + copy_idx += count; + } + + left = length; + copy_idx = 0; + + while (left > 0) { + size_t count = left; + struct cell *dst_cells = grid_get_range(grid, dst, &count); + + memcpy(dst_cells, ©[copy_idx], count * sizeof(copy[0])); + + left -= count; + dst += count; + copy_idx += count; + } +} diff --git a/grid.h b/grid.h index 3a5f3f4b..275d81bc 100644 --- a/grid.h +++ b/grid.h @@ -5,3 +5,4 @@ struct cell *grid_get_range(struct grid *grid, size_t start, size_t *length); void grid_memset(struct grid *grid, size_t start, int c, size_t length); +void grid_memmove(struct grid *grid, size_t dst, size_t src, size_t length); From c853f01027fa4affcdb1620489d093aeccd75693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 1 Jul 2019 19:19:32 +0200 Subject: [PATCH 04/50] render: re-enable drawing cursor on erase --- main.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/main.c b/main.c index a1e7f5ee..7e0216b8 100644 --- a/main.c +++ b/main.c @@ -260,21 +260,20 @@ grid_render_erase(struct context *c, struct buffer *buf, const struct damage *dm cairo_fill(buf->cairo); wl_surface_damage_buffer(c->wl.surface, x, y, width, height); } -#if 0 + /* Redraw cursor, if it's inside the erased range */ - if (c->term.grid->offset + c->term.cursor.linear >= dmg->range.start && - c->term.grid->offset + c->term.cursor.linear < dmg->range.start + dmg->range.length) + if (c->term.cursor.linear >= dmg->range.start - c->term.grid->offset && + c->term.cursor.linear < dmg->range.start - c->term.grid->offset + dmg->range.length) { grid_render_update( c, buf, &(struct damage){ .type = DAMAGE_UPDATE, .range = { - .start = (c->term.grid->offset + c->term.cursor.linear) % c->term.grid->size, + .start = c->term.grid->offset + c->term.cursor.linear, .length = 1} }); } - #endif } static void From 63e46b7b0bb22a7546d3040c078895471f72a7f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 1 Jul 2019 19:19:48 +0200 Subject: [PATCH 05/50] render: use actual grid size when re-allocating grid buffer --- main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index 7e0216b8..f81771ab 100644 --- a/main.c +++ b/main.c @@ -453,10 +453,10 @@ resize(struct context *c, int width, int height) c->term.normal.cells = realloc( c->term.normal.cells, - c->term.cols * c->term.rows * sizeof(c->term.normal.cells[0])); + c->term.normal.size * sizeof(c->term.normal.cells[0])); c->term.alt.cells = realloc( c->term.alt.cells, - c->term.cols * c->term.rows * sizeof(c->term.alt.cells[0])); + c->term.alt.size * sizeof(c->term.alt.cells[0])); for (size_t i = normal_old_size; i < c->term.normal.size; i++) { c->term.normal.cells[i] = (struct cell){ From 24395cf4cd2bd058ad194a8ba08c25d4b9c4d2a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 1 Jul 2019 19:20:21 +0200 Subject: [PATCH 06/50] scrolling: implement partial scrolling --- terminal.c | 70 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/terminal.c b/terminal.c index 0bd38915..c6129953 100644 --- a/terminal.c +++ b/terminal.c @@ -86,6 +86,7 @@ damage_adjust_after_scroll(struct terminal *term, enum damage_type damage_type, struct scroll_region region, int lines) { tll_foreach(term->grid->damage, it) { +#if 0 if (it->item.range.start < term->grid->offset) { int end = it->item.range.start + it->item.range.length; if (end >= term->grid->offset) { @@ -95,6 +96,12 @@ damage_adjust_after_scroll(struct terminal *term, enum damage_type damage_type, tll_remove(term->grid->damage, it); } } +#endif + + int start = it->item.range.start; + int end = start + it->item.range.length; + + if (start - } } #endif @@ -190,37 +197,52 @@ void term_scroll_partial(struct terminal *term, struct scroll_region region, int rows) { LOG_DBG("scroll: %d rows", rows); - if (rows >= region.end - region.start) { - assert(false && "untested"); - return; + + if (region.start > 0) { + /* TODO: check if it's worth memoving the scroll area instead, + * under certain circumstances */ + + grid_memmove(term->grid, rows * term->cols, 0, region.start * term->cols); + + tll_foreach(term->grid->damage, it) { + int start = it->item.range.start - term->grid->offset; + int end = start + it->item.range.length; + + if (start < region.start) { + assert(end <= region.start); + it->item.range.start += rows * term->cols; + } + } } -#if 0 - int cell_dst = (region.start + 0) * term->cols; - int cell_src = (region.start + rows) * term->cols; - int cell_count = (region.end - region.start - rows) * term->cols; + if (region.end < term->rows) { + /* Copy scrolled-up bottom region to new bottom region */ + grid_memmove( + term->grid, + (region.end + rows) * term->cols, + region.end * term->cols, + (term->rows - region.end) * term->cols); - LOG_DBG("moving %d lines from row %d to row %d", cell_count / term->cols, - cell_src / term->cols, cell_dst / term->cols); + tll_foreach(term->grid->damage, it) { + int start = it->item.range.start - term->grid->offset; + int end = start + it->item.range.length; - const int bytes = cell_count * sizeof(term->grid->cells[0]); - memmove( - &term->grid->cells[cell_dst], &term->grid->cells[cell_src], - bytes); - - memset(&term->grid->cells[(region.end - rows) * term->cols], 0, - rows * term->cols * sizeof(term->grid->cells[0])); - - term_damage_scroll(term, DAMAGE_SCROLL, region, rows); -#else - /* TODO */ - assert(region.start == 0 && region.end == term->rows); - assert(rows < term->rows); + if (end > region.end) { + assert(start >= region.end); + it->item.range.start += rows * term->cols; + } + } + } + /* Offset grid origin */ term->grid->offset += rows * term->cols; - grid_memset(term->grid, (region.end - rows) * term->cols, 0, rows * term->cols); + + /* Clear scrolled-in lines */ + grid_memset( + term->grid, + max(0, region.end - rows) * term->cols, 0, rows * term->cols); + term_damage_scroll(term, DAMAGE_SCROLL, region, rows); -#endif } void From ee476a7e5a2983a832856c2206f1a1b7fe2365c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 1 Jul 2019 19:23:31 +0200 Subject: [PATCH 07/50] csi: erase alt screen when enabling it --- csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csi.c b/csi.c index 22efbeab..95ba81a5 100644 --- a/csi.c +++ b/csi.c @@ -586,7 +586,7 @@ csi_dispatch(struct terminal *term, uint8_t final) if (term->grid != &term->alt) { term->grid = &term->alt; term->saved_cursor = term->cursor; - term_damage_all(term); + term_erase(term, 0, term->rows * term->cols); } break; From 7e4dd2de9a14cda63114ebc5a37c6ef10cd91172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 1 Jul 2019 19:25:04 +0200 Subject: [PATCH 08/50] csi: clear all existing alt damage when switch alt <--> normal --- csi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/csi.c b/csi.c index 95ba81a5..2cb5e7a5 100644 --- a/csi.c +++ b/csi.c @@ -586,6 +586,9 @@ csi_dispatch(struct terminal *term, uint8_t final) if (term->grid != &term->alt) { term->grid = &term->alt; term->saved_cursor = term->cursor; + + tll_free(term->alt.damage); + tll_free(term->alt.scroll_damage); term_erase(term, 0, term->rows * term->cols); } break; @@ -645,6 +648,9 @@ csi_dispatch(struct terminal *term, uint8_t final) term->scroll_region.start = 0; term->scroll_region.end = term->rows; + tll_free(term->alt.damage); + tll_free(term->alt.scroll_damage); + term_damage_all(term); } break; From dfc9554e89fa741884b2140f0f1c9c2b74da0220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 1 Jul 2019 21:13:24 +0200 Subject: [PATCH 09/50] render: always render cursor --- main.c | 44 +++++++++++++++++++++++++++----------------- terminal.c | 2 -- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/main.c b/main.c index f81771ab..5c2876e6 100644 --- a/main.c +++ b/main.c @@ -260,20 +260,6 @@ grid_render_erase(struct context *c, struct buffer *buf, const struct damage *dm cairo_fill(buf->cairo); wl_surface_damage_buffer(c->wl.surface, x, y, width, height); } - - /* Redraw cursor, if it's inside the erased range */ - if (c->term.cursor.linear >= dmg->range.start - c->term.grid->offset && - c->term.cursor.linear < dmg->range.start - c->term.grid->offset + dmg->range.length) - { - grid_render_update( - c, buf, - &(struct damage){ - .type = DAMAGE_UPDATE, - .range = { - .start = c->term.grid->offset + c->term.cursor.linear, - .length = 1} - }); - } } static void @@ -362,18 +348,19 @@ grid_render_scroll_reverse(struct context *c, struct buffer *buf, static void grid_render(struct context *c) { + struct buffer *buf = shm_get_buffer(c->wl.shm, c->width, c->height); + cairo_set_operator(buf->cairo, CAIRO_OPERATOR_SOURCE); + if (tll_length(c->term.grid->damage) == 0 && tll_length(c->term.grid->scroll_damage) == 0) { - return; + goto render_cursor; } assert(c->width > 0); assert(c->height > 0); - struct buffer *buf = shm_get_buffer(c->wl.shm, c->width, c->height); - cairo_set_operator(buf->cairo, CAIRO_OPERATOR_SOURCE); tll_foreach(c->term.grid->scroll_damage, it) { switch (it->item.type) { @@ -408,6 +395,29 @@ grid_render(struct context *c) tll_remove(c->term.grid->damage, it); } +render_cursor: + ; + + /* TODO: break out to function */ + /* Re-render last cursor cell and current cursor cell */ + static struct cursor last_cursor = {.linear = 0, .col = 0, .row = 0}; + + if (last_cursor.linear != c->term.cursor.linear) { + struct damage prev_cursor = { + .type = DAMAGE_UPDATE, + .range = {.start = c->term.grid->offset + last_cursor.linear, + .length = 1}, + }; + grid_render_update(c, buf, &prev_cursor); + } + + struct damage cursor = { + .type = DAMAGE_UPDATE, + .range = {.start = c->term.grid->offset + c->term.cursor.linear, .length = 1}, + }; + grid_render_update(c, buf, &cursor); + last_cursor = c->term.cursor; + c->term.grid->offset %= c->term.grid->size; //cairo_surface_flush(buf->cairo_surface); diff --git a/terminal.c b/terminal.c index c6129953..952476f3 100644 --- a/terminal.c +++ b/terminal.c @@ -156,8 +156,6 @@ term_cursor_to(struct terminal *term, int row, int col) int new_linear = row * term->cols + col; assert(new_linear < term->rows * term->cols); - term_damage_update(term, term->cursor.linear, 1); - term_damage_update(term, new_linear, 1); term->print_needs_wrap = false; term->cursor.linear = new_linear; From 0954ffdf223c277811776cb1097c85570c8a39d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 1 Jul 2019 21:14:07 +0200 Subject: [PATCH 10/50] vt: no need to clear temporary utf8 buffer after printing it --- vt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/vt.c b/vt.c index f52b6d0f..8328b330 100644 --- a/vt.c +++ b/vt.c @@ -689,7 +689,6 @@ action(struct terminal *term, enum action action, uint8_t c) //LOG_DBG("print: UTF8: %.*s", (int)term->vt.utf8.idx, term->vt.utf8.data); memcpy(cell->c, term->vt.utf8.data, term->vt.utf8.idx); cell->c[term->vt.utf8.idx] = '\0'; - memset(&term->vt.utf8, 0, sizeof(term->vt.utf8)); } else { //LOG_DBG("print: ASCII: %c", c); cell->c[0] = c; From 4b824d824c44c0694f4c3b8dd3ff039038ae76fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 1 Jul 2019 21:14:31 +0200 Subject: [PATCH 11/50] term: 'end' variable is only used in assert() --- terminal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminal.c b/terminal.c index 952476f3..ee46fea3 100644 --- a/terminal.c +++ b/terminal.c @@ -204,7 +204,7 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows tll_foreach(term->grid->damage, it) { int start = it->item.range.start - term->grid->offset; - int end = start + it->item.range.length; + int end __attribute__((unused)) = start + it->item.range.length; if (start < region.start) { assert(end <= region.start); From e6d9eb7c9e20afd8ee135ddc5e94e291e12721e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 1 Jul 2019 21:15:06 +0200 Subject: [PATCH 12/50] main: log resize event as INFO --- main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index 5c2876e6..b389d2da 100644 --- a/main.c +++ b/main.c @@ -482,8 +482,8 @@ resize(struct context *c, int width, int height) }; } - LOG_DBG("resize: %dx%d, grid: cols=%d, rows=%d", - c->width, c->height, c->term.cols, c->term.rows); + LOG_INFO("resize: %dx%d, grid: cols=%d, rows=%d", + c->width, c->height, c->term.cols, c->term.rows); /* Update environment variables */ char cols_s[12], rows_s[12]; From 3d8e140a287875503c0c210744395fd7a40f400e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 2 Jul 2019 12:22:05 +0200 Subject: [PATCH 13/50] render: cache glyph masks Cache rendered glyph masks for each font combo. When rendering text, use these when possible (standard ASCII). For now, use the cached glyph as a mask. This allows colors to just work. It would be faster to cache a completely pre-rendered image, but then we would need one image for each background/foreground combo. --- main.c | 140 +++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 102 insertions(+), 38 deletions(-) diff --git a/main.c b/main.c index b389d2da..1a3458f6 100644 --- a/main.c +++ b/main.c @@ -45,10 +45,16 @@ struct wayland { struct xdg_toplevel *xdg_toplevel; }; +struct glyph_cache { + cairo_scaled_font_t *font; + cairo_surface_t *surf[256]; + cairo_pattern_t *cache[256]; +}; + struct context { bool quit; - cairo_scaled_font_t *fonts[8]; + struct glyph_cache fonts[4]; cairo_font_extents_t fextents; int width; @@ -61,7 +67,6 @@ struct context { bool frame_is_scheduled; }; - static void frame_callback( void *data, struct wl_callback *wl_callback, uint32_t callback_data); @@ -69,11 +74,11 @@ static const struct wl_callback_listener frame_listener = { .done = &frame_callback, }; -static cairo_scaled_font_t * +static struct glyph_cache * attrs_to_font(struct context *c, const struct attributes *attrs) { int idx = attrs->italic << 1 | attrs->bold; - return c->fonts[idx]; + return &c->fonts[idx]; } static void @@ -138,15 +143,9 @@ grid_render_update(struct context *c, struct buffer *buf, const struct damage *d background = swap; } - //LOG_DBG("cell %dx%d dirty: c=0x%02x (%c)", - // row, col, cell->c[0], cell->c[0]); - - cairo_scaled_font_t *font = attrs_to_font(c, &cell->attrs); - cairo_set_scaled_font(buf->cairo, font); + /* Background */ cairo_set_source_rgba( buf->cairo, background.r, background.g, background.b, background.a); - - /* Background */ cairo_rectangle(buf->cairo, x, y, width, height); cairo_fill(buf->cairo); @@ -156,24 +155,45 @@ grid_render_update(struct context *c, struct buffer *buf, const struct damage *d if (cell->attrs.conceal) continue; - cairo_glyph_t *glyphs = NULL; - int num_glyphs = 0; - - cairo_status_t status = cairo_scaled_font_text_to_glyphs( - font, x, y + c->fextents.ascent, - cell->c, strlen(cell->c), &glyphs, &num_glyphs, - NULL, NULL, NULL); - - if (status != CAIRO_STATUS_SUCCESS) { - if (glyphs != NULL) - cairo_glyph_free(glyphs); - continue; - } + struct glyph_cache *cache = attrs_to_font(c, &cell->attrs); cairo_set_source_rgba( buf->cairo, foreground.r, foreground.g, foreground.b, foreground.a); - cairo_show_glyphs(buf->cairo, glyphs, num_glyphs); - cairo_glyph_free(glyphs); + + if (strlen(cell->c) == 1 && cache->cache[(int)cell->c[0]] != NULL) { + cairo_matrix_t matrix; + cairo_matrix_init_translate(&matrix, -x, -y); + + cairo_pattern_t *pat = cache->cache[(int)cell->c[0]]; + cairo_pattern_set_matrix(pat, &matrix); + +#if 1 + cairo_pattern_set_matrix(pat, &matrix); + cairo_mask(buf->cairo, pat); +#else /* TODO: blit image instead - but doesn't (yet) handle colors */ + cairo_set_source(buf->cairo, pat); + cairo_rectangle(buf->cairo, x, y, width, height); + cairo_fill(buf->cairo); +#endif + } else { + cairo_glyph_t *glyphs = NULL; + int num_glyphs = 0; + + cairo_status_t status = cairo_scaled_font_text_to_glyphs( + cache->font, x, y + c->fextents.ascent, + cell->c, strlen(cell->c), &glyphs, &num_glyphs, + NULL, NULL, NULL); + + if (status != CAIRO_STATUS_SUCCESS) { + if (glyphs != NULL) + cairo_glyph_free(glyphs); + continue; + } + + cairo_set_scaled_font(buf->cairo, cache->font); + cairo_show_glyphs(buf->cairo, glyphs, num_glyphs); + cairo_glyph_free(glyphs); + } } wl_surface_damage_buffer( @@ -453,8 +473,6 @@ resize(struct context *c, int width, int height) const size_t normal_old_size = c->term.normal.size; const size_t alt_old_size = c->term.alt.size; - c->term.cell_width = (int)ceil(c->fextents.max_x_advance); - c->term.cell_height = (int)ceil(c->fextents.height); c->term.cols = c->width / c->term.cell_width; c->term.rows = c->height / c->term.cell_height; @@ -802,30 +820,70 @@ main(int argc, const char *const *argv) thrd_create(&keyboard_repeater_id, &keyboard_repeater, &c.term); const char *font_name = "Dina:pixelsize=12"; - c.fonts[0] = font_from_name(font_name); - if (c.fonts[0] == NULL) + c.fonts[0].font = font_from_name(font_name); + if (c.fonts[0].font == NULL) goto out; { char fname[1024]; snprintf(fname, sizeof(fname), "%s:style=bold", font_name); - c.fonts[1] = font_from_name(fname); + c.fonts[1].font = font_from_name(fname); snprintf(fname, sizeof(fname), "%s:style=italic", font_name); - c.fonts[2] = font_from_name(fname); + c.fonts[2].font = font_from_name(fname); snprintf(fname, sizeof(fname), "%s:style=bold italic", font_name); - c.fonts[3] = font_from_name(fname); - - /* TODO; underline */ + c.fonts[3].font = font_from_name(fname); } - cairo_scaled_font_extents(c.fonts[0], &c.fextents); + cairo_scaled_font_extents(c.fonts[0].font, &c.fextents); + c.term.cell_width = (int)ceil(c.fextents.max_x_advance); + c.term.cell_height = (int)ceil(c.fextents.height); LOG_DBG("font: height: %.2f, x-advance: %.2f", c.fextents.height, c.fextents.max_x_advance); assert(c.fextents.max_y_advance == 0); + for (size_t i; i < sizeof(c.fonts) / sizeof(c.fonts[0]); i++) { + cairo_scaled_font_t *font = c.fonts[i].font; + + for (size_t j = 0; j < 256; j++) { + const char text[2] = {(char)j, '\0'}; + + cairo_glyph_t *glyphs = NULL; + int num_glyphs = 0; + + cairo_status_t status = cairo_scaled_font_text_to_glyphs( + font, 0, 0 + c.fextents.ascent, + text, 1, &glyphs, &num_glyphs, NULL, NULL, NULL); + + if (status != CAIRO_STATUS_SUCCESS || num_glyphs != 1) { + if (glyphs != NULL) + cairo_glyph_free(glyphs); + continue; + } + + cairo_surface_t *surf = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, c.term.cell_width, c.term.cell_height); + cairo_t *ca = cairo_create(surf); + + cairo_set_scaled_font(ca, font); + cairo_set_source_rgba(ca, 1.0, 1.0, 1.0, 1.0); + cairo_show_glyphs(ca, glyphs, num_glyphs); + + cairo_glyph_free(glyphs); + cairo_destroy(ca); + + assert(surf != NULL); + assert(cairo_surface_status(surf) == CAIRO_STATUS_SUCCESS); + c.fonts[i].surf[j] = surf; + + cairo_pattern_t *pat = cairo_pattern_create_for_surface(surf); + assert(cairo_pattern_status(pat) == CAIRO_STATUS_SUCCESS); + c.fonts[i].cache[j] = pat; + } + } + if (c.term.ptmx == -1) { LOG_ERRNO("failed to open pseudo terminal"); goto out; @@ -988,8 +1046,14 @@ out: free(c.term.alt.cells); for (size_t i = 0; i < sizeof(c.fonts) / sizeof(c.fonts[0]); i++) { - if (c.fonts[i] != NULL) - cairo_scaled_font_destroy(c.fonts[i]); + struct glyph_cache *fcache = &c.fonts[i]; + if (fcache->font != NULL) + cairo_scaled_font_destroy(fcache->font); + + for (size_t i = 0; i < sizeof(fcache->cache) / sizeof(fcache->cache[0]); i++) { + if (fcache->cache[i] != NULL) + cairo_pattern_destroy(fcache->cache[i]); + } } if (c.term.ptmx != -1) From 9392cfbbf37b0034da4d382b25d5387ab445dc7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 2 Jul 2019 12:50:18 +0200 Subject: [PATCH 14/50] render: draw pre-rendered glyph OVER background This allows us to render the background dynamically. Note that pre-rendered glyphs still have wrong foreground color. --- main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/main.c b/main.c index 1a3458f6..81cd4656 100644 --- a/main.c +++ b/main.c @@ -171,6 +171,7 @@ grid_render_update(struct context *c, struct buffer *buf, const struct damage *d cairo_pattern_set_matrix(pat, &matrix); cairo_mask(buf->cairo, pat); #else /* TODO: blit image instead - but doesn't (yet) handle colors */ + cairo_set_operator(buf->cairo, CAIRO_OPERATOR_OVER); cairo_set_source(buf->cairo, pat); cairo_rectangle(buf->cairo, x, y, width, height); cairo_fill(buf->cairo); From 607df23a92ca904a14b8858d5128703b144d4be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 2 Jul 2019 12:51:43 +0200 Subject: [PATCH 15/50] render: fix cursor not always being rendered In case the only difference between this and the last frame was cursor movement, the cursor was not rendered. --- main.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/main.c b/main.c index 81cd4656..feaa7f25 100644 --- a/main.c +++ b/main.c @@ -369,18 +369,27 @@ grid_render_scroll_reverse(struct context *c, struct buffer *buf, static void grid_render(struct context *c) { - struct buffer *buf = shm_get_buffer(c->wl.shm, c->width, c->height); - cairo_set_operator(buf->cairo, CAIRO_OPERATOR_SOURCE); + static struct cursor last_cursor = {.linear = 0, .col = 0, .row = 0}; if (tll_length(c->term.grid->damage) == 0 && - tll_length(c->term.grid->scroll_damage) == 0) + tll_length(c->term.grid->scroll_damage) == 0 && + last_cursor.linear == c->term.cursor.linear) { - goto render_cursor; + return; } assert(c->width > 0); assert(c->height > 0); + struct buffer *buf = shm_get_buffer(c->wl.shm, c->width, c->height); + cairo_set_operator(buf->cairo, CAIRO_OPERATOR_SOURCE); + + static struct buffer *last_buf = NULL; + if (last_buf != buf) { + if (last_buf != NULL) + LOG_WARN("new buffer"); + last_buf = buf; + } tll_foreach(c->term.grid->scroll_damage, it) { @@ -416,12 +425,8 @@ grid_render(struct context *c) tll_remove(c->term.grid->damage, it); } -render_cursor: - ; - /* TODO: break out to function */ /* Re-render last cursor cell and current cursor cell */ - static struct cursor last_cursor = {.linear = 0, .col = 0, .row = 0}; if (last_cursor.linear != c->term.cursor.linear) { struct damage prev_cursor = { From d04029d7035005b9747b8b4b3a48c61885c99052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 2 Jul 2019 19:44:02 +0200 Subject: [PATCH 16/50] render: revert glyph caching Instead, try to group as long sequences of glyphs as possible, as fewer calls to cairo_show_glyphs() is *much* faster. --- main.c | 177 ++++++++++++++++++++++++--------------------------------- 1 file changed, 74 insertions(+), 103 deletions(-) diff --git a/main.c b/main.c index feaa7f25..1e835a5b 100644 --- a/main.c +++ b/main.c @@ -45,16 +45,10 @@ struct wayland { struct xdg_toplevel *xdg_toplevel; }; -struct glyph_cache { - cairo_scaled_font_t *font; - cairo_surface_t *surf[256]; - cairo_pattern_t *cache[256]; -}; - struct context { bool quit; - struct glyph_cache fonts[4]; + cairo_scaled_font_t *fonts[4]; cairo_font_extents_t fextents; int width; @@ -74,13 +68,22 @@ static const struct wl_callback_listener frame_listener = { .done = &frame_callback, }; -static struct glyph_cache * +static cairo_scaled_font_t * attrs_to_font(struct context *c, const struct attributes *attrs) { int idx = attrs->italic << 1 | attrs->bold; - return &c->fonts[idx]; + return c->fonts[idx]; } +struct glyph_sequence { + cairo_glyph_t glyphs[10000]; + cairo_glyph_t *g; + int count; + + struct attributes attrs; + struct rgba foreground; +}; + static void grid_render_update(struct context *c, struct buffer *buf, const struct damage *dmg) { @@ -103,23 +106,25 @@ grid_render_update(struct context *c, struct buffer *buf, const struct damage *d const int cols = c->term.cols; + struct glyph_sequence gseq = {.g = gseq.glyphs}; + for (int linear_cursor = start, row = ((start - c->term.grid->offset) % c->term.grid->size) / cols, col = ((start - c->term.grid->offset) % c->term.grid->size) % cols; linear_cursor < start + length; linear_cursor++, - //col = (col + 1) % cols, col = col + 1 >= c->term.cols ? 0 : col + 1, row += col == 0 ? 1 : 0) { //LOG_DBG("UPDATE: %d (%dx%d)", linear_cursor, row, col); - //assert(linear_cursor < c->term.grid->size); - const struct cell *cell = &c->term.grid->cells[linear_cursor % c->term.grid->size]; + const struct cell *cell + = &c->term.grid->cells[linear_cursor % c->term.grid->size]; - bool has_cursor = c->term.cursor.linear == linear_cursor - c->term.grid->offset; - //bool has_cursor = false; + /* Cursor here? */ + bool has_cursor + = c->term.cursor.linear == linear_cursor - c->term.grid->offset; int x = col * c->term.cell_width; int y = row * c->term.cell_height; @@ -155,46 +160,58 @@ grid_render_update(struct context *c, struct buffer *buf, const struct damage *d if (cell->attrs.conceal) continue; - struct glyph_cache *cache = attrs_to_font(c, &cell->attrs); + /* + * cairo_show_glyphs() apparently works *much* faster when + * called once with a large array of glyphs, compared to + * multiple calls with a single glyph. + * + * So, collect glyphs until cell attributes change, then we + * 'flush' (render) the glyphs. + */ - cairo_set_source_rgba( - buf->cairo, foreground.r, foreground.g, foreground.b, foreground.a); + if (memcmp(&cell->attrs, &gseq.attrs, sizeof(cell->attrs)) != 0 || + gseq.count >= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0]) - 10 || + memcmp(&gseq.foreground, &foreground, sizeof(foreground)) != 0) + { + if (gseq.count >= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0]) - 10) + LOG_WARN("hit glyph limit"); + cairo_set_scaled_font(buf->cairo, attrs_to_font(c, &gseq.attrs)); + cairo_set_source_rgba( + buf->cairo, gseq.foreground.r, gseq.foreground.g, + gseq.foreground.b, gseq.foreground.a); - if (strlen(cell->c) == 1 && cache->cache[(int)cell->c[0]] != NULL) { - cairo_matrix_t matrix; - cairo_matrix_init_translate(&matrix, -x, -y); - - cairo_pattern_t *pat = cache->cache[(int)cell->c[0]]; - cairo_pattern_set_matrix(pat, &matrix); - -#if 1 - cairo_pattern_set_matrix(pat, &matrix); - cairo_mask(buf->cairo, pat); -#else /* TODO: blit image instead - but doesn't (yet) handle colors */ cairo_set_operator(buf->cairo, CAIRO_OPERATOR_OVER); - cairo_set_source(buf->cairo, pat); - cairo_rectangle(buf->cairo, x, y, width, height); - cairo_fill(buf->cairo); -#endif - } else { - cairo_glyph_t *glyphs = NULL; - int num_glyphs = 0; + cairo_show_glyphs(buf->cairo, gseq.glyphs, gseq.count); - cairo_status_t status = cairo_scaled_font_text_to_glyphs( - cache->font, x, y + c->fextents.ascent, - cell->c, strlen(cell->c), &glyphs, &num_glyphs, - NULL, NULL, NULL); - - if (status != CAIRO_STATUS_SUCCESS) { - if (glyphs != NULL) - cairo_glyph_free(glyphs); - continue; - } - - cairo_set_scaled_font(buf->cairo, cache->font); - cairo_show_glyphs(buf->cairo, glyphs, num_glyphs); - cairo_glyph_free(glyphs); + gseq.g = gseq.glyphs; + gseq.count = 0; + gseq.attrs = cell->attrs; + gseq.foreground = foreground; } + + int new_glyphs + = sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0]) - gseq.count; + + cairo_status_t status = cairo_scaled_font_text_to_glyphs( + attrs_to_font(c, &cell->attrs), x, y + c->fextents.ascent, + cell->c, strlen(cell->c), &gseq.g, &new_glyphs, + NULL, NULL, NULL); + + if (status != CAIRO_STATUS_SUCCESS) + continue; + + gseq.g += new_glyphs; + gseq.count += new_glyphs; + assert(gseq.count <= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0])); + } + + if (gseq.count > 0) { + cairo_set_scaled_font(buf->cairo, attrs_to_font(c, &gseq.attrs)); + cairo_set_source_rgba( + buf->cairo, gseq.foreground.r, gseq.foreground.g, + gseq.foreground.b, gseq.foreground.a); + cairo_set_operator(buf->cairo, CAIRO_OPERATOR_OVER); + cairo_show_glyphs(buf->cairo, gseq.glyphs, gseq.count); } wl_surface_damage_buffer( @@ -826,23 +843,23 @@ main(int argc, const char *const *argv) thrd_create(&keyboard_repeater_id, &keyboard_repeater, &c.term); const char *font_name = "Dina:pixelsize=12"; - c.fonts[0].font = font_from_name(font_name); - if (c.fonts[0].font == NULL) + c.fonts[0] = font_from_name(font_name); + if (c.fonts[0] == NULL) goto out; { char fname[1024]; snprintf(fname, sizeof(fname), "%s:style=bold", font_name); - c.fonts[1].font = font_from_name(fname); + c.fonts[1] = font_from_name(fname); snprintf(fname, sizeof(fname), "%s:style=italic", font_name); - c.fonts[2].font = font_from_name(fname); + c.fonts[2] = font_from_name(fname); snprintf(fname, sizeof(fname), "%s:style=bold italic", font_name); - c.fonts[3].font = font_from_name(fname); + c.fonts[3] = font_from_name(fname); } - cairo_scaled_font_extents(c.fonts[0].font, &c.fextents); + cairo_scaled_font_extents(c.fonts[0], &c.fextents); c.term.cell_width = (int)ceil(c.fextents.max_x_advance); c.term.cell_height = (int)ceil(c.fextents.height); @@ -850,46 +867,6 @@ main(int argc, const char *const *argv) c.fextents.height, c.fextents.max_x_advance); assert(c.fextents.max_y_advance == 0); - for (size_t i; i < sizeof(c.fonts) / sizeof(c.fonts[0]); i++) { - cairo_scaled_font_t *font = c.fonts[i].font; - - for (size_t j = 0; j < 256; j++) { - const char text[2] = {(char)j, '\0'}; - - cairo_glyph_t *glyphs = NULL; - int num_glyphs = 0; - - cairo_status_t status = cairo_scaled_font_text_to_glyphs( - font, 0, 0 + c.fextents.ascent, - text, 1, &glyphs, &num_glyphs, NULL, NULL, NULL); - - if (status != CAIRO_STATUS_SUCCESS || num_glyphs != 1) { - if (glyphs != NULL) - cairo_glyph_free(glyphs); - continue; - } - - cairo_surface_t *surf = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, c.term.cell_width, c.term.cell_height); - cairo_t *ca = cairo_create(surf); - - cairo_set_scaled_font(ca, font); - cairo_set_source_rgba(ca, 1.0, 1.0, 1.0, 1.0); - cairo_show_glyphs(ca, glyphs, num_glyphs); - - cairo_glyph_free(glyphs); - cairo_destroy(ca); - - assert(surf != NULL); - assert(cairo_surface_status(surf) == CAIRO_STATUS_SUCCESS); - c.fonts[i].surf[j] = surf; - - cairo_pattern_t *pat = cairo_pattern_create_for_surface(surf); - assert(cairo_pattern_status(pat) == CAIRO_STATUS_SUCCESS); - c.fonts[i].cache[j] = pat; - } - } - if (c.term.ptmx == -1) { LOG_ERRNO("failed to open pseudo terminal"); goto out; @@ -1052,14 +1029,8 @@ out: free(c.term.alt.cells); for (size_t i = 0; i < sizeof(c.fonts) / sizeof(c.fonts[0]); i++) { - struct glyph_cache *fcache = &c.fonts[i]; - if (fcache->font != NULL) - cairo_scaled_font_destroy(fcache->font); - - for (size_t i = 0; i < sizeof(fcache->cache) / sizeof(fcache->cache[0]); i++) { - if (fcache->cache[i] != NULL) - cairo_pattern_destroy(fcache->cache[i]); - } + if (c.fonts[i] != NULL) + cairo_scaled_font_destroy(c.fonts[i]); } if (c.term.ptmx != -1) From e17d297dca2ec2402f904a495cd5dd3d5bace032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 2 Jul 2019 19:45:17 +0200 Subject: [PATCH 17/50] term: if the damage list gets too long, replace with a full redraw --- terminal.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/terminal.c b/terminal.c index ee46fea3..2aac7f6e 100644 --- a/terminal.c +++ b/terminal.c @@ -48,10 +48,18 @@ static void term_damage_update_or_erase(struct terminal *term, enum damage_type damage_type, int start, int length) { +#if 1 + if (tll_length(term->grid->damage) > 1024) { + term_damage_all(term); + return; + } +#endif + struct damage dmg = { .type = damage_type, .range = {.start = term->grid->offset + start, .length = length}, }; + if (damage_merge_range(term, &dmg)) return; From dd4647e9ff792c93e596af87b9ff762e5d4580b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 2 Jul 2019 21:43:49 +0200 Subject: [PATCH 18/50] term: simplify horizontal cursor movement Since horizontal cursor movement is clamped to the current line, we can calculate the new linear cursor without any expensive multiplications and/or divisions. --- terminal.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/terminal.c b/terminal.c index 2aac7f6e..f11ddad6 100644 --- a/terminal.c +++ b/terminal.c @@ -175,14 +175,18 @@ void term_cursor_left(struct terminal *term, int count) { int move_amount = min(term->cursor.col, count); - term_cursor_to(term, term->cursor.row, term->cursor.col - move_amount); + term->cursor.linear -= move_amount; + term->cursor.col -= move_amount; + term->print_needs_wrap = false; } void term_cursor_right(struct terminal *term, int count) { int move_amount = min(term->cols - term->cursor.col - 1, count); - term_cursor_to(term, term->cursor.row, term->cursor.col + move_amount); + term->cursor.linear += move_amount; + term->cursor.col += move_amount; + term->print_needs_wrap = false; } void From ed68eafdf037516b4a20b3efbd2510fe6facfbae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 2 Jul 2019 21:44:58 +0200 Subject: [PATCH 19/50] vt: simplify CLEAR action; don't memset() more than necessary --- vt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vt.c b/vt.c index 8328b330..5d04008b 100644 --- a/vt.c +++ b/vt.c @@ -664,9 +664,9 @@ action(struct terminal *term, enum action action, uint8_t c) case ACTION_CLEAR: memset(&term->vt.params, 0, sizeof(term->vt.params)); - memset(&term->vt.intermediates, 0, sizeof(term->vt.intermediates)); - memset(&term->vt.osc, 0, sizeof(term->vt.osc)); - memset(&term->vt.utf8, 0, sizeof(term->vt.utf8)); + term->vt.intermediates.idx = 0; + term->vt.osc.idx = 0; + term->vt.utf8.idx = 0; break; case ACTION_PRINT: { From 9682e15deb2037b4da2b9796be81bd3f80e1a153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 2 Jul 2019 22:18:25 +0200 Subject: [PATCH 20/50] term: "cache" pointer to current line This adds a pointer to the first cell on the current line. This pointer must be updated every time the row changes. The advantage is mainly that PRINT doesn't have to call grid_get_range(), which is fairly expensive. --- terminal.c | 12 ++++++++++++ terminal.h | 1 + vt.c | 6 +----- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/terminal.c b/terminal.c index f11ddad6..25e1246e 100644 --- a/terminal.c +++ b/terminal.c @@ -169,6 +169,12 @@ term_cursor_to(struct terminal *term, int row, int col) term->cursor.linear = new_linear; term->cursor.col = col; term->cursor.row = row; + + size_t len = term->cols; + term->grid->cur_line = grid_get_range( + term->grid, term->cursor.linear - col, &len); + + assert(len == (size_t)term->cols); } void @@ -253,6 +259,12 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows max(0, region.end - rows) * term->cols, 0, rows * term->cols); term_damage_scroll(term, DAMAGE_SCROLL, region, rows); + + size_t len = term->cols; + term->grid->cur_line = grid_get_range( + term->grid, term->cursor.linear - term->cursor.col, &len); + + assert(len == (size_t)term->cols); } void diff --git a/terminal.h b/terminal.h index 4efaab9f..66f6bde0 100644 --- a/terminal.h +++ b/terminal.h @@ -66,6 +66,7 @@ struct grid { int offset; struct cell *cells; + struct cell *cur_line; tll(struct damage) damage; tll(struct damage) scroll_damage; diff --git a/vt.c b/vt.c index 5d04008b..f2d85d4b 100644 --- a/vt.c +++ b/vt.c @@ -678,11 +678,7 @@ action(struct terminal *term, enum action action, uint8_t c) term_cursor_to(term, term->cursor.row + 1, 0); } - size_t cell_count = 1; - struct cell *cell = grid_get_range( - term->grid, term->cursor.linear, &cell_count); - assert(cell_count == 1); - + struct cell *cell = &term->grid->cur_line[term->cursor.col]; term_damage_update(term, term->cursor.linear, 1); if (term->vt.utf8.idx > 0) { From cc2931007c9313db46871e1341f55dea358d4b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 2 Jul 2019 22:19:43 +0200 Subject: [PATCH 21/50] csi: repair DCH (delete character) The logic was simply broken. This fixes it, and also converts it to use the new "current line" pointer. --- csi.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/csi.c b/csi.c index 2cb5e7a5..def01848 100644 --- a/csi.c +++ b/csi.c @@ -452,24 +452,24 @@ csi_dispatch(struct terminal *term, uint8_t final) case 'P': { /* DCH: Delete character */ - int count = param_get(term, 0, 1); - /* Only delete up to the right margin */ - const int max_end = term_cursor_linear( - term, term->cursor.row, term->cols); + /* Number of characters to delete */ + int count = min( + param_get(term, 0, 1), term->cols - term->cursor.col); - int start = term->cursor.linear; - int end = min(start + count, max_end); + /* Number of characters left after deletion (on current line) */ + int remaining = term->cols - (term->cursor.col + count); - /* Erase the requested number of characters */ - term_erase(term, start, end); - - /* Move remaining (up til the right margin) characters */ - int remaining = max_end - end; - memmove(&term->grid->cells[start], - &term->grid->cells[end], - remaining * sizeof(term->grid->cells[0])); + /* 'Delete' characters by moving the remaining ones */ + memmove(&term->grid->cur_line[term->cursor.col], + &term->grid->cur_line[term->cursor.col + count], + remaining * sizeof(term->grid->cur_line[0])); term_damage_update(term, term->cursor.linear, remaining); + + /* Erase the remainder of the line */ + term_erase( + term, term->cursor.linear + remaining, + term->cursor.linear + remaining + count); break; } From 8ed723945802c2e2bbe18bb5653c6025ed14cc6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 2 Jul 2019 22:23:54 +0200 Subject: [PATCH 22/50] csi: re-set cursor when switching between alt and normal screen This forces a refresh of the "current line" pointer. --- csi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/csi.c b/csi.c index def01848..931ff36a 100644 --- a/csi.c +++ b/csi.c @@ -587,6 +587,8 @@ csi_dispatch(struct terminal *term, uint8_t final) term->grid = &term->alt; term->saved_cursor = term->cursor; + term_cursor_to(term, term->cursor.row, term->cursor.col); + tll_free(term->alt.damage); tll_free(term->alt.scroll_damage); term_erase(term, 0, term->rows * term->cols); @@ -644,6 +646,8 @@ csi_dispatch(struct terminal *term, uint8_t final) term->cursor = term->saved_cursor; + term_cursor_to(term, term->cursor.row, term->cursor.col); + /* Should these be restored from saved values? */ term->scroll_region.start = 0; term->scroll_region.end = term->rows; From d5158b2432dfdd64c464b3da733dae4ab31e4b52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 2 Jul 2019 22:31:52 +0200 Subject: [PATCH 23/50] render: track last cursor position in absolute values This seems to fix an issue where we sometimes saw "ghost" cursors when scrolling. --- main.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/main.c b/main.c index 1e835a5b..24371687 100644 --- a/main.c +++ b/main.c @@ -386,11 +386,11 @@ grid_render_scroll_reverse(struct context *c, struct buffer *buf, static void grid_render(struct context *c) { - static struct cursor last_cursor = {.linear = 0, .col = 0, .row = 0}; + static int last_cursor; if (tll_length(c->term.grid->damage) == 0 && tll_length(c->term.grid->scroll_damage) == 0 && - last_cursor.linear == c->term.cursor.linear) + last_cursor == c->term.cursor.linear) { return; } @@ -444,12 +444,11 @@ grid_render(struct context *c) /* TODO: break out to function */ /* Re-render last cursor cell and current cursor cell */ - - if (last_cursor.linear != c->term.cursor.linear) { + /* Make sure previous cursor is refreshed (to avoid "ghost" cursors) */ + if (last_cursor != c->term.cursor.linear) { struct damage prev_cursor = { .type = DAMAGE_UPDATE, - .range = {.start = c->term.grid->offset + last_cursor.linear, - .length = 1}, + .range = {.start = last_cursor, .length = 1}, }; grid_render_update(c, buf, &prev_cursor); } @@ -459,7 +458,7 @@ grid_render(struct context *c) .range = {.start = c->term.grid->offset + c->term.cursor.linear, .length = 1}, }; grid_render_update(c, buf, &cursor); - last_cursor = c->term.cursor; + last_cursor = c->term.grid->offset + c->term.cursor.linear; c->term.grid->offset %= c->term.grid->size; From dfaa5f56407f4035dcae6445ed5ffec52782258c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 2 Jul 2019 22:32:25 +0200 Subject: [PATCH 24/50] main: make sure cursor is inside the new row/col limits on resize --- main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/main.c b/main.c index 24371687..b82fa7a7 100644 --- a/main.c +++ b/main.c @@ -546,6 +546,11 @@ resize(struct context *c, int width, int height) if (c->term.scroll_region.end == old_rows) c->term.scroll_region.end = c->term.rows; + term_cursor_to( + &c->term, + min(c->term.cursor.row, c->term.rows), + min(c->term.cursor.col, c->term.cols)); + term_damage_all(&c->term); if (!c->frame_is_scheduled) From 0fef48c1fd7f2751ead2b1c619d56ea7fa3c72b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 08:37:16 +0200 Subject: [PATCH 25/50] csi: up to application to restore scroll region on alt switch? --- csi.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/csi.c b/csi.c index 931ff36a..dd292b79 100644 --- a/csi.c +++ b/csi.c @@ -645,13 +645,8 @@ csi_dispatch(struct terminal *term, uint8_t final) term->grid = &term->normal; term->cursor = term->saved_cursor; - term_cursor_to(term, term->cursor.row, term->cursor.col); - /* Should these be restored from saved values? */ - term->scroll_region.start = 0; - term->scroll_region.end = term->rows; - tll_free(term->alt.damage); tll_free(term->alt.scroll_damage); From e6c27645fab5b5c6319b180ae6052cd507c6ab4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 09:23:42 +0200 Subject: [PATCH 26/50] term: optimize damage list after scroll This optimizes the normal scrolling case; updates are done at the bottom of the screen and then scrolled up. In this case, the damage list will be a more or less sorted list of updates, oldest first. If we're scrolling a lot, the oldest updates will eventually scroll off screen. In this case, there's no need to keep them in the damage list. So, when scrolling, loop the damage list and adjust/remove updates that have scrolled off screen (either partially, or completely). Stop looping as soon as we see an update that has *not* scrolled off screen. Note that this means there may be update items in the damage list that *has* scrolled off screen. This is especially true for random screen writes (i.e. typical to some synthetic benchmarks). --- terminal.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/terminal.c b/terminal.c index 25e1246e..2c96e3cf 100644 --- a/terminal.c +++ b/terminal.c @@ -119,6 +119,23 @@ term_damage_scroll(struct terminal *term, enum damage_type damage_type, struct scroll_region region, int lines) { //damage_adjust_after_scroll(term, damage_type, region, lines); + if (damage_type == DAMAGE_SCROLL) { + tll_foreach(term->grid->damage, it) { + int start = it->item.range.start; + int length = it->item.range.length; + + if (start < term->grid->offset) { + int end = start + length; + if (end >= term->grid->offset) { + it->item.range.start = term->grid->offset; + it->item.range.length = end - it->item.range.start; + } else + tll_remove(term->grid->damage, it); + } else + break; + } + } else + assert(false); if (tll_length(term->grid->scroll_damage) > 0) { struct damage *dmg = &tll_back(term->grid->scroll_damage); From 2bca2a894ed9f4a4ce8ec15b30cc3e2ed487cc07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 09:46:13 +0200 Subject: [PATCH 27/50] main: add --font/-f command line option --- main.c | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/main.c b/main.c index b82fa7a7..88c08998 100644 --- a/main.c +++ b/main.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -408,7 +409,6 @@ grid_render(struct context *c) last_buf = buf; } - tll_foreach(c->term.grid->scroll_damage, it) { switch (it->item.type) { case DAMAGE_SCROLL: @@ -803,10 +803,40 @@ keyboard_repeater(void *arg) } int -main(int argc, const char *const *argv) +main(int argc, char *const *argv) { int ret = EXIT_FAILURE; + static const struct option longopts[] = { + {"font", required_argument, 0, 'f'}, + {NULL, no_argument, 0, 0}, + }; + + const char *font_name = "Dina:pixelsize=12"; + + while (true) { + int c = getopt_long(argc, argv, ":f:h", longopts, NULL); + if (c == -1) + break; + + switch (c) { + case 'f': + font_name = optarg; + break; + + case 'h': + break; + + case ':': + fprintf(stderr, "error: -%c: missing required argument\n", optopt); + return EXIT_FAILURE; + + case '?': + fprintf(stderr, "error: -%c: invalid option\n", optopt); + return EXIT_FAILURE; + } + } + setlocale(LC_ALL, ""); int repeat_pipe_fds[2] = {-1, -1}; @@ -846,7 +876,6 @@ main(int argc, const char *const *argv) thrd_t keyboard_repeater_id; thrd_create(&keyboard_repeater_id, &keyboard_repeater, &c.term); - const char *font_name = "Dina:pixelsize=12"; c.fonts[0] = font_from_name(font_name); if (c.fonts[0] == NULL) goto out; From 048f619b1916f844eca0366b762ed1b87128a27f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 09:46:39 +0200 Subject: [PATCH 28/50] render: fix last-cursor comparison --- main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.c b/main.c index 88c08998..e3612d2e 100644 --- a/main.c +++ b/main.c @@ -391,7 +391,7 @@ grid_render(struct context *c) if (tll_length(c->term.grid->damage) == 0 && tll_length(c->term.grid->scroll_damage) == 0 && - last_cursor == c->term.cursor.linear) + last_cursor == c->term.grid->offset + c->term.cursor.linear) { return; } From a7a28ff5816c0f80a439c4ad828c6f0028493b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 10:45:49 +0200 Subject: [PATCH 29/50] scrolling: initial reverse scrolling support - no scroll regions --- grid.c | 23 +++++++++++++---------- grid.h | 6 +++--- main.c | 19 +++++++++++++++---- terminal.c | 50 ++++++++++++++++---------------------------------- 4 files changed, 47 insertions(+), 51 deletions(-) diff --git a/grid.c b/grid.c index a21e0c88..99e66665 100644 --- a/grid.c +++ b/grid.c @@ -8,12 +8,15 @@ #include "log.h" struct cell * -grid_get_range(struct grid *grid, size_t start, size_t *length) +grid_get_range(struct grid *grid, int start, int *length) { #define min(x, y) ((x) < (y) ? (x) : (y)) assert(*length <= grid->size); - size_t real_start = (grid->offset + start) % grid->size; + int real_start = (grid->offset + start) % grid->size; + if (real_start < 0) + real_start += grid->size; + assert(real_start >= 0); assert(real_start < grid->size); *length = min(*length, grid->size - real_start); @@ -24,11 +27,11 @@ grid_get_range(struct grid *grid, size_t start, size_t *length) } void -grid_memset(struct grid *grid, size_t start, int c, size_t length) +grid_memset(struct grid *grid, int start, int c, int length) { - size_t left = length; + int left = length; while (left > 0) { - size_t count = left; + int count = left; struct cell *cells = grid_get_range(grid, start, &count); assert(count > 0); @@ -42,14 +45,14 @@ grid_memset(struct grid *grid, size_t start, int c, size_t length) } void -grid_memmove(struct grid *grid, size_t dst, size_t src, size_t length) +grid_memmove(struct grid *grid, int dst, int src, int length) { - size_t left = length; - size_t copy_idx = 0; + int left = length; + int copy_idx = 0; struct cell copy[left]; while (left > 0) { - size_t count = left; + int count = left; struct cell *src_cells = grid_get_range(grid, src, &count); memcpy(©[copy_idx], src_cells, count * sizeof(copy[0])); @@ -63,7 +66,7 @@ grid_memmove(struct grid *grid, size_t dst, size_t src, size_t length) copy_idx = 0; while (left > 0) { - size_t count = left; + int count = left; struct cell *dst_cells = grid_get_range(grid, dst, &count); memcpy(dst_cells, ©[copy_idx], count * sizeof(copy[0])); diff --git a/grid.h b/grid.h index 275d81bc..59d9e58b 100644 --- a/grid.h +++ b/grid.h @@ -3,6 +3,6 @@ #include #include "terminal.h" -struct cell *grid_get_range(struct grid *grid, size_t start, size_t *length); -void grid_memset(struct grid *grid, size_t start, int c, size_t length); -void grid_memmove(struct grid *grid, size_t dst, size_t src, size_t length); +struct cell *grid_get_range(struct grid *grid, int start, int *length); +void grid_memset(struct grid *grid, int start, int c, int length); +void grid_memmove(struct grid *grid, int dst, int src, int length); diff --git a/main.c b/main.c index e3612d2e..460f36e4 100644 --- a/main.c +++ b/main.c @@ -118,10 +118,19 @@ grid_render_update(struct context *c, struct buffer *buf, const struct damage *d row += col == 0 ? 1 : 0) { - //LOG_DBG("UPDATE: %d (%dx%d)", linear_cursor, row, col); + assert(row >= 0); + assert(row < c->term.rows); + assert(col >= 0); + assert(col < c->term.cols); - const struct cell *cell - = &c->term.grid->cells[linear_cursor % c->term.grid->size]; + int cell_idx = linear_cursor % c->term.grid->size; + if (cell_idx < 0) + cell_idx += c->term.grid->size; + + assert(cell_idx >= 0); + assert(cell_idx < c->term.rows * c->term.cols); + + const struct cell *cell = &c->term.grid->cells[cell_idx]; /* Cursor here? */ bool has_cursor @@ -376,7 +385,7 @@ grid_render_scroll_reverse(struct context *c, struct buffer *buf, struct damage erase = { .type = DAMAGE_ERASE, .range = { - .start = dmg->scroll.region.start * cols, + .start = c->term.grid->offset + dmg->scroll.region.start * cols, .length = min(dmg->scroll.region.end - dmg->scroll.region.start, dmg->scroll.lines) * cols, }, @@ -461,6 +470,8 @@ grid_render(struct context *c) last_cursor = c->term.grid->offset + c->term.cursor.linear; c->term.grid->offset %= c->term.grid->size; + if (c->term.grid->offset < 0) + c->term.grid->offset += c->term.grid->size; //cairo_surface_flush(buf->cairo_surface); wl_surface_attach(c->wl.surface, buf->wl_buf, 0, 0); diff --git a/terminal.c b/terminal.c index 2c96e3cf..d7eeba15 100644 --- a/terminal.c +++ b/terminal.c @@ -134,8 +134,7 @@ term_damage_scroll(struct terminal *term, enum damage_type damage_type, } else break; } - } else - assert(false); + } if (tll_length(term->grid->scroll_damage) > 0) { struct damage *dmg = &tll_back(term->grid->scroll_damage); @@ -187,11 +186,11 @@ term_cursor_to(struct terminal *term, int row, int col) term->cursor.col = col; term->cursor.row = row; - size_t len = term->cols; + int len = term->cols; term->grid->cur_line = grid_get_range( term->grid, term->cursor.linear - col, &len); - assert(len == (size_t)term->cols); + assert(len == term->cols); } void @@ -277,11 +276,11 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows term_damage_scroll(term, DAMAGE_SCROLL, region, rows); - size_t len = term->cols; + int len = term->cols; term->grid->cur_line = grid_get_range( term->grid, term->cursor.linear - term->cursor.col, &len); - assert(len == (size_t)term->cols); + assert(len == term->cols); } void @@ -294,37 +293,20 @@ void term_scroll_reverse_partial(struct terminal *term, struct scroll_region region, int rows) { - if (rows >= region.end - region.start) { - assert(false && "todo"); - return; - } - -#if 0 - int cell_dst = (region.start + rows) * term->cols; - int cell_src = (region.start + 0) * term->cols; - int cell_count = (region.end - region.start - rows) * term->cols; - - LOG_DBG("moving %d lines from row %d to row %d", cell_count / term->cols, - cell_src / term->cols, cell_dst / term->cols); - - const int bytes = cell_count * sizeof(term->grid->cells[0]); - memmove( - &term->grid->cells[cell_dst], &term->grid->cells[cell_src], - bytes); - - memset(&term->grid->cells[cell_src], 0, - rows * term->cols * sizeof(term->grid->cells[0])); - - term_damage_scroll(term, DAMAGE_SCROLL_REVERSE, region, rows); -#else - /* TODO */ - assert(false); - assert(region.start == 0 && region.end == 0); - assert(rows < term->rows); + assert(region.start == 0); + assert(region.end == term->rows); term->grid->offset -= rows * term->cols; + + grid_memset(term->grid, region.start * term->cols, 0, rows * term->cols); + term_damage_scroll(term, DAMAGE_SCROLL_REVERSE, region, rows); -#endif + + int len = term->cols; + term->grid->cur_line = grid_get_range( + term->grid, term->cursor.linear - term->cursor.col, &len); + + assert(len == term->cols); } void From 5e8c75aa38ee0fc394556e0759b42b1713948412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 14:14:46 +0200 Subject: [PATCH 30/50] csi: implement 'civis' (show/hide cursor) --- csi.c | 4 ++-- main.c | 3 ++- terminal.h | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/csi.c b/csi.c index dd292b79..57da1c06 100644 --- a/csi.c +++ b/csi.c @@ -567,7 +567,7 @@ csi_dispatch(struct terminal *term, uint8_t final) break; case 25: - LOG_WARN("unimplemented: civis"); + term->hide_cursor = false; break; case 1000: @@ -625,7 +625,7 @@ csi_dispatch(struct terminal *term, uint8_t final) break; case 25: - LOG_WARN("unimplemented: civis"); + term->hide_cursor = true; break; case 1000: diff --git a/main.c b/main.c index 460f36e4..a29f5d8c 100644 --- a/main.c +++ b/main.c @@ -134,7 +134,8 @@ grid_render_update(struct context *c, struct buffer *buf, const struct damage *d /* Cursor here? */ bool has_cursor - = c->term.cursor.linear == linear_cursor - c->term.grid->offset; + = (!c->term.hide_cursor && + (c->term.cursor.linear == linear_cursor - c->term.grid->offset)); int x = col * c->term.cell_width; int y = row * c->term.cell_height; diff --git a/terminal.h b/terminal.h index 66f6bde0..a96b1b70 100644 --- a/terminal.h +++ b/terminal.h @@ -131,6 +131,7 @@ struct terminal { pid_t slave; int ptmx; + bool hide_cursor; enum decckm decckm; enum keypad_mode keypad_mode; bool bracketed_paste; From 70743fddf4bd390e684f56d2c974e0b9c3507a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 14:15:23 +0200 Subject: [PATCH 31/50] scrolling: hopefully fix bad damage update of items outside scrolling region --- terminal.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/terminal.c b/terminal.c index d7eeba15..df74e9db 100644 --- a/terminal.c +++ b/terminal.c @@ -240,8 +240,8 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows int start = it->item.range.start - term->grid->offset; int end __attribute__((unused)) = start + it->item.range.length; - if (start < region.start) { - assert(end <= region.start); + if (start < region.start * term->cols) { + assert(end <= region.start * term->cols); it->item.range.start += rows * term->cols; } } @@ -259,8 +259,8 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows int start = it->item.range.start - term->grid->offset; int end = start + it->item.range.length; - if (end > region.end) { - assert(start >= region.end); + if (end > region.end * term->cols) { + assert(start >= region.end * term->cols); it->item.range.start += rows * term->cols; } } From 1f343527f065352f34af06d8d6a7cbd3e6c82cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 14:15:46 +0200 Subject: [PATCH 32/50] scrolling: make sure we don't clear too much memory --- terminal.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/terminal.c b/terminal.c index df74e9db..49c8e390 100644 --- a/terminal.c +++ b/terminal.c @@ -272,7 +272,9 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows /* Clear scrolled-in lines */ grid_memset( term->grid, - max(0, region.end - rows) * term->cols, 0, rows * term->cols); + max(0, region.end - rows) * term->cols, + 0, + min(rows, term->rows) * term->cols); term_damage_scroll(term, DAMAGE_SCROLL, region, rows); From da305abdd8b6a76156877e4cf1104767d77d6b56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 14:16:01 +0200 Subject: [PATCH 33/50] scrolling: initial implementation of scrolling region when reverse scrolling --- terminal.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/terminal.c b/terminal.c index 49c8e390..7ba0aa13 100644 --- a/terminal.c +++ b/terminal.c @@ -295,8 +295,44 @@ void term_scroll_reverse_partial(struct terminal *term, struct scroll_region region, int rows) { - assert(region.start == 0); - assert(region.end == term->rows); + if (region.end < term->rows) { + grid_memmove( + term->grid, + (region.end - rows) * term->cols, + region.end * term->cols, + (term->rows - region.end) * term->cols); + + tll_foreach(term->grid->damage, it) { + int start = it->item.range.start - term->grid->offset; + int end = start + it->item.range.length; + + if (end > region.end * term->cols) { + assert(start >= region.end * term->cols); + it->item.range.start -= rows * term->cols; + } + } + + } + + if (region.start > 0) { + grid_memmove( + term->grid, -rows * term->cols, 0, region.start * term->cols); + + tll_foreach(term->grid->damage, it) { + int start = it->item.range.start - term->grid->offset; + int end __attribute__((unused)) = start + it->item.range.length; + + if (start < region.start * term->cols) { + if (end > region.start * term->cols) { + LOG_ERR("region.start = %d, rows = %d, damage.start = %d, damage.end = %d (%s)", + region.start, rows, start, end, it->item.type == DAMAGE_UPDATE ? "UPDATE" : "ERASE"); + abort(); + } + assert(end <= region.start * term->cols); + it->item.range.start -= rows * term->cols; + } + } + } term->grid->offset -= rows * term->cols; From 2fad8138e1aee775825ce85f29dcd98d994e33f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 14:24:46 +0200 Subject: [PATCH 34/50] csi: scroll region changes are now logged as debug again, rather than info --- csi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/csi.c b/csi.c index 57da1c06..c849ed0d 100644 --- a/csi.c +++ b/csi.c @@ -483,9 +483,9 @@ csi_dispatch(struct terminal *term, uint8_t final) term_cursor_to(term, (start - 1) * term->cols, 0); - LOG_INFO("scroll region: %d-%d", - term->scroll_region.start, - term->scroll_region.end); + LOG_DBG("scroll region: %d-%d", + term->scroll_region.start, + term->scroll_region.end); break; } From 92f0d642795f3aa5dbe644f2fed0bd532418eba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 14:47:01 +0200 Subject: [PATCH 35/50] csi: add untested implementation of "horizontal index" --- csi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/csi.c b/csi.c index c849ed0d..56a4a83d 100644 --- a/csi.c +++ b/csi.c @@ -473,6 +473,17 @@ csi_dispatch(struct terminal *term, uint8_t final) break; } + case 'l': { + /* Horizontal index */ + assert(false && "untested"); + int param = param_get(term, 0, 1); + int col = term->cursor.col; + + col = (col + param * 8) / 8 * 8; + term_cursor_right(term, col - term->cursor.col); + break; + } + case 'r': { int start = param_get(term, 0, 1); int end = param_get(term, 1, term->rows); From a0602aedb5fd5fffb42d44c1ed48b74e4a056917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 14:47:34 +0200 Subject: [PATCH 36/50] csi: recognize 'smam', but log unimplemented --- csi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/csi.c b/csi.c index 56a4a83d..98055c5b 100644 --- a/csi.c +++ b/csi.c @@ -573,6 +573,10 @@ csi_dispatch(struct terminal *term, uint8_t final) LOG_WARN("unimplemented: flash"); break; + case 7: + LOG_WARN("unimplemented: smam (automatic margins)"); + break; + case 12: LOG_WARN("unimplemented: cursor blinking"); break; @@ -631,6 +635,10 @@ csi_dispatch(struct terminal *term, uint8_t final) LOG_WARN("unimplemented: flash"); break; + case 7: + LOG_WARN("unimplemented: smam (automatic margins)"); + break; + case 12: LOG_WARN("unimplemented: cursor blinking"); break; From f87d4f856a277553be35fbee8c3abe2765d978dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 15:16:38 +0200 Subject: [PATCH 37/50] main: mitigate screen flashes on slow client If a client writes to the PTY "too slow", we often end up flashing the screen. This could for example be caused by a client first erasing a line, then we render that frame, followed by the client updating the just-erased line. When we render _that_ frame, it will be perceived as a flash. Mitigate this by trying to read client data again, after finishing processing one batch, but before rendering. To avoid hanging on client output (and never rendering anything), limit this to at most 3 iterations. This may have to be tweaked. --- main.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/main.c b/main.c index a29f5d8c..133880ee 100644 --- a/main.c +++ b/main.c @@ -8,6 +8,7 @@ #include #include #include +#include #include //#include @@ -987,6 +988,20 @@ main(int argc, char *const *argv) break; } + /* Read logic requires non-blocking mode */ + { + int fd_flags = fcntl(c.term.ptmx, F_GETFL); + if (fd_flags == -1) { + LOG_ERRNO("failed to set non blocking mode on PTY master"); + goto out; + } + + if (fcntl(c.term.ptmx, F_SETFL, fd_flags | O_NONBLOCK) == -1) { + LOG_ERRNO("failed to set non blocking mode on PTY master"); + goto out; + } + } + while (true) { struct pollfd fds[] = { {.fd = wl_display_get_fd(c.wl.display), .events = POLLIN}, @@ -1011,16 +1026,18 @@ main(int argc, char *const *argv) } if (fds[1].revents & POLLIN) { - uint8_t data[8192]; - ssize_t count = read(c.term.ptmx, data, sizeof(data)); - if (count < 0) { - LOG_ERRNO("failed to read from pseudo terminal"); - break; + for (size_t i = 0; i < 3; i++) { + uint8_t data[8192]; + ssize_t count = read(c.term.ptmx, data, sizeof(data)); + if (count < 0) { + if (errno != EAGAIN) + LOG_ERRNO("failed to read from pseudo terminal"); + break; + } + + vt_from_slave(&c.term, data, count); } - //LOG_DBG("%.*s", (int)count, data); - - vt_from_slave(&c.term, data, count); if (!c.frame_is_scheduled) grid_render(&c); } From 66033b9b1a80a393b19c6ffffcc25462a2012910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 15:58:49 +0200 Subject: [PATCH 38/50] csi: implement 'erase characters' (CSI X) --- csi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/csi.c b/csi.c index 98055c5b..a9de94b2 100644 --- a/csi.c +++ b/csi.c @@ -473,6 +473,17 @@ csi_dispatch(struct terminal *term, uint8_t final) break; } + case 'X': { + /* Erase chars */ + int count = min( + param_get(term, 0, 1), term->cols - term->cursor.col); + + memset(&term->grid->cur_line[term->cursor.col], + 0, count * sizeof(term->grid->cur_line[0])); + term_damage_erase(term, term->cursor.linear, count); + break; + } + case 'l': { /* Horizontal index */ assert(false && "untested"); From f4c334338242d60f52f0ef48f748bc1b4fce313a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 15:59:28 +0200 Subject: [PATCH 39/50] csi: fix cursor positioning --- csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csi.c b/csi.c index a9de94b2..28c6baa1 100644 --- a/csi.c +++ b/csi.c @@ -503,7 +503,7 @@ csi_dispatch(struct terminal *term, uint8_t final) term->scroll_region.start = start - 1; term->scroll_region.end = end; - term_cursor_to(term, (start - 1) * term->cols, 0); + term_cursor_to(term, start - 1, 0); LOG_DBG("scroll region: %d-%d", term->scroll_region.start, From 43045a98e2e14e42572f5a89ac897bf8b06609ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 16:00:06 +0200 Subject: [PATCH 40/50] csi: recognize ?1005 and ?1015 private CSIs --- csi.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/csi.c b/csi.c index 28c6baa1..e1a5b540 100644 --- a/csi.c +++ b/csi.c @@ -604,10 +604,18 @@ csi_dispatch(struct terminal *term, uint8_t final) LOG_WARN("unimplemented: report cell mouse motion"); break; + case 1005: + LOG_WARN("unimplemented: UTF-8 mouse"); + break; + case 1006: LOG_WARN("unimplemented: SGR mouse"); break; + case 1015: + LOG_WARN("unimplemented: URXVT mosue"); + break; + case 1049: if (term->grid != &term->alt) { term->grid = &term->alt; @@ -666,10 +674,18 @@ csi_dispatch(struct terminal *term, uint8_t final) LOG_WARN("unimplemented: report cell mouse motion"); break; + case 1005: + LOG_WARN("unimplemented: UTF-8 mouse"); + break; + case 1006: LOG_WARN("unimplemented: SGR mouse"); break; + case 1015: + LOG_WARN("unimplemented: URXVT mosue"); + break; + case 1049: if (term->grid == &term->alt) { term->grid = &term->normal; From b5dccf28839f99c85727feecbadda56f65becde3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 16:00:27 +0200 Subject: [PATCH 41/50] csi: add unknown private escapes 's' and 'r', seen with ncmpcpp --- csi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/csi.c b/csi.c index e1a5b540..530a494c 100644 --- a/csi.c +++ b/csi.c @@ -714,6 +714,13 @@ csi_dispatch(struct terminal *term, uint8_t final) break; } + case 's': + case 'r': + /* ??? */ + /* Seen with ncmpcpp */ + LOG_WARN("unimplemented: CSI ?%c", final); + break; + default: LOG_ERR("CSI: intermediate '?': unimplemented final: %c", final); abort(); From cfd39c09674e24de18500a2de9d1cb2a0a8c9692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 16:20:28 +0200 Subject: [PATCH 42/50] vt: fix logging of ESC intermediates and parameters --- vt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vt.c b/vt.c index f2d85d4b..ca461a40 100644 --- a/vt.c +++ b/vt.c @@ -555,7 +555,7 @@ static const enum action exit_actions[] = { static bool esc_dispatch(struct terminal *term, uint8_t final) { -#if defined(_DEBUG) && defined(LOG_ENABLE_DBG) && LOG_ENABLED_DBG +#if defined(_DEBUG) && defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG char log[1024]; int c = snprintf(log, sizeof(log), "ESC: "); From bf7815015720077cf2e2c93276c43adae1391100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 16:21:09 +0200 Subject: [PATCH 43/50] vt: always log unimplemented as warnings, not errors --- vt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vt.c b/vt.c index ca461a40..ea9bbcde 100644 --- a/vt.c +++ b/vt.c @@ -568,6 +568,7 @@ esc_dispatch(struct terminal *term, uint8_t final) switch (final) { case 'B': { + /* Configure G0-G3 to use ASCII */ char param = term->vt.params.idx > 0 ? term->vt.params.v[0].value : '('; switch (param) { @@ -578,11 +579,11 @@ esc_dispatch(struct terminal *term, uint8_t final) case ')': case '*': case '+': - LOG_ERR("unimplemented: character charset: %c", param); + LOG_WARN("unimplemented: charset %c uses ASCII", param); return false; default: - LOG_ERR("ESC B: invalid charset identifier: %c", param); + LOG_ERR("%cB: invalid charset identifier", param); return false; } break; From df5be1061dc8819c802bc96e026253b4fb8b78a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 16:21:26 +0200 Subject: [PATCH 44/50] vt: recognize ESC(0 - switch character set --- vt.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/vt.c b/vt.c index ea9bbcde..b2cc07fa 100644 --- a/vt.c +++ b/vt.c @@ -589,6 +589,25 @@ esc_dispatch(struct terminal *term, uint8_t final) break; } + case '0': { + /* Configure G0-G3 to use special chars + line drawing */ + char param = term->vt.params.idx > 0 ? term->vt.params.v[0].value : '('; + + switch (param) { + case '(': + case ')': + case '*': + case '+': + LOG_WARN("unimplemented: charset %c uses special characters and line drawings", param); + break; + + default: + LOG_ERR("%c0: invalid charset identifier", param); + return false; + } + break; + } + case 'M': /* ri - reverse index (scroll reverse) */ term_scroll_reverse(term, 1); From 1373d18dbc7b04141347a20a58867c1c79cf6daf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 20:21:03 +0200 Subject: [PATCH 45/50] logging: disable debug logging by default --- csi.c | 2 +- grid.c | 2 +- input.c | 2 +- main.c | 2 +- osc.c | 2 +- slave.c | 2 +- terminal.c | 2 +- vt.c | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/csi.c b/csi.c index 530a494c..4be35c60 100644 --- a/csi.c +++ b/csi.c @@ -10,7 +10,7 @@ #endif #define LOG_MODULE "csi" -#define LOG_ENABLE_DBG 1 +#define LOG_ENABLE_DBG 0 #include "log.h" #include "grid.h" diff --git a/grid.c b/grid.c index 99e66665..178883e4 100644 --- a/grid.c +++ b/grid.c @@ -4,7 +4,7 @@ #include #define LOG_MODULE "grid" -#define LOG_ENABLE_DBG 1 +#define LOG_ENABLE_DBG 0 #include "log.h" struct cell * diff --git a/input.c b/input.c index 5e61d49b..319de914 100644 --- a/input.c +++ b/input.c @@ -10,7 +10,7 @@ #include #define LOG_MODULE "input" -#define LOG_ENABLE_DBG 1 +#define LOG_ENABLE_DBG 0 #include "log.h" #include "terminal.h" diff --git a/main.c b/main.c index 133880ee..b69f42de 100644 --- a/main.c +++ b/main.c @@ -17,7 +17,7 @@ #include #define LOG_MODULE "main" -#define LOG_ENABLE_DBG 1 +#define LOG_ENABLE_DBG 0 #include "log.h" #include "font.h" diff --git a/osc.c b/osc.c index 5f71287e..2882d546 100644 --- a/osc.c +++ b/osc.c @@ -1,7 +1,7 @@ #include "osc.h" #define LOG_MODULE "osc" -#define LOG_ENABLE_DBG 1 +#define LOG_ENABLE_DBG 0 #include "log.h" bool diff --git a/slave.c b/slave.c index edb5e9c6..676d4ce2 100644 --- a/slave.c +++ b/slave.c @@ -9,7 +9,7 @@ #include #define LOG_MODULE "slave" -#define LOG_ENABLE_DBG 1 +#define LOG_ENABLE_DBG 0 #include "log.h" void diff --git a/terminal.c b/terminal.c index 7ba0aa13..4de5a1a8 100644 --- a/terminal.c +++ b/terminal.c @@ -4,7 +4,7 @@ #include #define LOG_MODULE "terminal" -#define LOG_ENABLE_DBG 1 +#define LOG_ENABLE_DBG 0 #include "log.h" #include "grid.h" diff --git a/vt.c b/vt.c index b2cc07fa..c5786b2f 100644 --- a/vt.c +++ b/vt.c @@ -5,7 +5,7 @@ #include #define LOG_MODULE "vt" -#define LOG_ENABLE_DBG 1 +#define LOG_ENABLE_DBG 0 #include "log.h" #include "csi.h" #include "osc.h" From 90dadfcc228d890fb95cc7c210b24cd024f096ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 21:16:41 +0200 Subject: [PATCH 46/50] vt: implement (untested!) insert mode \E4l is not horizontal index, but disable INSERT mode --- csi.c | 16 ++++++++-------- terminal.h | 4 +++- vt.c | 9 +++++++++ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/csi.c b/csi.c index 4be35c60..6fca2c24 100644 --- a/csi.c +++ b/csi.c @@ -484,16 +484,16 @@ csi_dispatch(struct terminal *term, uint8_t final) break; } - case 'l': { - /* Horizontal index */ + case 'h': + /* smir - insert mode enable */ assert(false && "untested"); - int param = param_get(term, 0, 1); - int col = term->cursor.col; - - col = (col + param * 8) / 8 * 8; - term_cursor_right(term, col - term->cursor.col); + term->insert_mode = true; + break; + + case 'l': + /* rmir - insert mode disable */ + term->insert_mode = false; break; - } case 'r': { int start = param_get(term, 0, 1); diff --git a/terminal.h b/terminal.h index a96b1b70..f48158e5 100644 --- a/terminal.h +++ b/terminal.h @@ -131,9 +131,11 @@ struct terminal { pid_t slave; int ptmx; - bool hide_cursor; enum decckm decckm; enum keypad_mode keypad_mode; + bool hide_cursor; + bool auto_margin; + bool insert_mode; bool bracketed_paste; struct vt vt; diff --git a/vt.c b/vt.c index c5786b2f..b00b840d 100644 --- a/vt.c +++ b/vt.c @@ -701,6 +701,15 @@ action(struct terminal *term, enum action action, uint8_t c) struct cell *cell = &term->grid->cur_line[term->cursor.col]; term_damage_update(term, term->cursor.linear, 1); + if (term->insert_mode) { + assert(false && "untested"); + grid_memmove( + term->grid, term->cursor.linear + 1, term->cursor.linear, + term->cols - term->cursor.col - 1); + term_damage_update( + term, term->cursor.linear + 1, term->cols - term->cursor.col - 1); + } + if (term->vt.utf8.idx > 0) { //LOG_DBG("print: UTF8: %.*s", (int)term->vt.utf8.idx, term->vt.utf8.data); memcpy(cell->c, term->vt.utf8.data, term->vt.utf8.idx); From 3c67628c57b03f6faabe61d04b13297eac80bb6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 21:18:06 +0200 Subject: [PATCH 47/50] vt: implement (untested!) smam and rmam (auto margins) Default is auto-margins enabled (scroll when cursor reaches right margin). --- csi.c | 4 ++-- main.c | 1 + vt.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/csi.c b/csi.c index 6fca2c24..bb805134 100644 --- a/csi.c +++ b/csi.c @@ -585,7 +585,7 @@ csi_dispatch(struct terminal *term, uint8_t final) break; case 7: - LOG_WARN("unimplemented: smam (automatic margins)"); + term->auto_margin = true; break; case 12: @@ -655,7 +655,7 @@ csi_dispatch(struct terminal *term, uint8_t final) break; case 7: - LOG_WARN("unimplemented: smam (automatic margins)"); + term->auto_margin = false; break; case 12: diff --git a/main.c b/main.c index b69f42de..a87598c4 100644 --- a/main.c +++ b/main.c @@ -864,6 +864,7 @@ main(int argc, char *const *argv) .ptmx = posix_openpt(O_RDWR | O_NOCTTY), .decckm = DECCKM_CSI, .keypad_mode = KEYPAD_NUMERICAL, /* TODO: verify */ + .auto_margin = true, .vt = { .state = 1, /* STATE_GROUND */ }, diff --git a/vt.c b/vt.c index b00b840d..8f1db921 100644 --- a/vt.c +++ b/vt.c @@ -690,7 +690,7 @@ action(struct terminal *term, enum action action, uint8_t c) break; case ACTION_PRINT: { - if (term->print_needs_wrap) { + if (term->auto_margin && term->print_needs_wrap) { if (term->cursor.row == term->scroll_region.end - 1) { term_scroll(term, 1); term_cursor_to(term, term->cursor.row, 0); From 3d58d24963645125f23316be01f936932f88ee4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 21:33:23 +0200 Subject: [PATCH 48/50] csi: ?r and ?s are like ?h and ?l; they save/restore things Assuming ncmpcpp is correct, ?1001s and ?1001r saves and restores the 'highlight mouse tracking' mode. --- csi.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/csi.c b/csi.c index bb805134..170cbf6d 100644 --- a/csi.c +++ b/csi.c @@ -715,14 +715,39 @@ csi_dispatch(struct terminal *term, uint8_t final) } case 's': + for (size_t i = 0; i < term->vt.params.idx; i++) { + switch (term->vt.params.v[i].value) { + case 1001: /* save old highlight mouse tracking mode? */ + LOG_WARN( + "unimplemented: CSI ?1001s " + "(save 'highlight mouse tracking' mode)"); + break; + + default: + LOG_ERR("unimplemented: CSI ?%ds", term->vt.params.v[i].value); + abort(); + } + } + break; + case 'r': - /* ??? */ - /* Seen with ncmpcpp */ - LOG_WARN("unimplemented: CSI ?%c", final); + for (size_t i = 0; i < term->vt.params.idx; i++) { + switch (term->vt.params.v[i].value) { + case 1001: /* restore old highlight mouse tracking mode? */ + LOG_WARN( + "unimplemented: CSI ?1001r " + "(restore 'highlight mouse tracking' mode)"); + break; + + default: + LOG_ERR("unimplemented: CSI ?%dr", term->vt.params.v[i].value); + abort(); + } + } break; default: - LOG_ERR("CSI: intermediate '?': unimplemented final: %c", final); + LOG_ERR("unimplemented: CSI: ?%c", final); abort(); } From 2f3f4ac56f9cb6df445df1f91aa1913dd21c1b3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 22:21:23 +0200 Subject: [PATCH 49/50] csi: cleanup --- csi.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/csi.c b/csi.c index 170cbf6d..681d31f0 100644 --- a/csi.c +++ b/csi.c @@ -57,10 +57,6 @@ initialize_colors256(void) b * 51 / 255.0, 1.0, }; -#if 0 - colors256[16 + r * 6 * 6 + g * 6 + b] = - (51 * r) << 24 | (51 * g) << 16 | (51 * b) << 8 | 0xff; -#endif } } } @@ -72,7 +68,6 @@ initialize_colors256(void) i * 11 / 255.0, 1.0 }; - //(11 * i) << 24 | (11 * i) << 16 | (11 * i) << 8 | 0xff; } } @@ -292,11 +287,7 @@ csi_dispatch(struct terminal *term, uint8_t final) case 'd': { /* VPA - vertical line position absolute */ - int row = param_get(term, 0, 1); - - if (row > term->rows) - row = term->rows; - + int row = min(param_get(term, 0, 1), term->rows); term_cursor_to(term, row - 1, term->cursor.col); break; } @@ -334,14 +325,8 @@ csi_dispatch(struct terminal *term, uint8_t final) case 'H': { /* Move cursor */ - int row = param_get(term, 0, 1); - int col = param_get(term, 1, 1); - - if (row > term->rows) - row = term->rows; - if (col > term->cols) - col = term->cols; - + int row = min(param_get(term, 0, 1), term->rows); + int col = min(param_get(term, 1, 1), term->cols); term_cursor_to(term, row - 1, col - 1); break; } From 35d5035f614493196e3b93e2b60d6f88df75e067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jul 2019 22:21:44 +0200 Subject: [PATCH 50/50] csi: fix bug: cursor horizontal absolute ( Pn G) was off-by-one --- csi.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/csi.c b/csi.c index 681d31f0..4b545d31 100644 --- a/csi.c +++ b/csi.c @@ -314,12 +314,8 @@ csi_dispatch(struct terminal *term, uint8_t final) case 'G': { /* Cursor horizontal absolute */ - int col = param_get(term, 0, 1); - - if (col > term->cols) - col = term->cols; - - term_cursor_to(term, term->cursor.row, col); + int col = min(param_get(term, 0, 1), term->cols); + term_cursor_to(term, term->cursor.row, col - 1); break; }