Add an optional build-time switch to disable scrollback support

This change introduces a new `scrollback` Meson build option (enabled
by default) that lets foot be compiled without scrollback history.
When the option is off, `FOOT_HAVE_SCROLLBACK` is left
undefined and the relevant code is excluded from the build,
producing a slimmer terminal for use cases that do not need it.

With scrollback disabled:

- The `[scrollback]` section in `foot.ini` and its key bindings
  become no-ops.
- In-terminal search is removed,
  along with the "pipe scrollback" action, the scrollback indicator,
  and their colors/overlays.
- IME hooks for the search box, mouse-wheel scrollback handling,
  and related `terminal` state are compiled out.
- Selection auto-scroll (the timer that scrolls the viewport while
  a drag-selection extends past the visible area) is removed,
  since with no scrollback there is nowhere to scroll to.
  The associated function declarations,
  `enum selection_scroll_direction`,
  the `auto_scroll` field on `terminal::selection`,
  and their init/teardown sites are all excluded from the build.

To keep action enumerations stable across build configurations,
two range markers (`BIND_ACTION_PIPE_FIRST` / `BIND_ACTION_PIPE_LAST`)
are introduced so that pipe-action handling does not depend on
`PIPE_SCROLLBACK` being present. The build summary and
`foot --version` now report `+scrollback` or `-scrollback`,
and the config tests have been updated to account for optional section.
This commit is contained in:
Campbell Barton 2026-05-15 15:51:01 +10:00
parent f35e60577f
commit d28ac267d2
25 changed files with 317 additions and 22 deletions

View file

@ -72,6 +72,14 @@
## Unreleased
### Added
* New `scrollback` Meson build option (enabled by default). When
disabled, scrollback history, in-terminal search, the scrollback
indicator, the "pipe scrollback" action, and selection auto-scroll
are excluded from the build, producing a smaller binary. `foot
--version` reports `+scrollback` or `-scrollback`.
### Changed
### Deprecated
### Removed

View file

@ -10,6 +10,7 @@
#include "url-mode.h"
#include "util.h"
#if defined(FOOT_HAVE_SCROLLBACK)
void
cmd_scrollback_up(struct terminal *term, int rows)
{
@ -113,3 +114,4 @@ cmd_scrollback_down(struct terminal *term, int rows)
render_refresh_urls(term);
render_refresh(term);
}
#endif /* FOOT_HAVE_SCROLLBACK */

View file

@ -2,5 +2,7 @@
#include "terminal.h"
#if defined(FOOT_HAVE_SCROLLBACK)
void cmd_scrollback_up(struct terminal *term, int rows);
void cmd_scrollback_down(struct terminal *term, int rows);
#endif /* FOOT_HAVE_SCROLLBACK */

View file

@ -109,6 +109,7 @@ static const uint32_t default_sixel_colors[16] = {
static const char *const binding_action_map[] = {
[BIND_ACTION_NONE] = NULL,
[BIND_ACTION_NOOP] = "noop",
#if defined(FOOT_HAVE_SCROLLBACK)
[BIND_ACTION_SCROLLBACK_UP_PAGE] = "scrollback-up-page",
[BIND_ACTION_SCROLLBACK_UP_HALF_PAGE] = "scrollback-up-half-page",
[BIND_ACTION_SCROLLBACK_UP_LINE] = "scrollback-up-line",
@ -117,10 +118,13 @@ static const char *const binding_action_map[] = {
[BIND_ACTION_SCROLLBACK_DOWN_LINE] = "scrollback-down-line",
[BIND_ACTION_SCROLLBACK_HOME] = "scrollback-home",
[BIND_ACTION_SCROLLBACK_END] = "scrollback-end",
#endif /* FOOT_HAVE_SCROLLBACK */
[BIND_ACTION_CLIPBOARD_COPY] = "clipboard-copy",
[BIND_ACTION_CLIPBOARD_PASTE] = "clipboard-paste",
[BIND_ACTION_PRIMARY_PASTE] = "primary-paste",
#if defined(FOOT_HAVE_SCROLLBACK)
[BIND_ACTION_SEARCH_START] = "search-start",
#endif
[BIND_ACTION_FONT_SIZE_UP] = "font-increase",
[BIND_ACTION_FONT_SIZE_DOWN] = "font-decrease",
[BIND_ACTION_FONT_SIZE_RESET] = "font-reset",
@ -128,7 +132,9 @@ static const char *const binding_action_map[] = {
[BIND_ACTION_MINIMIZE] = "minimize",
[BIND_ACTION_MAXIMIZE] = "maximize",
[BIND_ACTION_FULLSCREEN] = "fullscreen",
#if defined(FOOT_HAVE_SCROLLBACK)
[BIND_ACTION_PIPE_SCROLLBACK] = "pipe-scrollback",
#endif
[BIND_ACTION_PIPE_VIEW] = "pipe-visible",
[BIND_ACTION_PIPE_SELECTED] = "pipe-selected",
[BIND_ACTION_PIPE_COMMAND_OUTPUT] = "pipe-command-output",
@ -149,8 +155,10 @@ static const char *const binding_action_map[] = {
[BIND_ACTION_THEME_TOGGLE] = "color-theme-toggle",
/* Mouse-specific actions */
#if defined(FOOT_HAVE_SCROLLBACK)
[BIND_ACTION_SCROLLBACK_UP_MOUSE] = "scrollback-up-mouse",
[BIND_ACTION_SCROLLBACK_DOWN_MOUSE] = "scrollback-down-mouse",
#endif /* FOOT_HAVE_SCROLLBACK */
[BIND_ACTION_SELECT_BEGIN] = "select-begin",
[BIND_ACTION_SELECT_BEGIN_BLOCK] = "select-begin-block",
[BIND_ACTION_SELECT_EXTEND] = "select-extend",
@ -161,6 +169,7 @@ static const char *const binding_action_map[] = {
[BIND_ACTION_SELECT_ROW] = "select-row",
};
#if defined(FOOT_HAVE_SCROLLBACK)
static const char *const search_binding_action_map[] = {
[BIND_ACTION_SEARCH_NONE] = NULL,
[BIND_ACTION_SEARCH_SCROLLBACK_UP_PAGE] = "scrollback-up-page",
@ -199,6 +208,7 @@ static const char *const search_binding_action_map[] = {
[BIND_ACTION_SEARCH_PRIMARY_PASTE] = "primary-paste",
[BIND_ACTION_SEARCH_UNICODE_INPUT] = "unicode-input",
};
#endif /* FOOT_HAVE_SCROLLBACK */
static const char *const url_binding_action_map[] = {
[BIND_ACTION_URL_NONE] = NULL,
@ -208,8 +218,10 @@ static const char *const url_binding_action_map[] = {
static_assert(ALEN(binding_action_map) == BIND_ACTION_COUNT,
"binding action map size mismatch");
#if defined(FOOT_HAVE_SCROLLBACK)
static_assert(ALEN(search_binding_action_map) == BIND_ACTION_SEARCH_COUNT,
"search binding action map size mismatch");
#endif
static_assert(ALEN(url_binding_action_map) == BIND_ACTION_URL_COUNT,
"URL binding action map size mismatch");
@ -1249,6 +1261,7 @@ parse_section_desktop_notifications(struct context *ctx)
}
}
#if defined(FOOT_HAVE_SCROLLBACK)
static bool
parse_section_scrollback(struct context *ctx)
{
@ -1291,6 +1304,7 @@ parse_section_scrollback(struct context *ctx)
return false;
}
}
#endif /* FOOT_HAVE_SCROLLBACK */
static bool
parse_section_url(struct context *ctx)
@ -1489,6 +1503,7 @@ parse_color_theme(struct context *ctx, struct color_theme *theme)
return true;
}
#if defined(FOOT_HAVE_SCROLLBACK)
else if (streq(key, "scrollback-indicator")) {
if (!value_to_two_colors(
ctx,
@ -1530,6 +1545,7 @@ parse_color_theme(struct context *ctx, struct color_theme *theme)
theme->use_custom.search_box_match = true;
return true;
}
#endif /* FOOT_HAVE_SCROLLBACK */
else if (streq(key, "cursor")) {
if (!value_to_two_colors(
@ -2313,8 +2329,8 @@ parse_key_binding_section(struct context *ctx,
/* TODO: this is ugly... */
if (action_map == binding_action_map &&
action >= BIND_ACTION_PIPE_SCROLLBACK &&
action <= BIND_ACTION_PIPE_COMMAND_OUTPUT)
action >= BIND_ACTION_PIPE_FIRST &&
action <= BIND_ACTION_PIPE_LAST)
{
ssize_t pipe_remove_len = pipe_argv_from_value(ctx, &aux.pipe);
if (pipe_remove_len <= 0)
@ -2478,6 +2494,7 @@ parse_section_key_bindings(struct context *ctx)
&ctx->conf->bindings.key);
}
#if defined(FOOT_HAVE_SCROLLBACK)
static bool
parse_section_search_bindings(struct context *ctx)
{
@ -2486,6 +2503,7 @@ parse_section_search_bindings(struct context *ctx)
BIND_ACTION_SEARCH_COUNT, search_binding_action_map,
&ctx->conf->bindings.search);
}
#endif /* FOOT_HAVE_SCROLLBACK */
static bool
parse_section_url_bindings(struct context *ctx)
@ -3046,7 +3064,9 @@ enum section {
SECTION_SECURITY,
SECTION_BELL,
SECTION_DESKTOP_NOTIFICATIONS,
#if defined(FOOT_HAVE_SCROLLBACK)
SECTION_SCROLLBACK,
#endif
SECTION_URL,
SECTION_REGEX,
SECTION_COLORS_DARK,
@ -3055,7 +3075,9 @@ enum section {
SECTION_MOUSE,
SECTION_CSD,
SECTION_KEY_BINDINGS,
#if defined(FOOT_HAVE_SCROLLBACK)
SECTION_SEARCH_BINDINGS,
#endif
SECTION_URL_BINDINGS,
SECTION_MOUSE_BINDINGS,
SECTION_TEXT_BINDINGS,
@ -3082,7 +3104,9 @@ static const struct {
[SECTION_SECURITY] = {&parse_section_security, "security"},
[SECTION_BELL] = {&parse_section_bell, "bell"},
[SECTION_DESKTOP_NOTIFICATIONS] = {&parse_section_desktop_notifications, "desktop-notifications"},
#if defined(FOOT_HAVE_SCROLLBACK)
[SECTION_SCROLLBACK] = {&parse_section_scrollback, "scrollback"},
#endif
[SECTION_URL] = {&parse_section_url, "url"},
[SECTION_REGEX] = {&parse_section_regex, "regex", true},
[SECTION_COLORS_DARK] = {&parse_section_colors_dark, "colors-dark"},
@ -3091,7 +3115,9 @@ static const struct {
[SECTION_MOUSE] = {&parse_section_mouse, "mouse"},
[SECTION_CSD] = {&parse_section_csd, "csd"},
[SECTION_KEY_BINDINGS] = {&parse_section_key_bindings, "key-bindings"},
#if defined(FOOT_HAVE_SCROLLBACK)
[SECTION_SEARCH_BINDINGS] = {&parse_section_search_bindings, "search-bindings"},
#endif
[SECTION_URL_BINDINGS] = {&parse_section_url_bindings, "url-bindings"},
[SECTION_MOUSE_BINDINGS] = {&parse_section_mouse_bindings, "mouse-bindings"},
[SECTION_TEXT_BINDINGS] = {&parse_section_text_bindings, "text-bindings"},
@ -3325,16 +3351,20 @@ static void
add_default_key_bindings(struct config *conf)
{
const struct config_key_binding bindings[] = {
#if defined(FOOT_HAVE_SCROLLBACK)
{BIND_ACTION_SCROLLBACK_UP_PAGE, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_Prior}}},
{BIND_ACTION_SCROLLBACK_UP_PAGE, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_KP_Prior}}},
{BIND_ACTION_SCROLLBACK_DOWN_PAGE, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_Next}}},
{BIND_ACTION_SCROLLBACK_DOWN_PAGE, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_KP_Next}}},
#endif /* FOOT_HAVE_SCROLLBACK */
{BIND_ACTION_CLIPBOARD_COPY, m(XKB_MOD_NAME_CTRL "+" XKB_MOD_NAME_SHIFT), {{XKB_KEY_c}}},
{BIND_ACTION_CLIPBOARD_COPY, m("none"), {{XKB_KEY_XF86Copy}}},
{BIND_ACTION_CLIPBOARD_PASTE, m(XKB_MOD_NAME_CTRL "+" XKB_MOD_NAME_SHIFT), {{XKB_KEY_v}}},
{BIND_ACTION_CLIPBOARD_PASTE, m("none"), {{XKB_KEY_XF86Paste}}},
{BIND_ACTION_PRIMARY_PASTE, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_Insert}}},
#if defined(FOOT_HAVE_SCROLLBACK)
{BIND_ACTION_SEARCH_START, m(XKB_MOD_NAME_CTRL "+" XKB_MOD_NAME_SHIFT), {{XKB_KEY_r}}},
#endif
{BIND_ACTION_FONT_SIZE_UP, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_plus}}},
{BIND_ACTION_FONT_SIZE_UP, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_equal}}},
{BIND_ACTION_FONT_SIZE_UP, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_KP_Add}}},
@ -3354,6 +3384,7 @@ add_default_key_bindings(struct config *conf)
}
#if defined(FOOT_HAVE_SCROLLBACK)
static void
add_default_search_bindings(struct config *conf)
{
@ -3407,6 +3438,7 @@ add_default_search_bindings(struct config *conf)
conf->bindings.search.count = ALEN(bindings);
conf->bindings.search.arr = xmemdup(bindings, sizeof(bindings));
}
#endif /* FOOT_HAVE_SCROLLBACK */
static void
add_default_url_bindings(struct config *conf)
@ -3427,8 +3459,10 @@ static void
add_default_mouse_bindings(struct config *conf)
{
const struct config_key_binding bindings[] = {
#if defined(FOOT_HAVE_SCROLLBACK)
{BIND_ACTION_SCROLLBACK_UP_MOUSE, m("none"), {.m = {BTN_WHEEL_BACK, 1}}},
{BIND_ACTION_SCROLLBACK_DOWN_MOUSE, m("none"), {.m = {BTN_WHEEL_FORWARD, 1}}},
#endif /* FOOT_HAVE_SCROLLBACK */
{BIND_ACTION_PRIMARY_PASTE, m("none"), {.m = {BTN_MIDDLE, 1}}},
{BIND_ACTION_SELECT_BEGIN, m("none"), {.m = {BTN_LEFT, 1}}},
{BIND_ACTION_SELECT_BEGIN_BLOCK, m(XKB_MOD_NAME_CTRL), {.m = {BTN_LEFT, 1}}},
@ -3530,6 +3564,7 @@ config_load(struct config *conf, const char *conf_path,
},
.custom_regexes = tll_init(),
.can_shape_grapheme = fcft_caps & FCFT_CAPABILITY_GRAPHEME_SHAPING,
#if defined(FOOT_HAVE_SCROLLBACK)
.scrollback = {
.lines = 1000,
.indicator = {
@ -3539,6 +3574,7 @@ config_load(struct config *conf, const char *conf_path,
},
.multiplier = 3.,
},
#endif /* FOOT_HAVE_SCROLLBACK */
.colors_dark = {
.fg = default_foreground,
.bg = default_background,
@ -3555,7 +3591,9 @@ config_load(struct config *conf, const char *conf_path,
},
.use_custom = {
.jump_label = false,
#if defined(FOOT_HAVE_SCROLLBACK)
.scrollback_indicator = false,
#endif
.url = false,
},
.blur = false,
@ -3707,7 +3745,9 @@ config_load(struct config *conf, const char *conf_path,
}
add_default_key_bindings(conf);
#if defined(FOOT_HAVE_SCROLLBACK)
add_default_search_bindings(conf);
#endif
add_default_url_bindings(conf);
add_default_mouse_bindings(conf);
@ -3766,8 +3806,10 @@ config_load(struct config *conf, const char *conf_path,
#if defined(_DEBUG)
for (size_t i = 0; i < conf->bindings.key.count; i++)
xassert(conf->bindings.key.arr[i].action != BIND_ACTION_NONE);
#if defined(FOOT_HAVE_SCROLLBACK)
for (size_t i = 0; i < conf->bindings.search.count; i++)
xassert(conf->bindings.search.arr[i].action != BIND_ACTION_SEARCH_NONE);
#endif /* FOOT_HAVE_SCROLLBACK */
for (size_t i = 0; i < conf->bindings.url.count; i++)
xassert(conf->bindings.url.arr[i].action != BIND_ACTION_URL_NONE);
#endif
@ -3842,9 +3884,11 @@ config_override_apply(struct config *conf, config_override_t *overrides,
resolve_key_binding_collisions(
conf, section_info[SECTION_KEY_BINDINGS].name,
binding_action_map, &conf->bindings.key, KEY_BINDING) &&
#if defined(FOOT_HAVE_SCROLLBACK)
resolve_key_binding_collisions(
conf, section_info[SECTION_SEARCH_BINDINGS].name,
search_binding_action_map, &conf->bindings.search, KEY_BINDING) &&
#endif /* FOOT_HAVE_SCROLLBACK */
resolve_key_binding_collisions(
conf, section_info[SECTION_URL_BINDINGS].name,
url_binding_action_map, &conf->bindings.url, KEY_BINDING) &&
@ -3935,7 +3979,9 @@ config_clone(const struct config *old)
conf->app_id = xstrdup(old->app_id);
conf->toplevel_tag = xstrdup(old->toplevel_tag);
conf->word_delimiters = xc32dup(old->word_delimiters);
#if defined(FOOT_HAVE_SCROLLBACK)
conf->scrollback.indicator.text = xc32dup(old->scrollback.indicator.text);
#endif
conf->server_socket_path = xstrdup(old->server_socket_path);
spawn_template_clone(&conf->bell.command, &old->bell.command);
spawn_template_clone(&conf->desktop_notifications.command,
@ -3969,7 +4015,9 @@ config_clone(const struct config *old)
}
key_binding_list_clone(&conf->bindings.key, &old->bindings.key);
#if defined(FOOT_HAVE_SCROLLBACK)
key_binding_list_clone(&conf->bindings.search, &old->bindings.search);
#endif
key_binding_list_clone(&conf->bindings.url, &old->bindings.url);
key_binding_list_clone(&conf->bindings.mouse, &old->bindings.mouse);
@ -4037,7 +4085,9 @@ config_free(struct config *conf)
free(conf->toplevel_tag);
free(conf->word_delimiters);
spawn_template_free(&conf->bell.command);
#if defined(FOOT_HAVE_SCROLLBACK)
free(conf->scrollback.indicator.text);
#endif
spawn_template_free(&conf->desktop_notifications.command);
spawn_template_free(&conf->desktop_notifications.command_action_arg);
spawn_template_free(&conf->desktop_notifications.close);
@ -4062,7 +4112,9 @@ config_free(struct config *conf)
}
free_key_binding_list(&conf->bindings.key);
#if defined(FOOT_HAVE_SCROLLBACK)
free_key_binding_list(&conf->bindings.search);
#endif
free_key_binding_list(&conf->bindings.url);
free_key_binding_list(&conf->bindings.mouse);
tll_free_and_free(conf->mouse.selection_override_modifiers, free);

View file

@ -166,11 +166,14 @@ struct color_theme {
uint32_t bg;
} jump_label;
#if defined(FOOT_HAVE_SCROLLBACK)
struct {
uint32_t fg;
uint32_t bg;
} scrollback_indicator;
#endif /* FOOT_HAVE_SCROLLBACK */
#if defined(FOOT_HAVE_SCROLLBACK)
struct {
struct {
uint32_t fg;
@ -182,14 +185,17 @@ struct color_theme {
uint32_t bg;
} match;
} search_box;
#endif /* FOOT_HAVE_SCROLLBACK */
struct {
bool cursor:1;
bool jump_label:1;
bool scrollback_indicator:1;
bool url:1;
#if defined(FOOT_HAVE_SCROLLBACK)
bool scrollback_indicator:1;
bool search_box_no_match:1;
bool search_box_match:1;
#endif /* FOOT_HAVE_SCROLLBACK */
uint8_t dim;
} use_custom;
@ -308,6 +314,7 @@ struct config {
bool command_focused;
} bell;
#if defined(FOOT_HAVE_SCROLLBACK)
struct {
uint32_t lines;
@ -328,6 +335,7 @@ struct config {
} indicator;
float multiplier;
} scrollback;
#endif /* FOOT_HAVE_SCROLLBACK */
struct {
char32_t *label_letters;
@ -375,9 +383,11 @@ struct config {
* Special modes
*/
#if defined(FOOT_HAVE_SCROLLBACK)
/* While searching (not - action to *start* a search is in the
* 'key' bindings above */
struct config_key_binding_list search;
#endif /* FOOT_HAVE_SCROLLBACK */
/* While showing URL jump labels */
struct config_key_binding_list url;

2
csi.c
View file

@ -991,11 +991,13 @@ csi_dispatch(struct terminal *term, uint8_t final)
term->grid->cursor.lcf = false;
break;
#if defined(FOOT_HAVE_SCROLLBACK)
case 3: {
/* Erase scrollback */
term_erase_scrollback(term);
break;
}
#endif
default:
UNHANDLED();

View file

@ -16,6 +16,12 @@ const char version_and_features[] =
" -ime"
#endif
#if defined(FOOT_HAVE_SCROLLBACK)
" +scrollback"
#else
" -scrollback"
#endif
#if defined(FOOT_GRAPHEME_CLUSTERING) && FOOT_GRAPHEME_CLUSTERING
" +graphemes"
#else

22
ime.c
View file

@ -177,10 +177,14 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
ime_reset_preedit(seat);
if (term != NULL) {
if (term->is_searching)
#if defined(FOOT_HAVE_SCROLLBACK)
if (term->is_searching) {
render_refresh_search(term);
else
} else
#endif
{
render_refresh(term);
}
}
}
@ -198,11 +202,15 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
size_t len = strlen(text);
if (term != NULL) {
#if defined(FOOT_HAVE_SCROLLBACK)
if (term->is_searching) {
search_add_chars(term, text, len);
render_refresh_search(term);
} else
#endif
{
term_to_slave(term, text, len);
}
}
ime_reset_pending_commit(seat);
}
@ -367,10 +375,14 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
ime_reset_pending_preedit(seat);
if (term != NULL) {
if (term->is_searching)
#if defined(FOOT_HAVE_SCROLLBACK)
if (term->is_searching) {
render_refresh_search(term);
else
} else
#endif
{
render_refresh(term);
}
}
}
@ -473,9 +485,11 @@ ime_update_cursor_rect(struct seat *seat)
if (seat->ime.preedit.cells != NULL)
goto update;
#if defined(FOOT_HAVE_SCROLLBACK)
/* Set in render_search_box() */
if (term->is_searching)
goto update;
#endif
int x, y, width, height;
int col = term->grid->cursor.point.col;

32
input.c
View file

@ -82,7 +82,9 @@ pipe_closed:
return true;
}
#if defined(FOOT_HAVE_SCROLLBACK)
static void alternate_scroll(struct seat *seat, int amount, int button);
#endif
static bool
execute_binding(struct seat *seat, struct terminal *term,
@ -97,6 +99,7 @@ execute_binding(struct seat *seat, struct terminal *term,
case BIND_ACTION_NOOP:
return true;
#if defined(FOOT_HAVE_SCROLLBACK)
case BIND_ACTION_SCROLLBACK_UP_PAGE:
if (term->grid == &term->normal) {
cmd_scrollback_up(term, term->rows);
@ -176,6 +179,7 @@ execute_binding(struct seat *seat, struct terminal *term,
return true;
}
break;
#endif /* FOOT_HAVE_SCROLLBACK */
case BIND_ACTION_CLIPBOARD_COPY:
selection_to_clipboard(seat, term, serial);
@ -191,9 +195,11 @@ execute_binding(struct seat *seat, struct terminal *term,
term_reset_view(term);
return true;
#if defined(FOOT_HAVE_SCROLLBACK)
case BIND_ACTION_SEARCH_START:
search_begin(term);
return true;
#endif /* FOOT_HAVE_SCROLLBACK */
case BIND_ACTION_FONT_SIZE_UP:
term_font_size_increase(term);
@ -231,10 +237,12 @@ execute_binding(struct seat *seat, struct terminal *term,
xdg_toplevel_set_fullscreen(term->window->xdg_toplevel, NULL);
return true;
#if defined(FOOT_HAVE_SCROLLBACK)
case BIND_ACTION_PIPE_SCROLLBACK:
if (term->grid == &term->alt)
break;
/* FALLTHROUGH */
#endif /* FOOT_HAVE_SCROLLBACK */
case BIND_ACTION_PIPE_VIEW:
case BIND_ACTION_PIPE_SELECTED:
case BIND_ACTION_PIPE_COMMAND_OUTPUT: {
@ -265,9 +273,11 @@ execute_binding(struct seat *seat, struct terminal *term,
bool success;
switch (action) {
#if defined(FOOT_HAVE_SCROLLBACK)
case BIND_ACTION_PIPE_SCROLLBACK:
success = term_scrollback_to_text(term, &text, &len);
break;
#endif /* FOOT_HAVE_SCROLLBACK */
case BIND_ACTION_PIPE_VIEW:
success = term_view_to_text(term, &text, &len);
@ -1649,6 +1659,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
return;
}
#if defined(FOOT_HAVE_SCROLLBACK)
else if (term->is_searching) {
if (pressed) {
if (should_repeat)
@ -1660,6 +1671,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
}
return;
}
#endif /* FOOT_HAVE_SCROLLBACK */
else if (urls_mode_is_active(term)) {
if (pressed) {
@ -1931,9 +1943,11 @@ UNITTEST
.start = {-1, -1},
.end = {-1, -1},
},
#if defined(FOOT_HAVE_SCROLLBACK)
.auto_scroll = {
.fd = -1,
},
#endif
},
};
@ -2768,6 +2782,7 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
/* Cursor is inside the grid, i.e. *not* in the margins */
const bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0;
#if defined(FOOT_HAVE_SCROLLBACK)
enum selection_scroll_direction auto_scroll_direction
= term->selection.coords.end.row < 0
? SELECTION_SCROLL_NOT
@ -2779,9 +2794,14 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
if (auto_scroll_direction == SELECTION_SCROLL_NOT)
selection_stop_scroll_timer(term);
#endif /* FOOT_HAVE_SCROLLBACK */
/* Update selection */
if (!term->is_searching) {
#if defined(FOOT_HAVE_SCROLLBACK)
if (!term->is_searching)
#endif
{
#if defined(FOOT_HAVE_SCROLLBACK)
if (auto_scroll_direction != SELECTION_SCROLL_NOT) {
/*
* Start 'selection auto-scrolling'
@ -2809,6 +2829,7 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
term, 400000000 / (divisor > 0 ? divisor : 1),
auto_scroll_direction, seat->mouse.col);
}
#endif /* FOOT_HAVE_SCROLLBACK */
if (term->selection.ongoing &&
(cursor_is_on_new_cell ||
@ -3244,7 +3265,9 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
break;
case TERM_SURF_GRID: {
#if defined(FOOT_HAVE_SCROLLBACK)
search_cancel(term);
#endif
urls_reset(term);
bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0;
@ -3301,6 +3324,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
}
}
#if defined(FOOT_HAVE_SCROLLBACK)
static void
alternate_scroll(struct seat *seat, int amount, int button)
{
@ -3320,6 +3344,7 @@ alternate_scroll(struct seat *seat, int amount, int button)
key_press_release(seat, term, seat->kbd.serial, key, XKB_KEY_DOWN);
key_press_release(seat, term, seat->kbd.serial, key, XKB_KEY_UP);
}
#endif /* FOOT_HAVE_SCROLLBACK */
static void
mouse_scroll(struct seat *seat, int amount, enum wl_pointer_axis axis)
@ -3367,10 +3392,15 @@ mouse_scroll(struct seat *seat, int amount, enum wl_pointer_axis axis)
static double
mouse_scroll_multiplier(const struct terminal *term, const struct seat *seat)
{
#if defined(FOOT_HAVE_SCROLLBACK)
return (term->grid == &term->normal ||
(term_mouse_grabbed(term, seat) && term->alt_scrolling))
? term->conf->scrollback.multiplier
: 1.0;
#else
(void)term; (void)seat;
return 1.0;
#endif /* FOOT_HAVE_SCROLLBACK */
}
static void

View file

@ -111,7 +111,9 @@ key_binding_new_for_seat(struct key_binding_manager *mgr,
struct key_set set = {
.public = {
.key = tll_init(),
#if defined(FOOT_HAVE_SCROLLBACK)
.search = tll_init(),
#endif
.url = tll_init(),
.mouse = tll_init(),
},
@ -152,7 +154,9 @@ key_binding_new_for_conf(struct key_binding_manager *mgr,
struct key_set set = {
.public = {
.key = tll_init(),
#if defined(FOOT_HAVE_SCROLLBACK)
.search = tll_init(),
#endif
.url = tll_init(),
.mouse = tll_init(),
},
@ -531,6 +535,7 @@ convert_key_bindings(struct key_set *set)
}
}
#if defined(FOOT_HAVE_SCROLLBACK)
static void
convert_search_bindings(struct key_set *set)
{
@ -541,6 +546,7 @@ convert_search_bindings(struct key_set *set)
convert_key_binding(set, binding, &set->public.search);
}
}
#endif /* FOOT_HAVE_SCROLLBACK */
static void
convert_url_bindings(struct key_set *set)
@ -597,7 +603,9 @@ load_keymap(struct key_set *set)
}
convert_key_bindings(set);
#if defined(FOOT_HAVE_SCROLLBACK)
convert_search_bindings(set);
#endif
convert_url_bindings(set);
convert_mouse_bindings(set);
@ -638,7 +646,9 @@ static void NOINLINE
unload_keymap(struct key_set *set)
{
key_bindings_destroy(&set->public.key);
#if defined(FOOT_HAVE_SCROLLBACK)
key_bindings_destroy(&set->public.search);
#endif
key_bindings_destroy(&set->public.url);
key_bindings_destroy(&set->public.mouse);
set->public.selection_overrides = 0;

View file

@ -10,6 +10,7 @@
enum bind_action_normal {
BIND_ACTION_NONE,
BIND_ACTION_NOOP,
#if defined(FOOT_HAVE_SCROLLBACK)
BIND_ACTION_SCROLLBACK_UP_PAGE,
BIND_ACTION_SCROLLBACK_UP_HALF_PAGE,
BIND_ACTION_SCROLLBACK_UP_LINE,
@ -18,10 +19,13 @@ enum bind_action_normal {
BIND_ACTION_SCROLLBACK_DOWN_LINE,
BIND_ACTION_SCROLLBACK_HOME,
BIND_ACTION_SCROLLBACK_END,
#endif /* FOOT_HAVE_SCROLLBACK */
BIND_ACTION_CLIPBOARD_COPY,
BIND_ACTION_CLIPBOARD_PASTE,
BIND_ACTION_PRIMARY_PASTE,
#if defined(FOOT_HAVE_SCROLLBACK)
BIND_ACTION_SEARCH_START,
#endif
BIND_ACTION_FONT_SIZE_UP,
BIND_ACTION_FONT_SIZE_DOWN,
BIND_ACTION_FONT_SIZE_RESET,
@ -29,8 +33,10 @@ enum bind_action_normal {
BIND_ACTION_MINIMIZE,
BIND_ACTION_MAXIMIZE,
BIND_ACTION_FULLSCREEN,
BIND_ACTION_PIPE_SCROLLBACK,
BIND_ACTION_PIPE_VIEW,
#if defined(FOOT_HAVE_SCROLLBACK)
BIND_ACTION_PIPE_SCROLLBACK,
#endif
BIND_ACTION_PIPE_SELECTED,
BIND_ACTION_PIPE_COMMAND_OUTPUT,
BIND_ACTION_SHOW_URLS_COPY,
@ -50,8 +56,10 @@ enum bind_action_normal {
BIND_ACTION_THEME_TOGGLE,
/* Mouse specific actions - i.e. they require a mouse coordinate */
#if defined(FOOT_HAVE_SCROLLBACK)
BIND_ACTION_SCROLLBACK_UP_MOUSE,
BIND_ACTION_SCROLLBACK_DOWN_MOUSE,
#endif /* FOOT_HAVE_SCROLLBACK */
BIND_ACTION_SELECT_BEGIN,
BIND_ACTION_SELECT_BEGIN_BLOCK,
BIND_ACTION_SELECT_EXTEND,
@ -63,8 +71,13 @@ enum bind_action_normal {
BIND_ACTION_KEY_COUNT = BIND_ACTION_THEME_TOGGLE + 1,
BIND_ACTION_COUNT = BIND_ACTION_SELECT_ROW + 1,
/* Range markers (aliases) - stable regardless of FOOT_HAVE_SCROLLBACK */
BIND_ACTION_PIPE_FIRST = BIND_ACTION_PIPE_VIEW,
BIND_ACTION_PIPE_LAST = BIND_ACTION_PIPE_COMMAND_OUTPUT,
};
#if defined(FOOT_HAVE_SCROLLBACK)
enum bind_action_search {
BIND_ACTION_SEARCH_NONE,
BIND_ACTION_SEARCH_SCROLLBACK_UP_PAGE,
@ -104,6 +117,7 @@ enum bind_action_search {
BIND_ACTION_SEARCH_UNICODE_INPUT,
BIND_ACTION_SEARCH_COUNT,
};
#endif /* FOOT_HAVE_SCROLLBACK */
enum bind_action_url {
BIND_ACTION_URL_NONE,
@ -142,7 +156,9 @@ struct wayland;
struct key_binding_set {
key_binding_list_t key;
#if defined(FOOT_HAVE_SCROLLBACK)
key_binding_list_t search;
#endif
key_binding_list_t url;
key_binding_list_t mouse;
xkb_mod_mask_t selection_overrides;

View file

@ -90,6 +90,9 @@ add_project_arguments(
(get_option('ime')
? ['-DFOOT_IME_ENABLED=1']
: []) +
(get_option('scrollback')
? ['-DFOOT_HAVE_SCROLLBACK']
: []) +
(get_option('b_pgo') == 'use'
? ['-DFOOT_PGO_ENABLED=1']
: []) +
@ -443,6 +446,7 @@ summary(
'Documentation': scdoc.found(),
'Themes': get_option('themes'),
'IME': get_option('ime'),
'Scrollback': get_option('scrollback'),
'Grapheme clustering': utf8proc.found(),
'utmp backend': utmp_backend,
'utmp helper default path': utmp_default_helper_path,

View file

@ -7,6 +7,9 @@ option('themes', type: 'boolean', value: true,
option('ime', type: 'boolean', value: true,
description: 'IME (Input Method Editor) support')
option('scrollback', type: 'boolean', value: true,
description: 'Enable scrollback history. When disabled, the [scrollback] section in foot.ini and the corresponding key bindings become no-ops.')
option('grapheme-clustering', type: 'feature',
description: 'Enables grapheme clustering using libutf8proc. Requires fcft with harfbuzz support to be useful.')

View file

@ -1732,8 +1732,10 @@ render_ime_preedit_for_seat(struct terminal *term, struct seat *seat,
if (likely(seat->ime.preedit.cells == NULL))
return;
#if defined(FOOT_HAVE_SCROLLBACK)
if (unlikely(term->is_searching))
return;
#endif
const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf);
@ -1975,7 +1977,9 @@ render_overlay(struct terminal *term)
const bool unicode_mode_active = term->unicode_mode.active;
const enum overlay_style style =
#if defined(FOOT_HAVE_SCROLLBACK)
term->is_searching ? OVERLAY_SEARCH :
#endif
term->flash.active ? OVERLAY_FLASH :
unicode_mode_active ? OVERLAY_UNICODE_MODE :
OVERLAY_NONE;
@ -1994,7 +1998,9 @@ render_overlay(struct terminal *term)
pixman_color_t color;
switch (style) {
#if defined(FOOT_HAVE_SCROLLBACK)
case OVERLAY_SEARCH:
#endif
case OVERLAY_UNICODE_MODE:
color = (pixman_color_t){0, 0, 0, 0x7fff};
break;
@ -2028,6 +2034,7 @@ render_overlay(struct terminal *term)
/* Bounding rectangle of damaged areas - for wl_surface_damage_buffer() */
pixman_box32_t damage_bounds;
#if defined(FOOT_HAVE_SCROLLBACK)
if (style == OVERLAY_SEARCH) {
/*
* When possible, we only update the areas that have *changed*
@ -2143,9 +2150,10 @@ render_overlay(struct terminal *term)
pixman_region32_fini(&new_dimmed);
pixman_region32_fini(&damage);
}
else if (buf == term->render.last_overlay_buf &&
style == term->render.last_overlay_style)
else
#endif /* FOOT_HAVE_SCROLLBACK */
if (buf == term->render.last_overlay_buf &&
style == term->render.last_overlay_style)
{
xassert(style == OVERLAY_FLASH || style == OVERLAY_UNICODE_MODE);
shm_did_not_use_buf(buf);
@ -2989,6 +2997,7 @@ render_csd(struct terminal *term)
render_csd_title(term, &infos[CSD_SURF_TITLE], bufs[CSD_SURF_TITLE]);
}
#if defined(FOOT_HAVE_SCROLLBACK)
static void
render_scrollback_position(struct terminal *term)
{
@ -3139,6 +3148,7 @@ render_scrollback_position(struct terminal *term)
fg, 0xffu << 24 | bg,
width - margin - c32len(text) * term->cell_width);
}
#endif /* FOOT_HAVE_SCROLLBACK */
static void
render_render_timer(struct terminal *term, struct timespec render_time)
@ -3612,7 +3622,9 @@ grid_render(struct terminal *term)
render_overlay(term);
render_ime_preedit(term, buf);
#if defined(FOOT_HAVE_SCROLLBACK)
render_scrollback_position(term);
#endif
if (term->conf->tweak.render_timer != RENDER_TIMER_NONE) {
struct timespec end_time;
@ -3716,6 +3728,7 @@ grid_render(struct terminal *term)
wl_surface_commit(term->window->surface.surf);
}
#if defined(FOOT_HAVE_SCROLLBACK)
static void
render_search_box(struct terminal *term)
{
@ -4061,6 +4074,7 @@ render_search_box(struct terminal *term)
#undef WINDOW_X
#undef WINDOW_Y
}
#endif /* FOOT_HAVE_SCROLLBACK */
static void
render_urls(struct terminal *term)
@ -4319,12 +4333,18 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da
bool grid = term->render.pending.grid;
bool csd = term->render.pending.csd;
#if defined(FOOT_HAVE_SCROLLBACK)
bool search = term->is_searching && term->render.pending.search;
#else
bool search = false;
#endif
bool urls = urls_mode_is_active(term) > 0 && term->render.pending.urls;
term->render.pending.grid = false;
term->render.pending.csd = false;
#if defined(FOOT_HAVE_SCROLLBACK)
term->render.pending.search = false;
#endif
term->render.pending.urls = false;
struct grid *original_grid = term->grid;
@ -4339,8 +4359,10 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da
quirk_weston_csd_off(term);
}
#if defined(FOOT_HAVE_SCROLLBACK)
if (search)
render_search_box(term);
#endif
if (urls)
render_urls(term);
@ -4689,8 +4711,13 @@ render_resize(struct terminal *term, int width, int height, uint8_t opts)
* the grid array.
*/
const unsigned max_scrollback = (INT_MAX >> 1) + 1;
#if defined(FOOT_HAVE_SCROLLBACK)
const uint64_t scrollback_lines = term->render.scrollback_lines;
#else
const uint64_t scrollback_lines = 0;
#endif
const unsigned scrollback_lines_not_yet_power_of_two =
min((uint64_t)term->render.scrollback_lines + new_rows - 1, max_scrollback);
min(scrollback_lines + new_rows - 1, max_scrollback);
/* Grid rows/cols after resize */
const int new_normal_grid_rows =
@ -4997,7 +5024,9 @@ damage_view:
term->render.last_buf = NULL;
term_damage_view(term);
render_refresh_csd(term);
#if defined(FOOT_HAVE_SCROLLBACK)
render_refresh_search(term);
#endif
render_refresh(term);
return true;
@ -5144,7 +5173,11 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
bool grid = term->render.refresh.grid;
bool csd = term->render.refresh.csd;
#if defined(FOOT_HAVE_SCROLLBACK)
bool search = term->is_searching && term->render.refresh.search;
#else
bool search = false;
#endif
bool urls = urls_mode_is_active(term) && term->render.refresh.urls;
if (!(grid | csd | search | urls))
@ -5155,7 +5188,9 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
term->render.refresh.grid = false;
term->render.refresh.csd = false;
#if defined(FOOT_HAVE_SCROLLBACK)
term->render.refresh.search = false;
#endif
term->render.refresh.urls = false;
if (term->window->frame_callback == NULL) {
@ -5170,8 +5205,10 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
render_csd(term);
quirk_weston_csd_off(term);
}
#if defined(FOOT_HAVE_SCROLLBACK)
if (search)
render_search_box(term);
#endif
if (urls)
render_urls(term);
if (grid | csd | search | urls)
@ -5187,7 +5224,9 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
/* Tells the frame callback to render again */
term->render.pending.grid |= grid;
term->render.pending.csd |= csd;
#if defined(FOOT_HAVE_SCROLLBACK)
term->render.pending.search |= search;
#endif
term->render.pending.urls |= urls;
}
}
@ -5305,12 +5344,14 @@ render_refresh_csd(struct terminal *term)
term->render.refresh.csd = true;
}
#if defined(FOOT_HAVE_SCROLLBACK)
void
render_refresh_search(struct terminal *term)
{
if (term->is_searching)
term->render.refresh.search = true;
}
#endif /* FOOT_HAVE_SCROLLBACK */
void
render_refresh_urls(struct terminal *term)

View file

@ -24,7 +24,9 @@ void render_refresh(struct terminal *term);
void render_refresh_app_id(struct terminal *term);
void render_refresh_icon(struct terminal *term);
void render_refresh_csd(struct terminal *term);
#if defined(FOOT_HAVE_SCROLLBACK)
void render_refresh_search(struct terminal *term);
#endif
void render_refresh_title(struct terminal *term);
void render_refresh_urls(struct terminal *term);
bool render_xcursor_set(

View file

@ -1,5 +1,7 @@
#include "search.h"
#if defined(FOOT_HAVE_SCROLLBACK)
#include <string.h>
#include <wayland-client.h>
@ -1510,3 +1512,5 @@ update_search:
if (redraw)
render_refresh_search(term);
}
#endif /* FOOT_HAVE_SCROLLBACK */

View file

@ -5,6 +5,7 @@
#include "key-binding.h"
#include "terminal.h"
#if defined(FOOT_HAVE_SCROLLBACK)
void search_begin(struct terminal *term);
void search_cancel(struct terminal *term);
void search_input(
@ -24,3 +25,4 @@ struct search_match_iterator {
struct search_match_iterator search_matches_new_iter(struct terminal *term);
struct range search_matches_next(struct search_match_iterator *iter);
#endif /* FOOT_HAVE_SCROLLBACK */

View file

@ -1549,7 +1549,9 @@ selection_finalize(struct seat *seat, struct terminal *term, uint32_t serial)
LOG_DBG("selection finalize");
#if defined(FOOT_HAVE_SCROLLBACK)
selection_stop_scroll_timer(term);
#endif
term->selection.ongoing = false;
if (term->selection.coords.start.row < 0 || term->selection.coords.end.row < 0)
@ -1599,7 +1601,9 @@ selection_cancel(struct terminal *term)
term->selection.coords.start.row, term->selection.coords.start.col,
term->selection.coords.end.row, term->selection.coords.end.col);
#if defined(FOOT_HAVE_SCROLLBACK)
selection_stop_scroll_timer(term);
#endif
if (term->selection.coords.start.row >= 0 && term->selection.coords.end.row >= 0) {
foreach_selected(
@ -1616,7 +1620,9 @@ selection_cancel(struct terminal *term)
term->selection.direction = SELECTION_UNDIR;
term->selection.ongoing = false;
#if defined(FOOT_HAVE_SCROLLBACK)
search_selection_cancelled(term);
#endif
}
bool
@ -1671,6 +1677,7 @@ selection_primary_unset(struct seat *seat)
primary->text = NULL;
}
#if defined(FOOT_HAVE_SCROLLBACK)
static bool
fdm_scroll_timer(struct fdm *fdm, int fd, int events, void *data)
{
@ -1773,6 +1780,7 @@ selection_stop_scroll_timer(struct terminal *term)
term->selection.auto_scroll.fd = -1;
term->selection.auto_scroll.direction = SELECTION_SCROLL_NOT;
}
#endif /* FOOT_HAVE_SCROLLBACK */
static void
target(void *data, struct wl_data_source *wl_data_source, const char *mime_type)

View file

@ -72,10 +72,12 @@ void text_from_primary(
void (*cb)(char *data, size_t size, void *user),
void (*dont)(void *user), void *user);
#if defined(FOOT_HAVE_SCROLLBACK)
void selection_start_scroll_timer(
struct terminal *term, int interval_ns,
enum selection_scroll_direction direction, int col);
void selection_stop_scroll_timer(struct terminal *term);
#endif
void selection_find_word_boundary_left(
const struct terminal *term, struct coord *pos, bool spaces_only);

View file

@ -1344,9 +1344,11 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
.start = {-1, -1},
.end = {-1, -1},
},
#if defined(FOOT_HAVE_SCROLLBACK)
.auto_scroll = {
.fd = -1,
},
#endif
},
.normal = {.scroll_damage = tll_init(), .sixel_images = tll_init()},
.alt = {.scroll_damage = tll_init(), .sixel_images = tll_init()},
@ -1365,14 +1367,18 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
.chains = {
.grid = shm_chain_new(wayl, true, 1 + conf->render_worker_count,
desired_bit_depth, &render_buffer_release_callback, term),
#if defined(FOOT_HAVE_SCROLLBACK)
.search = shm_chain_new(wayl, false, 1 ,desired_bit_depth, NULL, NULL),
.scrollback_indicator = shm_chain_new(wayl, false, 1, desired_bit_depth, NULL, NULL),
#endif
.render_timer = shm_chain_new(wayl, false, 1, desired_bit_depth, NULL, NULL),
.url = shm_chain_new(wayl, false, 1, desired_bit_depth, NULL, NULL),
.csd = shm_chain_new(wayl, false, 1, desired_bit_depth, NULL, NULL),
.overlay = shm_chain_new(wayl, false, 1, desired_bit_depth, NULL, NULL),
},
#if defined(FOOT_HAVE_SCROLLBACK)
.scrollback_lines = conf->scrollback.lines,
#endif
.app_sync_updates.timer_fd = app_sync_updates_fd,
.title = {
.timer_fd = title_update_fd,
@ -1718,7 +1724,9 @@ term_shutdown(struct terminal *term)
term_cursor_blink_update(term);
xassert(term->cursor_blink.fd < 0);
#if defined(FOOT_HAVE_SCROLLBACK)
fdm_del(term->fdm, term->selection.auto_scroll.fd);
#endif
fdm_del(term->fdm, term->render.app_sync_updates.timer_fd);
fdm_del(term->fdm, term->render.app_id.timer_fd);
fdm_del(term->fdm, term->render.icon.timer_fd);
@ -1770,7 +1778,9 @@ term_shutdown(struct terminal *term)
}
}
#if defined(FOOT_HAVE_SCROLLBACK)
term->selection.auto_scroll.fd = -1;
#endif
term->render.app_sync_updates.timer_fd = -1;
term->render.app_id.timer_fd = -1;
term->render.icon.timer_fd = -1;
@ -1825,7 +1835,9 @@ term_destroy(struct terminal *term)
del_utmp_record(term->conf, term->reaper, term->ptmx);
#if defined(FOOT_HAVE_SCROLLBACK)
fdm_del(term->fdm, term->selection.auto_scroll.fd);
#endif
fdm_del(term->fdm, term->render.app_sync_updates.timer_fd);
fdm_del(term->fdm, term->render.app_id.timer_fd);
fdm_del(term->fdm, term->render.icon.timer_fd);
@ -1891,8 +1903,10 @@ term_destroy(struct terminal *term)
free_custom_glyphs(
&term->custom_glyphs.octants, GLYPH_OCTANTS_COUNT);
#if defined(FOOT_HAVE_SCROLLBACK)
free(term->search.buf);
free(term->search.last.buf);
#endif
if (term->render.workers.threads != NULL) {
for (size_t i = 0; i < term->render.workers.count; i++) {
@ -1911,8 +1925,10 @@ term_destroy(struct terminal *term)
shm_unref(term->render.last_buf);
shm_chain_free(term->render.chains.grid);
#if defined(FOOT_HAVE_SCROLLBACK)
shm_chain_free(term->render.chains.search);
shm_chain_free(term->render.chains.scrollback_indicator);
#endif
shm_chain_free(term->render.chains.render_timer);
shm_chain_free(term->render.chains.url);
shm_chain_free(term->render.chains.csd);
@ -2680,6 +2696,7 @@ term_erase(struct terminal *term, int start_row, int start_col,
sixel_overwrite_by_row(term, end_row, 0, end_col + 1);
}
#if defined(FOOT_HAVE_SCROLLBACK)
void
term_erase_scrollback(struct terminal *term)
{
@ -2766,7 +2783,6 @@ term_erase_scrollback(struct terminal *term)
term_damage_view(term);
}
UNITTEST
{
const int scrollback_rows = 16;
@ -2893,6 +2909,7 @@ UNITTEST
free(term.normal.rows);
fdm_destroy(fdm);
}
#endif /* FOOT_HAVE_SCROLLBACK */
int
term_row_rel_to_abs(const struct terminal *term, int row)
@ -3086,9 +3103,11 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows
sixel_scroll_up(term, rows);
#if defined(FOOT_HAVE_SCROLLBACK)
/* How many lines from the scrollback start is the current viewport? */
int view_sb_start_distance = grid_row_abs_to_sb(
term->grid, term->rows, term->grid->view);
#endif
bool view_follows = term->grid->view == term->grid->offset;
term->grid->offset += rows;
@ -3098,12 +3117,15 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows
term_damage_scroll(term, DAMAGE_SCROLL, region, rows);
selection_view_down(term, term->grid->offset);
term->grid->view = term->grid->offset;
} else if (unlikely(rows > view_sb_start_distance)) {
}
#if defined(FOOT_HAVE_SCROLLBACK)
else if (unlikely(rows > view_sb_start_distance)) {
/* Part of current view is being scrolled out */
int new_view = grid_row_sb_to_abs(term->grid, term->rows, 0);
selection_view_down(term, new_view);
cmd_scrollback_down(term, rows - view_sb_start_distance);
}
#endif /* FOOT_HAVE_SCROLLBACK */
/* Top non-scrolling region. */
for (int i = region.start - 1; i >= 0; i--)
@ -3834,8 +3856,11 @@ term_enable_app_sync_updates(struct terminal *term)
/* Disable pending refresh *iff* the grid is the *only* thing
* scheduled to be re-rendered */
if (!term->render.refresh.csd && !term->render.refresh.search &&
!term->render.pending.csd && !term->render.pending.search)
if (!term->render.refresh.csd &&
#if defined(FOOT_HAVE_SCROLLBACK)
!term->render.refresh.search && !term->render.pending.search &&
#endif
!term->render.pending.csd)
{
term->render.refresh.grid = false;
term->render.pending.grid = false;
@ -4487,6 +4512,7 @@ out:
return extract_finish(ctx, text, len);
}
#if defined(FOOT_HAVE_SCROLLBACK)
bool
term_scrollback_to_text(const struct terminal *term, char **text, size_t *len)
{
@ -4514,6 +4540,7 @@ term_scrollback_to_text(const struct terminal *term, char **text, size_t *len)
return rows_to_text(term, start, end, 0, term->cols, text, len);
}
#endif /* FOOT_HAVE_SCROLLBACK */
bool
term_view_to_text(const struct terminal *term, char **text, size_t *len)

View file

@ -341,8 +341,10 @@ enum selection_kind {
SELECTION_BLOCK
};
enum selection_direction {SELECTION_UNDIR, SELECTION_LEFT, SELECTION_RIGHT};
#if defined(FOOT_HAVE_SCROLLBACK)
enum selection_scroll_direction {SELECTION_SCROLL_NOT, SELECTION_SCROLL_UP, SELECTION_SCROLL_DOWN};
enum search_direction { SEARCH_BACKWARD_SAME_POSITION, SEARCH_BACKWARD, SEARCH_FORWARD };
#endif
struct ptmx_buffer {
void *data;
@ -365,7 +367,9 @@ enum term_surface {
enum overlay_style {
OVERLAY_NONE,
#if defined(FOOT_HAVE_SCROLLBACK)
OVERLAY_SEARCH,
#endif
OVERLAY_FLASH,
OVERLAY_UNICODE_MODE,
};
@ -605,13 +609,16 @@ struct terminal {
struct range pivot;
#if defined(FOOT_HAVE_SCROLLBACK)
struct {
int fd;
int col;
enum selection_scroll_direction direction;
} auto_scroll;
#endif
} selection;
#if defined(FOOT_HAVE_SCROLLBACK)
bool is_searching;
struct {
char32_t *buf;
@ -629,6 +636,7 @@ struct terminal {
size_t len;
} last;
} search;
#endif /* FOOT_HAVE_SCROLLBACK */
struct wayland *wl;
struct wl_window *window;
@ -639,8 +647,10 @@ struct terminal {
struct {
struct {
struct buffer_chain *grid;
#if defined(FOOT_HAVE_SCROLLBACK)
struct buffer_chain *search;
struct buffer_chain *scrollback_indicator;
#endif
struct buffer_chain *render_timer;
struct buffer_chain *url;
struct buffer_chain *csd;
@ -651,7 +661,9 @@ struct terminal {
struct {
bool grid;
bool csd;
#if defined(FOOT_HAVE_SCROLLBACK)
bool search;
#endif
bool urls;
} refresh;
@ -659,7 +671,9 @@ struct terminal {
struct {
bool grid;
bool csd;
#if defined(FOOT_HAVE_SCROLLBACK)
bool search;
#endif
bool urls;
} pending;
@ -681,7 +695,9 @@ struct terminal {
int timer_fd;
} app_id;
#if defined(FOOT_HAVE_SCROLLBACK)
uint32_t scrollback_lines; /* Number of scrollback lines, from conf (TODO: move out from render struct?) */
#endif
struct {
bool enabled;
@ -722,7 +738,9 @@ struct terminal {
struct buffer *last_overlay_buf;
pixman_region32_t last_overlay_clip;
#if defined(FOOT_HAVE_SCROLLBACK)
size_t search_glyph_offset;
#endif
struct timespec input_time;
} render;
@ -889,7 +907,9 @@ void term_erase(
struct terminal *term,
int start_row, int start_col,
int end_row, int end_col);
#if defined(FOOT_HAVE_SCROLLBACK)
void term_erase_scrollback(struct terminal *term);
#endif
int term_row_rel_to_abs(const struct terminal *term, int row);
void term_cursor_home(struct terminal *term);
@ -958,8 +978,10 @@ void term_disable_app_sync_updates(struct terminal *term);
enum term_surface term_surface_kind(
const struct terminal *term, const struct wl_surface *surface);
#if defined(FOOT_HAVE_SCROLLBACK)
bool term_scrollback_to_text(
const struct terminal *term, char **text, size_t *len);
#endif
bool term_view_to_text(
const struct terminal *term, char **text, size_t *len);
bool term_command_output_to_text(

View file

@ -590,6 +590,7 @@ test_section_desktop_notifications(void)
config_free(&conf);
}
#if defined(FOOT_HAVE_SCROLLBACK)
static void
test_section_scrollback(void)
{
@ -616,6 +617,7 @@ test_section_scrollback(void)
config_free(&conf);
}
#endif /* FOOT_HAVE_SCROLLBACK */
static void
test_section_url(void)
@ -747,6 +749,7 @@ test_section_colors_dark(void)
test_two_colors(&ctx, &parse_section_colors_dark, "jump-labels", false,
&conf.colors_dark.jump_label.fg,
&conf.colors_dark.jump_label.bg);
#if defined(FOOT_HAVE_SCROLLBACK)
test_two_colors(&ctx, &parse_section_colors_dark, "scrollback-indicator", false,
&conf.colors_dark.scrollback_indicator.fg,
&conf.colors_dark.scrollback_indicator.bg);
@ -756,6 +759,7 @@ test_section_colors_dark(void)
test_two_colors(&ctx, &parse_section_colors_dark, "search-box-match", false,
&conf.colors_dark.search_box.match.fg,
&conf.colors_dark.search_box.match.bg);
#endif /* FOOT_HAVE_SCROLLBACK */
test_two_colors(&ctx, &parse_section_colors_dark, "cursor", false,
&conf.colors_dark.cursor.text,
@ -828,6 +832,7 @@ test_section_colors_light(void)
test_two_colors(&ctx, &parse_section_colors_light, "jump-labels", false,
&conf.colors_light.jump_label.fg,
&conf.colors_light.jump_label.bg);
#if defined(FOOT_HAVE_SCROLLBACK)
test_two_colors(&ctx, &parse_section_colors_light, "scrollback-indicator", false,
&conf.colors_light.scrollback_indicator.fg,
&conf.colors_light.scrollback_indicator.bg);
@ -837,6 +842,7 @@ test_section_colors_light(void)
test_two_colors(&ctx, &parse_section_colors_light, "search-box-match", false,
&conf.colors_light.search_box.match.fg,
&conf.colors_light.search_box.match.bg);
#endif /* FOOT_HAVE_SCROLLBACK */
test_two_colors(&ctx, &parse_section_colors_light, "cursor", false,
&conf.colors_light.cursor.text,
@ -1239,7 +1245,7 @@ test_section_key_bindings(void)
&ctx, &parse_section_key_bindings,
action, BIND_ACTION_KEY_COUNT - 1,
binding_action_map, &conf.bindings.key, KEY_BINDING,
action >= BIND_ACTION_PIPE_SCROLLBACK && action <= BIND_ACTION_PIPE_COMMAND_OUTPUT,
action >= BIND_ACTION_PIPE_FIRST && action <= BIND_ACTION_PIPE_LAST,
action >= BIND_ACTION_REGEX_LAUNCH && action <= BIND_ACTION_REGEX_COPY);
}
@ -1259,6 +1265,7 @@ test_section_key_bindings_collisions(void)
config_free(&conf);
}
#if defined(FOOT_HAVE_SCROLLBACK)
static void
test_section_search_bindings(void)
{
@ -1295,6 +1302,7 @@ test_section_search_bindings_collisions(void)
config_free(&conf);
}
#endif /* FOOT_HAVE_SCROLLBACK */
static void
test_section_url_bindings(void)
@ -1534,7 +1542,9 @@ main(int argc, const char *const *argv)
test_section_security();
test_section_bell();
test_section_desktop_notifications();
#if defined(FOOT_HAVE_SCROLLBACK)
test_section_scrollback();
#endif
test_section_url();
test_section_cursor();
test_section_mouse();
@ -1544,8 +1554,10 @@ main(int argc, const char *const *argv)
test_section_csd();
test_section_key_bindings();
test_section_key_bindings_collisions();
#if defined(FOOT_HAVE_SCROLLBACK)
test_section_search_bindings();
test_section_search_bindings_collisions();
#endif
test_section_url_bindings();
test_section_url_bindings_collisions();
test_section_mouse_bindings();

View file

@ -34,10 +34,14 @@ unicode_mode_updated(struct terminal *term)
if (term == NULL)
return;
if (term->is_searching)
#if defined(FOOT_HAVE_SCROLLBACK)
if (term->is_searching) {
render_refresh_search(term);
else
} else
#endif
{
render_refresh(term);
}
}
void
@ -57,10 +61,14 @@ unicode_mode_input(struct seat *seat, struct terminal *term,
term->unicode_mode.character, (int)chars, utf8);
if (chars != (size_t)-1) {
if (term->is_searching)
#if defined(FOOT_HAVE_SCROLLBACK)
if (term->is_searching) {
search_add_chars(term, utf8, chars);
else
} else
#endif
{
term_to_slave(term, utf8, chars);
}
}
seat->kbd.last_shortcut_sym = sym;

View file

@ -2210,6 +2210,7 @@ wayl_win_destroy(struct wl_window *win)
wl_surface_commit(win->render_timer.surface.surf);
}
#if defined(FOOT_HAVE_SCROLLBACK)
if (win->scrollback_indicator.surface.surf != NULL) {
wl_surface_attach(win->scrollback_indicator.surface.surf, NULL, 0, 0);
wl_surface_commit(win->scrollback_indicator.surface.surf);
@ -2220,6 +2221,7 @@ wayl_win_destroy(struct wl_window *win)
wl_surface_attach(win->search.surface.surf, NULL, 0, 0);
wl_surface_commit(win->search.surface.surf);
}
#endif /* FOOT_HAVE_SCROLLBACK */
/* URLs */
tll_foreach(win->urls, it) {
@ -2253,13 +2255,17 @@ wayl_win_destroy(struct wl_window *win)
render_wait_for_preapply_damage(term);
csd_destroy(win);
#if defined(FOOT_HAVE_SCROLLBACK)
wayl_win_subsurface_destroy(&win->search);
wayl_win_subsurface_destroy(&win->scrollback_indicator);
#endif
wayl_win_subsurface_destroy(&win->render_timer);
wayl_win_subsurface_destroy(&win->overlay);
#if defined(FOOT_HAVE_SCROLLBACK)
shm_purge(term->render.chains.search);
shm_purge(term->render.chains.scrollback_indicator);
#endif
shm_purge(term->render.chains.render_timer);
shm_purge(term->render.chains.grid);
shm_purge(term->render.chains.url);

View file

@ -397,8 +397,10 @@ struct wl_window {
bool minimize:1;
} wm_capabilities;
#if defined(FOOT_HAVE_SCROLLBACK)
struct wayl_sub_surface search;
struct wayl_sub_surface scrollback_indicator;
#endif
struct wayl_sub_surface render_timer;
struct wayl_sub_surface overlay;