But it *does* include the title bar. This simplifies the 'adjustment'
needed to be done to the configured window size.
It also fixes a number of issues:
* the compositor will now properly snap the window to screen
edges (before, there was an empty space between the edge and the
window - the CSD border).
* This also removes the need for the mutter 'commit' workaround. We
must be doing something right now.
When the seat capabilities change, we used to destroy all pointers and
keyboards, and then re-creating them as necessary.
This caused a crash on mutter - probably because we removed a keyboard
device the compositor had already sent an event for (or was about to).
Now, we only destroy and create devices when it's needed.
The user can now configure the following:
* Whether to prefer CSDs or SSDs. But note that this is only a hint to
the compositor - it may deny our request. Furthermore, not all
compositors implement the decoration manager protocol, meaning CSDs
will be used regardless of the user configuration (GNOME/mutter
being the most prominent one).
* Title bar size and color, including transparency
* Border size and color, including transparency
Also drop support for rendering the CSDs inside the main surface.
Normally, we resize and update the font's DPI whenever our window
enters or leaves an output.
This is since a change in the outputs we're mapped on means the scale
factor to use, or the DPI to use for the fonts may have changed.
However, a special case is when we're removed from the last
output. This should only happen at shutdown, when we're un-mapping
ourselves.
In this case, we typically don't have a access to e.g. the PTMX
fd (often, the reason we're shutting down is because the client
exited). This resulted in (harmless) error messages when emitting the
TIOCSWINSZ event.
Since we're shutting down anyway, we can simply skip the resize and
everything. This gets rid of the error message, and also means we're
shutting down faster.
When resizing the window under mutter, mutter seems to expect a
configure ack *and* a surface commit *right* away, or things get out
of sync.
Unlike kwin, which is requires a commit for each configure ack, but is
fine with having the commit arrive later (after we've rendered it),
mutter is not.
I even tried delaying the configure ack until just before the commit,
but still no go.
So for now, detect when we're running under mutter and always do a
surface commit right away.
This can *not* be done on any other compositor as it breaks the CSD
and main surface synchronization; we've resized the CSDs, but not the
main surface.
I.e. this *should* not work, but for some reason is the *only* way to
make things work on mutter.
Interestingly, doing it any other way on mutter causes visual
glitches; window jumping around when resizing, or de-synchronized
CSDs/main surface.
When we position CSDs inside the main surface, and CSDs are switched
on run-time, we need to force a grid resize, since the actual window
content will now be smaller.
Alternative solution: call resize with width/height increased, to
account for the CSDs. This would increase the window size, but would
keep the grid size fixed.
The size (width, height) arguments provided by the compositor in the
XDG toplevel configure event *include* the surrounding CSDs.
Since our code assumes the size is for the main surface only (and then
positions the sub-surfaces *outside* the main surface), adjust the
provided size when using CSDs.
This ensures our actual window size ends up being what the compositor
wants it to be, and it fixes resize-glitches when resizing using CSDs.
While most compositors handle instantiated but not-yet-mapped
sub-surfaces correctly, e.g. kwin does not.
For example, it will incorrectly offset the main surface both
horizontally and vertically with a couple of pixels, leaving two
transparent areas at the top and left, between the SSDs and the main
surface.
Note that a workaround is to position the sub-surfaces inside the main
surface while they're unmapped. However, since the surfaces may be
larger than the main surface (the CSDs, for examples, always are),
this doesn't quite work since kwin, at least, resizes the window to
include the sub-surfaces, even when unmapped.
So, instead we instantiate all sub-surfaces on demand, when we know
where to position them, and when they should be mapped.
For now, this behavior is controlled with an ifdef. At least kwin
seems very buggy when the decorations are positioned like this (but
normally you'd use server-side decorations with kwin anyway).
This commit also changes 'use_csd' to be a tri-state variable;
when instantiating a window it is set to 'unknown'.
If there's no decoration manager available (e.g. weston), we
immediately set it to 'yes' (use CSDs).
Otherwise, we wait for the decoration manager callback to indicate
whether we should use CSDs or not.
It seems kwin expects a wl_surface_commit() for each
xdg_surface_ack_configure().
We don't want to commit before we've rendered a resized surface. So,
if we *did* change the window size, let the normal rendering path do
the surface commit.
Only when we did *not* change the window size do we need to explicitly
commit the surface in xdg_surface_configure().
Call render_resize_force() when the decoration type changes
run-time. This ensures the CSDs are drawn when changing from server ->
client side decorations, and removed when going the other way.
xdg_output_handle_logical_size() reports _logical_ output size. That
is, it is scaled by the output's scale factor.
We want the _actual_ DPI. The _real_ output size is reported by
output_mode(), so use that instead.
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).
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.
When background alpha is 1.0 (0xffff), i.e. completely opaque, tell
the compositor this, via wl_surface_set_opaque_region().
This allows it to optimize drawing of surfaces _behind_ our window.
wl_display_dispatch() calls poll(), which is unnecessary since we
already know the FD is readable.
Use the more lower level wl_display_read_events() +
wl_display_dispatch_pending().
These require wl_display_prepare_read() to have been called.
The idea is to call wl_display_prepare_read() **before** calling
poll().
Thus, we do this more or less last in wayl_init(), and at the **end**
of the FDM handler.
However, having taking this lock also means we no longer can call
wl_display_roundtrip() directly (it will hang).
So, add a wrapper, wayl_roundtrip(), that cancels the read intent,
does the roundtrip, and then re-acquires the read intent.