diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd0a2ee7..3e31c396 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -47,6 +47,10 @@
font is used (https://codeberg.org/dnkl/foot/issues/169).
* Drag & drop support; text, files and URLs can now be dropped in a
foot terminal window (https://codeberg.org/dnkl/foot/issues/175).
+* **clipboard-paste** and **primary-paste** scrollback search
+ bindings. By default, they are bound to `ctrl+v ctrl+y` and
+ `shift+insert` respectively, and lets you paste from the clipboard
+ or primary selection into the search buffer.
### Changed
diff --git a/README.md b/README.md
index 2881d191..bcbddecd 100644
--- a/README.md
+++ b/README.md
@@ -162,6 +162,12 @@ These are the default shortcuts. See `man foot.ini` and the example
: Same as ctrl+w, except that the only word
separating characters are whitespace characters.
+ctrl+v
+: Paste from clipboard into the search buffer.
+
+shift+insert
+: Paste from primary selection into the search buffer.
+
escape, ctrl+g
: Cancel the search
diff --git a/config.c b/config.c
index edca071b..1b3c7f53 100644
--- a/config.c
+++ b/config.c
@@ -108,6 +108,8 @@ static const char *const search_binding_action_map[] = {
[BIND_ACTION_SEARCH_DELETE_NEXT_WORD] = "delete-next-word",
[BIND_ACTION_SEARCH_EXTEND_WORD] = "extend-to-word-boundary",
[BIND_ACTION_SEARCH_EXTEND_WORD_WS] = "extend-to-next-whitespace",
+ [BIND_ACTION_SEARCH_CLIPBOARD_PASTE] = "clipboard-paste",
+ [BIND_ACTION_SEARCH_PRIMARY_PASTE] = "primary-paste",
};
static_assert(ALEN(search_binding_action_map) == BIND_ACTION_SEARCH_COUNT,
@@ -1840,6 +1842,7 @@ add_default_search_bindings(struct config *conf)
const struct config_key_modifiers none = {0};
const struct config_key_modifiers alt = {.alt = true};
const struct config_key_modifiers ctrl = {.ctrl = true};
+ const struct config_key_modifiers shift = {.shift = true};
const struct config_key_modifiers ctrl_shift = {.ctrl = true, .shift = true};
add_binding(BIND_ACTION_SEARCH_CANCEL, ctrl, XKB_KEY_g);
@@ -1867,6 +1870,9 @@ add_default_search_bindings(struct config *conf)
add_binding(BIND_ACTION_SEARCH_DELETE_NEXT_WORD, alt, XKB_KEY_d);
add_binding(BIND_ACTION_SEARCH_EXTEND_WORD, ctrl, XKB_KEY_w);
add_binding(BIND_ACTION_SEARCH_EXTEND_WORD_WS, ctrl_shift, XKB_KEY_W);
+ add_binding(BIND_ACTION_SEARCH_CLIPBOARD_PASTE, ctrl, XKB_KEY_v);
+ add_binding(BIND_ACTION_SEARCH_CLIPBOARD_PASTE, ctrl, XKB_KEY_y);
+ add_binding(BIND_ACTION_SEARCH_PRIMARY_PASTE, shift, XKB_KEY_Insert);
#undef add_binding
}
diff --git a/doc/foot.1.scd b/doc/foot.1.scd
index 20ac9ebc..e8460cc7 100644
--- a/doc/foot.1.scd
+++ b/doc/foot.1.scd
@@ -186,6 +186,12 @@ Note that these are just the defaults; they can be changed in
Same as *ctrl*+*w*, except that the only word separating
characters are whitespace characters.
+*ctrl*+*v*, *ctrl*+*y*
+ Paste from clipboard into the searh buffer.
+
+*shift*+*insert*
+ Paste from primary selection into the search buffer.
+
*escape*, *ctrl*+*g*
Cancel the search
diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd
index 73ddd883..39d874e0 100644
--- a/doc/foot.ini.5.scd
+++ b/doc/foot.ini.5.scd
@@ -452,6 +452,13 @@ scrollback search mode. The syntax is exactly the same as the regular
Extend the current selection to the next whitespace. Default:
_Control+Shift+W_.
+*clipboard-paste*
+ Paste from the _clipboard_ into the search buffer. Default:
+ _Control+v Control+y_.
+
+*primary-paste*
+ Paste from the _primary selection_ into the search
+ buffer. Default: _Shift+Insert_.
# SECTION: mouse-bindings
diff --git a/foot.ini b/foot.ini
index 2a5fed73..8f8d4493 100644
--- a/foot.ini
+++ b/foot.ini
@@ -102,6 +102,8 @@
# delete-next-word=Mod1+d Control+Delete
# extend-to-word-boundary=Control+w
# extend-to-next-whitespace=Control+Shift+W
+# clipboard-paste=Control+v Control+y
+# primary-paste=Shift+Insert
[mouse-bindings]
# primary-paste=BTN_MIDDLE
diff --git a/search.c b/search.c
index 0f1b4610..bd5effce 100644
--- a/search.c
+++ b/search.c
@@ -332,6 +332,35 @@ search_find_next(struct terminal *term)
#undef ROW_DEC
}
+static void
+add_chars(struct terminal *term, const char *src, size_t count)
+{
+ mbstate_t ps = {0};
+ size_t wchars = mbsnrtowcs(NULL, &src, count, 0, &ps);
+
+ if (wchars == -1) {
+ LOG_ERRNO("failed to convert %.*s to wchars", (int)count, src);
+ return;
+ }
+
+ if (!search_ensure_size(term, term->search.len + wchars))
+ return;
+
+ assert(term->search.len + wchars < term->search.sz);
+
+ memmove(&term->search.buf[term->search.cursor + wchars],
+ &term->search.buf[term->search.cursor],
+ (term->search.len - term->search.cursor) * sizeof(wchar_t));
+
+ memset(&ps, 0, sizeof(ps));
+ mbsnrtowcs(&term->search.buf[term->search.cursor], &src, count,
+ wchars, &ps);
+
+ term->search.len += wchars;
+ term->search.cursor += wchars;
+ term->search.buf[term->search.len] = L'\0';
+}
+
static void
search_match_to_end_of_word(struct terminal *term, bool spaces_only)
{
@@ -461,6 +490,23 @@ distance_prev_word(const struct terminal *term)
return term->search.cursor - cursor;
}
+static void
+from_clipboard_cb(char *text, size_t size, void *user)
+{
+ struct terminal *term = user;
+ add_chars(term, text, size);
+}
+
+static void
+from_clipboard_done(void *user)
+{
+ struct terminal *term = user;
+
+ LOG_DBG("search: buffer: %ls", term->search.buf);
+ search_find_next(term);
+ render_refresh_search(term);
+}
+
static bool
execute_binding(struct seat *seat, struct terminal *term,
enum bind_action_search action, uint32_t serial)
@@ -609,6 +655,16 @@ execute_binding(struct seat *seat, struct terminal *term,
search_match_to_end_of_word(term, true);
return false;
+ case BIND_ACTION_SEARCH_CLIPBOARD_PASTE:
+ text_from_clipboard(
+ seat, term, &from_clipboard_cb, &from_clipboard_done, term);
+ return false;
+
+ case BIND_ACTION_SEARCH_PRIMARY_PASTE:
+ text_from_primary(
+ seat, term, &from_clipboard_cb, &from_clipboard_done, term);
+ return false;
+
case BIND_ACTION_SEARCH_COUNT:
assert(false);
return false;
@@ -666,31 +722,7 @@ search_input(struct seat *seat, struct terminal *term, uint32_t key,
if (count == 0)
return;
- const char *src = (const char *)buf;
- mbstate_t ps = {0};
- size_t wchars = mbsnrtowcs(NULL, &src, count, 0, &ps);
-
- if (wchars == -1) {
- LOG_ERRNO("failed to convert %.*s to wchars", count, buf);
- return;
- }
-
- if (!search_ensure_size(term, term->search.len + wchars))
- return;
-
- assert(term->search.len + wchars < term->search.sz);
-
- memmove(&term->search.buf[term->search.cursor + wchars],
- &term->search.buf[term->search.cursor],
- (term->search.len - term->search.cursor) * sizeof(wchar_t));
-
- memset(&ps, 0, sizeof(ps));
- mbsnrtowcs(&term->search.buf[term->search.cursor], &src, count,
- wchars, &ps);
-
- term->search.len += wchars;
- term->search.cursor += wchars;
- term->search.buf[term->search.len] = L'\0';
+ add_chars(term, (const char *)buf, count);
update_search:
LOG_DBG("search: buffer: %ls", term->search.buf);
diff --git a/wayland.h b/wayland.h
index 98830d8f..e64663e2 100644
--- a/wayland.h
+++ b/wayland.h
@@ -93,6 +93,8 @@ enum bind_action_search {
BIND_ACTION_SEARCH_DELETE_NEXT_WORD,
BIND_ACTION_SEARCH_EXTEND_WORD,
BIND_ACTION_SEARCH_EXTEND_WORD_WS,
+ BIND_ACTION_SEARCH_CLIPBOARD_PASTE,
+ BIND_ACTION_SEARCH_PRIMARY_PASTE,
BIND_ACTION_SEARCH_COUNT,
};