Commit graph

173 commits

Author SHA1 Message Date
Ronan Pigott
143f220527
search: do not emit composing keys
When we are in the composing state for XCompose key sequences, we
should not add the compose component keys to the search buffer.
2025-11-01 08:08:55 +01:00
c4llv07e
5ae4955e83
search: use case insensitive search only if there's no uppercase in search 2025-10-30 06:36:12 +01:00
Adrian fxj9a
6d39f66eb7
config: add search-bindings.delete-to-{start,end} key bindings
Defaults to ctrl+u and ctrl+k respectively.

Closes #1972
2025-03-05 08:48:23 +01:00
Daniel Eklöf
51128a3484
input: match unshifted key-bindings before shifted
That is, try to match e.g. Control+shift+a, before trying to match
Control+A.

In most cases, order doesn't matter. There are however a couple of
symbols where the layout consumes the shift-modifier, and the
generated symbol is the same in both the shifted and unshifted
form. One such example is backspace.

Before this patch, key-bindings with shift-backspace would be ignored,
if there were another key-binding with backspace.

So, for example, if we had one key-binding with Control+Backspace, and
another with Control+Shift+Backspace, the latter would never trigger,
as we would always match the first one.

By checking for unshifted matches first, we ensure
Control+Shift+Backspace does match.
2025-01-31 09:07:42 +01:00
Daniel Eklöf
bee17a95b8
input: ignore key-bindings without modifiers when matching untranslated/raw
When matching the unshifted symbol, or the raw key code, ignore all
key bindings that don't have any modifiers.

This fixes an issue where it was impossible to enter (some of the)
numbers on the keypad, **if** there was a key-binding for
e.g. KP_Page_Up, or KP_Page_Down.
2025-01-31 07:35:54 +01:00
Daniel Eklöf
8d6f0d0583
key-bindings: try all bindings in translated mode before matching untranslated, and then finally raw
When trying to match key bindings, we do three types of matching:

* Match the _translated_ symbol (e.g. Control+C)
* Match the _untranslated_ symbol (e.g. Control+Shift+c)
* Match raw keyboard codes

This was done for *each* key binding. This meant we sometimes matched
a keybinding in raw mode, even though there was a
translated/untranslated binding that would match it too. All depending
on the internal order of the key binding list.

This patch changes it, so that we first try all bindings in translated
mode, then all bindings in untranslated mode, and finally all bindings
in raw mode.

Closes #1929
2025-01-27 12:31:40 +01:00
Daniel Eklöf
18b702b249
unicode-mode: move state from seat to term
This fixes an issue where entering unicode-mode in one foot client,
also enabled unicode-mode on other foot clients. Both
visually (although glitchy), and in effect.

The reason the state was originally in the seat objects, was to fully
support multi-seat. That is, one seat/keyboard entering unicode-mode
should not affect other seats/keyboards.

The issue with this is that seat objects are Wayland global. Thus, in
server mode, all seat objects are shared between the foot clients.

There is a similarity with IME, which also keeps state in the
seat. There's one big difference, however, and that is IME has Wayland
native enter/leave events, that the compositor emits when windows are
focused/unfocused. These events allow us to reset IME state. For our
own Unicode mode, there is nothing similar.

This patch moves the Unicode state from seats, to the terminal
struct. This does mean that if one seat/keyboard enters Unicode mode,
then *all* seats/keyboards will affect the unicode state. This
potential downside is outweighed by the fact that different foot
clients no longer affect each other.

Closes #1717
2024-05-21 08:36:56 +02:00
Daniel Eklöf
0aefc2c65d
input: remove the concept of "significant" modifiers
For the purpose of matching key bindings, "significant" modifiers are
no more.

We're really only interested in filtering out "locked"
modifiers. We're already doing this, so there's no need to *also*
match against a set of "significant" modifiers.

Furthermore, we *never* want to consider locked keys (e.g. when
emitting escapes to the client application), thus we can filter those
out already when retrieving the set of active modifiers.

The exception is the kitty keyboard protocol, which has support for
CapsLock and NumLock. Since we're already re-retrieving the "consumed"
modifiers (using the GTK style, rather than normal "XKB" style, to
better match the kitty terminal), we might as well re-retrieve the
effective modifiers as well.
2024-02-06 11:08:42 +01:00
Daniel Eklöf
3e67415e3e
config: add remaining search.scrollback key bindings
All scrollback up/down key bindings are now available in search mode.
2023-10-08 10:38:39 +02:00
Daniel Eklöf
a772179b6c
search: fix mixup in search_extend_find_line()
The has_wrapped_around_{right,left} functions were mixed up, causing
false positives and false negatives, resulting in bad search matches.

Also make all search_extend_find* functions return a boolean; false
means no change in the selection. In this case, we can skip trying to
extend the selection, and updating the UI.
2023-10-08 10:38:39 +02:00
Daniel Eklöf
ddf4eb3b78
search: don't try to extend a search match when there is none 2023-10-08 10:38:39 +02:00
Daniel Eklöf
78665a7e80
search: add more key bindings to extend the current match
This patch adds the following new search key bindings:

* extend-char (shift+right)
* extend-line-down (shift+down)
* extend-backward-char (shift+left)
* extend-backward-to-word-boundary (ctrl+shift+left)
* extend-backward-to-next-whitespace (ctrl+shift+alt+left)
* extend-line-up (shift+up)

They can be used to extend the search match (i.e. the selection).

This patch also adds an initial set of key bindings to scroll in the
scrollback history:

* scrollback-up-page
* scrollback-down-page

These work just like the key bindings for the normal mode. Also note
that it was already possible to scroll using the mouse.

This patch also fixes a couple of search mode bugs:

* crashing when a search match ends in the last column
* grapheme clusters not being matched correctly
* Search match not being "extendable" after a pointer leave event
* A few others, related to either large matches, or extending matches
  after moving the viewport.

There are still a couple of (known) issues:

* A search match isn't correctly highlighted if its *starting* point
  is outside the viewport.
* Extending the match to end of the scrollback (i.e. the most recent
  output) is simply buggy.

Related to #419
2023-10-08 10:38:37 +02:00
Daniel Eklöf
738deb2368
search: regression: refresh current view when canceling a scrollback search
3b41379be4 introduced a regression,
where canceling a scrollback search didn’t refresh the viewport
correctly; the viewport was changed, but the screen content was not
refreshed.

This worked before, because the workaround for
https://github.com/swaywm/sway/issues/6960 always called
term_damage_view() when exiting scrollback search mode.

3b41379be4 removed that call since it’s
no longer required. *Except* when executing the
BIND_ACTION_SEARCH_CANCEL binding, since then the viewport may be
moved.

Note that this regression affected *all* compositors, not just Sway.

Closes #1354
2023-05-15 20:34:58 +02:00
Daniel Eklöf
3b41379be4
quirks: sway does not damage surface beneath sub-surface, when unmapped
When unmapping a sub-surface, Sway <= 1.8 does not damage the surface
beneath the sub-surface.

https://github.com/swaywm/sway/issues/6960

The workaround is to manually damage the main surface. Previously,
this was done when exiting scrollback search, and after the ‘flash’
OSC. But other sub-surfaces, that may also be unmapped, did not.

This patch adds a quirk handler that does this, and calls it when:

* Exiting scrollback search
* Ending the ‘flash’ OSC
* Exiting unicode input mode
* Clearing URL labels
* Removing the scrollback position indicator

Closes #1335
2023-05-12 14:51:05 +02:00
Daniel Eklöf
8967dd9cfe
input: add new Unicode input mode
This mode is activated through the new key-bindings.unicode-input and
search-bindings.unicode-input key bindings.

When active, the user can “build” a Unicode codepoint by typing its
hexadecimal value.

Note that there’s no visual feedback in this mode. This is
intentional. This mode is intended to be a fallback for users that
don’t use an IME.

Closes #1116
2022-07-29 09:26:31 +02:00
Daniel Eklöf
632c4839cd
search: find_next(): handle trailing SPACER cells
Make sure to increment match_end_col to account for trailing SPACER
cells after a match.

This fixes an issue where search matches weren’t highlighted correctly
when the match *ends* with a double-width character.
2022-07-28 21:05:06 +02:00
Daniel Eklöf
7630510448
selection: find_word_boundary_right: add “stop-on-space-to-word-boundary”
When true, selection_find_word_boundary_right() behaves as before - it
stops as soon as it encounters a character that isn’t of the
same *type* as the “initial” character (the last character in the
selection).

Take this, for example:

  The Quick Brown Fox

The selection will first stop at the end of “the”, then just *before*
“quick”, then at the end of “quick”. Then just *before* “brown”, and
then at the end of “brown”, and so on.

This suits mouse selections pretty good. But when
selection_find_word_boundary_right() is used to extend a search match,
it’s better to ignore space-to-word character transitions. That is, we
want

  The Quick Brown Fox

to first extend to the end of “the”, then immediately to the end of
“quick”, then to the end of “brown”, and so on.

Setting the ‘stop_to_space_to_word_boundary’ argument to false results
in latter behavior.

This is now done by search, when executing the
“extend-to-word-boundary” and “extend-to-next-whitespace” key
bindings.
2022-04-27 18:44:57 +02:00
Daniel Eklöf
0e9ebf433b
search: fix infinite loop when highlighting all matches
find_next() did not always terminate correctly, causing
search_matches_next() to never terminate, which finally leads to an
infinite loop when rendering the search overlay surface, while finding
all matches to highlight.

The problem is that find_next(), after having found the initial
matching characters, enters a nested while loop that tries to match
the rest of the search criteria. This inner while loop did not check
if we’ve reached the last cell, and happily continued past
it (eventually wrappping around the scrollback buffer).

Closes #1047
2022-04-26 20:36:28 +02:00
Daniel Eklöf
694938b85b
search: assert that the match is *inside* the new viewport 2022-04-26 19:47:02 +02:00
Daniel Eklöf
c82c6116ed
search: regression: crash when moving viewport
5c4ddebc3c refactored
search_update_selection(), specifically, the logic that moves the
viewport.

It did so by converting the absolute row number (of the match) to
scrollback relative coordinates. This way we could ensure the viewport
wasn’t moved “too much” (e.g. beyond the scrollback start).

However, grid_row_abs_to_sb() and grid_row_sb_to_abs() doesn’t take a
partially filled scrollback into account. This means the row (numbers)
it returns may refer to *uninitialized* rows.

Since:

* The match row itself is valid (we *know* it has text on it)
* We *subtract* from it, when setting the new viewport (to center the
  match on the screen).

it’s only the *upper* part of the new viewport that may be
uninitialized. I.e. we may have adjusted it too much.

So, what we need to do is move the viewport forward until its *first*
row is initialized. Then we know the rest will be too.
2022-04-26 19:32:08 +02:00
Daniel Eklöf
1e87dbc4dc
search: work around Sway sub-surface unmap bug
Unmapping a sub-surface in Sway does not damage the underlying
surface.

As a result, "committing" a scrollback search will typically leave
most of the foot window dimmed.

It can be seen when "cancelling" a search as well, but there it's less
obvious - only the margins are left dimmed. This is because cancelling
a search damaged the current viewport (something that shouldn't be
needed).

Out of sway, river, weston and mutter, only Sway needs this
workaround.

This is a workaround for https://github.com/swaywm/sway/issues/6960
2022-04-26 17:41:38 +02:00
Daniel Eklöf
5c4ddebc3c
search: fix multiple crashes
* When extending the selection to the next word boundary, ensure the
  row numbers are valid:

  - use selection_get_end() when retrieving the current end
    coordinate. This alone fixes a crash where we previously would
    crash in an out-of-bounds array access in grid->rows[], due to
    term->selection.coords.end being unbounded.

  - ensure the new end coordinate is bounded before and after calling
    selection_find_word_boundary_right().

* When moving the viewport (to ensure a new match is visible), make
  sure we don’t end up with the match outside the new viewport.

  Under certain, unusual, circumstances, the moved viewport _still_
  did not contain the match. This resulted in assertions triggering
  later, that assumed the match(es) are *always* visible.

  It’s fairly easy to trigger this one by running foot with e.g.

    foot -o scrollback.lines=0 --window-size-chars 25x3

  and then hitting enter a couple of times, to fill the scrollback
  history (which should consist of a single row in the example above),
  and the do a scrollback search for (part of) the prompt, and keep
  searching backward until it crashes.

  This would happen if calculated (new) viewport had to be adjusted
  (for example, to ensure it didn’t go past the scrollback end).

  This patch changes the logic used when calculating the new
  viewport. Instead of calculating the wanted viewport (match is
  vertically centered) and then trying to adjust it to ensure the new
  viewport is valid, start with a “safe” new viewport value, and then
  determine how much we can move it, if at all, to center the match.

  This is done by using scrollback relative coordinates. In this
  coordinate system, the new viewport must be

    >= 0, and < grid->num_lines - term->rows

  This makes it very easy to limit the amount by which the viewport is
  adjusted.

  As a side-effect, we can remove all the old re-adjustment logic.

* The match iterator no longer special cases the primary match. This
  was needed before, when the search iterator did not handle
  overlapping matches correctly. Now that we do, the iterator is
  guaranteed to find the primary match, and thus we no longer need to
  special case it.

  This fixes a bug where the primary match was returned twice, due to
  the logic checking if a secondary match is the same as the primary
  match was flawed...

Closes #1036
2022-04-25 20:00:47 +02:00
Daniel Eklöf
1d48b7b77c
search: matches_next: assert start’s ‘col’ is valid 2022-04-24 20:17:37 +02:00
Daniel Eklöf
082e242ce5
search: matches_next: stop searching when start.row >= term->rows
As this means the last call to sarch_matches_next() found a match at
the bottom of the view, and then set the iter’s *next* start position
to a row outside the view.

This is fine, but we need to handle it, by immediately stopping the
iter.
2022-04-24 20:17:37 +02:00
Daniel Eklöf
d068e821d6
search: matches_next: don’t wrap around grid->num_rows
When bumping the iter’s start.row, we’re working with view-local
coordinates. That is, 0 >= row < term->rows.

This means it is wrong to and with grid->num_rows - 1, because a),
‘row’ should **never** be that big. And b), if we do, we’ll just end
up in an infinite loop, where the next call to matches_next() just
starts over from the beginning again (and eventually hitting the exact
same place that got us started).
2022-04-24 20:17:37 +02:00
Daniel Eklöf
f7c29ee394
search: maches_next: assert match coordinates are valid
* They are within range (i.e. ‘row’ does not exceed term->rows-1)
* ‘end’ comes after ‘start’
2022-04-24 20:17:37 +02:00
Daniel Eklöf
b68d5da71b
search: fix debug log
This has been broken since the forward/backward search logic was
refactored.
2022-04-24 12:03:31 +02:00
Daniel Eklöf
9907d7bbe9
search: don't modify search.start coord *before* finding next match
The match logic uses the last start coordinate to determine which end
points in the selection to update. This sometimes fails when the start
coordinate has been changed by e.g. a key binding - the new start
coordinate is incorrectly matched against the old-but-modified start
coordinate, causing foot to e.g. *not* upate the selection start
coordinate.

Example:

  $ echo 'test\n\test\ntest'

Then do a scrollback search for 'test. The first match is found
correctly (the last 'test'), but searching for the previous match
(ctrl+r) does not select the middle 'test'.

Fix by passing the search direction to search_find_next(), and have
_it_ calculate the coordinate to start search. There are three possibilities:

* forward
* backward
* "backward", but at the same position

The first two are used when searching for next/prev match with ctrl+s
and ctrl+r. The last one is used when the search criteria is
updated. In this case, we don't want to move to the previous match,
*unless* the current match no longer matches.
2022-04-21 18:54:27 +02:00
Daniel Eklöf
e3683b58fc
search: add comment to ‘advance_pos()’ macro 2022-04-18 15:12:56 +02:00
Daniel Eklöf
b3d18e3c8d
search: codespell: backards -> backwards 2022-04-18 15:06:20 +02:00
Daniel Eklöf
006c75d2d3
search: rename ‘newline’ -> ‘advance_pos’ macro, reverse return value 2022-04-18 14:57:31 +02:00
Daniel Eklöf
2898ec70b6
search: input: log consumed modifiers 2022-04-18 14:57:13 +02:00
Daniel Eklöf
7b1994fce6
search: match iterator: wrap iterator’s row number, when needed 2022-04-18 14:47:17 +02:00
Daniel Eklöf
a9ed719f90
search: find_next(): assert start/end coords are valid 2022-04-18 14:47:07 +02:00
Daniel Eklöf
a46d7d95c6
search: find_next(): stop at the exact cell position specified by ‘end’
And not just when reaching end’s row.
2022-04-18 14:43:01 +02:00
Daniel Eklöf
b58ab2fd48
search: find_next(): prefix {start,end}_{row_col} with ‘match’ 2022-04-18 12:38:09 +02:00
Daniel Eklöf
9117ebebef
search: find_next(): pass an ‘end’ coord, instead of a row count 2022-04-18 12:31:07 +02:00
Daniel Eklöf
4cdae0c05b
search: refactor: find_next(): pass start as a ‘coord’ struct 2022-04-18 12:17:49 +02:00
Daniel Eklöf
68db8ff1f5
search: search_update_selection() no longer expects end coordinate to be exclusive 2022-04-18 00:01:24 +02:00
Daniel Eklöf
529da8e238
search: match iterator: handle overlapping matches 2022-04-17 19:40:51 +02:00
Daniel Eklöf
3905212651
search: match iterator: always return the primary match
Normally, the primary match is automatically included by the iterator,
but if the secondary and primary matches overlap, it is possible we
don’t return the _actual_ primary match. This lead to highlighting
issues, where part of the primary match wasn’t highlighted correctly.
2022-04-17 19:22:28 +02:00
Daniel Eklöf
c5519e2aa6
search: fix next/prev not updating selection correctly when matches overlap
When the new match overlapped with the old match, the selection wasn’t
updated correctly.
2022-04-17 19:16:47 +02:00
Daniel Eklöf
90a2ca966f
key-binding: new API, for handling sets of key bindings
Up until now, our Wayland seats have been tracking key bindings. This
makes sense, since the seat’s keymap determines how the key bindings
are resolved.

However, tying bindings to the seat/keymap alone isn’t enough, since
we also depend on the current configuration (i.e. user settings) when
resolving a key binding.

This means configurations that doesn’t match the wayland object’s
configuration, currently don’t resolve key bindings correctly. This
applies to footclients where the user has overridden key bindings on
the command line (e.g. --override key-bindings.foo=bar).

Thus, to correctly resolve key bindings, each set of key bindings must
be tied *both* to a seat/keymap, *and* a configuration.

This patch introduces a key-binding manager, with an API to
add/remove/lookup, and load/unload keymaps from sets of key bindings.

In the API, sets are tied to a seat and terminal instance, since this
makes the most sense (we need to instantiate, or incref a set whenever
a new terminal instance is created). Internally, the set is tied to a
seat and the terminal’s configuration.

Sets are *added* when a new seat is added, and when a new terminal
instance is created. Since there can only be one instance of each
seat, sets are always removed when a seat is removed.

Terminals on the other hand can re-use the same configuration (and
typically do). Thus, sets ref-count the configuration. In other words,
when instantiating a new terminal, we may not have to instantiate a
new set of key bindings, but can often be incref:ed instead.

Whenever the keymap changes on a seat, all key bindings sets
associated with that seat reloads (re-resolves) their key bindings.

Closes #931
2022-04-17 15:39:51 +02:00
Daniel Eklöf
074bbf767e
search: refactor: search_update_selection() takes a ‘struct range’ 2022-04-16 20:23:15 +02:00
Daniel Eklöf
bd089c845f
search: break out “find next match” logic to a new function 2022-04-16 20:13:22 +02:00
Daniel Eklöf
90c91d6148
search/render: initial support for highlighting all visible matches
Before this patch, only the currently “selected” match was
highlighted (by having the “selected” attribute, and by *not* dimming
it, like the rest of the grid during a scrollback search).

With this patch, we now highlight matches within the viewport. While
searching, only the “primary” match is searched-for, and tracked.

Then, when rendering a frame, we find all “secondary” matches as
well. “holes” are added to the search-mode overlay by the means of an
search-match iterator.

The iterator’s text matching logic is *very* similar to what we do
when the search criteria has been updated, and we re-search the
scrollback. It should be possible to refactor this, and share code.
2022-04-16 19:26:20 +02:00
Daniel Eklöf
129deaffa8
wayland: optionally disable pointer input on subsurfaces
We have a number of sub-surfaces for which we are *not* interrested in
pointer (or touch) input.

Up until now, we’ve manually dealt with these, by recognizing these
surfaces in all pointer events, and ignoring them.

But, lo and behold, there are better ways of doing this. By clearing
the subsurface’s input region, the compositor will do this for us -
when a pointer is outside a surface’s input region, the event is
passed to the next surface underneath it.

This is exactly what we want! Do this for all subsurfaces, *except*
the CSDs.
2022-04-16 17:41:14 +02:00
Daniel Eklöf
07253c29d1
search: use a local ‘grid’ variable to hold term->grid 2022-04-09 17:28:16 +02:00
Daniel Eklöf
5b1f1602bc
refactor: add a ‘range’ struct, grouping a start and end coord together 2022-04-09 15:09:02 +02:00
Daniel Eklöf
1e63dddb89
input: don’t allow non-significant modifiers when matching untranslated symbols
When matching “untranslated” bindings (by matching the base symbol of
the key, e.g. ctrl+shift+2 in US layout), require that no
non-significant modifiers are active.

This fixes an issue where AltGr was “ignored”, and would cause certain
combinations to match a key binding.

Example: ctrl+altgr+0, on many European layouts matched against the
default ctrl+0 (reset the font size), instead of emitting ^]

To make this work, we now need to filter out “locked”
modifiers (e.g. NumLock and CapsLock). Otherwise having e.g. NumLock
active would prevent *all* untranslated matching to fail.

Closes #983
2022-03-20 13:29:29 +01:00