diff --git a/README.md b/README.md
index 5451a78e..cbf2ffb6 100644
--- a/README.md
+++ b/README.md
@@ -52,8 +52,6 @@ This is a list of known, but probably not all, issues:
Examples: á (`LATIN SMALL LETTER A` + `COMBINING ACUTE ACCENT`)
-* Reflow text on window resize
-
* GNOME; might work, but without window decorations.
Strictly speaking, foot is at fault here; all Wayland applications
@@ -108,13 +106,13 @@ is **not** possible to define new key bindings.
ctrl+shift+r
: Start a scrollback search
-ctrl++
+ctrl++, ctrl+=
: Increase font size by 1pt
ctrl+-
: Decrease font size by 1pt
-ctrl+0, ctrl+=
+ctrl+0
: Reset font size
diff --git a/doc/foot.1.scd b/doc/foot.1.scd
index 313579fe..639ddf19 100644
--- a/doc/foot.1.scd
+++ b/doc/foot.1.scd
@@ -112,13 +112,13 @@ be changed.
*ctrl*+*shift*+*r*
Start a scrollback search
-*ctrl*+*+*
+*ctrl*+*+*, *ctrl*+*=*
Increase font size by 1pt
*ctrl*+*-*
Decrease font size by 1pt
-*ctrl*+*0*, *ctrl*+*=*
+*ctrl*+*0*
Reset font size
## SCROLLBACK SEARCH
diff --git a/input.c b/input.c
index 21fc6f37..77355918 100644
--- a/input.c
+++ b/input.c
@@ -386,7 +386,7 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
}
else if (effective_mods == ctrl) {
- if (sym == XKB_KEY_plus || sym == XKB_KEY_KP_Add) {
+ if (sym == XKB_KEY_equal || sym == XKB_KEY_plus || sym == XKB_KEY_KP_Add) {
term_font_size_increase(term);
goto maybe_repeat;
}
@@ -396,7 +396,7 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
goto maybe_repeat;
}
- else if (sym == XKB_KEY_0 || sym == XKB_KEY_equal || sym == XKB_KEY_KP_Equal || sym == XKB_KEY_KP_0) {
+ else if (sym == XKB_KEY_0 || sym == XKB_KEY_KP_Equal || sym == XKB_KEY_KP_0) {
term_font_size_reset(term);
goto maybe_repeat;
}
diff --git a/render.c b/render.c
index 7fa7dd86..4aac652a 100644
--- a/render.c
+++ b/render.c
@@ -970,28 +970,109 @@ render_search_box(struct terminal *term)
wl_surface_commit(term->window->search_surface);
}
-static void
-reflow(struct row **new_grid, int new_cols, int new_rows,
- struct row *const *old_grid, int old_cols, int old_rows)
+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)
{
- /* TODO: actually reflow */
- for (int r = 0; r < min(new_rows, old_rows); r++) {
- size_t copy_cols = min(new_cols, old_cols);
- size_t clear_cols = new_cols - copy_cols;
+ int new_col_idx = 0;
+ int new_row_idx = 0;
- if (old_grid[r] == NULL)
+ struct row *new_row = new_grid[new_row_idx];
+
+ assert(new_row == NULL);
+ new_row = grid_row_alloc(new_cols, true);
+ new_row->dirty = 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;
- if (new_grid[r] == NULL)
- new_grid[r] = grid_row_alloc(new_cols, false);
+ /*
+ * 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;
- struct cell *new_cells = new_grid[r]->cells;
- const struct cell *old_cells = old_grid[r]->cells;
+ /* Walk current line of the old grid */
+ for (int c = 0; c < old_cols; c++) {
+ const struct cell *old_cell = &old_row->cells[c];
- new_grid[r]->dirty = old_grid[r]->dirty;
- memcpy(new_cells, old_cells, copy_cols * sizeof(new_cells[0]));
- memset(&new_cells[copy_cols], 0, clear_cols * sizeof(new_cells[0]));
+ if (old_cell->wc == 0) {
+ empty_count++;
+ continue;
+ }
+
+ assert(old_cell->wc != 0);
+
+ /* Non-empty cell. Emit preceeding string of empty cells,
+ * and possibly line break for current cell */
+
+ for (int i = 0; i < empty_count + 1; i++) {
+ if (new_col_idx >= new_cols) {
+ 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_row->dirty = true;
+ new_grid[new_row_idx] = new_row;
+ } else
+ memset(new_row->cells, 0, new_cols * sizeof(new_row->cells[0]));
+ }
+
+ new_col_idx++;
+ }
+
+ empty_count = 0;
+ new_col_idx--;
+
+ assert(new_row != NULL);
+ assert(new_col_idx >= 0);
+ assert(new_col_idx < new_cols);
+
+ /* Copy current cell */
+ new_row->cells[new_col_idx].attrs.clean = 1;
+ new_row->cells[new_col_idx++] = *old_cell;
+ }
+
+ /*
+ * If last cell of the old grid's line if empty, then we
+ * insert a linebreak in the new grid's line too. Unless, the
+ * *entire* old line was empty.
+ */
+
+ if (empty_count < old_cols &&
+ (old_row->cells[old_cols - 1].wc == 0 ||
+ old_row->cells[old_cols - 1].attrs.linefeed))
+ {
+ 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_row->dirty = true;
+ new_grid[new_row_idx] = new_row;
+ }
+ }
}
+
+ return new_row_idx;
}
/* Move to terminal.c? */
@@ -1045,31 +1126,52 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
term->x_margin = (term->width - new_cols * term->cell_width) / 2;
term->y_margin = (term->height - new_rows * term->cell_height) / 2;
- term->normal.offset %= new_normal_grid_rows;
- term->normal.view %= new_normal_grid_rows;
-
- term->alt.offset %= new_alt_grid_rows;
- term->alt.view %= new_alt_grid_rows;
-
- /* Allocate new 'normal' grid */
+ /* Allocate new 'normal' and 'alt' grids */
struct row **normal = calloc(new_normal_grid_rows, sizeof(normal[0]));
- for (int r = 0; r < new_rows; r++) {
- size_t real_r = (term->normal.view + r) & (new_normal_grid_rows - 1);
- normal[real_r] = grid_row_alloc(new_cols, true);
- }
-
- /* Allocate new 'alt' grid */
struct row **alt = calloc(new_alt_grid_rows, sizeof(alt[0]));
- for (int r = 0; r < new_rows; r++) {
- int real_r = (term->alt.view + r) & (new_alt_grid_rows - 1);
- alt[real_r] = grid_row_alloc(new_cols, true);
- }
/* Reflow content */
- reflow(normal, new_cols, new_normal_grid_rows,
- term->normal.rows, old_cols, old_normal_grid_rows);
- reflow(alt, new_cols, new_alt_grid_rows,
- term->alt.rows, old_cols, old_alt_grid_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;
+ term->alt.offset = last_alt_row - new_rows;
+
+ /* 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);
+
+ 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++)
@@ -1117,9 +1219,20 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
if (term->scroll_region.end >= old_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;
+ cursor_row--;
+
+ assert(cursor_row < term->rows);
term_cursor_to(
term,
- min(term->cursor.point.row, term->rows - 1),
+ max(cursor_row, 0),
min(term->cursor.point.col, term->cols - 1));
term->render.last_cursor.cell = NULL;
diff --git a/selection.c b/selection.c
index 2c7bc0b2..f1485f45 100644
--- a/selection.c
+++ b/selection.c
@@ -212,7 +212,8 @@ extract_one(struct terminal *term, struct row *row, struct cell *cell,
struct extract *ctx = data;
if (ctx->last_row != NULL && row != ctx->last_row &&
- ((term->selection.kind == SELECTION_NORMAL && ctx->last_cell->wc == 0) ||
+ ((term->selection.kind == SELECTION_NORMAL &&
+ (ctx->last_cell->wc == 0 || ctx->last_cell->attrs.linefeed)) ||
term->selection.kind == SELECTION_BLOCK))
{
/* Last cell was the last column in the selection */
diff --git a/terminal.c b/terminal.c
index b179862d..852d7fa4 100644
--- a/terminal.c
+++ b/terminal.c
@@ -1154,13 +1154,13 @@ term_font_size_adjust(struct terminal *term, double amount)
void
term_font_size_increase(struct terminal *term)
{
- term_font_size_adjust(term, 1.);
+ term_font_size_adjust(term, 0.5);
}
void
term_font_size_decrease(struct terminal *term)
{
- term_font_size_adjust(term, -1.);
+ term_font_size_adjust(term, -0.5);
}
void
@@ -1495,6 +1495,14 @@ term_scroll_reverse(struct terminal *term, int rows)
term_scroll_reverse_partial(term, term->scroll_region, rows);
}
+void
+term_formfeed(struct terminal *term)
+{
+ if (term->cursor.point.col > 0)
+ term->grid->cur_row->cells[term->cursor.point.col - 1].attrs.linefeed = 1;
+ term_cursor_left(term, term->cursor.point.col);
+}
+
void
term_linefeed(struct terminal *term)
{
diff --git a/terminal.h b/terminal.h
index b4ce1212..449a1329 100644
--- a/terminal.h
+++ b/terminal.h
@@ -42,7 +42,8 @@ struct attributes {
uint32_t have_fg:1;
uint32_t have_bg:1;
uint32_t selected:2;
- uint32_t reserved:3;
+ uint32_t linefeed:1;
+ uint32_t reserved:2;
uint32_t bg:24;
};
static_assert(sizeof(struct attributes) == 8, "bad size");
@@ -403,6 +404,7 @@ void term_scroll_partial(
void term_scroll_reverse_partial(
struct terminal *term, struct scroll_region region, int rows);
+void term_formfeed(struct terminal *term);
void term_linefeed(struct terminal *term);
void term_reverse_index(struct terminal *term);
diff --git a/vt.c b/vt.c
index 06445e4c..3cdd5b24 100644
--- a/vt.c
+++ b/vt.c
@@ -128,7 +128,7 @@ action_execute(struct terminal *term, uint8_t c)
case '\r':
/* FF - form feed */
- term_cursor_left(term, term->cursor.point.col);
+ term_formfeed(term);
break;
case '\b':
@@ -361,8 +361,8 @@ action_esc_dispatch(struct terminal *term, uint8_t final)
break;
case 'E':
+ term_formfeed(term);
term_linefeed(term);
- term_cursor_left(term, term->cursor.point.col);
break;
case 'H':