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 ## Unreleased
### Added ### 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 ### Changed
### Deprecated ### Deprecated
### Removed ### Removed

View file

@ -10,6 +10,7 @@
#include "url-mode.h" #include "url-mode.h"
#include "util.h" #include "util.h"
#if defined(FOOT_HAVE_SCROLLBACK)
void void
cmd_scrollback_up(struct terminal *term, int rows) 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_urls(term);
render_refresh(term); render_refresh(term);
} }
#endif /* FOOT_HAVE_SCROLLBACK */

View file

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

View file

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

View file

@ -16,6 +16,12 @@ const char version_and_features[] =
" -ime" " -ime"
#endif #endif
#if defined(FOOT_HAVE_SCROLLBACK)
" +scrollback"
#else
" -scrollback"
#endif
#if defined(FOOT_GRAPHEME_CLUSTERING) && FOOT_GRAPHEME_CLUSTERING #if defined(FOOT_GRAPHEME_CLUSTERING) && FOOT_GRAPHEME_CLUSTERING
" +graphemes" " +graphemes"
#else #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); ime_reset_preedit(seat);
if (term != NULL) { if (term != NULL) {
if (term->is_searching) #if defined(FOOT_HAVE_SCROLLBACK)
if (term->is_searching) {
render_refresh_search(term); render_refresh_search(term);
else } else
#endif
{
render_refresh(term); render_refresh(term);
}
} }
} }
@ -198,11 +202,15 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
size_t len = strlen(text); size_t len = strlen(text);
if (term != NULL) { if (term != NULL) {
#if defined(FOOT_HAVE_SCROLLBACK)
if (term->is_searching) { if (term->is_searching) {
search_add_chars(term, text, len); search_add_chars(term, text, len);
render_refresh_search(term); render_refresh_search(term);
} else } else
#endif
{
term_to_slave(term, text, len); term_to_slave(term, text, len);
}
} }
ime_reset_pending_commit(seat); 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); ime_reset_pending_preedit(seat);
if (term != NULL) { if (term != NULL) {
if (term->is_searching) #if defined(FOOT_HAVE_SCROLLBACK)
if (term->is_searching) {
render_refresh_search(term); render_refresh_search(term);
else } else
#endif
{
render_refresh(term); render_refresh(term);
}
} }
} }
@ -473,9 +485,11 @@ ime_update_cursor_rect(struct seat *seat)
if (seat->ime.preedit.cells != NULL) if (seat->ime.preedit.cells != NULL)
goto update; goto update;
#if defined(FOOT_HAVE_SCROLLBACK)
/* Set in render_search_box() */ /* Set in render_search_box() */
if (term->is_searching) if (term->is_searching)
goto update; goto update;
#endif
int x, y, width, height; int x, y, width, height;
int col = term->grid->cursor.point.col; int col = term->grid->cursor.point.col;

32
input.c
View file

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

View file

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

View file

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

View file

@ -90,6 +90,9 @@ add_project_arguments(
(get_option('ime') (get_option('ime')
? ['-DFOOT_IME_ENABLED=1'] ? ['-DFOOT_IME_ENABLED=1']
: []) + : []) +
(get_option('scrollback')
? ['-DFOOT_HAVE_SCROLLBACK']
: []) +
(get_option('b_pgo') == 'use' (get_option('b_pgo') == 'use'
? ['-DFOOT_PGO_ENABLED=1'] ? ['-DFOOT_PGO_ENABLED=1']
: []) + : []) +
@ -443,6 +446,7 @@ summary(
'Documentation': scdoc.found(), 'Documentation': scdoc.found(),
'Themes': get_option('themes'), 'Themes': get_option('themes'),
'IME': get_option('ime'), 'IME': get_option('ime'),
'Scrollback': get_option('scrollback'),
'Grapheme clustering': utf8proc.found(), 'Grapheme clustering': utf8proc.found(),
'utmp backend': utmp_backend, 'utmp backend': utmp_backend,
'utmp helper default path': utmp_default_helper_path, '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, option('ime', type: 'boolean', value: true,
description: 'IME (Input Method Editor) support') 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', option('grapheme-clustering', type: 'feature',
description: 'Enables grapheme clustering using libutf8proc. Requires fcft with harfbuzz support to be useful.') 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)) if (likely(seat->ime.preedit.cells == NULL))
return; return;
#if defined(FOOT_HAVE_SCROLLBACK)
if (unlikely(term->is_searching)) if (unlikely(term->is_searching))
return; return;
#endif
const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); 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 bool unicode_mode_active = term->unicode_mode.active;
const enum overlay_style style = const enum overlay_style style =
#if defined(FOOT_HAVE_SCROLLBACK)
term->is_searching ? OVERLAY_SEARCH : term->is_searching ? OVERLAY_SEARCH :
#endif
term->flash.active ? OVERLAY_FLASH : term->flash.active ? OVERLAY_FLASH :
unicode_mode_active ? OVERLAY_UNICODE_MODE : unicode_mode_active ? OVERLAY_UNICODE_MODE :
OVERLAY_NONE; OVERLAY_NONE;
@ -1994,7 +1998,9 @@ render_overlay(struct terminal *term)
pixman_color_t color; pixman_color_t color;
switch (style) { switch (style) {
#if defined(FOOT_HAVE_SCROLLBACK)
case OVERLAY_SEARCH: case OVERLAY_SEARCH:
#endif
case OVERLAY_UNICODE_MODE: case OVERLAY_UNICODE_MODE:
color = (pixman_color_t){0, 0, 0, 0x7fff}; color = (pixman_color_t){0, 0, 0, 0x7fff};
break; break;
@ -2028,6 +2034,7 @@ render_overlay(struct terminal *term)
/* Bounding rectangle of damaged areas - for wl_surface_damage_buffer() */ /* Bounding rectangle of damaged areas - for wl_surface_damage_buffer() */
pixman_box32_t damage_bounds; pixman_box32_t damage_bounds;
#if defined(FOOT_HAVE_SCROLLBACK)
if (style == OVERLAY_SEARCH) { if (style == OVERLAY_SEARCH) {
/* /*
* When possible, we only update the areas that have *changed* * 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(&new_dimmed);
pixman_region32_fini(&damage); pixman_region32_fini(&damage);
} }
else
else if (buf == term->render.last_overlay_buf && #endif /* FOOT_HAVE_SCROLLBACK */
style == term->render.last_overlay_style) if (buf == term->render.last_overlay_buf &&
style == term->render.last_overlay_style)
{ {
xassert(style == OVERLAY_FLASH || style == OVERLAY_UNICODE_MODE); xassert(style == OVERLAY_FLASH || style == OVERLAY_UNICODE_MODE);
shm_did_not_use_buf(buf); 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]); render_csd_title(term, &infos[CSD_SURF_TITLE], bufs[CSD_SURF_TITLE]);
} }
#if defined(FOOT_HAVE_SCROLLBACK)
static void static void
render_scrollback_position(struct terminal *term) render_scrollback_position(struct terminal *term)
{ {
@ -3139,6 +3148,7 @@ render_scrollback_position(struct terminal *term)
fg, 0xffu << 24 | bg, fg, 0xffu << 24 | bg,
width - margin - c32len(text) * term->cell_width); width - margin - c32len(text) * term->cell_width);
} }
#endif /* FOOT_HAVE_SCROLLBACK */
static void static void
render_render_timer(struct terminal *term, struct timespec render_time) render_render_timer(struct terminal *term, struct timespec render_time)
@ -3612,7 +3622,9 @@ grid_render(struct terminal *term)
render_overlay(term); render_overlay(term);
render_ime_preedit(term, buf); render_ime_preedit(term, buf);
#if defined(FOOT_HAVE_SCROLLBACK)
render_scrollback_position(term); render_scrollback_position(term);
#endif
if (term->conf->tweak.render_timer != RENDER_TIMER_NONE) { if (term->conf->tweak.render_timer != RENDER_TIMER_NONE) {
struct timespec end_time; struct timespec end_time;
@ -3716,6 +3728,7 @@ grid_render(struct terminal *term)
wl_surface_commit(term->window->surface.surf); wl_surface_commit(term->window->surface.surf);
} }
#if defined(FOOT_HAVE_SCROLLBACK)
static void static void
render_search_box(struct terminal *term) render_search_box(struct terminal *term)
{ {
@ -4061,6 +4074,7 @@ render_search_box(struct terminal *term)
#undef WINDOW_X #undef WINDOW_X
#undef WINDOW_Y #undef WINDOW_Y
} }
#endif /* FOOT_HAVE_SCROLLBACK */
static void static void
render_urls(struct terminal *term) 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 grid = term->render.pending.grid;
bool csd = term->render.pending.csd; bool csd = term->render.pending.csd;
#if defined(FOOT_HAVE_SCROLLBACK)
bool search = term->is_searching && term->render.pending.search; 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; bool urls = urls_mode_is_active(term) > 0 && term->render.pending.urls;
term->render.pending.grid = false; term->render.pending.grid = false;
term->render.pending.csd = false; term->render.pending.csd = false;
#if defined(FOOT_HAVE_SCROLLBACK)
term->render.pending.search = false; term->render.pending.search = false;
#endif
term->render.pending.urls = false; term->render.pending.urls = false;
struct grid *original_grid = term->grid; 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); quirk_weston_csd_off(term);
} }
#if defined(FOOT_HAVE_SCROLLBACK)
if (search) if (search)
render_search_box(term); render_search_box(term);
#endif
if (urls) if (urls)
render_urls(term); render_urls(term);
@ -4689,8 +4711,13 @@ render_resize(struct terminal *term, int width, int height, uint8_t opts)
* the grid array. * the grid array.
*/ */
const unsigned max_scrollback = (INT_MAX >> 1) + 1; 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 = 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 */ /* Grid rows/cols after resize */
const int new_normal_grid_rows = const int new_normal_grid_rows =
@ -4997,7 +5024,9 @@ damage_view:
term->render.last_buf = NULL; term->render.last_buf = NULL;
term_damage_view(term); term_damage_view(term);
render_refresh_csd(term); render_refresh_csd(term);
#if defined(FOOT_HAVE_SCROLLBACK)
render_refresh_search(term); render_refresh_search(term);
#endif
render_refresh(term); render_refresh(term);
return true; return true;
@ -5144,7 +5173,11 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
bool grid = term->render.refresh.grid; bool grid = term->render.refresh.grid;
bool csd = term->render.refresh.csd; bool csd = term->render.refresh.csd;
#if defined(FOOT_HAVE_SCROLLBACK)
bool search = term->is_searching && term->render.refresh.search; 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; bool urls = urls_mode_is_active(term) && term->render.refresh.urls;
if (!(grid | csd | search | 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.grid = false;
term->render.refresh.csd = false; term->render.refresh.csd = false;
#if defined(FOOT_HAVE_SCROLLBACK)
term->render.refresh.search = false; term->render.refresh.search = false;
#endif
term->render.refresh.urls = false; term->render.refresh.urls = false;
if (term->window->frame_callback == NULL) { if (term->window->frame_callback == NULL) {
@ -5170,8 +5205,10 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
render_csd(term); render_csd(term);
quirk_weston_csd_off(term); quirk_weston_csd_off(term);
} }
#if defined(FOOT_HAVE_SCROLLBACK)
if (search) if (search)
render_search_box(term); render_search_box(term);
#endif
if (urls) if (urls)
render_urls(term); render_urls(term);
if (grid | csd | search | urls) 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 */ /* Tells the frame callback to render again */
term->render.pending.grid |= grid; term->render.pending.grid |= grid;
term->render.pending.csd |= csd; term->render.pending.csd |= csd;
#if defined(FOOT_HAVE_SCROLLBACK)
term->render.pending.search |= search; term->render.pending.search |= search;
#endif
term->render.pending.urls |= urls; term->render.pending.urls |= urls;
} }
} }
@ -5305,12 +5344,14 @@ render_refresh_csd(struct terminal *term)
term->render.refresh.csd = true; term->render.refresh.csd = true;
} }
#if defined(FOOT_HAVE_SCROLLBACK)
void void
render_refresh_search(struct terminal *term) render_refresh_search(struct terminal *term)
{ {
if (term->is_searching) if (term->is_searching)
term->render.refresh.search = true; term->render.refresh.search = true;
} }
#endif /* FOOT_HAVE_SCROLLBACK */
void void
render_refresh_urls(struct terminal *term) 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_app_id(struct terminal *term);
void render_refresh_icon(struct terminal *term); void render_refresh_icon(struct terminal *term);
void render_refresh_csd(struct terminal *term); void render_refresh_csd(struct terminal *term);
#if defined(FOOT_HAVE_SCROLLBACK)
void render_refresh_search(struct terminal *term); void render_refresh_search(struct terminal *term);
#endif
void render_refresh_title(struct terminal *term); void render_refresh_title(struct terminal *term);
void render_refresh_urls(struct terminal *term); void render_refresh_urls(struct terminal *term);
bool render_xcursor_set( bool render_xcursor_set(

View file

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

View file

@ -5,6 +5,7 @@
#include "key-binding.h" #include "key-binding.h"
#include "terminal.h" #include "terminal.h"
#if defined(FOOT_HAVE_SCROLLBACK)
void search_begin(struct terminal *term); void search_begin(struct terminal *term);
void search_cancel(struct terminal *term); void search_cancel(struct terminal *term);
void search_input( void search_input(
@ -24,3 +25,4 @@ struct search_match_iterator {
struct search_match_iterator search_matches_new_iter(struct terminal *term); struct search_match_iterator search_matches_new_iter(struct terminal *term);
struct range search_matches_next(struct search_match_iterator *iter); 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"); LOG_DBG("selection finalize");
#if defined(FOOT_HAVE_SCROLLBACK)
selection_stop_scroll_timer(term); selection_stop_scroll_timer(term);
#endif
term->selection.ongoing = false; term->selection.ongoing = false;
if (term->selection.coords.start.row < 0 || term->selection.coords.end.row < 0) 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.start.row, term->selection.coords.start.col,
term->selection.coords.end.row, term->selection.coords.end.col); term->selection.coords.end.row, term->selection.coords.end.col);
#if defined(FOOT_HAVE_SCROLLBACK)
selection_stop_scroll_timer(term); selection_stop_scroll_timer(term);
#endif
if (term->selection.coords.start.row >= 0 && term->selection.coords.end.row >= 0) { if (term->selection.coords.start.row >= 0 && term->selection.coords.end.row >= 0) {
foreach_selected( foreach_selected(
@ -1616,7 +1620,9 @@ selection_cancel(struct terminal *term)
term->selection.direction = SELECTION_UNDIR; term->selection.direction = SELECTION_UNDIR;
term->selection.ongoing = false; term->selection.ongoing = false;
#if defined(FOOT_HAVE_SCROLLBACK)
search_selection_cancelled(term); search_selection_cancelled(term);
#endif
} }
bool bool
@ -1671,6 +1677,7 @@ selection_primary_unset(struct seat *seat)
primary->text = NULL; primary->text = NULL;
} }
#if defined(FOOT_HAVE_SCROLLBACK)
static bool static bool
fdm_scroll_timer(struct fdm *fdm, int fd, int events, void *data) 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.fd = -1;
term->selection.auto_scroll.direction = SELECTION_SCROLL_NOT; term->selection.auto_scroll.direction = SELECTION_SCROLL_NOT;
} }
#endif /* FOOT_HAVE_SCROLLBACK */
static void static void
target(void *data, struct wl_data_source *wl_data_source, const char *mime_type) 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 (*cb)(char *data, size_t size, void *user),
void (*dont)(void *user), void *user); void (*dont)(void *user), void *user);
#if defined(FOOT_HAVE_SCROLLBACK)
void selection_start_scroll_timer( void selection_start_scroll_timer(
struct terminal *term, int interval_ns, struct terminal *term, int interval_ns,
enum selection_scroll_direction direction, int col); enum selection_scroll_direction direction, int col);
void selection_stop_scroll_timer(struct terminal *term); void selection_stop_scroll_timer(struct terminal *term);
#endif
void selection_find_word_boundary_left( void selection_find_word_boundary_left(
const struct terminal *term, struct coord *pos, bool spaces_only); 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}, .start = {-1, -1},
.end = {-1, -1}, .end = {-1, -1},
}, },
#if defined(FOOT_HAVE_SCROLLBACK)
.auto_scroll = { .auto_scroll = {
.fd = -1, .fd = -1,
}, },
#endif
}, },
.normal = {.scroll_damage = tll_init(), .sixel_images = tll_init()}, .normal = {.scroll_damage = tll_init(), .sixel_images = tll_init()},
.alt = {.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 = { .chains = {
.grid = shm_chain_new(wayl, true, 1 + conf->render_worker_count, .grid = shm_chain_new(wayl, true, 1 + conf->render_worker_count,
desired_bit_depth, &render_buffer_release_callback, term), 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), .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), .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), .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), .url = shm_chain_new(wayl, false, 1, desired_bit_depth, NULL, NULL),
.csd = 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), .overlay = shm_chain_new(wayl, false, 1, desired_bit_depth, NULL, NULL),
}, },
#if defined(FOOT_HAVE_SCROLLBACK)
.scrollback_lines = conf->scrollback.lines, .scrollback_lines = conf->scrollback.lines,
#endif
.app_sync_updates.timer_fd = app_sync_updates_fd, .app_sync_updates.timer_fd = app_sync_updates_fd,
.title = { .title = {
.timer_fd = title_update_fd, .timer_fd = title_update_fd,
@ -1718,7 +1724,9 @@ term_shutdown(struct terminal *term)
term_cursor_blink_update(term); term_cursor_blink_update(term);
xassert(term->cursor_blink.fd < 0); xassert(term->cursor_blink.fd < 0);
#if defined(FOOT_HAVE_SCROLLBACK)
fdm_del(term->fdm, term->selection.auto_scroll.fd); 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_sync_updates.timer_fd);
fdm_del(term->fdm, term->render.app_id.timer_fd); fdm_del(term->fdm, term->render.app_id.timer_fd);
fdm_del(term->fdm, term->render.icon.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; term->selection.auto_scroll.fd = -1;
#endif
term->render.app_sync_updates.timer_fd = -1; term->render.app_sync_updates.timer_fd = -1;
term->render.app_id.timer_fd = -1; term->render.app_id.timer_fd = -1;
term->render.icon.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); del_utmp_record(term->conf, term->reaper, term->ptmx);
#if defined(FOOT_HAVE_SCROLLBACK)
fdm_del(term->fdm, term->selection.auto_scroll.fd); 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_sync_updates.timer_fd);
fdm_del(term->fdm, term->render.app_id.timer_fd); fdm_del(term->fdm, term->render.app_id.timer_fd);
fdm_del(term->fdm, term->render.icon.timer_fd); fdm_del(term->fdm, term->render.icon.timer_fd);
@ -1891,8 +1903,10 @@ term_destroy(struct terminal *term)
free_custom_glyphs( free_custom_glyphs(
&term->custom_glyphs.octants, GLYPH_OCTANTS_COUNT); &term->custom_glyphs.octants, GLYPH_OCTANTS_COUNT);
#if defined(FOOT_HAVE_SCROLLBACK)
free(term->search.buf); free(term->search.buf);
free(term->search.last.buf); free(term->search.last.buf);
#endif
if (term->render.workers.threads != NULL) { if (term->render.workers.threads != NULL) {
for (size_t i = 0; i < term->render.workers.count; i++) { 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_unref(term->render.last_buf);
shm_chain_free(term->render.chains.grid); 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.search);
shm_chain_free(term->render.chains.scrollback_indicator); shm_chain_free(term->render.chains.scrollback_indicator);
#endif
shm_chain_free(term->render.chains.render_timer); shm_chain_free(term->render.chains.render_timer);
shm_chain_free(term->render.chains.url); shm_chain_free(term->render.chains.url);
shm_chain_free(term->render.chains.csd); 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); sixel_overwrite_by_row(term, end_row, 0, end_col + 1);
} }
#if defined(FOOT_HAVE_SCROLLBACK)
void void
term_erase_scrollback(struct terminal *term) term_erase_scrollback(struct terminal *term)
{ {
@ -2766,7 +2783,6 @@ term_erase_scrollback(struct terminal *term)
term_damage_view(term); term_damage_view(term);
} }
UNITTEST UNITTEST
{ {
const int scrollback_rows = 16; const int scrollback_rows = 16;
@ -2893,6 +2909,7 @@ UNITTEST
free(term.normal.rows); free(term.normal.rows);
fdm_destroy(fdm); fdm_destroy(fdm);
} }
#endif /* FOOT_HAVE_SCROLLBACK */
int int
term_row_rel_to_abs(const struct terminal *term, int row) 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); sixel_scroll_up(term, rows);
#if defined(FOOT_HAVE_SCROLLBACK)
/* How many lines from the scrollback start is the current viewport? */ /* How many lines from the scrollback start is the current viewport? */
int view_sb_start_distance = grid_row_abs_to_sb( int view_sb_start_distance = grid_row_abs_to_sb(
term->grid, term->rows, term->grid->view); term->grid, term->rows, term->grid->view);
#endif
bool view_follows = term->grid->view == term->grid->offset; bool view_follows = term->grid->view == term->grid->offset;
term->grid->offset += rows; 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); term_damage_scroll(term, DAMAGE_SCROLL, region, rows);
selection_view_down(term, term->grid->offset); selection_view_down(term, term->grid->offset);
term->grid->view = 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 */ /* Part of current view is being scrolled out */
int new_view = grid_row_sb_to_abs(term->grid, term->rows, 0); int new_view = grid_row_sb_to_abs(term->grid, term->rows, 0);
selection_view_down(term, new_view); selection_view_down(term, new_view);
cmd_scrollback_down(term, rows - view_sb_start_distance); cmd_scrollback_down(term, rows - view_sb_start_distance);
} }
#endif /* FOOT_HAVE_SCROLLBACK */
/* Top non-scrolling region. */ /* Top non-scrolling region. */
for (int i = region.start - 1; i >= 0; i--) 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 /* Disable pending refresh *iff* the grid is the *only* thing
* scheduled to be re-rendered */ * scheduled to be re-rendered */
if (!term->render.refresh.csd && !term->render.refresh.search && if (!term->render.refresh.csd &&
!term->render.pending.csd && !term->render.pending.search) #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.refresh.grid = false;
term->render.pending.grid = false; term->render.pending.grid = false;
@ -4487,6 +4512,7 @@ out:
return extract_finish(ctx, text, len); return extract_finish(ctx, text, len);
} }
#if defined(FOOT_HAVE_SCROLLBACK)
bool bool
term_scrollback_to_text(const struct terminal *term, char **text, size_t *len) 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); return rows_to_text(term, start, end, 0, term->cols, text, len);
} }
#endif /* FOOT_HAVE_SCROLLBACK */
bool bool
term_view_to_text(const struct terminal *term, char **text, size_t *len) term_view_to_text(const struct terminal *term, char **text, size_t *len)

View file

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

View file

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

View file

@ -34,10 +34,14 @@ unicode_mode_updated(struct terminal *term)
if (term == NULL) if (term == NULL)
return; return;
if (term->is_searching) #if defined(FOOT_HAVE_SCROLLBACK)
if (term->is_searching) {
render_refresh_search(term); render_refresh_search(term);
else } else
#endif
{
render_refresh(term); render_refresh(term);
}
} }
void void
@ -57,10 +61,14 @@ unicode_mode_input(struct seat *seat, struct terminal *term,
term->unicode_mode.character, (int)chars, utf8); term->unicode_mode.character, (int)chars, utf8);
if (chars != (size_t)-1) { if (chars != (size_t)-1) {
if (term->is_searching) #if defined(FOOT_HAVE_SCROLLBACK)
if (term->is_searching) {
search_add_chars(term, utf8, chars); search_add_chars(term, utf8, chars);
else } else
#endif
{
term_to_slave(term, utf8, chars); term_to_slave(term, utf8, chars);
}
} }
seat->kbd.last_shortcut_sym = sym; 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); wl_surface_commit(win->render_timer.surface.surf);
} }
#if defined(FOOT_HAVE_SCROLLBACK)
if (win->scrollback_indicator.surface.surf != NULL) { if (win->scrollback_indicator.surface.surf != NULL) {
wl_surface_attach(win->scrollback_indicator.surface.surf, NULL, 0, 0); wl_surface_attach(win->scrollback_indicator.surface.surf, NULL, 0, 0);
wl_surface_commit(win->scrollback_indicator.surface.surf); 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_attach(win->search.surface.surf, NULL, 0, 0);
wl_surface_commit(win->search.surface.surf); wl_surface_commit(win->search.surface.surf);
} }
#endif /* FOOT_HAVE_SCROLLBACK */
/* URLs */ /* URLs */
tll_foreach(win->urls, it) { tll_foreach(win->urls, it) {
@ -2253,13 +2255,17 @@ wayl_win_destroy(struct wl_window *win)
render_wait_for_preapply_damage(term); render_wait_for_preapply_damage(term);
csd_destroy(win); csd_destroy(win);
#if defined(FOOT_HAVE_SCROLLBACK)
wayl_win_subsurface_destroy(&win->search); wayl_win_subsurface_destroy(&win->search);
wayl_win_subsurface_destroy(&win->scrollback_indicator); wayl_win_subsurface_destroy(&win->scrollback_indicator);
#endif
wayl_win_subsurface_destroy(&win->render_timer); wayl_win_subsurface_destroy(&win->render_timer);
wayl_win_subsurface_destroy(&win->overlay); wayl_win_subsurface_destroy(&win->overlay);
#if defined(FOOT_HAVE_SCROLLBACK)
shm_purge(term->render.chains.search); shm_purge(term->render.chains.search);
shm_purge(term->render.chains.scrollback_indicator); shm_purge(term->render.chains.scrollback_indicator);
#endif
shm_purge(term->render.chains.render_timer); shm_purge(term->render.chains.render_timer);
shm_purge(term->render.chains.grid); shm_purge(term->render.chains.grid);
shm_purge(term->render.chains.url); shm_purge(term->render.chains.url);

View file

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