mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-04-02 07:15:31 -04:00
render/grid: move grid reflow code to grid.c
This commit is contained in:
parent
304f8d6982
commit
38a682f0d0
3 changed files with 158 additions and 184 deletions
146
grid.c
146
grid.c
|
|
@ -1,12 +1,14 @@
|
||||||
#include "grid.h"
|
#include "grid.h"
|
||||||
|
|
||||||
//#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#define LOG_MODULE "grid"
|
#define LOG_MODULE "grid"
|
||||||
#define LOG_ENABLE_DBG 0
|
#define LOG_ENABLE_DBG 0
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
|
#define max(x, y) ((x) > (y) ? (x) : (y))
|
||||||
|
|
||||||
void
|
void
|
||||||
grid_swap_row(struct grid *grid, int row_a, int row_b, bool initialize)
|
grid_swap_row(struct grid *grid, int row_a, int row_b, bool initialize)
|
||||||
{
|
{
|
||||||
|
|
@ -49,3 +51,145 @@ grid_row_free(struct row *row)
|
||||||
free(row->cells);
|
free(row->cells);
|
||||||
free(row);
|
free(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
grid_reflow(struct grid *grid, int new_rows, int new_cols,
|
||||||
|
int old_screen_rows, int new_screen_rows)
|
||||||
|
{
|
||||||
|
struct row *const *old_grid = grid->rows;
|
||||||
|
const int old_rows = grid->num_rows;
|
||||||
|
const int old_cols = grid->num_cols;
|
||||||
|
|
||||||
|
assert(old_rows != new_rows || old_cols != new_cols);
|
||||||
|
|
||||||
|
int new_col_idx = 0;
|
||||||
|
int new_row_idx = 0;
|
||||||
|
|
||||||
|
struct row **new_grid = calloc(new_rows, sizeof(new_grid[0]));
|
||||||
|
struct row *new_row = new_grid[new_row_idx];
|
||||||
|
|
||||||
|
assert(new_row == NULL);
|
||||||
|
new_row = grid_row_alloc(new_cols, true);
|
||||||
|
new_grid[new_row_idx] = new_row;
|
||||||
|
|
||||||
|
/* Start at the beginning of the old grid's scrollback. That is,
|
||||||
|
* at the output that is *oldest* */
|
||||||
|
int offset = grid->offset + old_screen_rows;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Walk the old grid
|
||||||
|
*/
|
||||||
|
for (int r = 0; r < old_rows; r++) {
|
||||||
|
|
||||||
|
/* Unallocated (empty) rows we can simply skip */
|
||||||
|
const struct row *old_row = old_grid[(offset + r) & (old_rows - 1)];
|
||||||
|
if (old_row == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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,
|
||||||
|
* add those to the new line. However, if there are non-empty
|
||||||
|
* cells *after* the string of empty cells, we need to emit
|
||||||
|
* the empty cells too. And that may trigger linebreaks
|
||||||
|
*/
|
||||||
|
int empty_count = 0;
|
||||||
|
|
||||||
|
/* Walk current line of the old grid */
|
||||||
|
for (int c = 0; c < old_cols; c++) {
|
||||||
|
if (old_row->cells[c].wc == 0) {
|
||||||
|
empty_count++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int old_cols_left = old_cols - c;
|
||||||
|
int cols_needed = empty_count + old_cols_left;
|
||||||
|
int new_cols_left = new_cols - new_col_idx;
|
||||||
|
if (new_cols_left < cols_needed && new_cols_left >= old_cols_left)
|
||||||
|
empty_count = max(0, empty_count - (cols_needed - new_cols_left));
|
||||||
|
|
||||||
|
for (int i = 0; i < empty_count + 1; i++) {
|
||||||
|
const struct cell *old_cell = &old_row->cells[c - empty_count + i];
|
||||||
|
|
||||||
|
/* Out of columns on current row in new grid? */
|
||||||
|
if (new_col_idx >= new_cols) {
|
||||||
|
/*
|
||||||
|
* If last cell on last row and first cell on new
|
||||||
|
* row are non-empty, wrap the line, otherwise
|
||||||
|
* insert a hard line break.
|
||||||
|
*/
|
||||||
|
if (new_row->cells[new_cols - 1].wc == 0 ||
|
||||||
|
old_cell->wc == 0)
|
||||||
|
{
|
||||||
|
new_row->linebreak = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(new_row != NULL);
|
||||||
|
assert(new_col_idx >= 0);
|
||||||
|
assert(new_col_idx < new_cols);
|
||||||
|
|
||||||
|
new_row->cells[new_col_idx] = *old_cell;
|
||||||
|
new_row->cells[new_col_idx].attrs.clean = 1;
|
||||||
|
new_col_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
empty_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_row->linebreak) {
|
||||||
|
new_row->linebreak = true;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set offset such that the last reflowed row is at the bottom */
|
||||||
|
grid->offset = new_row_idx - new_screen_rows + 1;
|
||||||
|
while (grid->offset < 0)
|
||||||
|
grid->offset += new_rows;
|
||||||
|
while (new_grid[grid->offset] == NULL)
|
||||||
|
grid->offset = (grid->offset + 1) & (new_rows - 1);
|
||||||
|
grid->view = grid->offset;
|
||||||
|
|
||||||
|
/* Ensure all visible rows have been allocated */
|
||||||
|
for (int r = 0; r < new_screen_rows; r++) {
|
||||||
|
int idx = (grid->offset + r) & (new_rows - 1);
|
||||||
|
if (new_grid[idx] == NULL)
|
||||||
|
new_grid[idx] = grid_row_alloc(new_cols, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free old grid */
|
||||||
|
for (int r = 0; r < grid->num_rows; r++)
|
||||||
|
grid_row_free(old_grid[r]);
|
||||||
|
free(grid->rows);
|
||||||
|
|
||||||
|
grid->cur_row = new_grid[new_row_idx];
|
||||||
|
grid->rows = new_grid;
|
||||||
|
grid->num_rows = new_rows;
|
||||||
|
grid->num_cols = new_cols;
|
||||||
|
|
||||||
|
return new_row_idx;
|
||||||
|
}
|
||||||
|
|
|
||||||
3
grid.h
3
grid.h
|
|
@ -6,6 +6,9 @@
|
||||||
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(
|
||||||
|
struct grid *grid, int new_rows, int new_cols,
|
||||||
|
int old_screen_rows, int new_screen_rows);
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
||||||
193
render.c
193
render.c
|
|
@ -972,116 +972,6 @@ render_search_box(struct terminal *term)
|
||||||
wl_surface_commit(term->window->search_surface);
|
wl_surface_commit(term->window->search_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
reflow(struct terminal *term, struct row **new_grid, int new_cols, int new_rows,
|
|
||||||
struct row *const *old_grid, int old_cols, int old_rows, int offset)
|
|
||||||
{
|
|
||||||
int new_col_idx = 0;
|
|
||||||
int new_row_idx = 0;
|
|
||||||
|
|
||||||
struct row *new_row = new_grid[new_row_idx];
|
|
||||||
|
|
||||||
assert(new_row == NULL);
|
|
||||||
new_row = grid_row_alloc(new_cols, true);
|
|
||||||
new_grid[new_row_idx] = new_row;
|
|
||||||
|
|
||||||
/* Start at the beginning of the old grid's scrollback. That is,
|
|
||||||
* at the output that is *oldest* */
|
|
||||||
offset += term->rows;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Walk the old grid
|
|
||||||
*/
|
|
||||||
for (int r = 0; r < old_rows; r++) {
|
|
||||||
|
|
||||||
/* Unallocated (empty) rows we can simply skip */
|
|
||||||
const struct row *old_row = old_grid[(offset + r) & (old_rows - 1)];
|
|
||||||
if (old_row == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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,
|
|
||||||
* add those to the new line. However, if there are non-empty
|
|
||||||
* cells *after* the string of empty cells, we need to emit
|
|
||||||
* the empty cells too. And that may trigger linebreaks
|
|
||||||
*/
|
|
||||||
int empty_count = 0;
|
|
||||||
|
|
||||||
/* Walk current line of the old grid */
|
|
||||||
for (int c = 0; c < old_cols; c++) {
|
|
||||||
if (old_row->cells[c].wc == 0) {
|
|
||||||
empty_count++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int old_cols_left = old_cols - c;
|
|
||||||
int cols_needed = empty_count + old_cols_left;
|
|
||||||
int new_cols_left = new_cols - new_col_idx;
|
|
||||||
if (new_cols_left < cols_needed && new_cols_left >= old_cols_left)
|
|
||||||
empty_count = max(0, empty_count - (cols_needed - new_cols_left));
|
|
||||||
|
|
||||||
for (int i = 0; i < empty_count + 1; i++) {
|
|
||||||
const struct cell *old_cell = &old_row->cells[c - empty_count + i];
|
|
||||||
|
|
||||||
/* Out of columns on current row in new grid? */
|
|
||||||
if (new_col_idx >= new_cols) {
|
|
||||||
/*
|
|
||||||
* If last cell on last row and first cell on new
|
|
||||||
* row are non-empty, wrap the line, otherwise
|
|
||||||
* insert a hard line break.
|
|
||||||
*/
|
|
||||||
if (new_row->cells[new_cols - 1].wc == 0 ||
|
|
||||||
old_cell->wc == 0)
|
|
||||||
{
|
|
||||||
new_row->linebreak = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(new_row != NULL);
|
|
||||||
assert(new_col_idx >= 0);
|
|
||||||
assert(new_col_idx < new_cols);
|
|
||||||
|
|
||||||
new_row->cells[new_col_idx] = *old_cell;
|
|
||||||
new_row->cells[new_col_idx].attrs.clean = 1;
|
|
||||||
new_col_idx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
empty_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (old_row->linebreak) {
|
|
||||||
new_row->linebreak = true;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new_row_idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Move to terminal.c? */
|
/* Move to terminal.c? */
|
||||||
static void
|
static void
|
||||||
maybe_resize(struct terminal *term, int width, int height, bool force)
|
maybe_resize(struct terminal *term, int width, int height, bool force)
|
||||||
|
|
@ -1126,10 +1016,6 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
|
||||||
const int old_cols = term->cols;
|
const int old_cols = term->cols;
|
||||||
const int old_rows = term->rows;
|
const int old_rows = term->rows;
|
||||||
|
|
||||||
/* Grid rows/cols before resize */
|
|
||||||
const int old_normal_grid_rows = term->normal.num_rows;
|
|
||||||
const int old_alt_grid_rows = term->alt.num_rows;
|
|
||||||
|
|
||||||
/* Padding */
|
/* Padding */
|
||||||
const int pad_x = term->width > 2 * scale * term->conf->pad_x ? scale * term->conf->pad_x : 0;
|
const int pad_x = term->width > 2 * scale * term->conf->pad_x ? scale * term->conf->pad_x : 0;
|
||||||
const int pad_y = term->height > 2 * scale * term->conf->pad_y ? scale * term->conf->pad_y : 0;
|
const int pad_y = term->height > 2 * scale * term->conf->pad_y ? scale * term->conf->pad_y : 0;
|
||||||
|
|
@ -1149,68 +1035,16 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
|
||||||
term->x_margin = (term->width - new_cols * term->cell_width) / 2;
|
term->x_margin = (term->width - new_cols * term->cell_width) / 2;
|
||||||
term->y_margin = (term->height - new_rows * term->cell_height) / 2;
|
term->y_margin = (term->height - new_rows * term->cell_height) / 2;
|
||||||
|
|
||||||
if (old_rows == new_rows && old_cols == new_cols) {
|
if (new_cols == old_cols && new_rows == old_rows) {
|
||||||
/* Skip reflow if grid layout hasn't changed */
|
LOG_DBG("grid layout unaffected; skipping reflow");
|
||||||
goto done;
|
goto damage_view;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate new 'normal' and 'alt' grids */
|
/* Reflow grids */
|
||||||
struct row **normal = calloc(new_normal_grid_rows, sizeof(normal[0]));
|
int last_normal_row = grid_reflow(
|
||||||
struct row **alt = calloc(new_alt_grid_rows, sizeof(alt[0]));
|
&term->normal, new_normal_grid_rows, new_cols, old_rows, new_rows);
|
||||||
|
int last_alt_row = grid_reflow(
|
||||||
/* Reflow content */
|
&term->alt, new_alt_grid_rows, new_cols, old_rows, new_rows);
|
||||||
int last_normal_row = reflow(
|
|
||||||
term, normal, new_cols, new_normal_grid_rows,
|
|
||||||
term->normal.rows, old_cols, old_normal_grid_rows, term->normal.offset);
|
|
||||||
int last_alt_row = reflow(
|
|
||||||
term, alt, new_cols, new_alt_grid_rows,
|
|
||||||
term->alt.rows, old_cols, old_alt_grid_rows, term->alt.offset);
|
|
||||||
|
|
||||||
/* Re-set current row pointers */
|
|
||||||
term->normal.cur_row = normal[last_normal_row];
|
|
||||||
term->alt.cur_row = alt[last_alt_row];
|
|
||||||
|
|
||||||
/* Reset offset such that the last copied row ends up at the
|
|
||||||
* bottom of the screen */
|
|
||||||
term->normal.offset = last_normal_row - new_rows + 1;
|
|
||||||
term->alt.offset = last_alt_row - new_rows + 1;
|
|
||||||
|
|
||||||
/* Can't have negative offsets, so wrap 'em */
|
|
||||||
while (term->normal.offset < 0)
|
|
||||||
term->normal.offset += new_normal_grid_rows;
|
|
||||||
while (term->alt.offset < 0)
|
|
||||||
term->alt.offset += new_alt_grid_rows;
|
|
||||||
|
|
||||||
/* Make sure offset doesn't point to empty line */
|
|
||||||
while (normal[term->normal.offset] == NULL)
|
|
||||||
term->normal.offset = (term->normal.offset + 1) & (new_normal_grid_rows - 1);
|
|
||||||
while (alt[term->alt.offset] == NULL)
|
|
||||||
term->alt.offset = (term->alt.offset + 1) & (new_alt_grid_rows - 1);
|
|
||||||
|
|
||||||
/* TODO: try to keep old view */
|
|
||||||
term->normal.view = term->normal.offset;
|
|
||||||
term->alt.view = term->alt.offset;
|
|
||||||
|
|
||||||
/* Make sure all visible lines have been allocated */
|
|
||||||
for (int r = 0; r < new_rows; r++) {
|
|
||||||
int idx = (term->normal.offset + r) & (new_normal_grid_rows - 1);
|
|
||||||
if (normal[idx] == NULL)
|
|
||||||
normal[idx] = grid_row_alloc(new_cols, true);
|
|
||||||
|
|
||||||
idx = (term->alt.offset + r) & (new_alt_grid_rows - 1);
|
|
||||||
if (alt[idx] == NULL)
|
|
||||||
alt[idx] = grid_row_alloc(new_cols, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free old 'normal' grid */
|
|
||||||
for (int r = 0; r < term->normal.num_rows; r++)
|
|
||||||
grid_row_free(term->normal.rows[r]);
|
|
||||||
free(term->normal.rows);
|
|
||||||
|
|
||||||
/* Free old 'alt' grid */
|
|
||||||
for (int r = 0; r < term->alt.num_rows; r++)
|
|
||||||
grid_row_free(term->alt.rows[r]);
|
|
||||||
free(term->alt.rows);
|
|
||||||
|
|
||||||
/* Reset tab stops */
|
/* Reset tab stops */
|
||||||
tll_free(term->tab_stops);
|
tll_free(term->tab_stops);
|
||||||
|
|
@ -1220,13 +1054,6 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
|
||||||
term->cols = new_cols;
|
term->cols = new_cols;
|
||||||
term->rows = new_rows;
|
term->rows = new_rows;
|
||||||
|
|
||||||
term->normal.rows = normal;
|
|
||||||
term->normal.num_rows = new_normal_grid_rows;
|
|
||||||
term->normal.num_cols = new_cols;
|
|
||||||
term->alt.rows = alt;
|
|
||||||
term->alt.num_rows = new_alt_grid_rows;
|
|
||||||
term->alt.num_cols = new_cols;
|
|
||||||
|
|
||||||
LOG_INFO("resize: %dx%d, grid: cols=%d, rows=%d (x-margin=%d, y-margin=%d)",
|
LOG_INFO("resize: %dx%d, grid: cols=%d, rows=%d (x-margin=%d, y-margin=%d)",
|
||||||
term->width, term->height, term->cols, term->rows,
|
term->width, term->height, term->cols, term->rows,
|
||||||
term->x_margin, term->y_margin);
|
term->x_margin, term->y_margin);
|
||||||
|
|
@ -1266,10 +1093,10 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
|
||||||
min(term->cursor.point.col, term->cols - 1));
|
min(term->cursor.point.col, term->cols - 1));
|
||||||
|
|
||||||
term->render.last_cursor.cell = NULL;
|
term->render.last_cursor.cell = NULL;
|
||||||
|
|
||||||
|
damage_view:
|
||||||
tll_free(term->normal.scroll_damage);
|
tll_free(term->normal.scroll_damage);
|
||||||
tll_free(term->alt.scroll_damage);
|
tll_free(term->alt.scroll_damage);
|
||||||
|
|
||||||
done:
|
|
||||||
term->render.last_buf = NULL;
|
term->render.last_buf = NULL;
|
||||||
term_damage_view(term);
|
term_damage_view(term);
|
||||||
render_refresh(term);
|
render_refresh(term);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue