Merge branch 'translate-cursor-coords-when-reflowing'

This commit is contained in:
Daniel Eklöf 2020-04-17 21:16:38 +02:00
commit a9af8290ab
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
9 changed files with 236 additions and 195 deletions

View file

@ -50,6 +50,7 @@
* Alt+Return to emit "ESC \r". * Alt+Return to emit "ESC \r".
* Trackpad sloooow scrolling to eventually scroll a line. * Trackpad sloooow scrolling to eventually scroll a line.
* Memory leak in terminal reset. * Memory leak in terminal reset.
* Translation of cursor coordinates on resize
### Security ### Security

128
csi.c
View file

@ -382,7 +382,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
/* VPA - vertical line position absolute */ /* VPA - vertical line position absolute */
int rel_row = vt_param_get(term, 0, 1) - 1; int rel_row = vt_param_get(term, 0, 1) - 1;
int row = term_row_rel_to_abs(term, rel_row); int row = term_row_rel_to_abs(term, rel_row);
term_cursor_to(term, row, term->cursor.point.col); term_cursor_to(term, row, term->grid->cursor.point.col);
break; break;
} }
@ -411,13 +411,13 @@ csi_dispatch(struct terminal *term, uint8_t final)
case 'E': case 'E':
/* CNL - Cursor Next Line */ /* CNL - Cursor Next Line */
term_cursor_down(term, vt_param_get(term, 0, 1)); term_cursor_down(term, vt_param_get(term, 0, 1));
term_cursor_left(term, term->cursor.point.col); term_cursor_left(term, term->grid->cursor.point.col);
break; break;
case 'F': case 'F':
/* CPL - Cursor Previous Line */ /* CPL - Cursor Previous Line */
term_cursor_up(term, vt_param_get(term, 0, 1)); term_cursor_up(term, vt_param_get(term, 0, 1));
term_cursor_left(term, term->cursor.point.col); term_cursor_left(term, term->grid->cursor.point.col);
break; break;
case 'g': { case 'g': {
@ -426,9 +426,9 @@ csi_dispatch(struct terminal *term, uint8_t final)
case 0: case 0:
/* Clear tab stop at *current* column */ /* Clear tab stop at *current* column */
tll_foreach(term->tab_stops, it) { tll_foreach(term->tab_stops, it) {
if (it->item == term->cursor.point.col) if (it->item == term->grid->cursor.point.col)
tll_remove(term->tab_stops, it); tll_remove(term->tab_stops, it);
else if (it->item > term->cursor.point.col) else if (it->item > term->grid->cursor.point.col)
break; break;
} }
@ -450,7 +450,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
case 'G': { case 'G': {
/* Cursor horizontal absolute */ /* Cursor horizontal absolute */
int col = min(vt_param_get(term, 0, 1), term->cols) - 1; int col = min(vt_param_get(term, 0, 1), term->cols) - 1;
term_cursor_to(term, term->cursor.point.row, col); term_cursor_to(term, term->grid->cursor.point.row, col);
break; break;
} }
@ -473,15 +473,15 @@ csi_dispatch(struct terminal *term, uint8_t final)
/* From cursor to end of screen */ /* From cursor to end of screen */
term_erase( term_erase(
term, term,
&term->cursor.point, &term->grid->cursor.point,
&(struct coord){term->cols - 1, term->rows - 1}); &(struct coord){term->cols - 1, term->rows - 1});
term->cursor.lcf = false; term->grid->cursor.lcf = false;
break; break;
case 1: case 1:
/* From start of screen to cursor */ /* From start of screen to cursor */
term_erase(term, &(struct coord){0, 0}, &term->cursor.point); term_erase(term, &(struct coord){0, 0}, &term->grid->cursor.point);
term->cursor.lcf = false; term->grid->cursor.lcf = false;
break; break;
case 2: case 2:
@ -490,7 +490,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
term, term,
&(struct coord){0, 0}, &(struct coord){0, 0},
&(struct coord){term->cols - 1, term->rows - 1}); &(struct coord){term->cols - 1, term->rows - 1});
term->cursor.lcf = false; term->grid->cursor.lcf = false;
break; break;
case 3: { case 3: {
@ -531,25 +531,25 @@ csi_dispatch(struct terminal *term, uint8_t final)
/* From cursor to end of line */ /* From cursor to end of line */
term_erase( term_erase(
term, term,
&term->cursor.point, &term->grid->cursor.point,
&(struct coord){term->cols - 1, term->cursor.point.row}); &(struct coord){term->cols - 1, term->grid->cursor.point.row});
term->cursor.lcf = false; term->grid->cursor.lcf = false;
break; break;
case 1: case 1:
/* From start of line to cursor */ /* From start of line to cursor */
term_erase( term_erase(
term, &(struct coord){0, term->cursor.point.row}, &term->cursor.point); term, &(struct coord){0, term->grid->cursor.point.row}, &term->grid->cursor.point);
term->cursor.lcf = false; term->grid->cursor.lcf = false;
break; break;
case 2: case 2:
/* Entire line */ /* Entire line */
term_erase( term_erase(
term, term,
&(struct coord){0, term->cursor.point.row}, &(struct coord){0, term->grid->cursor.point.row},
&(struct coord){term->cols - 1, term->cursor.point.row}); &(struct coord){term->cols - 1, term->grid->cursor.point.row});
term->cursor.lcf = false; term->grid->cursor.lcf = false;
break; break;
default: default:
@ -561,36 +561,36 @@ csi_dispatch(struct terminal *term, uint8_t final)
} }
case 'L': { case 'L': {
if (term->cursor.point.row < term->scroll_region.start || if (term->grid->cursor.point.row < term->scroll_region.start ||
term->cursor.point.row >= term->scroll_region.end) term->grid->cursor.point.row >= term->scroll_region.end)
break; break;
int count = min( int count = min(
vt_param_get(term, 0, 1), vt_param_get(term, 0, 1),
term->scroll_region.end - term->cursor.point.row); term->scroll_region.end - term->grid->cursor.point.row);
term_scroll_reverse_partial( term_scroll_reverse_partial(
term, term,
(struct scroll_region){ (struct scroll_region){
.start = term->cursor.point.row, .start = term->grid->cursor.point.row,
.end = term->scroll_region.end}, .end = term->scroll_region.end},
count); count);
break; break;
} }
case 'M': { case 'M': {
if (term->cursor.point.row < term->scroll_region.start || if (term->grid->cursor.point.row < term->scroll_region.start ||
term->cursor.point.row >= term->scroll_region.end) term->grid->cursor.point.row >= term->scroll_region.end)
break; break;
int count = min( int count = min(
vt_param_get(term, 0, 1), vt_param_get(term, 0, 1),
term->scroll_region.end - term->cursor.point.row); term->scroll_region.end - term->grid->cursor.point.row);
term_scroll_partial( term_scroll_partial(
term, term,
(struct scroll_region){ (struct scroll_region){
.start = term->cursor.point.row, .start = term->grid->cursor.point.row,
.end = term->scroll_region.end}, .end = term->scroll_region.end},
count); count);
break; break;
@ -601,26 +601,26 @@ csi_dispatch(struct terminal *term, uint8_t final)
/* Number of characters to delete */ /* Number of characters to delete */
int count = min( int count = min(
vt_param_get(term, 0, 1), term->cols - term->cursor.point.col); vt_param_get(term, 0, 1), term->cols - term->grid->cursor.point.col);
/* Number of characters left after deletion (on current line) */ /* Number of characters left after deletion (on current line) */
int remaining = term->cols - (term->cursor.point.col + count); int remaining = term->cols - (term->grid->cursor.point.col + count);
/* 'Delete' characters by moving the remaining ones */ /* 'Delete' characters by moving the remaining ones */
memmove(&term->grid->cur_row->cells[term->cursor.point.col], memmove(&term->grid->cur_row->cells[term->grid->cursor.point.col],
&term->grid->cur_row->cells[term->cursor.point.col + count], &term->grid->cur_row->cells[term->grid->cursor.point.col + count],
remaining * sizeof(term->grid->cur_row->cells[0])); remaining * sizeof(term->grid->cur_row->cells[0]));
for (size_t c = 0; c < remaining; c++) for (size_t c = 0; c < remaining; c++)
term->grid->cur_row->cells[term->cursor.point.col + c].attrs.clean = 0; term->grid->cur_row->cells[term->grid->cursor.point.col + c].attrs.clean = 0;
term->grid->cur_row->dirty = true; term->grid->cur_row->dirty = true;
/* Erase the remainder of the line */ /* Erase the remainder of the line */
term_erase( term_erase(
term, term,
&(struct coord){term->cursor.point.col + remaining, term->cursor.point.row}, &(struct coord){term->grid->cursor.point.col + remaining, term->grid->cursor.point.row},
&(struct coord){term->cols - 1, term->cursor.point.row}); &(struct coord){term->cols - 1, term->grid->cursor.point.row});
term->cursor.lcf = false; term->grid->cursor.lcf = false;
break; break;
} }
@ -629,25 +629,25 @@ csi_dispatch(struct terminal *term, uint8_t final)
/* Number of characters to insert */ /* Number of characters to insert */
int count = min( int count = min(
vt_param_get(term, 0, 1), term->cols - term->cursor.point.col); vt_param_get(term, 0, 1), term->cols - term->grid->cursor.point.col);
/* Characters to move */ /* Characters to move */
int remaining = term->cols - (term->cursor.point.col + count); int remaining = term->cols - (term->grid->cursor.point.col + count);
/* Push existing characters */ /* Push existing characters */
memmove(&term->grid->cur_row->cells[term->cursor.point.col + count], memmove(&term->grid->cur_row->cells[term->grid->cursor.point.col + count],
&term->grid->cur_row->cells[term->cursor.point.col], &term->grid->cur_row->cells[term->grid->cursor.point.col],
remaining * sizeof(term->grid->cur_row->cells[0])); remaining * sizeof(term->grid->cur_row->cells[0]));
for (size_t c = 0; c < remaining; c++) for (size_t c = 0; c < remaining; c++)
term->grid->cur_row->cells[term->cursor.point.col + count + c].attrs.clean = 0; term->grid->cur_row->cells[term->grid->cursor.point.col + count + c].attrs.clean = 0;
term->grid->cur_row->dirty = true; term->grid->cur_row->dirty = true;
/* Erase (insert space characters) */ /* Erase (insert space characters) */
term_erase( term_erase(
term, term,
&term->cursor.point, &term->grid->cursor.point,
&(struct coord){term->cursor.point.col + count - 1, term->cursor.point.row}); &(struct coord){term->grid->cursor.point.col + count - 1, term->grid->cursor.point.row});
term->cursor.lcf = false; term->grid->cursor.lcf = false;
break; break;
} }
@ -662,13 +662,13 @@ csi_dispatch(struct terminal *term, uint8_t final)
case 'X': { case 'X': {
/* Erase chars */ /* Erase chars */
int count = min( int count = min(
vt_param_get(term, 0, 1), term->cols - term->cursor.point.col); vt_param_get(term, 0, 1), term->cols - term->grid->cursor.point.col);
term_erase( term_erase(
term, term,
&term->cursor.point, &term->grid->cursor.point,
&(struct coord){term->cursor.point.col + count - 1, term->cursor.point.row}); &(struct coord){term->grid->cursor.point.col + count - 1, term->grid->cursor.point.row});
term->cursor.lcf = false; term->grid->cursor.lcf = false;
break; break;
} }
@ -677,13 +677,13 @@ csi_dispatch(struct terminal *term, uint8_t final)
for (int i = 0; i < vt_param_get(term, 0, 1); i++) { for (int i = 0; i < vt_param_get(term, 0, 1); i++) {
int new_col = term->cols - 1; int new_col = term->cols - 1;
tll_foreach(term->tab_stops, it) { tll_foreach(term->tab_stops, it) {
if (it->item > term->cursor.point.col) { if (it->item > term->grid->cursor.point.col) {
new_col = it->item; new_col = it->item;
break; break;
} }
} }
assert(new_col >= term->cursor.point.col); assert(new_col >= term->grid->cursor.point.col);
term_cursor_right(term, new_col - term->cursor.point.col); term_cursor_right(term, new_col - term->grid->cursor.point.col);
} }
break; break;
} }
@ -693,13 +693,13 @@ csi_dispatch(struct terminal *term, uint8_t final)
for (int i = 0; i < vt_param_get(term, 0, 1); i++) { for (int i = 0; i < vt_param_get(term, 0, 1); i++) {
int new_col = 0; int new_col = 0;
tll_rforeach(term->tab_stops, it) { tll_rforeach(term->tab_stops, it) {
if (it->item < term->cursor.point.col) { if (it->item < term->grid->cursor.point.col) {
new_col = it->item; new_col = it->item;
break; break;
} }
} }
assert(term->cursor.point.col >= new_col); assert(term->grid->cursor.point.col >= new_col);
term_cursor_left(term, term->cursor.point.col - new_col); term_cursor_left(term, term->grid->cursor.point.col - new_col);
} }
break; break;
@ -757,11 +757,11 @@ csi_dispatch(struct terminal *term, uint8_t final)
} }
case 's': case 's':
term->saved_cursor = term->cursor; term->grid->saved_cursor = term->grid->cursor;
break; break;
case 'u': case 'u':
term_restore_cursor(term, &term->saved_cursor); term_restore_cursor(term, &term->grid->saved_cursor);
break; break;
case 't': { case 't': {
@ -861,15 +861,15 @@ csi_dispatch(struct terminal *term, uint8_t final)
/* u7 - cursor position query */ /* u7 - cursor position query */
int row = term->origin == ORIGIN_ABSOLUTE int row = term->origin == ORIGIN_ABSOLUTE
? term->cursor.point.row ? term->grid->cursor.point.row
: term->cursor.point.row - term->scroll_region.start; : term->grid->cursor.point.row - term->scroll_region.start;
/* TODO: we use 0-based position, while the xterm /* TODO: we use 0-based position, while the xterm
* terminfo says the receiver of the reply should * terminfo says the receiver of the reply should
* decrement, hence we must add 1 */ * decrement, hence we must add 1 */
char reply[64]; char reply[64];
snprintf(reply, sizeof(reply), "\x1b[%d;%dR", snprintf(reply, sizeof(reply), "\x1b[%d;%dR",
row + 1, term->cursor.point.col + 1); row + 1, term->grid->cursor.point.col + 1);
term_to_slave(term, reply, strlen(reply)); term_to_slave(term, reply, strlen(reply));
break; break;
} }
@ -1014,9 +1014,11 @@ csi_dispatch(struct terminal *term, uint8_t final)
selection_cancel(term); selection_cancel(term);
term->grid = &term->alt; term->grid = &term->alt;
term->alt_saved_cursor = term->cursor;
term_cursor_to(term, term->cursor.point.row, term->cursor.point.col); term_cursor_to(
term,
min(term->grid->cursor.point.row, term->rows - 1),
min(term->grid->cursor.point.col, term->cols - 1));
tll_free(term->alt.damage); tll_free(term->alt.damage);
tll_free(term->alt.scroll_damage); tll_free(term->alt.scroll_damage);
@ -1137,7 +1139,11 @@ csi_dispatch(struct terminal *term, uint8_t final)
selection_cancel(term); selection_cancel(term);
term->grid = &term->normal; term->grid = &term->normal;
term_restore_cursor(term, &term->alt_saved_cursor);
term_cursor_to(
term,
min(term->grid->cursor.point.row, term->rows - 1),
min(term->grid->cursor.point.col, term->cols - 1));
tll_free(term->alt.damage); tll_free(term->alt.damage);
tll_free(term->alt.scroll_damage); tll_free(term->alt.scroll_damage);

125
grid.c
View file

@ -53,16 +53,16 @@ grid_row_free(struct row *row)
free(row); free(row);
} }
int void
grid_reflow(struct grid *grid, int new_rows, int new_cols, grid_reflow(struct grid *grid, int new_rows, int new_cols,
int old_screen_rows, int new_screen_rows) int old_screen_rows, int new_screen_rows,
size_t tracking_points_count,
struct coord *_tracking_points[static tracking_points_count])
{ {
struct row *const *old_grid = grid->rows; struct row *const *old_grid = grid->rows;
const int old_rows = grid->num_rows; const int old_rows = grid->num_rows;
const int old_cols = grid->num_cols; const int old_cols = grid->num_cols;
//assert(old_rows != new_rows || old_cols != new_cols);
int new_col_idx = 0; int new_col_idx = 0;
int new_row_idx = 0; int new_row_idx = 0;
@ -79,13 +79,31 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols,
tll(struct sixel) new_sixels = tll_init(); tll(struct sixel) new_sixels = tll_init();
/* Turn cursor coordinates into grid absolute coordinates */
struct coord cursor = grid->cursor.point;
cursor.row += grid->offset;
cursor.row &= old_rows - 1;
struct coord saved_cursor = grid->saved_cursor.point;
saved_cursor.row += grid->offset;
saved_cursor.row &= old_rows - 1;
tll(struct coord *) tracking_points = tll_init();
tll_push_back(tracking_points, &cursor);
tll_push_back(tracking_points, &saved_cursor);
for (size_t i = 0; i < tracking_points_count; i++)
tll_push_back(tracking_points, _tracking_points[i]);
/* /*
* Walk the old grid * Walk the old grid
*/ */
for (int r = 0; r < old_rows; r++) { for (int r = 0; r < old_rows; r++) {
const size_t old_row_idx = (offset + r) & (old_rows - 1);
/* Unallocated (empty) rows we can simply skip */ /* Unallocated (empty) rows we can simply skip */
const struct row *old_row = old_grid[(offset + r) & (old_rows - 1)]; const struct row *old_row = old_grid[old_row_idx];
if (old_row == NULL) if (old_row == NULL)
continue; continue;
@ -102,7 +120,7 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols,
* the "real" sixel list. * the "real" sixel list.
*/ */
tll_foreach(grid->sixel_images, it) { tll_foreach(grid->sixel_images, it) {
if (it->item.pos.row == ((offset + r) & (old_rows - 1))) { if (it->item.pos.row == old_row_idx) {
struct sixel six = it->item; struct sixel six = it->item;
six.pos.row = new_row_idx; six.pos.row = new_row_idx;
@ -111,6 +129,21 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols,
} }
} }
#define line_wrap() \
do { \
new_col_idx = 0; \
new_row_idx = (new_row_idx + 1) & (new_rows - 1); \
\
new_row = new_grid[new_row_idx]; \
if (new_row == NULL) { \
new_row = grid_row_alloc(new_cols, true); \
new_grid[new_row_idx] = new_row; \
} else { \
memset(new_row->cells, 0, new_cols * sizeof(new_row->cells[0])); \
new_row->linebreak = false; \
} \
} while(0)
/* /*
* Keep track of empty cells. If the old line ends with a * Keep track of empty cells. If the old line ends with a
* string of empty cells, we don't need to, nor do we want to, * string of empty cells, we don't need to, nor do we want to,
@ -122,11 +155,23 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols,
/* Walk current line of the old grid */ /* Walk current line of the old grid */
for (int c = 0; c < old_cols; c++) { for (int c = 0; c < old_cols; c++) {
if (old_row->cells[c].wc == 0) {
/* Check if this cell is one of the tracked cells */
bool is_tracking_point = false;
tll_foreach(tracking_points, it) {
if (it->item->row == old_row_idx && it->item->col == c) {
is_tracking_point = true;
break;
}
}
if (old_row->cells[c].wc == 0 && !is_tracking_point) {
empty_count++; empty_count++;
continue; continue;
} }
/* Allow left-adjusted and right-adjusted text, with empty
* cells in between, to be "pushed together" */
int old_cols_left = old_cols - c; int old_cols_left = old_cols - c;
int cols_needed = empty_count + old_cols_left; int cols_needed = empty_count + old_cols_left;
int new_cols_left = new_cols - new_col_idx; int new_cols_left = new_cols - new_col_idx;
@ -138,6 +183,7 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols,
/* Out of columns on current row in new grid? */ /* Out of columns on current row in new grid? */
if (new_col_idx >= new_cols) { if (new_col_idx >= new_cols) {
#if 0
/* /*
* If last cell on last row and first cell on new * If last cell on last row and first cell on new
* row are non-empty, wrap the line, otherwise * row are non-empty, wrap the line, otherwise
@ -148,18 +194,8 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols,
{ {
new_row->linebreak = true; new_row->linebreak = true;
} }
#endif
new_col_idx = 0; line_wrap();
new_row_idx = (new_row_idx + 1) & (new_rows - 1);
new_row = new_grid[new_row_idx];
if (new_row == NULL) {
new_row = grid_row_alloc(new_cols, true);
new_grid[new_row_idx] = new_row;
} else {
memset(new_row->cells, 0, new_cols * sizeof(new_row->cells[0]));
new_row->linebreak = false;
}
} }
assert(new_row != NULL); assert(new_row != NULL);
@ -168,6 +204,17 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols,
new_row->cells[new_col_idx] = *old_cell; new_row->cells[new_col_idx] = *old_cell;
new_row->cells[new_col_idx].attrs.clean = 1; new_row->cells[new_col_idx].attrs.clean = 1;
/* Translate tracking point(s) */
if (is_tracking_point && i >= empty_count) {
tll_foreach(tracking_points, it) {
if (it->item->row == old_row_idx && it->item->col == c) {
it->item->row = new_row_idx;
it->item->col = new_col_idx;
tll_remove(tracking_points, it);
}
}
}
new_col_idx++; new_col_idx++;
} }
@ -176,19 +223,10 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols,
if (old_row->linebreak) { if (old_row->linebreak) {
new_row->linebreak = true; new_row->linebreak = true;
line_wrap();
new_col_idx = 0;
new_row_idx = (new_row_idx + 1) & (new_rows - 1);
new_row = new_grid[new_row_idx];
if (new_row == NULL) {
new_row = grid_row_alloc(new_cols, true);
new_grid[new_row_idx] = new_row;
} else {
memset(new_row->cells, 0, new_cols * sizeof(new_row->cells[0]));
new_row->linebreak = false;
}
} }
#undef line_wrap
} }
/* Set offset such that the last reflowed row is at the bottom */ /* Set offset such that the last reflowed row is at the bottom */
@ -211,11 +249,32 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols,
grid_row_free(old_grid[r]); grid_row_free(old_grid[r]);
free(grid->rows); free(grid->rows);
grid->cur_row = new_grid[new_row_idx]; grid->cur_row = new_grid[cursor.row];
grid->rows = new_grid; grid->rows = new_grid;
grid->num_rows = new_rows; grid->num_rows = new_rows;
grid->num_cols = new_cols; grid->num_cols = new_cols;
/* Convert absolute coordinates to screen relative */
cursor.row -= grid->offset;
while (cursor.row < 0)
cursor.row += grid->num_rows;
assert(cursor.row >= 0);
assert(cursor.row < grid->num_rows);
saved_cursor.row -= grid->offset;
while (saved_cursor.row < 0)
saved_cursor.row += grid->num_rows;
assert(saved_cursor.row >= 0);
assert(saved_cursor.row < grid->num_rows);
grid->cursor.point = cursor;
grid->saved_cursor.point = saved_cursor;
grid->cursor.lcf = false;
grid->saved_cursor.lcf = false;
/* Destroy any non-moved sixels */ /* Destroy any non-moved sixels */
tll_foreach(grid->sixel_images, it) tll_foreach(grid->sixel_images, it)
sixel_destroy(&it->item); sixel_destroy(&it->item);
@ -226,5 +285,5 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols,
tll_push_back(grid->sixel_images, it->item); tll_push_back(grid->sixel_images, it->item);
tll_free(new_sixels); tll_free(new_sixels);
return new_row_idx; tll_free(tracking_points);
} }

6
grid.h
View file

@ -6,9 +6,11 @@
void grid_swap_row(struct grid *grid, int row_a, int row_b, bool initialize); void grid_swap_row(struct grid *grid, int row_a, int row_b, bool initialize);
struct row *grid_row_alloc(int cols, bool initialize); struct row *grid_row_alloc(int cols, bool initialize);
void grid_row_free(struct row *row); void grid_row_free(struct row *row);
int grid_reflow( void grid_reflow(
struct grid *grid, int new_rows, int new_cols, struct grid *grid, int new_rows, int new_cols,
int old_screen_rows, int new_screen_rows); int old_screen_rows, int new_screen_rows,
size_t tracking_points_count,
struct coord *tracking_points[static tracking_points_count]);
static inline int static inline int
grid_row_absolute(const struct grid *grid, int row_no) grid_row_absolute(const struct grid *grid, int row_no)

View file

@ -1373,7 +1373,7 @@ grid_render(struct terminal *term)
*/ */
bool cursor_is_visible = false; bool cursor_is_visible = false;
int view_end = (term->grid->view + term->rows - 1) & (term->grid->num_rows - 1); int view_end = (term->grid->view + term->rows - 1) & (term->grid->num_rows - 1);
int cursor_row = (term->grid->offset + term->cursor.point.row) & (term->grid->num_rows - 1); int cursor_row = (term->grid->offset + term->grid->cursor.point.row) & (term->grid->num_rows - 1);
if (view_end >= term->grid->view) { if (view_end >= term->grid->view) {
/* Not wrapped */ /* Not wrapped */
if (cursor_row >= term->grid->view && cursor_row <= view_end) if (cursor_row >= term->grid->view && cursor_row <= view_end)
@ -1401,21 +1401,21 @@ grid_render(struct terminal *term)
int view_aligned_row int view_aligned_row
= (cursor_row - term->grid->view + term->grid->num_rows) & (term->grid->num_rows - 1); = (cursor_row - term->grid->view + term->grid->num_rows) & (term->grid->num_rows - 1);
term->render.last_cursor.actual = term->cursor.point; term->render.last_cursor.actual = term->grid->cursor.point;
term->render.last_cursor.in_view = (struct coord) { term->render.last_cursor.in_view = (struct coord) {
term->cursor.point.col, view_aligned_row}; term->grid->cursor.point.col, view_aligned_row};
struct row *row = grid_row_in_view(term->grid, view_aligned_row); struct row *row = grid_row_in_view(term->grid, view_aligned_row);
struct cell *cell = &row->cells[term->cursor.point.col]; struct cell *cell = &row->cells[term->grid->cursor.point.col];
cell->attrs.clean = 0; cell->attrs.clean = 0;
term->render.last_cursor.cell = cell; term->render.last_cursor.cell = cell;
int cols_updated = render_cell( int cols_updated = render_cell(
term, buf->pix, cell, term->cursor.point.col, view_aligned_row, true); term, buf->pix, cell, term->grid->cursor.point.col, view_aligned_row, true);
wl_surface_damage_buffer( wl_surface_damage_buffer(
term->window->surface, term->window->surface,
term->margins.left + term->cursor.point.col * term->cell_width, term->margins.left + term->grid->cursor.point.col * term->cell_width,
term->margins.top + view_aligned_row * term->cell_height, term->margins.top + view_aligned_row * term->cell_height,
cols_updated * term->cell_width, term->cell_height); cols_updated * term->cell_width, term->cell_height);
} }
@ -1711,8 +1711,6 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
if (!force && width == term->width && height == term->height && scale == term->scale) if (!force && width == term->width && height == term->height && scale == term->scale)
return false; return false;
selection_cancel(term);
/* Cancel an application initiated "Synchronized Update" */ /* Cancel an application initiated "Synchronized Update" */
term_disable_app_sync_updates(term); term_disable_app_sync_updates(term);
@ -1754,10 +1752,8 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
} }
/* Reflow grids */ /* Reflow grids */
int last_normal_row = grid_reflow( grid_reflow(&term->normal, new_normal_grid_rows, new_cols, old_rows, new_rows, 0, NULL);
&term->normal, new_normal_grid_rows, new_cols, old_rows, new_rows); grid_reflow(&term->alt, new_alt_grid_rows, new_cols, old_rows, new_rows, 0, NULL);
int last_alt_row = grid_reflow(
&term->alt, new_alt_grid_rows, new_cols, old_rows, new_rows);
/* Reset tab stops */ /* Reset tab stops */
tll_free(term->tab_stops); tll_free(term->tab_stops);
@ -1789,36 +1785,6 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
if (term->scroll_region.end >= old_rows) if (term->scroll_region.end >= old_rows)
term->scroll_region.end = term->rows; term->scroll_region.end = term->rows;
/* Position cursor at the last copied row */
/* TODO: can we do better? */
int cursor_row = term->grid == &term->normal
? last_normal_row - term->normal.offset
: last_alt_row - term->alt.offset;
while (cursor_row < 0)
cursor_row += term->grid->num_rows;
assert(cursor_row >= 0);
assert(cursor_row < term->rows);
term_cursor_to(
term,
cursor_row,
min(term->cursor.point.col, term->cols - 1));
/* If in alt screen, update the saved 'normal' cursor too */
if (term->grid == &term->alt) {
int cursor_row = last_normal_row - term->normal.offset;
while (cursor_row < 0)
cursor_row += term->grid->num_rows;
term->alt_saved_cursor.lcf = false;
term->alt_saved_cursor.point.row = cursor_row;
term->alt_saved_cursor.point.col = min(
term->alt_saved_cursor.point.col, term->cols - 1);
}
term->render.last_cursor.cell = NULL; term->render.last_cursor.cell = NULL;
damage_view: damage_view:
@ -1846,6 +1812,12 @@ damage_view:
term->height / term->scale + title_height); term->height / term->scale + title_height);
} }
/* Make sure selection is within bounds */
term->selection.start.row = min(term->selection.start.row, term->rows - 1);
term->selection.start.col = min(term->selection.start.col, term->cols - 1);
term->selection.end.row = min(term->selection.end.row, term->rows - 1);
term->selection.end.col = min(term->selection.end.col, term->cols - 1);
tll_free(term->normal.scroll_damage); tll_free(term->normal.scroll_damage);
tll_free(term->alt.scroll_damage); tll_free(term->alt.scroll_damage);

View file

@ -125,7 +125,7 @@ sixel_delete_in_range(struct terminal *term, int _start, int _end)
void void
sixel_delete_at_cursor(struct terminal *term) sixel_delete_at_cursor(struct terminal *term)
{ {
sixel_delete_at_row(term, term->cursor.point.row); sixel_delete_at_row(term, term->grid->cursor.point.row);
} }
void void
@ -142,8 +142,8 @@ sixel_unhook(struct terminal *term)
.height = term->sixel.image.height, .height = term->sixel.image.height,
.rows = (term->sixel.image.height + term->cell_height - 1) / term->cell_height, .rows = (term->sixel.image.height + term->cell_height - 1) / term->cell_height,
.pos = (struct coord){ .pos = (struct coord){
term->cursor.point.col, term->grid->cursor.point.col,
(term->grid->offset + term->cursor.point.row) & (term->grid->num_rows - 1)}, (term->grid->offset + term->grid->cursor.point.row) & (term->grid->num_rows - 1)},
}; };
LOG_DBG("generating %dx%d pixman image", image.width, image.height); LOG_DBG("generating %dx%d pixman image", image.width, image.height);

View file

@ -354,7 +354,7 @@ term_arm_blink_timer(struct terminal *term)
static void static void
cursor_refresh(struct terminal *term) cursor_refresh(struct terminal *term)
{ {
term->grid->cur_row->cells[term->cursor.point.col].attrs.clean = 0; term->grid->cur_row->cells[term->grid->cursor.point.col].attrs.clean = 0;
term->grid->cur_row->dirty = true; term->grid->cur_row->dirty = true;
render_refresh(term); render_refresh(term);
} }
@ -1163,7 +1163,6 @@ term_reset(struct terminal *term, bool hard)
if (term->grid == &term->alt) { if (term->grid == &term->alt) {
term->grid = &term->normal; term->grid = &term->normal;
term_restore_cursor(term, &term->alt_saved_cursor);
selection_cancel(term); selection_cancel(term);
} }
@ -1188,10 +1187,12 @@ term_reset(struct terminal *term, bool hard)
for (size_t i = 0; i < 256; i++) for (size_t i = 0; i < 256; i++)
term->colors.table[i] = term->colors.default_table[i]; term->colors.table[i] = term->colors.default_table[i];
term->origin = ORIGIN_ABSOLUTE; term->origin = ORIGIN_ABSOLUTE;
term->cursor.lcf = false; term->normal.cursor.lcf = false;
term->cursor = (struct cursor){.point = {0, 0}}; term->alt.cursor.lcf = false;
term->saved_cursor = (struct cursor){.point = {0, 0}}; term->normal.cursor = (struct cursor){.point = {0, 0}};
term->alt_saved_cursor = (struct cursor){.point = {0, 0}}; term->normal.saved_cursor = (struct cursor){.point = {0, 0}};
term->alt.cursor = (struct cursor){.point = {0, 0}};
term->alt.saved_cursor = (struct cursor){.point = {0, 0}};
term->cursor_style = term->default_cursor_style; term->cursor_style = term->default_cursor_style;
term_cursor_blink_disable(term); term_cursor_blink_disable(term);
term->cursor_color.text = term->default_cursor_color.text; term->cursor_color.text = term->default_cursor_color.text;
@ -1431,10 +1432,10 @@ term_cursor_to(struct terminal *term, int row, int col)
assert(row < term->rows); assert(row < term->rows);
assert(col < term->cols); assert(col < term->cols);
term->cursor.lcf = false; term->grid->cursor.lcf = false;
term->cursor.point.col = col; term->grid->cursor.point.col = col;
term->cursor.point.row = row; term->grid->cursor.point.row = row;
term->grid->cur_row = grid_row(term->grid, row); term->grid->cur_row = grid_row(term->grid, row);
} }
@ -1448,39 +1449,39 @@ term_cursor_home(struct terminal *term)
void void
term_cursor_left(struct terminal *term, int count) term_cursor_left(struct terminal *term, int count)
{ {
int move_amount = min(term->cursor.point.col, count); int move_amount = min(term->grid->cursor.point.col, count);
term->cursor.point.col -= move_amount; term->grid->cursor.point.col -= move_amount;
assert(term->cursor.point.col >= 0); assert(term->grid->cursor.point.col >= 0);
term->cursor.lcf = false; term->grid->cursor.lcf = false;
} }
void void
term_cursor_right(struct terminal *term, int count) term_cursor_right(struct terminal *term, int count)
{ {
int move_amount = min(term->cols - term->cursor.point.col - 1, count); int move_amount = min(term->cols - term->grid->cursor.point.col - 1, count);
term->cursor.point.col += move_amount; term->grid->cursor.point.col += move_amount;
assert(term->cursor.point.col < term->cols); assert(term->grid->cursor.point.col < term->cols);
term->cursor.lcf = false; term->grid->cursor.lcf = false;
} }
void void
term_cursor_up(struct terminal *term, int count) term_cursor_up(struct terminal *term, int count)
{ {
int top = term->origin == ORIGIN_ABSOLUTE ? 0 : term->scroll_region.start; int top = term->origin == ORIGIN_ABSOLUTE ? 0 : term->scroll_region.start;
assert(term->cursor.point.row >= top); assert(term->grid->cursor.point.row >= top);
int move_amount = min(term->cursor.point.row - top, count); int move_amount = min(term->grid->cursor.point.row - top, count);
term_cursor_to(term, term->cursor.point.row - move_amount, term->cursor.point.col); term_cursor_to(term, term->grid->cursor.point.row - move_amount, term->grid->cursor.point.col);
} }
void void
term_cursor_down(struct terminal *term, int count) term_cursor_down(struct terminal *term, int count)
{ {
int bottom = term->origin == ORIGIN_ABSOLUTE ? term->rows : term->scroll_region.end; int bottom = term->origin == ORIGIN_ABSOLUTE ? term->rows : term->scroll_region.end;
assert(bottom >= term->cursor.point.row); assert(bottom >= term->grid->cursor.point.row);
int move_amount = min(bottom - term->cursor.point.row - 1, count); int move_amount = min(bottom - term->grid->cursor.point.row - 1, count);
term_cursor_to(term, term->cursor.point.row + move_amount, term->cursor.point.col); term_cursor_to(term, term->grid->cursor.point.row + move_amount, term->grid->cursor.point.col);
} }
static bool static bool
@ -1569,7 +1570,7 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows
sixel_delete_in_range(term, max(region.end - rows, region.start), region.end - 1); sixel_delete_in_range(term, max(region.end - rows, region.start), region.end - 1);
term_damage_scroll(term, DAMAGE_SCROLL, region, rows); term_damage_scroll(term, DAMAGE_SCROLL, region, rows);
term->grid->cur_row = grid_row(term->grid, term->cursor.point.row); term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row);
} }
void void
@ -1622,7 +1623,7 @@ term_scroll_reverse_partial(struct terminal *term,
sixel_delete_in_range(term, region.start, min(region.start + rows, region.end) - 1); sixel_delete_in_range(term, region.start, min(region.start + rows, region.end) - 1);
term_damage_scroll(term, DAMAGE_SCROLL_REVERSE, region, rows); term_damage_scroll(term, DAMAGE_SCROLL_REVERSE, region, rows);
term->grid->cur_row = grid_row(term->grid, term->cursor.point.row); term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row);
} }
void void
@ -1634,14 +1635,14 @@ term_scroll_reverse(struct terminal *term, int rows)
void void
term_formfeed(struct terminal *term) term_formfeed(struct terminal *term)
{ {
term_cursor_left(term, term->cursor.point.col); term_cursor_left(term, term->grid->cursor.point.col);
} }
void void
term_linefeed(struct terminal *term) term_linefeed(struct terminal *term)
{ {
term->grid->cur_row->linebreak = true; term->grid->cur_row->linebreak = true;
if (term->cursor.point.row == term->scroll_region.end - 1) if (term->grid->cursor.point.row == term->scroll_region.end - 1)
term_scroll(term, 1); term_scroll(term, 1);
else else
term_cursor_down(term, 1); term_cursor_down(term, 1);
@ -1650,7 +1651,7 @@ term_linefeed(struct terminal *term)
void void
term_reverse_index(struct terminal *term) term_reverse_index(struct terminal *term)
{ {
if (term->cursor.point.row == term->scroll_region.start) if (term->grid->cursor.point.row == term->scroll_region.start)
term_scroll_reverse(term, 1); term_scroll_reverse(term, 1);
else else
term_cursor_up(term, 1); term_cursor_up(term, 1);
@ -1672,7 +1673,7 @@ term_restore_cursor(struct terminal *term, const struct cursor *cursor)
int row = min(cursor->point.row, term->rows - 1); int row = min(cursor->point.row, term->rows - 1);
int col = min(cursor->point.col, term->cols - 1); int col = min(cursor->point.col, term->cols - 1);
term_cursor_to(term, row, col); term_cursor_to(term, row, col);
term->cursor.lcf = cursor->lcf; term->grid->cursor.lcf = cursor->lcf;
} }
void void
@ -2095,7 +2096,7 @@ term_disable_app_sync_updates(struct terminal *term)
static inline void static inline void
print_linewrap(struct terminal *term) print_linewrap(struct terminal *term)
{ {
if (likely(!term->cursor.lcf)) { if (likely(!term->grid->cursor.lcf)) {
/* Not and end of line */ /* Not and end of line */
return; return;
} }
@ -2105,11 +2106,11 @@ print_linewrap(struct terminal *term)
return; return;
} }
if (term->cursor.point.row == term->scroll_region.end - 1) { if (term->grid->cursor.point.row == term->scroll_region.end - 1) {
term_scroll(term, 1); term_scroll(term, 1);
term_cursor_to(term, term->cursor.point.row, 0); term_cursor_to(term, term->grid->cursor.point.row, 0);
} else } else
term_cursor_to(term, min(term->cursor.point.row + 1, term->rows - 1), 0); term_cursor_to(term, min(term->grid->cursor.point.row + 1, term->rows - 1), 0);
} }
static inline void static inline void
@ -2119,15 +2120,15 @@ print_insert(struct terminal *term, int width)
if (unlikely(term->insert_mode)) { if (unlikely(term->insert_mode)) {
struct row *row = term->grid->cur_row; struct row *row = term->grid->cur_row;
const size_t move_count = max(0, term->cols - term->cursor.point.col - width); const size_t move_count = max(0, term->cols - term->grid->cursor.point.col - width);
memmove( memmove(
&row->cells[term->cursor.point.col + width], &row->cells[term->grid->cursor.point.col + width],
&row->cells[term->cursor.point.col], &row->cells[term->grid->cursor.point.col],
move_count * sizeof(struct cell)); move_count * sizeof(struct cell));
/* Mark moved cells as dirty */ /* Mark moved cells as dirty */
for (size_t i = term->cursor.point.col + width; i < term->cols; i++) for (size_t i = term->grid->cursor.point.col + width; i < term->cols; i++)
row->cells[i].attrs.clean = 0; row->cells[i].attrs.clean = 0;
} }
} }
@ -2145,7 +2146,7 @@ term_print(struct terminal *term, wchar_t wc, int width)
/* *Must* get current cell *after* linewrap+insert */ /* *Must* get current cell *after* linewrap+insert */
struct row *row = term->grid->cur_row; struct row *row = term->grid->cur_row;
struct cell *cell = &row->cells[term->cursor.point.col]; struct cell *cell = &row->cells[term->grid->cursor.point.col];
cell->wc = term->vt.last_printed = wc; cell->wc = term->vt.last_printed = wc;
cell->attrs = term->vt.attrs; cell->attrs = term->vt.attrs;
@ -2154,20 +2155,20 @@ term_print(struct terminal *term, wchar_t wc, int width)
cell->attrs.clean = 0; cell->attrs.clean = 0;
/* Advance cursor the 'additional' columns while dirty:ing the cells */ /* Advance cursor the 'additional' columns while dirty:ing the cells */
for (int i = 1; i < width && term->cursor.point.col < term->cols - 1; i++) { for (int i = 1; i < width && term->grid->cursor.point.col < term->cols - 1; i++) {
term_cursor_right(term, 1); term_cursor_right(term, 1);
assert(term->cursor.point.col < term->cols); assert(term->grid->cursor.point.col < term->cols);
struct cell *cell = &row->cells[term->cursor.point.col]; struct cell *cell = &row->cells[term->grid->cursor.point.col];
cell->wc = 0; cell->wc = 0;
cell->attrs.clean = 0; cell->attrs.clean = 0;
} }
/* Advance cursor */ /* Advance cursor */
if (term->cursor.point.col < term->cols - 1) if (term->grid->cursor.point.col < term->cols - 1)
term_cursor_right(term, 1); term_cursor_right(term, 1);
else else
term->cursor.lcf = true; term->grid->cursor.lcf = true;
} }
enum term_surface enum term_surface

View file

@ -101,6 +101,9 @@ struct grid {
int offset; int offset;
int view; int view;
struct cursor cursor;
struct cursor saved_cursor;
struct row **rows; struct row **rows;
struct row *cur_row; struct row *cur_row;
@ -278,9 +281,6 @@ struct terminal {
} colors; } colors;
enum cursor_origin origin; enum cursor_origin origin;
struct cursor cursor;
struct cursor saved_cursor;
struct cursor alt_saved_cursor;
enum cursor_style default_cursor_style; enum cursor_style default_cursor_style;
enum cursor_style cursor_style; enum cursor_style cursor_style;
struct { struct {

16
vt.c
View file

@ -146,13 +146,13 @@ action_execute(struct terminal *term, uint8_t c)
/* HT - horizontal tab */ /* HT - horizontal tab */
int new_col = term->cols - 1; int new_col = term->cols - 1;
tll_foreach(term->tab_stops, it) { tll_foreach(term->tab_stops, it) {
if (it->item > term->cursor.point.col) { if (it->item > term->grid->cursor.point.col) {
new_col = it->item; new_col = it->item;
break; break;
} }
} }
assert(new_col >= term->cursor.point.col); assert(new_col >= term->grid->cursor.point.col);
term_cursor_right(term, new_col - term->cursor.point.col); term_cursor_right(term, new_col - term->grid->cursor.point.col);
break; break;
} }
@ -341,13 +341,13 @@ action_esc_dispatch(struct terminal *term, uint8_t final)
case 0: case 0:
switch (final) { switch (final) {
case '7': case '7':
term->saved_cursor = term->cursor; term->grid->saved_cursor = term->grid->cursor;
term->vt.saved_attrs = term->vt.attrs; term->vt.saved_attrs = term->vt.attrs;
term->saved_charsets = term->charsets; term->saved_charsets = term->charsets;
break; break;
case '8': case '8':
term_restore_cursor(term, &term->saved_cursor); term_restore_cursor(term, &term->grid->saved_cursor);
term->vt.attrs = term->vt.saved_attrs; term->vt.attrs = term->vt.saved_attrs;
term->charsets = term->saved_charsets; term->charsets = term->saved_charsets;
break; break;
@ -367,13 +367,13 @@ action_esc_dispatch(struct terminal *term, uint8_t final)
case 'H': case 'H':
tll_foreach(term->tab_stops, it) { tll_foreach(term->tab_stops, it) {
if (it->item >= term->cursor.point.col) { if (it->item >= term->grid->cursor.point.col) {
tll_insert_before(term->tab_stops, it, term->cursor.point.col); tll_insert_before(term->tab_stops, it, term->grid->cursor.point.col);
break; break;
} }
} }
tll_push_back(term->tab_stops, term->cursor.point.col); tll_push_back(term->tab_stops, term->grid->cursor.point.col);
break; break;
case 'M': case 'M':