diff --git a/grid.c b/grid.c index c6591dc0..dbd76d5d 100644 --- a/grid.c +++ b/grid.c @@ -85,6 +85,72 @@ grid_damage_erase(struct grid *grid, int start, int length) tll_push_back(grid->damage, dmg); } +static void +damage_adjust_after_scroll(struct grid *grid, int top_margin, + int bottom_margin, int lines) +{ + const int adjustment = lines * grid->cols; + tll_foreach(grid->damage, it) { + if (it->item.type == DAMAGE_SCROLL) + continue; + + assert(top_margin == 0 && "must check if item is in the non-scrolling region"); + + it->item.range.start -= adjustment; + + if (it->item.range.start < 0) { + int new_length = it->item.range.length + 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; + } + } + } +} + +void +grid_damage_scroll(struct grid *grid, int top_margin, int bottom_margin, + int lines) +{ + if (tll_length(grid->damage) > 0 && + tll_front(grid->damage).type == DAMAGE_SCROLL) + { + /* Merge with existing scroll damage */ + + struct damage *dmg = &tll_front(grid->damage); + + assert(dmg->scroll.top_margin == top_margin); + assert(dmg->scroll.bottom_margin == bottom_margin); + + dmg->scroll.lines += lines; + + /* If we've scrolled away the entire screen, replace with an erase */ + if (dmg->scroll.lines >= (grid->rows - (dmg->scroll.top_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; + } + } else { + /* TODO: yield a DAMAGE_ERASE if lines > screen */ + struct damage dmg = { + .type = DAMAGE_SCROLL, + .scroll = { + .top_margin = top_margin, + .bottom_margin = bottom_margin, + .lines = lines + }, + }; + tll_push_front(grid->damage, dmg); + } + + damage_adjust_after_scroll(grid, top_margin, bottom_margin, lines); +} + void grid_erase(struct grid *grid, int start, int end) { @@ -159,24 +225,27 @@ grid_cursor_down(struct grid *grid, int count) void grid_scroll(struct grid *grid, int rows) { - if (rows >= grid->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 && "need to take margins into account"); grid_erase(grid, 0, grid->rows * grid->cols); return; } - int first_row = rows; - int cell_start = first_row * grid->cols; - int cell_end = grid->rows * grid->cols; - int count = cell_end - cell_start; - LOG_DBG("moving %d cells from %d", count, cell_start); + 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); const size_t bytes = count * sizeof(grid->cells[0]); memmove( - &grid->cells[0], &grid->cells[cell_start], + &grid->cells[cell_dst], &grid->cells[cell_src], bytes); - tll_free(grid->damage); - - grid_damage_update(grid, 0, count); + grid_damage_scroll(grid, top_margin, bottom_margin, rows); grid_erase(grid, cell_end - rows * grid->cols, grid->rows * grid->cols); } diff --git a/grid.h b/grid.h index e8abbd89..e208a603 100644 --- a/grid.h +++ b/grid.h @@ -4,6 +4,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); void grid_erase(struct grid *grid, int start, int end); diff --git a/main.c b/main.c index 07d3a280..d27f832a 100644 --- a/main.c +++ b/main.c @@ -220,6 +220,51 @@ grid_render_update(struct context *c, struct buffer *buf, const struct damage *d buf->width, (dmg->range.length + cols - 1) / cols * c->term.grid.cell_height); } +static void +grid_render_scroll(struct context *c, struct buffer *buf, + const struct damage *dmg) +{ + //int x = 0; + int dst_y = (dmg->scroll.top_margin + 0) * c->term.grid.cell_height; + int src_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: %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); + +#if 1 + const int cols = c->term.grid.cols; + struct damage erase = { + .type = DAMAGE_ERASE, + .range = { + .start = (c->term.grid.rows - + dmg->scroll.bottom_margin - + dmg->scroll.lines) * cols, + .length = dmg->scroll.lines * cols + }, + }; + grid_render_erase(c, buf, &erase); +#endif +} + static void grid_render(struct context *c) { @@ -234,6 +279,7 @@ grid_render(struct context *c) cairo_set_operator(buf->cairo, CAIRO_OPERATOR_SOURCE); cairo_set_scaled_font(buf->cairo, c->font); + //bool scroll = false; tll_foreach(c->term.grid.damage, it) { switch (it->item.type) { case DAMAGE_ERASE: @@ -245,13 +291,15 @@ grid_render(struct context *c) break; case DAMAGE_SCROLL: - assert(false); + //scroll = true; + grid_render_scroll(c, buf, &it->item); break; } tll_remove(c->term.grid.damage, it); } + //cairo_surface_flush(buf->cairo_surface); wl_surface_attach(c->wl.surface, buf->wl_buf, 0, 0); struct wl_callback *cb = wl_surface_frame(c->wl.surface); diff --git a/terminal.h b/terminal.h index ad40dc3e..7a033330 100644 --- a/terminal.h +++ b/terminal.h @@ -32,11 +32,18 @@ enum damage_type {DAMAGE_UPDATE, DAMAGE_ERASE, DAMAGE_SCROLL}; struct damage { enum damage_type type; union { + /* DAMAGE_UPDATE, DAMAGE_ERASE */ struct { int start; int length; - } range; /* DAMAGE_UPDATE, DAMAGE_ERASE */ - int lines; /* DAMAGE_SCROLL */ + } range; + + /* DAMAGE_SCROLL */ + struct { + int top_margin; + int bottom_margin; + int lines; + } scroll; }; }; diff --git a/vt.c b/vt.c index 95df06d2..29073b83 100644 --- a/vt.c +++ b/vt.c @@ -158,9 +158,10 @@ 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.rows - 1) + if (term->grid.cursor.row == term->grid.rows - 1) { grid_scroll(&term->grid, 1); - else + /* TODO: simulate \r? */ + } else grid_cursor_down(&term->grid, 1); break;