diff --git a/csi.c b/csi.c index 9cbbbd45..49431bca 100644 --- a/csi.c +++ b/csi.c @@ -386,37 +386,37 @@ csi_dispatch(struct terminal *term, uint8_t final) } case 'L': { - if (term->grid.cursor.row < term->grid.scrolling_region.start || - term->grid.cursor.row >= term->grid.scrolling_region.end) + if (term->grid.cursor.row < term->grid.scroll_region.start || + term->grid.cursor.row >= term->grid.scroll_region.end) break; int count = min( param_get(term, 0, 1), - term->grid.scrolling_region.end - term->grid.cursor.row); - - LOG_DBG("reverse partial: %d, %d rows", - term->grid.cursor.row - term->grid.scrolling_region.start, - count); + term->grid.scroll_region.end - term->grid.cursor.row); grid_scroll_reverse_partial( &term->grid, - term->grid.cursor.row - term->grid.scrolling_region.start, + (struct scroll_region){ + .start = term->grid.cursor.row, + .end = term->grid.scroll_region.end}, count); break; } case 'M': { - if (term->grid.cursor.row < term->grid.scrolling_region.start || - term->grid.cursor.row >= term->grid.scrolling_region.end) + if (term->grid.cursor.row < term->grid.scroll_region.start || + term->grid.cursor.row >= term->grid.scroll_region.end) break; int count = min( param_get(term, 0, 1), - term->grid.scrolling_region.end - term->grid.cursor.row); + term->grid.scroll_region.end - term->grid.cursor.row); grid_scroll_partial( &term->grid, - term->grid.cursor.row - term->grid.scrolling_region.start, + (struct scroll_region){ + .start = term->grid.cursor.row, + .end = term->grid.scroll_region.end}, count); break; } @@ -449,16 +449,12 @@ csi_dispatch(struct terminal *term, uint8_t final) int end = param_get(term, 1, term->grid.rows); /* 1-based */ - term->grid.scrolling_region.start = start - 1; - term->grid.scrolling_region.end = end; + term->grid.scroll_region.start = start - 1; + term->grid.scroll_region.end = end; - LOG_INFO("scrolling region: %d-%d", - term->grid.scrolling_region.start, - term->grid.scrolling_region.end); - - tll_free(term->grid.damage); - grid_damage_update(&term->grid, 0, term->grid.rows * term->grid.cols); - grid_cursor_to(&term->grid, 0, 0); + LOG_INFO("scroll region: %d-%d", + term->grid.scroll_region.start, + term->grid.scroll_region.end); break; } @@ -562,8 +558,7 @@ csi_dispatch(struct terminal *term, uint8_t final) term->grid.alt_saved_cursor.row = term->grid.cursor.row; term->grid.alt_saved_cursor.col = term->grid.cursor.col; - tll_free(term->grid.damage); - grid_erase(&term->grid, 0, term->grid.cols * term->grid.rows); + grid_damage_all(&term->grid); } break; @@ -622,12 +617,10 @@ csi_dispatch(struct terminal *term, uint8_t final) &term->grid, term->grid.cursor.row, term->grid.cursor.col); /* Should these be restored from saved values? */ - term->grid.scrolling_region.start = 0; - term->grid.scrolling_region.end = term->grid.rows; + term->grid.scroll_region.start = 0; + term->grid.scroll_region.end = term->grid.rows; - tll_free(term->grid.damage); - grid_damage_update( - &term->grid, 0, term->grid.cols * term->grid.rows); + grid_damage_all(&term->grid); } break; diff --git a/grid.c b/grid.c index d4b8cbe5..f588f6e0 100644 --- a/grid.c +++ b/grid.c @@ -51,48 +51,6 @@ static void grid_damage_update_or_erase(struct grid *grid, enum damage_type damage_type, int start, int length) { - const int cols = grid->cols; - - if (length == 0) - return; - - if (start < grid->scrolling_region.start * cols) { - int len = min(length, grid->scrolling_region.start * cols); - - struct damage dmg = { - .type = damage_type + 1, - .range = {.start = start, .length = len}, - }; - - tll_push_back(grid->damage, dmg); - start += len; - length -= len; - } - - assert(start >= grid->scrolling_region.start * cols); - - if (length == 0) - return; - - if (start + length > (grid->scrolling_region.end * cols)) { - int partial_start = max(start, grid->scrolling_region.end * cols); - int len = start + length - partial_start; - - struct damage dmg = { - .type = damage_type + 1, - .range = {.start = partial_start, .length = len}, - }; - - tll_push_back(grid->damage, dmg); - length -= len; - } - - if (length == 0) - return; - - assert(start < grid->scrolling_region.end * cols); - assert(start + length <= grid->scrolling_region.end * cols); - struct damage dmg = { .type = damage_type, .range = {.start = start, .length = length}, @@ -121,86 +79,111 @@ grid_damage_erase(struct grid *grid, int start, int length) grid_damage_update_or_erase(grid, DAMAGE_ERASE, start, length); } +void +grid_damage_all(struct grid *grid) +{ + tll_free(grid->damage); + tll_free(grid->scroll_damage); + grid_damage_update(grid, 0, grid->rows * grid->cols); +} + static void damage_adjust_after_scroll(struct grid *grid, enum damage_type damage_type, - int lines) + struct scroll_region region, int lines) { - int top_margin = grid->scrolling_region.start; - int bottom_margin = grid->rows - grid->scrolling_region.end; - const int adjustment = lines * grid->cols * (damage_type == DAMAGE_SCROLL_REVERSE ? -1 : 1); - top_margin *= grid->cols; - bottom_margin *= grid->cols; + + const int scroll_start = region.start * grid->cols; + const int scroll_end = region.end * grid->cols; tll_foreach(grid->damage, it) { - if (!(it->item.type == DAMAGE_UPDATE || it->item.type == DAMAGE_ERASE)) - continue; + int start = it->item.range.start; + int length = it->item.range.length; + int end = start + length; - assert(it->item.range.start >= grid->scrolling_region.start * grid->cols); - assert(it->item.range.start + it->item.range.length <= - grid->scrolling_region.end * grid->cols); + 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}, + }; - it->item.range.start -= adjustment; - int end = it->item.range.start + it->item.range.length; + tll_push_back(grid->damage, outside); + start = scroll_start; + length = end - start; - if (damage_type == DAMAGE_SCROLL && it->item.range.start < top_margin) { - /* Scrolled of screen (partially, or completely) */ - int new_length = it->item.range.length - (top_margin - it->item.range.start); - assert(new_length < it->item.range.length); - - if (new_length <= 0) - tll_remove(grid->damage, it); - else { - it->item.range.length = new_length; - it->item.range.start = 0; - } } - else if (damage_type == DAMAGE_SCROLL_REVERSE && - end >= (grid->rows * grid->cols - bottom_margin)) - { - /* Scrolled of screen (partially, or completely) */ - if (it->item.range.start >= (grid->rows * grid->cols - bottom_margin)) - tll_remove(grid->damage, it); - else - it->item.range.length = - (grid->rows * grid->cols - bottom_margin) - it->item.range.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(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(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(grid->damage, it); + else { + it->item.range.start = start; + it->item.range.length = scroll_end - start; + } + } } } } void grid_damage_scroll(struct grid *grid, enum damage_type damage_type, - int offset, int lines) + struct scroll_region region, int lines) { - if (tll_length(grid->damage) > 0 && - tll_front(grid->damage).type == damage_type && - tll_front(grid->damage).scroll.offset == offset) + damage_adjust_after_scroll(grid, damage_type, region, lines); + + if (tll_length(grid->scroll_damage) > 0 && + tll_back(grid->scroll_damage).type == damage_type && + tll_back(grid->scroll_damage).scroll.region.start == region.start && + tll_back(grid->scroll_damage).scroll.region.end == region.end) { /* Merge with existing scroll damage */ - struct damage *dmg = &tll_front(grid->damage); + struct damage *dmg = &tll_back(grid->scroll_damage); dmg->scroll.lines += lines; - const int scrolling_region = - grid->scrolling_region.end - grid->scrolling_region.start; - - /* If we've scrolled away the entire screen, replace with an erase */ - if (dmg->scroll.lines >= scrolling_region - offset) { - dmg->type = DAMAGE_ERASE; - dmg->range.start = (grid->scrolling_region.start + offset) * grid->cols; - dmg->range.length = (scrolling_region - offset) * grid->cols; - } + /* If we've scrolled away the entire screen, remove it all together */ + if (dmg->scroll.lines >= region.end - region.start) + tll_pop_back(grid->scroll_damage); } else { struct damage dmg = { .type = damage_type, - .scroll = {.offset = offset, .lines = lines}, + .scroll = {.region = region, .lines = lines}, }; - tll_push_front(grid->damage, dmg); + tll_push_back(grid->scroll_damage, dmg); } - - damage_adjust_after_scroll(grid, damage_type, lines); } void @@ -275,74 +258,68 @@ grid_cursor_down(struct grid *grid, int count) } void -grid_scroll_partial(struct grid *grid, int offset, int rows) +grid_scroll_partial(struct grid *grid, struct scroll_region region, int rows) { - const int grid_rows = grid->scrolling_region.end - grid->scrolling_region.start; - const int top_margin = grid->scrolling_region.start; - //const int bottom_margin = grid->rows - grid->scrolling_region.end; - - if (rows >= grid_rows - offset) { + if (rows >= region.end - region.start) { assert(false && "untested"); return; } - int cell_dst = (top_margin + offset) * grid->cols; - int cell_src = (top_margin + offset + rows) * grid->cols; - int cell_count = (grid_rows - offset - rows) * grid->cols; + int cell_dst = (region.start + 0) * grid->cols; + int cell_src = (region.start + rows) * grid->cols; + int cell_count = (region.end - region.start - rows) * grid->cols; LOG_DBG("moving %d lines from row %d to row %d", cell_count / grid->cols, - cell_src / grid->cols, (top_margin + offset) / grid->cols); + cell_src / grid->cols, cell_dst / grid->cols); const size_t bytes = cell_count * sizeof(grid->cells[0]); memmove( &grid->cells[cell_dst], &grid->cells[cell_src], bytes); - grid_damage_scroll(grid, DAMAGE_SCROLL, offset, rows); + grid_damage_scroll(grid, DAMAGE_SCROLL, region, rows); grid_erase( grid, - (grid->scrolling_region.end - rows) * grid->cols, - grid->scrolling_region.end * grid->cols); + (region.end - rows) * grid->cols, + region.end * grid->cols); } void grid_scroll(struct grid *grid, int rows) { - grid_scroll_partial(grid, 0, rows); + grid_scroll_partial(grid, grid->scroll_region, rows); } void -grid_scroll_reverse_partial(struct grid *grid, int offset, int rows) +grid_scroll_reverse_partial(struct grid *grid, + struct scroll_region region, int rows) { - const int grid_rows = grid->scrolling_region.end - grid->scrolling_region.start; - const int top_margin = grid->scrolling_region.start; - - if (rows >= grid_rows - offset) { + if (rows >= region.end - region.start) { assert(false && "todo"); return; } - int cell_dst = (top_margin + offset + rows) * grid->cols; - int cell_src = (top_margin + offset) * grid->cols; - int cell_count = (grid_rows - offset - rows) * grid->cols; + int cell_dst = (region.start + rows) * grid->cols; + int cell_src = (region.start + 0) * grid->cols; + int cell_count = (region.end - region.start - rows) * grid->cols; LOG_DBG("moving %d lines from row %d to row %d", cell_count / grid->cols, - cell_src / grid->cols, (top_margin + offset) / grid->cols); + cell_src / grid->cols, cell_dst / grid->cols); const size_t bytes = cell_count * sizeof(grid->cells[0]); memmove( &grid->cells[cell_dst], &grid->cells[cell_src], bytes); - grid_damage_scroll(grid, DAMAGE_SCROLL_REVERSE, offset, rows); + grid_damage_scroll(grid, DAMAGE_SCROLL_REVERSE, region, rows); grid_erase( grid, - (top_margin + offset) * grid->cols, - (top_margin + offset + rows) * grid->cols); + region.start * grid->cols, + (region.start + rows) * grid->cols); } void grid_scroll_reverse(struct grid *grid, int rows) { - grid_scroll_reverse_partial(grid, 0, rows); + grid_scroll_reverse_partial(grid, grid->scroll_region, rows); } diff --git a/grid.h b/grid.h index d27099cd..4854582d 100644 --- a/grid.h +++ b/grid.h @@ -2,10 +2,12 @@ #include "terminal.h" +void grid_damage_all(struct grid *grid); void grid_damage_update(struct grid *grid, int start, int length); void grid_damage_erase(struct grid *grid, int start, int length); void grid_damage_scroll( - struct grid *grid, enum damage_type damage_type, int offset, int lines); + struct grid *grid, enum damage_type damage_type, + struct scroll_region region, int lines); void grid_erase(struct grid *grid, int start, int end); @@ -18,8 +20,10 @@ void grid_cursor_down(struct grid *grid, int count); void grid_scroll(struct grid *grid, int rows); void grid_scroll_reverse(struct grid *grid, int rows); -void grid_scroll_partial(struct grid *grid, int offset, int rows); -void grid_scroll_reverse_partial(struct grid *grid, int offset, int rows); +void grid_scroll_partial( + struct grid *grid, struct scroll_region region, int rows); +void grid_scroll_reverse_partial( + struct grid *grid, struct scroll_region region, int rows); int grid_cursor_linear(const struct grid *grid, int row, int col); diff --git a/main.c b/main.c index c7a89fed..296414b7 100644 --- a/main.c +++ b/main.c @@ -255,20 +255,16 @@ static void grid_render_scroll(struct context *c, struct buffer *buf, const struct damage *dmg) { - //int x = 0; - const int scrolling_region - = c->term.grid.scrolling_region.end - c->term.grid.scrolling_region.start; - - int dst_y = (c->term.grid.scrolling_region.start + dmg->scroll.offset + 0) * c->term.grid.cell_height; - int src_y = (c->term.grid.scrolling_region.start + dmg->scroll.offset + dmg->scroll.lines) * c->term.grid.cell_height; + int dst_y = (dmg->scroll.region.start + 0) * c->term.grid.cell_height; + int src_y = (dmg->scroll.region.start + dmg->scroll.lines) * c->term.grid.cell_height; int width = buf->width; - int height = (scrolling_region - dmg->scroll.offset - dmg->scroll.lines) * c->term.grid.cell_height; + int height = (dmg->scroll.region.end - dmg->scroll.region.start - dmg->scroll.lines) * c->term.grid.cell_height; const uint32_t stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); LOG_DBG("damage: SCROLL: %d-%d by %d lines (dst-y: %d, src-y: %d, " "height: %d, stride: %d, mmap-size: %zu)", - c->term.grid.scrolling_region.start, c->term.grid.scrolling_region.end, + dmg->scroll.region.start, dmg->scroll.region.end, dmg->scroll.lines, dst_y, src_y, height, stride, buf->size); @@ -281,38 +277,33 @@ grid_render_scroll(struct context *c, struct buffer *buf, wl_surface_damage_buffer(c->wl.surface, 0, dst_y, width, height); - assert(dmg->scroll.offset == 0 && "todo"); - +#if 0 const int cols = c->term.grid.cols; struct damage erase = { .type = DAMAGE_ERASE, .range = { - .start = (c->term.grid.scrolling_region.end - - dmg->scroll.lines) * cols, + .start = (dmg->scroll.region.end - dmg->scroll.lines) * cols, .length = dmg->scroll.lines * cols }, }; grid_render_erase(c, buf, &erase); +#endif } static void grid_render_scroll_reverse(struct context *c, struct buffer *buf, const struct damage *dmg) { - //int x = 0; - const int scrolling_region = - c->term.grid.scrolling_region.end - c->term.grid.scrolling_region.start; - - int src_y = (c->term.grid.scrolling_region.start + dmg->scroll.offset + 0) * c->term.grid.cell_height; - int dst_y = (c->term.grid.scrolling_region.start + dmg->scroll.offset + dmg->scroll.lines) * c->term.grid.cell_height; + int src_y = (dmg->scroll.region.start + 0) * c->term.grid.cell_height; + int dst_y = (dmg->scroll.region.start + dmg->scroll.lines) * c->term.grid.cell_height; int width = buf->width; - int height = (scrolling_region - dmg->scroll.offset - dmg->scroll.lines) * c->term.grid.cell_height; + int height = (dmg->scroll.region.end - dmg->scroll.region.start - dmg->scroll.lines) * c->term.grid.cell_height; const uint32_t stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); LOG_DBG("damage: SCROLL REVERSE: %d-%d by %d lines (dst-y: %d, src-y: %d, " "height: %d, stride: %d, mmap-size: %zu)", - c->term.grid.scrolling_region.start, c->term.grid.scrolling_region.end, + dmg->scroll.region.start, dmg->scroll.region.end, dmg->scroll.lines, dst_y, src_y, height, stride, buf->size); @@ -324,16 +315,6 @@ grid_render_scroll_reverse(struct context *c, struct buffer *buf, cairo_surface_mark_dirty(buf->cairo_surface); wl_surface_damage_buffer(c->wl.surface, 0, dst_y, width, height); - - const int cols = c->term.grid.cols; - struct damage erase = { - .type = DAMAGE_ERASE, - .range = { - .start = (c->term.grid.scrolling_region.start + dmg->scroll.offset) * cols, - .length = dmg->scroll.lines * cols - }, - }; - grid_render_erase(c, buf, &erase); } static void @@ -349,19 +330,8 @@ grid_render(struct context *c) cairo_set_operator(buf->cairo, CAIRO_OPERATOR_SOURCE); - //bool scroll = false; - tll_foreach(c->term.grid.damage, it) { + tll_foreach(c->term.grid.scroll_damage, it) { switch (it->item.type) { - case DAMAGE_ERASE: - case DAMAGE_ERASE_NO_SCROLL: - grid_render_erase(c, buf, &it->item); - break; - - case DAMAGE_UPDATE: - case DAMAGE_UPDATE_NO_SCROLL: - grid_render_update(c, buf, &it->item); - break; - case DAMAGE_SCROLL: grid_render_scroll(c, buf, &it->item); break; @@ -369,6 +339,25 @@ grid_render(struct context *c) case DAMAGE_SCROLL_REVERSE: grid_render_scroll_reverse(c, buf, &it->item); break; + + case DAMAGE_UPDATE: + case DAMAGE_ERASE: + assert(false); + break; + } + + tll_remove(c->term.grid.scroll_damage, it); + } + + tll_foreach(c->term.grid.damage, it) { + switch (it->item.type) { + case DAMAGE_ERASE: grid_render_erase(c, buf, &it->item); break; + case DAMAGE_UPDATE: grid_render_update(c, buf, &it->item); break; + + case DAMAGE_SCROLL: + case DAMAGE_SCROLL_REVERSE: + assert(false); + break; } tll_remove(c->term.grid.damage, it); @@ -458,12 +447,10 @@ resize(struct context *c, int width, int height) LOG_ERRNO("TIOCSWINSZ"); } - if (c->term.grid.scrolling_region.end == old_rows) - c->term.grid.scrolling_region.end = c->term.grid.rows; + if (c->term.grid.scroll_region.end == old_rows) + c->term.grid.scroll_region.end = c->term.grid.rows; - tll_free(c->term.grid.damage); - assert(tll_length(c->term.grid.damage) == 0); - grid_damage_update(&c->term.grid, 0, c->term.grid.rows * c->term.grid.cols); + grid_damage_all(&c->term.grid); if (!c->frame_is_scheduled) grid_render(c); diff --git a/terminal.h b/terminal.h index 5dba62c6..08a0e538 100644 --- a/terminal.h +++ b/terminal.h @@ -28,9 +28,12 @@ struct cell { struct attributes attrs; }; -enum damage_type {DAMAGE_UPDATE, DAMAGE_UPDATE_NO_SCROLL, - DAMAGE_ERASE, DAMAGE_ERASE_NO_SCROLL, - DAMAGE_SCROLL, DAMAGE_SCROLL_REVERSE}; +struct scroll_region { + int start; + int end; +}; + +enum damage_type {DAMAGE_UPDATE, DAMAGE_ERASE, DAMAGE_SCROLL, DAMAGE_SCROLL_REVERSE}; struct damage { enum damage_type type; union { @@ -42,7 +45,7 @@ struct damage { /* DAMAGE_SCROLL, DAMAGE_SCROLL_REVERSE */ struct { - int offset; + struct scroll_region region; int lines; } scroll; }; @@ -54,12 +57,7 @@ struct grid { int cell_width; int cell_height; - /* Scrolling region - counted as lines excluded from scrolling, - * from top and from bottom */ - struct { - int start; - int end; - } scrolling_region; + struct scroll_region scroll_region; int linear_cursor; struct { @@ -80,6 +78,7 @@ struct grid { uint32_t background; tll(struct damage) damage; + tll(struct damage) scroll_damage; }; struct vt_subparams { diff --git a/vt.c b/vt.c index 710b706e..b74202bf 100644 --- a/vt.c +++ b/vt.c @@ -604,7 +604,7 @@ action(struct terminal *term, enum action action, uint8_t c) LOG_DBG("execute: 0x%02x", c); switch (c) { case '\n': - if (term->grid.cursor.row == term->grid.scrolling_region.end - 1) { + if (term->grid.cursor.row == term->grid.scroll_region.end - 1) { grid_scroll(&term->grid, 1); } else grid_cursor_down(&term->grid, 1); @@ -638,7 +638,7 @@ action(struct terminal *term, enum action action, uint8_t c) case ACTION_PRINT: { if (term->grid.print_needs_wrap) { - if (term->grid.cursor.row == term->grid.scrolling_region.end - 1) { + if (term->grid.cursor.row == term->grid.scroll_region.end - 1) { grid_scroll(&term->grid, 1); grid_cursor_to(&term->grid, term->grid.cursor.row, 0); } else