diff --git a/CHANGELOG.md b/CHANGELOG.md index e85fb439..62e8a367 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,8 @@ * OSC-22 - set xcursor pointer. * Add "xterm" as fallback cursor where "text" is not available. +* `[key-bindings].scrollback-home|end` options. + ### Changed diff --git a/commands.c b/commands.c index 4b0306aa..d6ec9fa0 100644 --- a/commands.c +++ b/commands.c @@ -18,68 +18,58 @@ cmd_scrollback_up(struct terminal *term, int rows) if (urls_mode_is_active(term)) return; - rows = min(rows, term->rows); - xassert(term->grid->offset >= 0); + const struct grid *grid = term->grid; + const int offset = grid->offset; + const int view = grid->view; + const int grid_rows = grid->num_rows; + const int screen_rows = term->rows; - int new_view = term->grid->view - rows; - while (new_view < 0) - new_view += term->grid->num_rows; - new_view %= term->grid->num_rows; + int scrollback_start = (offset + screen_rows) & (grid_rows - 1); - xassert(new_view >= 0); - xassert(new_view < term->grid->num_rows); - - /* Avoid scrolling in uninitialized rows */ - while (term->grid->rows[new_view] == NULL) - new_view = (new_view + 1) % term->grid->num_rows; - - if (new_view == term->grid->view) { - /* - * This happens when scrolling up in a newly opened terminal; - * every single line (except those already visible) are - * uninitialized, and the loop above will bring us back to - * where we started. - */ - return; + /* Part of the scrollback may be uninitialized */ + while (grid->rows[scrollback_start] == NULL) { + scrollback_start++; + scrollback_start &= grid_rows - 1; } - /* Don't scroll past scrollback history */ - int end = (term->grid->offset + term->rows - 1) % term->grid->num_rows; - if (end >= term->grid->offset) { - /* Not wrapped */ - if (new_view >= term->grid->offset && new_view <= end) - new_view = (end + 1) % term->grid->num_rows; + /* Number of rows to scroll, without going past the scrollback start */ + int max_rows = 0; + if (view + screen_rows >= grid_rows) { + /* View crosses scrollback wrap-around */ + xassert(scrollback_start <= view); + max_rows = view - scrollback_start; } else { - if (new_view >= term->grid->offset || new_view <= end) - new_view = (end + 1) % term->grid->num_rows; + if (scrollback_start <= view) + max_rows = view - scrollback_start; + else + max_rows = view + (grid_rows - scrollback_start); } - while (term->grid->rows[new_view] == NULL) - new_view = (new_view + 1) % term->grid->num_rows; + rows = min(rows, max_rows); + if (rows == 0) + return; + int new_view = (view + grid_rows) - rows; + new_view &= grid_rows - 1; + + xassert(new_view != view); + xassert(grid->rows[new_view] != NULL); #if defined(_DEBUG) for (int r = 0; r < term->rows; r++) - xassert(term->grid->rows[(new_view + r) % term->grid->num_rows] != NULL); + xassert(grid->rows[(new_view + r) & (grid->num_rows - 1)] != NULL); #endif LOG_DBG("scrollback UP: %d -> %d (offset = %d, end = %d, rows = %d)", - term->grid->view, new_view, term->grid->offset, end, term->grid->num_rows); - - if (new_view == term->grid->view) - return; - - int diff = -1; - if (new_view < term->grid->view) - diff = term->grid->view - new_view; - else - diff = (term->grid->num_rows - new_view) + term->grid->view; + view, new_view, offset, end, grid_rows); selection_view_up(term, new_view); term->grid->view = new_view; - if (diff >= 0 && diff < term->rows) { - term_damage_scroll(term, DAMAGE_SCROLL_REVERSE_IN_VIEW, (struct scroll_region){0, term->rows}, diff); - term_damage_rows_in_view(term, 0, diff - 1); + if (rows < term->rows) { + term_damage_scroll( + term, DAMAGE_SCROLL_REVERSE_IN_VIEW, + (struct scroll_region){0, term->rows}, rows); + term_damage_rows_in_view(term, 0, rows - 1); } else term_damage_view(term); @@ -95,65 +85,45 @@ cmd_scrollback_down(struct terminal *term, int rows) if (urls_mode_is_active(term)) return; - if (term->grid->view == term->grid->offset) + const struct grid *grid = term->grid; + const int offset = grid->offset; + const int view = grid->view; + const int grid_rows = grid->num_rows; + const int screen_rows = term->rows; + + const int scrollback_end = offset; + + /* Number of rows to scroll, without going past the scrollback end */ + int max_rows = 0; + if (view <= scrollback_end) + max_rows = scrollback_end - view; + else + max_rows = offset + (grid_rows - view); + + rows = min(rows, max_rows); + if (rows == 0) return; - rows = min(rows, term->rows); - xassert(term->grid->offset >= 0); - - int new_view = (term->grid->view + rows) % term->grid->num_rows; - xassert(new_view >= 0); - xassert(new_view < term->grid->num_rows); - - /* Prevent scrolling in uninitialized rows */ - bool all_initialized = false; - do { - all_initialized = true; - - for (int i = 0; i < term->rows; i++) { - int row_no = (new_view + i) % term->grid->num_rows; - if (term->grid->rows[row_no] == NULL) { - all_initialized = false; - new_view--; - break; - } - } - } while (!all_initialized); - - /* Don't scroll past scrollback history */ - int end = (term->grid->offset + term->rows - 1) % term->grid->num_rows; - if (end >= term->grid->offset) { - /* Not wrapped */ - if (new_view >= term->grid->offset && new_view <= end) - new_view = term->grid->offset; - } else { - if (new_view >= term->grid->offset || new_view <= end) - new_view = term->grid->offset; - } + int new_view = (view + rows) & (grid_rows - 1); + xassert(new_view != view); + xassert(grid->rows[new_view] != NULL); #if defined(_DEBUG) for (int r = 0; r < term->rows; r++) - xassert(term->grid->rows[(new_view + r) % term->grid->num_rows] != NULL); + xassert(grid->rows[(new_view + r) & (grid_rows - 1)] != NULL); #endif LOG_DBG("scrollback DOWN: %d -> %d (offset = %d, end = %d, rows = %d)", - term->grid->view, new_view, term->grid->offset, end, term->grid->num_rows); - - if (new_view == term->grid->view) - return; - - int diff = -1; - if (new_view > term->grid->view) - diff = new_view - term->grid->view; - else - diff = (term->grid->num_rows - term->grid->view) + new_view; + view, new_view, offset, end, grid_rows); selection_view_down(term, new_view); term->grid->view = new_view; - if (diff >= 0 && diff < term->rows) { - term_damage_scroll(term, DAMAGE_SCROLL_IN_VIEW, (struct scroll_region){0, term->rows}, diff); - term_damage_rows_in_view(term, term->rows - diff, term->rows - 1); + if (rows < term->rows) { + term_damage_scroll( + term, DAMAGE_SCROLL_IN_VIEW, + (struct scroll_region){0, term->rows}, rows); + term_damage_rows_in_view(term, term->rows - rows, screen_rows - 1); } else term_damage_view(term); diff --git a/config.c b/config.c index f50ee685..b247f3e9 100644 --- a/config.c +++ b/config.c @@ -95,6 +95,8 @@ static const char *const binding_action_map[] = { [BIND_ACTION_SCROLLBACK_DOWN_PAGE] = "scrollback-down-page", [BIND_ACTION_SCROLLBACK_DOWN_HALF_PAGE] = "scrollback-down-half-page", [BIND_ACTION_SCROLLBACK_DOWN_LINE] = "scrollback-down-line", + [BIND_ACTION_SCROLLBACK_HOME] = "scrollback-home", + [BIND_ACTION_SCROLLBACK_END] = "scrollback-end", [BIND_ACTION_CLIPBOARD_COPY] = "clipboard-copy", [BIND_ACTION_CLIPBOARD_PASTE] = "clipboard-paste", [BIND_ACTION_PRIMARY_PASTE] = "primary-paste", diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 7763a8b3..9127c015 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -681,6 +681,12 @@ e.g. *search-start=none*. *scrollback-down-line* Scroll down/forward a single line in history. Default: _not bound_. +*scrollback-home* + Scroll to the beginning of the scrollback. Default: _not bound_. + +*scrollback-end* + Scroll to the end (bottom) of the scrollback. Default: _not bound_. + *clipboard-copy* Copies the current selection into the _clipboard_. Default: _Control+Shift+c_ _XF86Copy_. diff --git a/input.c b/input.c index 26af0807..f402fa3d 100644 --- a/input.c +++ b/input.c @@ -134,6 +134,20 @@ execute_binding(struct seat *seat, struct terminal *term, } break; + case BIND_ACTION_SCROLLBACK_HOME: + if (term->grid == &term->normal) { + cmd_scrollback_up(term, term->grid->num_rows); + return true; + } + break; + + case BIND_ACTION_SCROLLBACK_END: + if (term->grid == &term->normal) { + cmd_scrollback_down(term, term->grid->num_rows); + return true; + } + break; + case BIND_ACTION_CLIPBOARD_COPY: selection_to_clipboard(seat, term, serial); return true; diff --git a/wayland.h b/wayland.h index 2d753e70..e593073e 100644 --- a/wayland.h +++ b/wayland.h @@ -38,6 +38,8 @@ enum bind_action_normal { BIND_ACTION_SCROLLBACK_DOWN_PAGE, BIND_ACTION_SCROLLBACK_DOWN_HALF_PAGE, BIND_ACTION_SCROLLBACK_DOWN_LINE, + BIND_ACTION_SCROLLBACK_HOME, + BIND_ACTION_SCROLLBACK_END, BIND_ACTION_CLIPBOARD_COPY, BIND_ACTION_CLIPBOARD_PASTE, BIND_ACTION_PRIMARY_PASTE,