Add interactive_set_grab_context() which is called when the mouse button
is first pressed, before interactive_begin(). This fixes two small issues:
- The cursor origin position for interactive move/resize was slightly
off (depending on mouse resolution), because it was set after the
mouse had already moved slightly. Now it's exact.
- If app- or keybind-initiated maximize (etc.) happened after the button
press but before the mouse was moved, then interactive_begin() would
still start move/resize even though the view might now be far away
from the cursor. Now interactive_cancel() works as expected, even if
called before interactive_begin().
Also, make sure to call interactive_cancel() for un-maximize as well.
- check for valid scene_output in output_is_usable()
- change many "output != NULL" checks to use output_is_usable()
- remove one now-redundant separate check for valid scene_output
Fixes a crash at startup (with autoEnableOutputs=no) due to
dereferencing null scene_output in create_output_config() since:
7d7ece21d9
("output: suppress error when output position is unavailable")
Fixes: #3357
This mitigates a race where the XWayland server may generate an unwanted
FocusOut event for the newly activated window, if it receives mouse/
pointer events over the parallel wayland connection first.
In particular, this fixes an issue with certain fullscreen applications
(such as Minecraft) that self-minimize when receiving FocusOut.
Also limit a previous similar workaround to apply only to XWayland views.
Fixes: #3344
See also: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/4044
f58b532 implemented output-relative position saving/restoring on output
un-plugging/re-plugging. It worked as follows:
1. Store the output-relative view geometry in `view->last_placement`
(if not set) before adding/removing an output from the layout.
2. After adding/removing an output, call `view_adjust_for_layout_change()`
after the layout change to restore the output-relative view geometry
based on `view->last_placement`.
However, it didn't consider `view_adjust_for_layout_change()` being
called from other places such as `regions_reconfigure()` and
`output_update_all_usable_areas()`, causing an error message "view has
no last placement info". This can happen when a panel is mapped or
unmapped, or on Reconfigure.
This commit fixes it by changing the life cycle of
`view->last_placement`. It used to be set only before output layout
changes and cleared on user-initiated moves/resizes, but now it is set
and updated on user-initiated moves/resizes. I think this is more
intuitive, too.
Currently, with enough dexterity, you can minimize a window via hotkey
with one hand while continuing to move it (now invisible) with the
mouse in your other hand. The same can probably happen with XWayland
windows that self-unmap at an inconvenient time.
Probably not a common occurrence, but it's trivial to handle.
This commit does not change any behaviors.
We don't need to call `desktop_update_top_layer_visibility()` in
`view_discover_output()` since it's called in `view_update_outputs()`.
Before this commit, the window switcher skipped all the child windows.
However, as child windows not marked as modal dialogs can lose focus
(ref. `desktop_focus_view()`), it will make sense to include them in the
window switcher so that users can refocus them with keyboard.
This behavior follows KWin.
Currently, every click within a sub-view results in first restacking
the parent view in front, and then the sub-view. This is unnecessary
and has caused issues in the past, such as with Xaw popups (which
we've worked around in d748dc78bc by adding an xcb_flush()).
It would be better not to do the unnecessary restacking at all.
When unminimizing a group of N parent/child views, we currently end up
triggering N focus changes (as well as O(N^2) surface restackings) due
to calling desktop_focus_view() for each view in turn.
Since desktop_focus_view() already raises all sibling views together
via view_move_to_front(), let's make view_minimize() call it only once
at the very end, once all views are visible.
Test cases:
- Audacious with floating plugin views (XWayland)
- xfce4-terminal with About dialog (xdg-shell)
v2: also avoid repeated focus changes when minimizing
Before this patch, we always tried to preserve the global positions of
floating windows across layout changes, which meant that windows could
jump to different outputs if the output coordinates changed.
Instead, this patch adds the output name and output-relative position in
`view->last_placement` to keep them across layout changes, like KDE and
GNOME do.
This also allows us to remove `view->lost_output_due_to_layout_change`,
which was required to keep the output of fullscreen/maximized windows,
since we now always try to keep `view->output` whether or not the window
is floating.
I will add output name and relative view position in
`view->last_placement` later, which needs to be saved before layout
changes unlike the global position.
After several iterations, this is basically a complete re-work. The old
implementation was difficult to follow and sometimes failed to restore
fullscreen/maximized/tiled geometry correctly, since it was based
entirely on natural (floating) geometry.
The new implementation:
- always saves the actual (pending) geometry at first layout change
- explicitly tracks whether a view has moved between outputs
- consolidates invalidating the saved geometry into one place, rather
than having lots of invalidate() calls sprinkled everywhere
These were added to fix handling of natural geometry for snap-to-edge
behavior back in 9021020f6e and seemed like a good idea at the time.
Since then, the number of call sites has exploded, so it seems more
maintainable to put explicit checks for interactive move within the
three functions affected.
This allows changing the cycled order in the future, e.g. focused order vs
created order.
Functionally, this commit also changes the initially selected window;
before this commit, the previous/next of the topmost window was always
selected, but now the previous/next of the active window is selected first
if it is in the cycled list. This won't change behaviors for most users,
but this ensures that the user can go back to the focused window with
Alt-Tab + Alt-Shift-Tab even when it is not the topmost window.
This commit fixes the TODO in the previous commit by trying to preserve
the selected view when a view is destroyed during window cycling.
This commit clarifies the lifecycle of the window switcher (cycle) by:
- init_cycle(): initializes the window switcher (e.g. OSD).
- update_cycle(): updates the window switcher states including OSD,
preview and outlines.
- destroy_cycle(): clears all the window switcher states.
This commit temporarily regresses by not trying to preserve the selected
view when a view is destroyed. This will be addressed in the next commit.
We were using the word "osd" to describe the window switcher, but it can
be used with on-screen display (OSD) disabled by
`<windowSwitcher><osd show="false">`. Let's use "cycle" instead to avoid
confusion.
We were only using it to allow quick bitset comparisons of sets of
outputs (such as view->outputs). We can maintain our own bit IDs for
this purpose and avoid using the private wlroots field.
Note: from my reading of wlr_scene_output_create(), it appears to
always take the lowest unused index, resulting in aggressive re-use of
index values when outputs are disconnected and reconnected. I've tried
to make re-use as infrequent as possible. This could theoretically
reduce the chance of a mix-up in view_update_outputs(), although I'm
not aware of any practical scenario where it matters.
v2: prevent adding more than 64 outputs
Map/unmap logic is currently re-used for minimize/unminimize, but lots
of it doesn't actually apply in that case. This is both confusing and
creates some extra complexity, such as:
- extra "client_request" parameter to unmap(), in which case it has to
still do some cleanup even if view->mapped is already false
- various "view->mapped || view->minimized" checks when we really just
mean "is the view mapped"
To clean this all up, let's put the logic that really is common into
a new view_update_visiblity() function, and stop using map/unmap for
minimize/unminimize.
Note that this changes the meaning of "view->mapped", which used to
mean "mapped and not minimized" but now really just means "mapped".
I left some "view->mapped" conditions as-is (rather than changing to
"view->mapped && !view->minimized") where it seemed to make sense.
v2: add view_update_visibility() as suggested by tokyo4j
The previous "minimal fix" (5148c2aa31) worked but was a bit of a
hack, as it basically un-minimized and then immediately minimized the
view again at map. It's not actually too difficult to make the map
handlers aware of minimized views, eliminating the need for the hack.
Note: this depends on the previous commit ("xwayland: connect commit
and surface_destroy handlers together") otherwise the xwayland map
handler registers the commit handler twice, leading to a crash.
Factor out set_surface() which consolidates connecting/disconnecting
the wlr_surface event listeners in one place.
In theory, this means we can receive commit events for minimized views.
However, with a test app that resizes itself, I didn't see any change,
i.e. the commits still don't come through until un-minimize. It's
possible they are being filtered at wlroots or protocol level.
Also remove an old, semi-related TODO from view.c.
It's possible for a fullscreen xwayland view to be unmapped without
being destroyed. In this case, we need to update top layer visibility,
otherwise panels and the like will remain hidden.
Since unmap is always called before destroy, it's sufficient to do the
update only in view_impl_unmap() and not in view_destroy().
Adaptive sync logic needs work still, but I tried to minimize changes
to it since I don't have hardware to test it.
In 2ac4811, I was missing that windows can be tiled to "center".
As a result, after executing
`<action name="SnapToEdge" combined="yes" direction="left" />` against a
center-tiled window, `view->tiled` is set to `CENTER|LEFT`.
This patch adds `combine` argument to (Toggle)SnapToEdge actions.
This allows to snap a window to e.g. up-left by running two actions:
- `<action name="SnapToEdge" direction="left" combine="yes" />`
- `<action name="SnapToEdge" direction="up" combine="yes" />`
Then running `<action name="SnapToEdge" direction="down" combine="yes" />`
snaps it to left again. This behavior is almost the same as KWin, except
that snapping a up-right-tiled window to right doesn't move it to the
right-adjacent output, but makes it right-tiled first.
Before this commit, <else> branch was always executed with
monitor="current", monitor="left" or monitor="right" queries.
For example:
<action name="If">
<query monitor="current" />
<then>
<action />
</then>
<else>
<action />
</else>
</action>
view_restore_to() (which is just set_maximized() + view_move_resize())
hasn't aged well and doesn't line up with typical usage anymore:
- it's missing view_set_untiled(), which has to be called separately
- it always forces view_move_resize() even when that's not needed
- it doesn't allow un-maximizing only one axis (see next commit)
- the fullscreen check is unnecessary (already checked in callers)
Eliminate it and just expose view_set_maximized() instead.
No functional change intended in this commit.
`update_last_layout_geometry()` stores `view->natural_geometry` in
`view->last_layout_geometry`, but it's empty for initially-maximized
windows, so their positions were not restored after outputs are
unplugged and plugged (also when VT switching in wlroots 0.19.0).
This commit sets the fallback natural geometry (at the center of the
output) so that initially-maximized windows reappears in the same output.
Now it's not very reasonable to determine the default window width based
on the titlebar geometry, as the titlebar can be shrunk to 1px.
Let's use the fixed value of 100px for simplification.