Commit graph

582 commits

Author SHA1 Message Date
Daniel Eklöf
53851e13ec
shm: refactor: move away from a single, global, buffer list
Up until now, *all* buffers have been tracked in a single, global
buffer list. We've used 'cookies' to separate buffers from different
contexts (so that shm_get_buffer() doesn't try to re-use e.g. a
search-box buffer for the main grid).

This patch refactors this, and completely removes the global
list.

Instead of cookies, we now use 'chains'. A chain tracks both the
properties to apply to newly created buffers (scrollable, number of
pixman instances to instantiate etc), as well as the instantiated
buffers themselves.

This means there's strictly speaking not much use for shm_fini()
anymore, since its up to the chain owner to call shm_chain_free(),
which will also purge all buffers.

However, since purging a buffer may be deferred, if the buffer is
owned by the compositor at the time of the call to shm_purge() or
shm_chain_free(), we still keep a global 'deferred' list, on to which
deferred buffers are pushed. shm_fini() iterates this list and
destroys the buffers _even_ if they are still owned by the
compositor. This only happens at program termination, and not when
destroying a terminal instance. I.e. closing a window in a “foot
--server” does *not* trigger this.

Each terminal instatiates a number of chains, and these chains are
destroyed when the terminal instance is destroyed. Note that some
buffers may be put on the deferred list, as mentioned above.
2021-07-17 19:14:42 +02:00
Daniel Eklöf
232fb20269
shm: replace 'locked' attribute with a ref-counter
The initial ref-count is either 1 or 0, depending on whether the
buffer is supposed to be released "immeidately" (meaning, as soon as
the compositor releases it).

Two new user facing functions have been added: shm_addref() and
shm_unref().

Our renderer now uses these two functions instead of manually setting
and clearing the 'locked' attribute.

shm_unref() will decrement the ref-counter, and destroy the buffer
when the counter reaches zero. Except if the buffer is currently
"busy" (compositor owned), in which case destruction is deferred to
the release event. The buffer is still removed from the list though.
2021-07-17 19:14:42 +02:00
Daniel Eklöf
fcb60abc13
config: add locked-title=no|yes
Closes #386
2021-07-04 17:59:40 +02:00
Daniel Eklöf
07652d3b9e
term: rows printed to now defaults to having a hard linebreak
That is, instead of requiring a ‘\n’ to be printed, non-empty lines
are now treated as having a hard linebreak by default.

The linebreak is cleared on an explicit wrap.
2021-07-03 18:05:45 +02:00
Daniel Eklöf
117e24dbf4
term: destroy: free URLs before free:ing the grids
Fixes use-after-free when destroying a terminal with “live”
URLs (i.e. when URL mode is active).
2021-06-28 22:33:57 +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
f7860aec76
config: add new function config_clone() 2021-06-23 10:39:14 +02:00
Daniel Eklöf
078f790b72
term: fix regression after merging #599
Don’t increment ‘j’ twice in each loop iteration, and fix typo in loop
condition (check ‘j’, not ‘i’).
2021-06-18 15:56:34 +02:00
Daniel Eklöf
07b455e882
render: don’t create/destroy the title update timer each time
Create it once when instantiating the terminal. Add a boolean to track
whether the timer is running or not.
2021-06-18 15:53:47 +02:00
Daniel Eklöf
535c82d628
render: use a timer instead of relying on the frame callback for title update throttling
Using the frame callback works most of the time, but e.g. Sway doesn’t
call it while the window is hidden, and thus prevents us from updating
the title in e.g. stacked views.

This patch uses a timer FD instead. We store a timestamp from when the
title was last updated. When the application wants to update the
title, we first check if we already have a timer running, and if so,
does nothing.

If no timer is running, check the timestamp. If enough time has
passed, update the title immediately.

If not, instantiate a timer and wait for it to trigger.

Set the minimum time between two updates to ~8ms (twice per frame, for
a 60Hz output, and ~once per frame on a 120Hz output).

Closes #591
2021-06-18 15:36:58 +02:00
Daniel Eklöf
495c730487
config: don’t use tllist where it isn’t necessary
tllists are great when dealing with dynamically changing lists. They
are also very easy to use when building lists/arrays where the final
size is unknown.

However, this ease of use comes at a price: code size. tll-macros
expand to a lot of code.

Since things in the config are static, once the config has been
loaded, using tllists for configuration data structures doesn’t make
much sense.

This patch replaces nearly all tllists used by the configuration, with
dynamically allocated arrays.
2021-06-18 15:35:57 +02:00
Daniel Eklöf
45a04393c4
term: erase_cell_range: early break out from URI loop
URI ranges are sorted. This means we don’t have to check any more
ranges once we’ve seen one that starts *after* the erase range.

Closes #594
2021-06-15 16:26:43 +02:00
Daniel Eklöf
5caa87adaf
term: no need to set attrs.clean=0 right after copying the VT attrs struct
When printing a character to a cell, we copy the current VT state’s
attributes to the cell. And then clear the ‘clean’ bit.

But the ‘clean’ bit is part of the VT state, and is *always*
zero. Thus there’s no need to explicitly clear it right after copying
the VT state.
2021-06-15 12:38:35 +02:00
Daniel Eklöf
cd37cdc940
term: free the data associated with the pixman image when destroying box-drawing glyphs
Closes #586
2021-06-09 11:27:08 +02:00
Craig Barnes
e030a2ca08 terminal: add 'charset_designator' enum to make code more self-documenting
This commit also renames the term_set_single_shift_ascii_printer()
function to term_single_shift(), since the former is overly verbose
and not really even accurate.
2021-06-09 10:00:25 +01:00
Craig Barnes
a2c9c56f19 vt: fix SS2/SS3 escape sequences to act correctly as single shifts
These sequences are supposed to affect the next printable ASCII
character and then reset to the previous character set, but before
this commit they were behaving like locking shifts.
2021-06-08 21:09:40 +01:00
Craig Barnes
b34d76f711 terminal: fix compilation error when debug logging is enabled
Without this fix, setting LOG_ENABLE_DBG to 1 in terminal.c would
cause the following error:

> terminal.c:1787: 'struct terminal' has no member named 'font_scale'

The 'font_scale' member was removed in commit
2afc678236.
2021-06-08 13:10:46 +01:00
Daniel Eklöf
a012c2fb3e
term: fix crash when an OSC-8 URI wraps around the scrollback
Long OSC-8 URIs, that are split up over multiple rows, are handled by
emitting one URI range on each row the URL touches.

This was done by initializing a row index variable, and then
incrementing it each loop iteration.

This caused an out-of-bounds array access when the row index reached
the maximum number of scrollback lines.

The fix is simple: make sure the row index variable wraps around,
instead of incrementing without any bounds.

Closes #552
2021-05-26 14:11:03 +02:00
Daniel Eklöf
1868fa27e4
term: osc8_close: debug: assert all URI coordinates are unique 2021-05-23 10:29:32 +02:00
Daniel Eklöf
0f483d65ce
config: move url-mode related options to a dedicated section, ‘url’ 2021-05-22 14:50:47 +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
1bc9fd5fe1
extract: add extract_finish_wide(), and optionally skip stripping trailing empty cells
extract_finish() returns the extracted text in UTF-8, while
extract_finish_wide() returns the extracted text in Unicode.

This patch also adds a new argument to extract_finish{,_wide},
that when set to true, skips stripping trailing empty cells.
2021-05-17 18:14:09 +02:00
Daniel Eklöf
2afc678236
term: get rid of term->font_scale, use term->scale only
We only needed term->font_scale to be able to detect scaling factor
changes (term->font_scale != term->scale).

But, we already have the old scaling factor in all places where
term_font_dpi_changed() is called, so let’s pass the old scaling
factor as an argument instead.
2021-05-17 17:55:49 +02:00
Daniel Eklöf
0935428695
term: remove get_font_scale()
get_font_scale() was used to get the new scaling factor when loading
fonts. This was then compared to the last seen font scaling factor. If
there was no difference, the fonts were not reloaded.

The problem was, the initial term->scale was set differently. This
sometimes led to term->scale=2, while get_font_scale() return 1. That
meant, fonts were initially scaled by 2 (when dpi-aware=no). Later,
when mapped on an output (and thus term->scale being set to 1), the
fonts weren’t reloaded with the correct scaling factor since the
cached term->font_scale value was already 1.

Since term->scale always reflects the *new* scaling factor when
term_font_dpi_changed() is called, use that directly, and remove
get_font_scale().

Also rename the following functions:

* font_should_size_by_dpi() -> font_size_by_dpi_for_scale()
* font_size_by_dpi() -> font_sized_by_dpi()
* font_size_by_scale() -> font_sized_by_scale()
2021-05-17 17:55:39 +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
3e92361534
xdg-activation: initial support for setting urgency using XDG activation 2021-05-14 13:26:04 +02:00
Daniel Eklöf
f3b3bc1f71
term: reset: combine memset+initializers of vt struct 2021-05-08 20:27:20 +02:00
Daniel Eklöf
bfd3eb47ec
term: reset: fix reset of the VT struct
1. Free buffers and strings
2. memset() the vt struct
3. re-initialize members that must not be zero

We _could_ replace the memset() with explicit zeroing of all the
members. It’s just that there’s a lot of arrays, so this is much
easier.

Closes #495
2021-05-08 20:27:20 +02:00
Daniel Eklöf
bd8c936b00
term: osc8-open: assert URI in VT struct is NULL 2021-05-08 20:27:12 +02:00
Ryan Farley
dc9751cbfe style, consistent bool names, deprecate old config 2021-05-07 04:40:07 -05:00
Ryan Farley
5d71ccc174 enhanced bell configuration
Add a separate section for bell configuration, with a bell-specific
command option and a setting to allow that command to run without regard
to keyboard focus (for those of us who enjoy being beeped at at all
times, for example). The actions are also no longer mutually exclusive;
this is primarily anticipating urgency support which cannot be
replicated outside the process (in server mode anyway) and would thus be
complementary to any notification or arbitrary command.
2021-05-07 03:12:27 -05:00
Daniel Eklöf
7ce767ab87
config: add ‘beam-thickness’ option
* Rename cursor.style value ‘bar’ to ‘beam’. ‘bar’ remains recognized,
  but should eventually be deprecated and then removed.
* Add ‘cursor.beam-thickness’ option, a pt-or-px value specifying the
  thickness of the beam cursor. Defaults to 1.5pt.
* Rename (and export) pt_or_px_as_pixels() to
  term_pt_or_px_as_pixels()
* Change term_pt_or_px_as_pixels() to round point values instead of
  truncating them.
2021-04-30 20:36:16 +02:00
Oleh Hushchenkov
e9dfb21022 Fix comment 2021-04-18 14:05:28 +03:00
Oleh Hushchenkov
dfc517f671 term: stop reading on eof 2021-04-18 13:56:56 +03:00
Daniel Eklöf
5be2c53d8c
term/vt: only do reverse-wrapping (‘bw’) on cub1
Foot currently does reverse-wrapping (‘auto_left_margin’, or ’bw’) on
everything that calls ‘term_cursor_left()’. This is wrong; it should
only be done for cub1. From man terminfo:

    auto_left_margin | bw | bw | cub1 wraps from column 0 to last
    column

This patch moves the reverse-wrapping logic from term_cursor_left() to
the handling of BS (backspace).

Closes #441
2021-04-08 13:11:58 +02:00
Daniel Eklöf
55b343f690
osc: implement OSC 17+19: change selection background/foreground colors
And of course, we also implement the corresponding reset sequences,
OSC 117+119.
2021-04-08 10:43:36 +02:00
Daniel Eklöf
28c2bea800
osc: reset background alpha when resetting the background color 2021-04-08 10:41:39 +02:00
Daniel Eklöf
fb4066f94e
terminal: drain PTY when client terminates
This is done by:

* Not limiting the number of times we try to read from the PTY when
  we’ve have POLLHUP
* Not requiring the entire the previous read to have filled our
  buffer.
* Not erroring out on EIO.
2021-04-07 19:09:31 +02:00
Daniel Eklöf
936063271f
config/terminal: refactor: remove “default_*” color members from terminal struct
Access the original colors in the configuration instead.
2021-04-07 08:07:43 +02:00
Daniel Eklöf
37245fe4e6
term: free vt.osc8.uri when destroying a terminal
Free vt.osc8.uri in term_destroy(), in the off chance, that the client
application emitted an un-terminated OSC-8 URI.
2021-03-28 21:01:22 +02:00
Daniel Eklöf
e8ffb05bc7
ime: move preedit state from terminal struct to the seat struct
This ensures different seat’s don’t step on each others IME pre-edit
state.

It also removes most dependencies on having a valid term pointer for
many IME operations.

We’re still not all the way, since we support disabling IME with a
private mode, which is per terminal, not seat.

Thus, we still require the seat to have keyboard focus on one of our
windows.

Closes #324. But note that *rendering* of multiple seat’s IME pre-edit
strings is still broken.
2021-03-25 09:36:07 +01:00
Daniel Eklöf
ee75e10e71
term: term_print(): clear line break flag
The line break flag is used by the text reflow and text
extraction (i.e. copy-paste) logic, to determine whether or not to
insert a newline between two lines.

There’s some amount of heuristics involved in this. For example, the
client application could emit a newline, move the cursor back up, and
print text. What does that mean for us?

Foot’s behavior up until now has been this:

The line break flag is set on the row, when the application emits an
explicit linefeed. The flag is cleared when the line is erased. But
otherwise not.

This meant that emitting a linefeed and then moving the cursor back up
and printing text, did not clear the line break flag. This in turn
meant that text copied always had newlines inserted, even though that
was not the client applications intention.

By clearing the line break flag whenever _anything_ is printed to a
row, the new behavior is, in practice, that the line break flag is
only set on a row if a linefeed was that *last* thing printed to that
row.

Closes #410
2021-03-23 11:02:51 +01:00
Daniel Eklöf
7609fbba47
term: increase/decrease custom line-height with font size changes
When the user has set a custom line-height, we now adjust it when
increasing/decreasing (“zooming”) the font size at run-time.

Previously, the line-height was fixed at the size specified in
foot.ini.
2021-03-21 16:29:35 +01:00
Daniel Eklöf
b601307fae
term: update_ascii_printer(): log when we (actually) switch printer 2021-03-16 12:57:25 +01:00
Daniel Eklöf
60b3ccc641
term: runtime switch between a ‘fast’ and a ‘generic’ ASCII print function
term_print() is called whenever the client application “prints”
something to the grid. It is called for both ASCII and UTF-8
characters, and needs to handle sixels, insert mode and ASCII
vs. graphical charsets.

Since it’s on the hot path, this becomes unnecessarily slow.

This patch adds a “fast” version of term_print(), tailored for the
common case: ASCII characters in non-insert mode, without any sixels
and non-graphical charsets.

A new function, term_update_ascii_printer(), has been added, and must
be called whenever:

* The currently selected charset *index* changes
* The currently selected charset changes (from ASCII to graphical, or
  vice verse)
* Sixels are added to the grid
* Sixels are removed from the grid
* Insert mode is enabled/disabled
2021-03-16 08:45:18 +01:00
Daniel Eklöf
2451699c35
term: term_print(): assume we’re *not* at the right margin
That is, assume we can, and should, increment the cursor column.

This changes the emitted assembly from:

   518fb:       8b 8b f8 05 00 00       mov    0x5f8(%rbx),%ecx
   51901:       ff c9                   dec    %ecx
   51903:       39 d1                   cmp    %edx,%ecx
   51905:       7e 11                   jle    51918 <term_print.constprop.0+0x78>
   51907:       ff c2                   inc    %edx
   51909:       89 50 10                mov    %edx,0x10(%rax)
   5190c:       5b                      pop    %rbx
   5190d:       5d                      pop    %rbp
   5190e:       41 5c                   pop    %r12
   51910:       c3                      ret
   51911:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)
   51918:       c6 40 18 01             movb   $0x1,0x18(%rax)
   5191c:       5b                      pop    %rbx
   5191d:       5d                      pop    %rbp
   5191e:       41 5c                   pop    %r12
   51920:       c3                      ret

To:

   5191c:       41 8d 50 01             lea    0x1(%r8),%edx
   51920:       89 50 10                mov    %edx,0x10(%rax)
   51923:       3b 93 f8 05 00 00       cmp    0x5f8(%rbx),%edx
   51929:       0f 8d 21 01 00 00       jge    51a50 <term_print.constprop.0+0x190>
   5192f:       5b                      pop    %rbx
   51930:       5d                      pop    %rbp
   51931:       41 5c                   pop    %r12
   51933:       c3                      ret
   ...
   51a50:       c6 40 18 01             movb   $0x1,0x18(%rax)
   51a54:       44 89 40 10             mov    %r8d,0x10(%rax)
   51a58:       5b                      pop    %rbx
   51a59:       5d                      pop    %rbp
   51a5a:       41 5c                   pop    %r12
   51a5c:       c3                      ret

I.e. it cuts the normal path from 10 instructions down to 8. It
increases the "bad" path with one extra instruction.
2021-03-14 20:47:44 +01:00
Daniel Eklöf
329f2a488c
term: don’t close PTY when client application dies
Closing it as soon as we detect that the client has died, means we may
not have drained it completely.

The PTY is either closed _by_ the client application, or by us when we
shutdown the terminal. Thus, leaving it open (until we call
term_shutdown()) is fine.
2021-03-12 22:06:50 +01:00
Daniel Eklöf
849427bf10
sixel: implement private mode 80 - sixel scrolling
When enabled (the default), sixels behave much like normal output; the
start where the cursor is, and the cursor moves with the
sixel. I.e. after emitting a sixel the cursor is left after the image;
either to the right, if private mode 8452 is enabled, or otherwise on
the next line. Terminal content is scrolled up if the sixel is larger
than the screen.

When disabled, sixels *always* start at (0,0), the cursor never moves,
and the terminal content never scrolls.

In other words, the ‘disabled’ mode is a much simpler mode.

All we need to do to support both modes is re-write the sixel-emitting
loop to:

* break early if we’re “out of rows”, i.e. we’ve reached the bottom of
  the screen.
* not linefeed, or move the cursor when scrolling is disabled

This patch also fixes a bug in the (new) implementation of private
mode 8452.

When emitting a sixel, we may break it up into smaller pieces, to
ensure a single sixel (as tracked internally) does not cross the
scrollback wrap-around.

The code that checked if we should do a linefeed or not, would skip
the linefeed on the last row of *each* such sixel piece. The correct
thing to do is to skip it only on the last row of the *last* piece.

I chose not to fix this bug in a separate patch since doing so would
have meant re-writing it again when implementing private mode 80.
2021-02-26 09:28:03 +01:00
Daniel Eklöf
2cb624ee43
grid: grid_free(): free scroll damage list 2021-02-26 09:15:45 +01:00
Daniel Eklöf
54b5ae95c1
url-mode: snapshot screen state when entering URL mode
Previously, we automatically exited URL mode whenever we received data
on the PTY. This was done since we don’t know _what_ has changed on
the screen, and we don’t want to display misleading jump labels.

However, this becomes a problem in curses-like applications that
periodically updates part of the screen. For example, a statusbar with
a clock.

This patch changes this behavior; instead of cancelling URL mode when
receiving PTY data, we snapshot the grid when entering URL mode.

When *rendering*, we use the snapshot:ed grid, while PTY updates
modify the “real” grid.

Snapshot:ing the grid means taking a full/deep copy of the current
grid, including sixel images etc.

Finally, it isn’t necessary to “damage” the entire view
when *entering* URL mode, since we’re at that point the renderer is in
sync with the grid. But we *do* need to damage the entire view when
exiting URL mode, since the grid changes on the “real” grid hasn’t
been tracked by the renderer.
2021-02-26 09:15:45 +01:00