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
If we had a non-empty bottom scroll region, and the window was resized
to a smaller size, the scroll region was not reset correctly.
This led to a crash when scrolling the screen content.
Fix by making sure the scroll region’s endpoint is within range.
When applying scroll damage, we calculate the affected region’s
height (in pixels), by subtracting the number of rows to scroll, from
the scrolling region, and finally multiply by the cell height.
If the number of rows to scroll is very large, the subtraction may
underflow, resulting in a very large height value instead of a
negative one.
This caused the check for "scrolling too many lines" to fail. That in
turn resulted in an integer overflow when calculating the source
offset into the rendered surface buffer, which typically triggered a
segfault.
This bug happened when there was continuous output in the terminal
without any new frames being rendered. This caused a buildup of scroll
damage, that triggered the underflow+overflow when we finally did
render a new frame.
For example, a compositor that doesn’t send any frame callbacks (for
example because the terminal window is minimized, or on a different
workspace/tag) would cause this.
Closes#1305
The selection coordinates are in absolute row numbers. As such,
selection breaks when interactively resizing the normal grid, since we
then instantiate a temporary grid mapping directly to the current
viewport (for performance reason, to avoid reflowing the entire grid
over and over again).
Fix by stashing the actual selection coordinates, and ajusting the
"active" ones to the temporary grid.
No need to check if terminal colors have been reversed - this is done
by the cell rendering logic.
This hopefully fixes all remaining issues with invisible text when
background alpha < 1.0
If cells overflowed (for example, by using an italic font that isn’t
truly monospaced) into a double-width glyph (that itself is *not*
overflowing), then the double-width glyph would glitch when being
rendered; typically the second half of it would occasionally
disappear.
This happened because we tried to rasterize the second cell of the
double-width glyph. This cell contains a special “spacer”
value. Rasterizing that typically results the font’s “not available”
glyph.
If _that_ glyph overflows, things broke; we’d later end up forcing a
re-render of it (thus erasing half the double-width glyph). But since
the double-width glyph _itself_ doesn’t overflow, _it_ wouldn’t be
re-rendered, leaving it half erased.
Fix by recognizing spacer cells, and not trying to rasterize them (set
glyph count to 0, and cell count to 1).
Closes#1256
When drawing a block cursor using inversed fg/bg colors, we didn’t
strip the alpha from the background color. This meant that the text
"behind" the cursor was rendered with transparency. If alpha was set
to 0, the text was completely invisible.
We should never apply alpha to the text color. So, detect this, and
force alpha to 1.0.
Normally, when selecting the cursor’s color, we don’t really know
_where_ the background color is coming from (or more accurately,
_what_ it is).
However, the *only* background color that can have a non-1.0 alpha is
the *default* background color.
This is why we can ignore the bg parameter, and use term->colors.fg/bg
instead.
Closes#1205
Re-initialize the temporary ‘normal’ grid instance each time we
receive a configure event while doing an interactive resize.
This way, window content will not be "erased" when the window is first
made smaller, then larger again.
And, if the viewport is up in the scrollback history, increasing the
window size will reveal more of the scrollback, instead of just being
black.
The last issue is the cursor; it’s currently not "stuck" where it
should be. Instead, it follows the window around. This is due to two
things:
1) the temporary grid we create is large enough to contain the current
viewport, but not more than that. That means we can’t "scroll up", to
hide the cursor.
2) grid_resize_without_reflow() doesn’t know anything about
"interactive resizing". As such, it will ensure the cursor is bound
to the new grid dimensions.
I don’t yet have a solution for this. This patch implements a
workaround to at least reduce the impact, by simply hiding the cursor
while we’re doing an interactive resize.
But also, more importantly, logical fixes:
* Stash the number of new scrollback lines the stashed ‘normal’ grid
should be resized *to*.
There’s also a couple of performance changes here:
* When doing a delayed reflow (tiocswinsz timer), call
sixel_reflow_grid(term, &term->normal) - there’s no need to reflow
sixels in the ‘alt’ screen.
* When doing a delayed reflow, free all scroll damage. It’s not
needed, since we’re damaging the entire window anyway.
* Use minimum size for the temporary ‘normal’ grid (that contains the
current viewport). We just need it to be large enough to fit the
current viewport, and be a valid grid row count (power of 2). This
just so happens to be the current ‘alt’ grid’s row count...
Instead of copying the entire grid when an interactive resize is
started, stash the complete grid (to be used in the final reflow).
Copy the current viewport only, to be used during the interactive
resize.
This gets rid of the initial "pause" when snapshotting the grid when
an interactive resize is started.
Reflowing a large scrollback is *slow*. During an interactive resize,
it can easily take long enough that the compositor fills the Wayland
socket with configure events. Eventually, the socket becomes full and
the compositor terminates the connection, causing foot to exit.
This patch is work-in-progress, and the first step towards alleviating
this.
It delays the reflow by:
* Snapshotting (copying) the original grid when an interactive resize
is started.
* While resizing, we apply a simple truncation resize of the
grid (like we handle the alt screen).
* When the resize is done, or paused for ‘resize-delay-ms’, the grid
is reflowed.
TODO: we *must* not allow any changes to the temporary (truncated)
grid during the resize. Any changes to the grid would be lost when the
final reflow is applied. That is, we must completely pause the ptmx
pipe while a resize is in progress.
Future improvements:
The initial copy can be slow. We should be able to avoid it by
rewriting the reflow algorithm to not free anything. This is
complicated by the fact that some resources (e.g. sixel images) are
currently *moved* to the new grid. They’d instead have to be copied.
When rendering the overlay for scrollback search, the logic assumed
buffer re-use. On some compositors this isn’t happening (on
e.g. KDE/plasma we’re forced to double buffer).
This resulted in matches not being highlighted correctly.
The problem is in how we calculated the region for which areas to
clear ("un-dim"). It uses the "previous frame’s see-through area"
minus the current frame’s see-through area.
However, when we’ve detected that the current buffer isn’t the same as
the last one, we set the last frame’s see-through region to "the
entire buffer". Thus, when calculating the diff, we end up with an
empty region, and nothing is highlighted.
Fix by simply using the current frame’s see-through region as-is when
we’ve detected we’re not re-using the last frame’s buffer.
On compositors that forces us to double buffer, we need to re-apply
the last frame’s damage to the current frame (which uses the buffer
from the next-to-last frame).
General cell updates are handled by simply copying from the last
frame’s pixman buffer to the current frame’s.
In an attempt to improve performance, scroll damage were up until now
handled by re-playing the last frame’s scroll damage (on the current
frame’s buffer). This does not work, and resulted in glitches when
scrolling in the scrollback.
This patch does the following:
* grid_render_scroll{,_reverse}() now update the buffer’s "dirty"
region. This means the generic copy-old-frames-buffer handles the
scroll damage (albeit in, potentially, a less efficient way).
* Tracking of, and re-applying old scroll damage is completely
removed.
Closes#1173
This adds an "underline-thickness" setting to the "main" section,
similar to the existing "underline-offset" setting. This setting is used
to specify a custom height for regular (= non-cursor) underlines.
Fixes#1136
Replace the seat->ime.focused boolean with a terminal instace pointer,
seat->ime_focus.
Set and reset this on ime::enter() and ime::leave() events, and use
this instead of seat->kbd_focus on all other IME events.
This fixes two issues:
a) buggy compositors that sometimes sends an IME enter event without
first having sent a keyboard enter event.
b) seats may be IME capable while still lacking the keyboard
capability. Such seats will *always* see IME enter events without a
corresponding keyboard enter event.
Our CSDs, the search-box and URL labels are all implemented using
sub-surfaces, synchronized with the main grid.
This means we *must* commit the main surface as well, when updating
one of these sub-surfaces.
The logic for doing so in the frame callback was flawed, and only
triggered when the main grid was actually dirty.
That is, e.g. search box updates that did not also resulted in grid
updates (for example - pasting a search criteria that doesn’t match),
did not result in a UI refresh.
Closes#1040
The global config doesn’t necessarily reflect the correct
configuration to use - we should *always* use the current terminal
instance’s conf pointer.
* Move selection override modifier mask to the key_binding_set struct
* Always warn if XDG activation is unavailable, not just if
bell.urgent is set (we no longer have access to this information)
* Pass ‘bool presentation_timings’ as a parameter to wayl_init()
* Remove ‘presentation_timings’ member from the ‘terminal’ struct
Closes#932
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.
Search mode and ‘flash’ (OSC-555) both achieves similar visual
effects: flash tints the entire window yellow, and search mode dims
it (except the search match).
But, they do so in completely different ways. Search mode is detected
in render_cell(), and the colors are then dimmed there.
Flash is implemented by blending a yellow, semi-transparent color on
top of the rendered grid.
This patch replaces those two implementations with a single one. We
add a new sub-surface, called the ‘overlay’. In normal mode, it’s
unmapped.
When either search mode, or flash, is enabled, we enable it, and
fill it with a semi-transparent color. Yellow for ‘flash’, and
“black” (i.e. no color) for search mode.
The compositor then blends it with the grid. Hopefully on the GPU,
meaning it’ll be faster than if we blend in software.
There are more performance benefits however. By using a separate
surface, we can do much better damage tracking.
The normal grid rendering code no longer have to care about neither
search mode, nor flash. Thus, we get rid of a couple of ‘if’
statements in render_cell(), which is nice. But more importantly, we
can drop full grid repaints in a couple of circumstances:
* Entering/exiting search mode
* Every frame while flash is active
Now, when rendering the search mode overlay, we do want to do some
damage tracking, also of the overlay.
This, since search mode doesn’t dim the *entire* window. The search
match is *not* dimmed. This is implemented by punching a hole in the
overlay sub-surface. That is, we make part of it *fully*
transparent. The basic idea is to set a clip region that excludes the
search match, and then dim the rest of the overlay.
It’s slightly more complicated than that however, if we want to reuse
the last frame’s overlay buffer (i.e we don’t want to re-render
the *entire* overlay every frame).
In short, we need to:
* Clear (punch hole) in areas that are part of this frame’s search
match, but not the last frame’s (since those parts are _already_
cleared).
* Dim the areas that were part of the last frame’s search match, but
aren’t anymore (the rest of the overlay should already be dimmed).
To do this, we save the last frame’s “holes” (as a pixman
region). Then, when rendering the next frame, we first calculate the
new frame’s “holes” region.
The region to clear is “this frame’s holes minus last frame’s holes”
The region to dim is “last frame’s holes minus this frames holes”.
Finally, we compute the bounding box of all modified cells by taking
the union of the two diff regions mentioned above. This allows us to
limit the buffer damage sent to the compositor.
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.
The underline cursor is positioned just below regular underlines. A
bug in the positioning logic related to this, sometimes resulted in
the cursor being thinner than what it should be, or even invisible.
Fixes#1005
This fixes a compilation error on FreeBSD:
../../foot/render.c:2055:45: error: no member named 'epoll_shim_close' in 'struct config::(anonymous at ../../foot/config.h:254:9)'
conf_color = &term->conf->csd.color.close;
~~~~~~~~~~~~~~~~~~~~~ ^
/usr/local/include/libepoll-shim/epoll-shim/detail/common.h:8:15:
note: expanded from macro 'close': #define close epoll_shim_close
Before this patch, wl_cursor_theme_get_cursor() was called in the FDM
hook, just before we’re about to update the mouse cursor “for real”.
This relies on seat->pointer.xcursor still being valid. This is true
as long as we’re only using our compiled-in static xcursor names, but
not otherwise.
Now, we call wl_cursor_theme_get_cursor() in render_xcursor_set(). At
this point, we *know* seat->pointer.xcursor is valid.
There is a slight chance of added overhead here, if the client
application is switching mouse grabbing on/off rapidly. Before, the
calls to wl_cursor_theme_get_cursor() would automatically be
throttled.
However, the main point of delaying the actual pointer update to the FDM
hook is to throttle the *Wayland* calls. And this is still happening:
wl_cursor_theme_get_cursor() is client-side only.
This fixes a crash caused by the selection’s pivot point not being
translated during reflow.
While we could simply reflow the pivot point as well, testing shows
irregular behavior with ongoing selections across window resizes, with
different compositors behaving differently.
For now, we simply finalize the selection, instead of trying to handle
ongoing selections.
Closes#922
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
POSIX.1-2008 has marked gettimeofday(2) as obsolete, recommending the
use of clock_gettime(2) instead.
CLOCK_MONOTONIC has been used instead of CLOCK_REALTIME because it is
unaffected by manual changes in the system clock. This makes it better
for our purposes, namely, measuring the difference between two points in
time.
tv_sec has been casted to long in most places since POSIX does not
define the actual type of time_t.