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':