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, };