Commit graph

130 commits

Author SHA1 Message Date
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
Daniel Eklöf
fed90646d3
input/search/url: pass pointer-to key-binding struct to execute() 2022-02-09 17:51:05 +01:00
Daniel Eklöf
dcd79065c8
wayland: unify key- and mouse-binding structs 2022-02-09 17:51:04 +01:00
Daniel Eklöf
e0227266ca
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.

Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.

For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).

Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.

These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.

For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the  __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.

FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.

Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.

Other fcft API changes:

* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2022-02-05 17:00:54 +01:00
Daniel Eklöf
739e7d76b4
search: remember last searched-for string between searches
Regardless of how we exit search mode (commit or cancel), the search
string is remembered.

The next time we enter search mode, the last searched-for string will
be used when searching for the next/prev match (ctrl+r, ctrl+s), and
the search query is empty.
2022-01-29 17:50:42 +01:00
Daniel Eklöf
22266e384b
search: refactor: add add_wchars(), make search_add_chars() use it
search_add_chars() converts the incoming UTF-8 string into a wide-char
string, and then calls add_wchars().

add_wchars() inserts the string into the search buffer, at the current
cursor position.
2022-01-29 17:50:42 +01:00
Daniel Eklöf
7092a72ce4
search: redraw search box after selection has been cancelled 2021-07-22 17:57:52 +02:00
Daniel Eklöf
251545203b
search: reset match state when selection is cancelled
While we’re in scrollback search mode, the selection may be
cancelled (for example, if the application is scrolling out the
selected text). Trying to e.g. extend the search selection after this
has happened triggered a crash.

This fixes it by simply resetting the search match state when the
selection is cancelled.

Closes #644
2021-07-22 17:57:43 +02:00
Daniel Eklöf
fe8ca23cfe
composed: store compose chains in a binary search tree
The previous implementation stored compose chains in a dynamically
allocated array. Adding a chain was easy: resize the array and append
the new chain at the end. Looking up a compose chain given a compose
chain key/index was also easy: just index into the array.

However, searching for a pre-existing chain given a codepoint sequence
was very slow. Since the array wasn’t sorted, we typically had to scan
through the entire array, just to realize that there is no
pre-existing chain, and that we need to add a new one.

Since this happens for *each* codepoint in a grapheme cluster, things
quickly became really slow.

Things were ok:ish as long as the compose chain struct was small, as
that made it possible to hold all the chains in the cache. Once the
number of chains reached a certain point, or when we were forced to
bump maximum number of allowed codepoints in a chain, we started
thrashing the cache and things got much much worse.

So what can we do?

We can’t sort the array, because

a) that would invalidate all existing chain keys in the grid (and
iterating the entire scrollback and updating compose keys is *not* an
option).

b) inserting a chain becomes slow as we need to first find _where_ to
insert it, and then memmove() the rest of the array.

This patch uses a binary search tree to store the chains instead of a
simple array.

The tree is sorted on a “key”, which is the XOR of all codepoints,
truncated to the CELL_COMB_CHARS_HI-CELL_COMB_CHARS_LO range.

The grid now stores CELL_COMB_CHARS_LO+key, instead of
CELL_COMB_CHARS_LO+index.

Since the key is truncated, collisions may occur. This is handled by
incrementing the key by 1.

Lookup is of course slower than before, O(log n) instead of
O(1).

Insertion is slightly slower as well: technically it’s O(log n)
instead of O(1). However, we also need to take into account the
re-allocating the array will occasionally force a full copy of the
array when it cannot simply be growed.

But finding a pre-existing chain is now *much* faster: O(log n)
instead of O(n). In most cases, the first lookup will either
succeed (return a true match), or fail (return NULL). However, since
key collisions are possible, it may also return false matches. This
means we need to verify the contents of the chain before deciding to
use it instead of inserting a new chain. But remember that this
comparison was being done for each and every chain in the previous
implementation.

With lookups being much faster, and in particular, no longer requiring
us to check the chain contents for every singlec chain, we can now use
a dynamically allocated ‘chars’ array in the chain. This was
previously a hardcoded array of 10 chars.

Using a dynamic allocated array means looking in the array is slower,
since we now need two loads: one to load the pointer, and a second to
load _from_ the pointer.

As a result, the base size of a compose chain (i.e. an “empty” chain)
has now been reduced from 48 bytes to 32. A chain with two codepoints
is 40 bytes. This means we have up to 4 codepoints while still using
less, or the same amount, of memory as before.

Furthermore, the Unicode random test (i.e. write random “unicode”
chars) is now **faster** than current master (i.e. before text-shaping
support was added), **with** test-shaping enabled. With text-shaping
disabled, we’re _even_ faster.
2021-06-24 17:30:49 +02:00
Daniel Eklöf
b9ef703eb1
wip: grapheme shaping 2021-06-24 17:30:45 +02:00
Daniel Eklöf
11829931a7
search: don’t crash when we don’t have any XKB compose state 2021-06-16 17:44:14 +02:00
Daniel Eklöf
96b19212d3
search: match_to_end_of_word(): we update the selection ourselves
No need to call search_find_next() after doing a
search_match_to_end_of_word(), since we already *know* we have a
match, and where it is.
2021-05-17 18:14:10 +02:00
Daniel Eklöf
a6d9f01c0d
extract: move ‘strip_trailing_empty’ parameter from extra_finish() to extract_begin() 2021-05-17 18:14:10 +02:00
Daniel Eklöf
0945e71572
search: find_next(): correctly wrap row number 2021-05-17 18:14:10 +02:00
Daniel Eklöf
5e621bbdb9
search: match_to_end_of_word(): use a local macro to bump coord 2021-05-17 18:14:10 +02:00
Daniel Eklöf
1c8bdf34ce
search: match_to_end_of_word(): use has_wrapped_around() while extracting text 2021-05-17 18:14:10 +02:00
Daniel Eklöf
11f7a6616b
search: match_to_end_of_word(): we always extend the selection with at least one character 2021-05-17 18:14:10 +02:00
Daniel Eklöf
7cf0e2aae4
search: match_to_end_of_word(): skip newlines when copying from extraction buffer 2021-05-17 18:14:10 +02:00
Daniel Eklöf
e460727afd
search: match_to_end_of_word(): refactor
Rewrite match_to_end_of_word() in terms of

* selection_find_word_boundary_right()
* extract_begin() + extract_one() + extract_finish()

This adds a small overhead, in that extract_*() allocates an internal
buffer, from which we then immediately copy, into our newly resized
prompt buffer.

On the other hand, this makes the matching behavior more consistent
with regular mouse selections, and we don’t have to keep two very
similar match-to-next-word-boundary implementations in sync.
2021-05-17 18:14:10 +02:00
Daniel Eklöf
e415f85829
search: find_next(): proper check for scrollback wrap around
Besides disallowing matches that crosses the scrollback wrap-around,
this also fixes a crash when the trying to search beyond the last
output, when the scrollback history hasn’t yet been completely filled.
2021-05-17 18:14:09 +02:00
Daniel Eklöf
d9e1aefb91
term: rename CELL_MULT_COL_SPACER -> CELL_SPACER, and change its definition
Instead of using CELL_SPACER for *all* cells that previously used
CELL_MULT_COL_SPACER, include the remaining number of spacers
following, and including, itself. This is encoded by adding to the
CELL_SPACER value.

So, a double width character will now store the character itself in
the first cell (just like before), and CELL_SPACER+1 in the second
cell.

A three-cell character would store the character itself, then
CELL_SPACER+2, and finally CELL_SPACER+1.

In other words, the last spacer is always CELL_SPACER+1.

CELL_SPACER+0 is used when padding at the right margin. I.e. when
writing e.g. a double width character in the last column, we insert a
CELL_SPACER+0 pad character, and then write the double width character
in the first column on the next row.
2021-05-14 14:41:02 +02:00
Daniel Eklöf
2b7c5db188
search: when matching cell content, treat empty cells as spaces 2021-03-28 13:21:18 +02:00
Daniel Eklöf
5e64e06a55
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:

* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes

A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.

Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.

More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.

Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.

This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.

When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.

However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-03-04 09:43:43 +01:00
Daniel Eklöf
5c8579043d
wayland: drop ‘_surface’ suffix from subsurface struct instances 2021-02-17 21:48:08 +01:00
Daniel Eklöf
e049124f6d
search: use wayl_win_subsurface_new/destroy() 2021-02-17 21:48:07 +01:00
Craig Barnes
2f81a1d07c Convert some more uses of xassert(false) to BUG("...") 2021-02-09 15:16:19 +00:00
Daniel Eklöf
03bac9dada
key-bindings: refactor: use a single type for all key bindings
Up until now, the various key binding modes (“normal”, “search” and
“url”) have used their own struct definitions for their key bindings.

The only reason for this was to have a properly typed “action” (using
the appropriate “action” enum).

This caused lots of duplicated code. This patch refactors this to use
a single struct definition for the “unparsed” key bindings handled by
the configuration, and another single definition for “parsed” bindings
used while handling input.

This allows us to implement configuration parsing, keymap translation
and so on using one set of functions, regardless of key binding mode.
2021-02-08 10:09:14 +01:00
Daniel Eklöf
2310ffd1b8
search: match composed characters when extending the search string to the next word boundary 2021-01-26 20:26:34 +01:00
Daniel Eklöf
fc33619b19
search: fix matching of multi-column characters
We were matching the sub-sequence SPACERs as if they were regular
characters, which obviously failed.
2021-01-26 20:05:14 +01:00
Daniel Eklöf
dd8a6fdd4d
search: don’t re-scan the scrollback unless the search string actually has changed 2021-01-26 19:29:05 +01:00
Daniel Eklöf
89b810261e
Merge branch 'search-match-composed-characters' 2021-01-26 19:28:21 +01:00
Daniel Eklöf
d3d5897bb7
search: fix matching against composed characters 2021-01-24 12:05:57 +01:00
Daniel Eklöf
cbf90d67a2
search: filter out non-printable characters
Don’t allow non-printable characters in the search input string.
2021-01-24 11:32:45 +01:00
Craig Barnes
e56136ce11 debug: rename assert() to xassert(), to avoid clashing with <assert.h> 2021-01-16 20:16:00 +00:00
Craig Barnes
22f25a9e4f Print stack trace on assert() failure or when calling fatal_error()
Note: this uses the __sanitizer_print_stack_trace() function from the
AddressSanitizer runtime, so it only works when AddressSanitizer is
in use.
2021-01-16 19:56:33 +00:00
Daniel Eklöf
3a9172342f
selection: combine enum selection_kind with selection_semantic 2021-01-06 10:53:27 +01:00
Daniel Eklöf
3afc5a723e
selection: use selection_start() to initialize word/row-based selection
This removes the selection_mark_word() and selection_mark_row()
functions. To start a word/row-based selection, use selection_start()
with SELECTION_SEMANTIC_{WORD,ROW}
2021-01-04 19:48:41 +01:00
Daniel Eklöf
30de262d29
selection: wip: update selection word-wise when initial selection was by word 2021-01-04 19:48:26 +01:00
Daniel Eklöf
fc2bcf9bc0
search: reset IME state when entering/exiting search mode 2020-12-07 20:44:15 +01:00
Daniel Eklöf
b4a0f5b13b
render: search: glyph_offset is in *cells*, cursor position in *characters*
When calculating the offset into the search string, from where to
start rendering, take into account that the cursor position is
in *characters*, and the glyph-offset is in *cells*.
2020-12-07 20:44:13 +01:00
Daniel Eklöf
f51ce34753
search: new function: search_add_chars()
This function inserts a string into the search buffer, at the current
insertion point (i.e. where the cursor is).
2020-12-07 20:44:12 +01:00
Daniel Eklöf
f3e6941c9a
search: add {clipboard,primary}-paste key bindings
These bindings copy from the clipboard or primary selection into the
search buffer.

Default bindings:

* clipboard-paste: ctrl+v, ctrl+y
* primary-paste: shift+insert
2020-11-01 12:39:57 +01:00
Daniel Eklöf
49f4b3da64
config: add ‘[default].word-delimiters’
This option lets the user configure which characters act as word
delimiters when selecting text.

This affects both “double clicking”, and ‘ctrl-w’ in scrollback search
mode.

Closes #156
2020-10-09 19:44:23 +02:00
Daniel Eklöf
dabdffafa5
don't use empty struct initializers 2020-08-23 10:07:00 +02:00