diff --git a/grid.c b/grid.c index dbd76d5d..78fb9e40 100644 --- a/grid.c +++ b/grid.c @@ -86,20 +86,27 @@ grid_damage_erase(struct grid *grid, int start, int length) } static void -damage_adjust_after_scroll(struct grid *grid, int top_margin, - int bottom_margin, int lines) +damage_adjust_after_scroll(struct grid *grid, enum damage_type damage_type, + int top_margin, int bottom_margin, int lines) { - const int adjustment = lines * grid->cols; + const int adjustment + = lines * grid->cols * (damage_type == DAMAGE_SCROLL_REVERSE ? -1 : 1); + top_margin *= grid->cols; + bottom_margin *= grid->cols; + tll_foreach(grid->damage, it) { - if (it->item.type == DAMAGE_SCROLL) + if (it->item.type == DAMAGE_SCROLL || + it->item.type == DAMAGE_SCROLL_REVERSE) continue; assert(top_margin == 0 && "must check if item is in the non-scrolling region"); it->item.range.start -= adjustment; + int end = it->item.range.start + it->item.range.length; - if (it->item.range.start < 0) { - int new_length = it->item.range.length + it->item.range.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) @@ -109,15 +116,26 @@ damage_adjust_after_scroll(struct grid *grid, int top_margin, 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; + } } } void -grid_damage_scroll(struct grid *grid, int top_margin, int bottom_margin, - int lines) +grid_damage_scroll(struct grid *grid, enum damage_type damage_type, + int top_margin, int bottom_margin, int lines) { if (tll_length(grid->damage) > 0 && - tll_front(grid->damage).type == DAMAGE_SCROLL) + tll_front(grid->damage).type == damage_type) { /* Merge with existing scroll damage */ @@ -133,12 +151,11 @@ grid_damage_scroll(struct grid *grid, int top_margin, int bottom_margin, dmg->scroll.bottom_margin))) { dmg->type = DAMAGE_ERASE; dmg->range.start = dmg->scroll.top_margin * grid->cols; - dmg->range.length = dmg->scroll.lines * grid->cols; + dmg->range.length = (grid->rows - top_margin - bottom_margin) * grid->cols; } } else { - /* TODO: yield a DAMAGE_ERASE if lines > screen */ struct damage dmg = { - .type = DAMAGE_SCROLL, + .type = damage_type, .scroll = { .top_margin = top_margin, .bottom_margin = bottom_margin, @@ -148,7 +165,8 @@ grid_damage_scroll(struct grid *grid, int top_margin, int bottom_margin, tll_push_front(grid->damage, dmg); } - damage_adjust_after_scroll(grid, top_margin, bottom_margin, lines); + damage_adjust_after_scroll( + grid, damage_type, top_margin, bottom_margin, lines); } void @@ -230,22 +248,57 @@ grid_scroll(struct grid *grid, int rows) const int grid_rows = grid->rows - top_margin - bottom_margin; if (rows >= grid_rows) { - assert(false && "need to take margins into account"); - grid_erase(grid, 0, grid->rows * grid->cols); + assert(false && "untested"); + grid_erase(grid, + top_margin * grid->cols, + (grid->rows - bottom_margin) * grid->cols); return; } int cell_dst = top_margin * grid->cols; int cell_src = (top_margin + rows) * grid->cols; - int cell_end = (grid->rows - bottom_margin) * grid->cols; - int count = cell_end - cell_src; - LOG_DBG("moving %d cells from %d", count, cell_src); + int cell_count = (grid_rows - bottom_margin - top_margin - rows) * grid->cols; - const size_t bytes = count * sizeof(grid->cells[0]); + LOG_DBG("moving %d cells from %d", cell_count, cell_src); + + const size_t bytes = cell_count * sizeof(grid->cells[0]); memmove( &grid->cells[cell_dst], &grid->cells[cell_src], bytes); - grid_damage_scroll(grid, top_margin, bottom_margin, rows); - grid_erase(grid, cell_end - rows * grid->cols, grid->rows * grid->cols); + grid_damage_scroll(grid, DAMAGE_SCROLL, top_margin, bottom_margin, rows); + grid_erase( + grid, + (grid_rows - bottom_margin - rows) * grid->cols, + grid->rows * grid->cols); +} + +void +grid_scroll_reverse(struct grid *grid, int rows) +{ + const int top_margin = 0; + const int bottom_margin = 0; + const int grid_rows = grid->rows - top_margin - bottom_margin; + + if (rows >= grid_rows) { + assert(false && "todo"); + return; + } + + int cell_dst = (top_margin + rows) * grid->cols; + int cell_src = top_margin * grid->cols; + int cell_count = (grid_rows - bottom_margin - top_margin - rows) * grid->cols; + + LOG_DBG("moving %d cells from %d", cell_count, cell_src); + + 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, top_margin, bottom_margin, rows); + grid_erase( + grid, + top_margin * grid->cols, + (top_margin + rows) * grid->cols); } diff --git a/grid.h b/grid.h index e208a603..1951f54f 100644 --- a/grid.h +++ b/grid.h @@ -5,7 +5,8 @@ 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, int top_margin, int bottom_margin, int lines); + struct grid *grid, enum damage_type damage_type, + int top_margin, int bottom_margin, int lines); void grid_erase(struct grid *grid, int start, int end); @@ -16,6 +17,7 @@ void grid_cursor_up(struct grid *grid, int count); 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); int grid_cursor_linear(const struct grid *grid, int row, int col); diff --git a/main.c b/main.c index 5a2dbc93..b0395839 100644 --- a/main.c +++ b/main.c @@ -266,7 +266,8 @@ grid_render_scroll(struct context *c, struct buffer *buf, 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)", + LOG_DBG("damage: SCROLL: %d-%d by %d lines (dst-y: %d, src-y: %d, " + "height: %d, stride: %d, mmap-size: %zu)", dmg->scroll.top_margin, c->term.grid.rows - dmg->scroll.bottom_margin, dmg->scroll.lines, @@ -281,7 +282,6 @@ grid_render_scroll(struct context *c, struct buffer *buf, wl_surface_damage_buffer(c->wl.surface, 0, dst_y, width, height); -#if 1 const int cols = c->term.grid.cols; struct damage erase = { .type = DAMAGE_ERASE, @@ -293,7 +293,48 @@ grid_render_scroll(struct context *c, struct buffer *buf, }, }; 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; + int src_y = (dmg->scroll.top_margin + 0) * c->term.grid.cell_height; + int dst_y = (dmg->scroll.top_margin + dmg->scroll.lines) * c->term.grid.cell_height; + int width = buf->width; + int height = (c->term.grid.rows - + dmg->scroll.top_margin - + dmg->scroll.bottom_margin - + 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)", + dmg->scroll.top_margin, + c->term.grid.rows - dmg->scroll.bottom_margin, + dmg->scroll.lines, + dst_y, src_y, height, stride, + buf->size); + + cairo_surface_flush(buf->cairo_surface); + uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface); + + memmove(raw + dst_y * stride, raw + src_y * stride, height * stride); + 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 = dmg->scroll.top_margin * cols, + .length = dmg->scroll.lines * cols + }, + }; + grid_render_erase(c, buf, &erase); } static void @@ -321,9 +362,12 @@ grid_render(struct context *c) break; case DAMAGE_SCROLL: - //scroll = true; grid_render_scroll(c, buf, &it->item); break; + + case DAMAGE_SCROLL_REVERSE: + grid_render_scroll_reverse(c, buf, &it->item); + break; } tll_remove(c->term.grid.damage, it); diff --git a/terminal.h b/terminal.h index 28bfca17..08b441d9 100644 --- a/terminal.h +++ b/terminal.h @@ -28,7 +28,7 @@ struct cell { struct attributes attrs; }; -enum damage_type {DAMAGE_UPDATE, DAMAGE_ERASE, DAMAGE_SCROLL}; +enum damage_type {DAMAGE_UPDATE, DAMAGE_ERASE, DAMAGE_SCROLL, DAMAGE_SCROLL_REVERSE}; struct damage { enum damage_type type; union { @@ -38,7 +38,7 @@ struct damage { int length; } range; - /* DAMAGE_SCROLL */ + /* DAMAGE_SCROLL, DAMAGE_SCROLL_REVERSE */ struct { int top_margin; int bottom_margin; diff --git a/vt.c b/vt.c index 1ba0609b..3a71c924 100644 --- a/vt.c +++ b/vt.c @@ -567,6 +567,11 @@ esc_dispatch(struct terminal *term, uint8_t final) #endif switch (final) { + case 'M': + /* ri - reverse index (scroll reverse) */ + grid_scroll_reverse(&term->grid, 1); + break; + case '=': /* Other half of xterm's smkx */ LOG_WARN("ignoring ESC with final %c", final);