diff --git a/csi.c b/csi.c index 86ad06db..54d7255d 100644 --- a/csi.c +++ b/csi.c @@ -251,24 +251,32 @@ csi_dispatch(struct terminal *term, uint8_t final) case 'A': { int count = term->vt.params.idx > 0 ? term->vt.params.v[0].value : 1; + if (count == 0) + count = 1; grid_cursor_up(&term->grid, count); break; } case 'B': { int count = term->vt.params.idx > 0 ? term->vt.params.v[0].value : 1; + if (count == 0) + count = 1; grid_cursor_down(&term->grid, count); break; } case 'C': { int count = term->vt.params.idx > 0 ? term->vt.params.v[0].value : 1; + if (count == 0) + count = 1; grid_cursor_right(&term->grid, count); break; } case 'D': { int count = term->vt.params.idx > 0 ? term->vt.params.v[0].value : 1; + if (count == 0) + count = 1; grid_cursor_left(&term->grid, count); break; } @@ -278,6 +286,11 @@ csi_dispatch(struct terminal *term, uint8_t final) int row = term->vt.params.idx > 0 ? term->vt.params.v[0].value : 1; int col = term->vt.params.idx > 1 ? term->vt.params.v[1].value : 1; + if (row == 0) + row = 1; + if (col == 0) + col = 1; + grid_cursor_to(&term->grid, row - 1, col - 1); break; } @@ -285,10 +298,7 @@ csi_dispatch(struct terminal *term, uint8_t final) case 'J': { /* Erase screen */ - int param = 0; - if (term->vt.params.idx > 0) - param = term->vt.params.v[0].value; - + int param = term->vt.params.idx > 0 ? term->vt.params.v[0].value : 0; int start = -1; int end = -1; switch (param) { @@ -357,9 +367,48 @@ csi_dispatch(struct terminal *term, uint8_t final) break; } + case 'L': { + if (term->grid.cursor.row < term->grid.scrolling_region.start || + term->grid.cursor.row >= term->grid.scrolling_region.end) + break; + + int count = min( + term->vt.params.idx > 0 ? term->vt.params.v[0].value : 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); + + grid_scroll_reverse_partial( + &term->grid, + term->grid.cursor.row - term->grid.scrolling_region.start, + count); + grid_cursor_to(&term->grid, term->grid.cursor.row, 0); + break; + } + + case 'M': { + if (term->grid.cursor.row < term->grid.scrolling_region.start || + term->grid.cursor.row >= term->grid.scrolling_region.end) + break; + + int count = min( + term->vt.params.idx > 0 ? term->vt.params.v[0].value : 1, + term->grid.scrolling_region.end - term->grid.cursor.row); + + grid_scroll_partial( + &term->grid, + term->grid.cursor.row - term->grid.scrolling_region.start, + count); + break; + } + case 'P': { /* DCH: Delete character */ int param = term->vt.params.idx > 0 ? term->vt.params.v[0].value : 1; + if (param == 0) + param = 1; /* Only delete up to the right margin */ const int max_end = grid_cursor_linear( @@ -385,10 +434,19 @@ csi_dispatch(struct terminal *term, uint8_t final) int end = term->vt.params.idx > 1 ? term->vt.params.v[1].value : term->grid.rows; + if (start == 0) + start = 1; + if (end == 0) + end = term->grid.rows; + /* 1-based */ term->grid.scrolling_region.start = start - 1; term->grid.scrolling_region.end = end - 1; + 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); @@ -521,6 +579,10 @@ csi_dispatch(struct terminal *term, uint8_t final) term->grid.linear_cursor = grid_cursor_linear( &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; + tll_free(term->grid.damage); grid_damage_update( &term->grid, 0, term->grid.cols * term->grid.rows); diff --git a/grid.c b/grid.c index 7952e7de..057b3d0d 100644 --- a/grid.c +++ b/grid.c @@ -14,7 +14,7 @@ static bool damage_merge_range(struct grid *grid, const struct damage *dmg) { if (tll_length(grid->damage) == 0) - return false;; + return false; struct damage *old = &tll_back(grid->damage); if (old->type != dmg->type) @@ -47,11 +47,54 @@ damage_merge_range(struct grid *grid, const struct damage *dmg) return false; } -void -grid_damage_update(struct grid *grid, int start, int length) +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_UPDATE, + .type = damage_type, .range = {.start = start, .length = length}, }; @@ -66,23 +109,16 @@ grid_damage_update(struct grid *grid, int start, int length) tll_push_back(grid->damage, dmg); } +void +grid_damage_update(struct grid *grid, int start, int length) +{ + grid_damage_update_or_erase(grid, DAMAGE_UPDATE, start, length); +} + void grid_damage_erase(struct grid *grid, int start, int length) { - struct damage dmg = { - .type = DAMAGE_ERASE, - .range = {.start = start, .length = length}, - }; - - assert(dmg.range.start >= 0); - assert(dmg.range.start < grid->rows * grid->cols); - assert(dmg.range.length >= 0); - assert(dmg.range.start + dmg.range.length <= grid->rows * grid->cols); - - if (damage_merge_range(grid, &dmg)) - return; - - tll_push_back(grid->damage, dmg); + grid_damage_update_or_erase(grid, DAMAGE_ERASE, start, length); } static void @@ -98,11 +134,12 @@ damage_adjust_after_scroll(struct grid *grid, enum damage_type damage_type, bottom_margin *= grid->cols; tll_foreach(grid->damage, it) { - if (it->item.type == DAMAGE_SCROLL || - it->item.type == DAMAGE_SCROLL_REVERSE) + if (!(it->item.type == DAMAGE_UPDATE || it->item.type == DAMAGE_ERASE)) continue; - assert(top_margin == 0 && "must check if item is in the non-scrolling region"); + 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); it->item.range.start -= adjustment; int end = it->item.range.start + it->item.range.length; @@ -134,10 +171,12 @@ damage_adjust_after_scroll(struct grid *grid, enum damage_type damage_type, } void -grid_damage_scroll(struct grid *grid, enum damage_type damage_type, int lines) +grid_damage_scroll(struct grid *grid, enum damage_type damage_type, + int offset, int lines) { if (tll_length(grid->damage) > 0 && - tll_front(grid->damage).type == damage_type) + tll_front(grid->damage).type == damage_type && + tll_front(grid->damage).scroll.offset == offset) { /* Merge with existing scroll damage */ @@ -148,15 +187,15 @@ grid_damage_scroll(struct grid *grid, enum damage_type damage_type, int lines) 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) { + if (dmg->scroll.lines >= scrolling_region - offset) { dmg->type = DAMAGE_ERASE; - dmg->range.start = grid->scrolling_region.start * grid->cols; - dmg->range.length = scrolling_region * grid->cols; + dmg->range.start = (grid->scrolling_region.start + offset) * grid->cols; + dmg->range.length = (scrolling_region - offset) * grid->cols; } } else { struct damage dmg = { .type = damage_type, - .scroll = {.lines = lines}, + .scroll = {.offset = offset, .lines = lines}, }; tll_push_front(grid->damage, dmg); } @@ -236,20 +275,20 @@ grid_cursor_down(struct grid *grid, int count) } void -grid_scroll(struct grid *grid, int rows) +grid_scroll_partial(struct grid *grid, int offset, 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; + //const int bottom_margin = grid->rows - grid->scrolling_region.end; - if (rows >= grid_rows) { + if (rows >= grid_rows - offset) { assert(false && "untested"); return; } - int cell_dst = top_margin * grid->cols; - int cell_src = (top_margin + rows) * grid->cols; - int cell_count = (grid_rows - bottom_margin - top_margin - rows) * grid->cols; + 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; LOG_DBG("moving %d cells from %d", cell_count, cell_src); @@ -258,39 +297,51 @@ grid_scroll(struct grid *grid, int rows) &grid->cells[cell_dst], &grid->cells[cell_src], bytes); - grid_damage_scroll(grid, DAMAGE_SCROLL, rows); + grid_damage_scroll(grid, DAMAGE_SCROLL, offset, rows); grid_erase( grid, - (grid_rows - bottom_margin - rows) * grid->cols, - grid->rows * grid->cols); + (grid_rows - rows) * grid->cols, + grid_rows * grid->cols); +} + +void +grid_scroll(struct grid *grid, int rows) +{ + grid_scroll_partial(grid, 0, rows); +} + +void +grid_scroll_reverse_partial(struct grid *grid, int offset, 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) { + 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 - bottom_margin - top_margin - offset - 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, offset, rows); + grid_erase( + grid, + (top_margin + offset) * grid->cols, + (top_margin + offset + rows) * grid->cols); } void grid_scroll_reverse(struct grid *grid, 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) { - 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, rows); - grid_erase( - grid, - top_margin * grid->cols, - (top_margin + rows) * grid->cols); + grid_scroll_reverse_partial(grid, 0, rows); } diff --git a/grid.h b/grid.h index 9a52b108..d27099cd 100644 --- a/grid.h +++ b/grid.h @@ -5,7 +5,7 @@ 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 lines); + struct grid *grid, enum damage_type damage_type, int offset, int lines); void grid_erase(struct grid *grid, int start, int end); @@ -18,6 +18,9 @@ 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); + int grid_cursor_linear(const struct grid *grid, int row, int col); diff --git a/main.c b/main.c index 6bec90bc..2e891cd1 100644 --- a/main.c +++ b/main.c @@ -259,10 +259,10 @@ grid_render_scroll(struct context *c, struct buffer *buf, 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 + 0) * c->term.grid.cell_height; - int src_y = (c->term.grid.scrolling_region.start + dmg->scroll.lines) * c->term.grid.cell_height; + 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 width = buf->width; - int height = (scrolling_region - dmg->scroll.lines) * c->term.grid.cell_height; + int height = (scrolling_region - dmg->scroll.offset - dmg->scroll.lines) * c->term.grid.cell_height; const uint32_t stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); @@ -281,6 +281,8 @@ 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"); + const int cols = c->term.grid.cols; struct damage erase = { .type = DAMAGE_ERASE, @@ -301,10 +303,10 @@ grid_render_scroll_reverse(struct context *c, struct buffer *buf, 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 + 0) * c->term.grid.cell_height; - int dst_y = (c->term.grid.scrolling_region.start + dmg->scroll.lines) * c->term.grid.cell_height; + 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 width = buf->width; - int height = (scrolling_region - dmg->scroll.lines) * c->term.grid.cell_height; + int height = (scrolling_region - dmg->scroll.offset - dmg->scroll.lines) * c->term.grid.cell_height; const uint32_t stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); @@ -327,7 +329,7 @@ grid_render_scroll_reverse(struct context *c, struct buffer *buf, struct damage erase = { .type = DAMAGE_ERASE, .range = { - .start = c->term.grid.scrolling_region.start * cols, + .start = (c->term.grid.scrolling_region.start + dmg->scroll.offset) * cols, .length = dmg->scroll.lines * cols }, }; @@ -351,10 +353,12 @@ grid_render(struct context *c) tll_foreach(c->term.grid.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; diff --git a/terminal.h b/terminal.h index ee866b83..5dba62c6 100644 --- a/terminal.h +++ b/terminal.h @@ -28,7 +28,9 @@ struct cell { struct attributes attrs; }; -enum damage_type {DAMAGE_UPDATE, DAMAGE_ERASE, DAMAGE_SCROLL, DAMAGE_SCROLL_REVERSE}; +enum damage_type {DAMAGE_UPDATE, DAMAGE_UPDATE_NO_SCROLL, + DAMAGE_ERASE, DAMAGE_ERASE_NO_SCROLL, + DAMAGE_SCROLL, DAMAGE_SCROLL_REVERSE}; struct damage { enum damage_type type; union { @@ -40,6 +42,7 @@ struct damage { /* DAMAGE_SCROLL, DAMAGE_SCROLL_REVERSE */ struct { + int offset; int lines; } scroll; }; diff --git a/vt.c b/vt.c index df5f559a..710b706e 100644 --- a/vt.c +++ b/vt.c @@ -604,9 +604,8 @@ 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.scrolling_region.end - 1) { grid_scroll(&term->grid, 1); - /* TODO: simulate \r? */ } else grid_cursor_down(&term->grid, 1); break; @@ -639,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.rows - 1) { + if (term->grid.cursor.row == term->grid.scrolling_region.end - 1) { grid_scroll(&term->grid, 1); grid_cursor_to(&term->grid, term->grid.cursor.row, 0); } else