Make both the server listening socket and the connecting client
sockets non-blocking.
Then, when reading the initial length of the setup packet, handle read
errors from partial reads separately.
Assume the client writes all four bytes of the 'length' field at once,
and bail out if we are unable to read those 4 bytes.
Limit the maximum setup packet size to 128K. This is to prevent
clients from pretending the setup packet is insanely large, causing us
to fail to malloc.
This means that the read handler now has to keep reading until we get
EAGAIN.
But it also means we don't have to flip the EPOLLOUT flag in the FDM
handler back and forth when writing to the PTY.
This fixes an issue where a "forced" selection (shift being pressed
while slave is tracking mouse events) would not finalize if the user
released shift *before* releasing the mouse button.
Previously when updating a selection, we would unmark *all* cells in
the old selection, and then mark all cells in the new selection.
This caused *all* cells to be dirtied and thus re-rendered.
Avoid this, by adding a temporary state to the cells' selected state.
Before unmarking the old selection, pre-mark the new selection using a
temporary state.
When unmarking the old selection, ignore cells in this temporary state.
When marking the new selection, ignore cells in this temporary
state (except clearing the temporary state).
Maintain a view 'offset' (which glyph from the search string to start
rendering at).
This defines the start of the viewable area. The end is the offset +
the search box size (which is limited to the window size).
Adjust this offset whenever the cursor moves outside the viewable
area. For now, this is always done in the same way: set the offset to
the cursor position.
This means that when we're entering text at the end of the search
criteria (i.e. the normal case; we're simply typing), and the search
box reaches the window size, the cursor will jump to the start of the
search box, which will be empty. This could be confusing, but let's go
with for now.
That is, only call it if we have POLLIN. If not, then we never read
any events and we still hold the read lock and thus we shouldn't try
to acquire it again.
This way, we don't have to manually insert flushes in code paths that
may execute outside of a wl_display_dispatch_pending().
(Those that execute inside a wl_display_dispatch_pending() are subject
to the flush performed at the end of the normal wayland FDM handler).
There are now three hook priorities: low, normal, high.
High priority hooks are executed *first*. Normal next, and last the
low priority hooks.
The renderer's terminal refresh hook now runs as a normal priority
hook.
With a bad behaving client (e.g. 'less' with mouse support enabled),
we can end up with a *lot* of xcursor updates (so much, that we
flooded the wayland socket before we implemented a blocking
wayl_flush()).
Since there's little point in updating the cursor more than once per
frame interval, use frame callbacks to throttle the updates.
This works more or lesslike normal terminal refreshes:
render_xcursor_set() stores the last terminal (window) that had (and
updated) the cursor.
The renderer's FDM hook checks if we have such a pending terminal set,
and if so, tries to refresh the cursor.
This is done by first checking if we're already waiting for a callback
from a previous cursor update, and if so we do nothing; the callback
will update the cursor for the next frame. If we're *not* already
waiting for a callback, we update the cursor immediately.
Since it doesn't block, we need to detect EAGAIN failures and ensure
we actually flush everything.
If we don't, we sooner or later end up in a wayland client library
call that aborts due to the socket buffer being full.
Ideally, we'd simply enable POLLOUT in the FDM. However, we cannot
write *anything* to the wayland socket until we've actually managed to
send everything. This means enabling POLLOUT in the FDM wont work
since we may (*will*) end up trying to write more data to it before
we've flushed it.
So, add a wrapper function, wayl_flush(), that acts as a blocking
variant of wl_display_flush(), by detecting EAGAIN failiures and
calling poll() itself, on the wayland socket only, until all data has
been sent.
In some cases, we end up calling render_refresh() multiple times in
the same FDM iteration. This means will render the first update
immediately, and then set the 'pending' flag, causing the updated
content to be rendered in the next frame.
This can cause flicker, or flashes, since we're presenting one or more
intermediate frames until the final content is shown.
Not to mention that it is inefficient to render multiple frames like
this.
Fix by:
* render_refresh() only sets a flag in the terminal
* install an FDM hook; this hook loops all terminals and executes what
render_refresh() _used_ to do (that is, render immediately if we're
not waiting for a frame callback, otherwise set 'pending' flag). for
all terminals that have the 'refresh_needed' flag set.