view: use midpoint of actual geometry to pick output after layout change

This ensures that maximized/fullscreen/tiled views are restored to the
correct output after it is disconnected and reconnected, including:

- initially maximized/fullscreen xdg-shell views (which have invalid
  natural geometry until after being un-maximized/un-fullscreened)
- views which were maximized/tiled via snap-to-edge on a different
  output than the natural/floating geometry
This commit is contained in:
John Lindgren 2026-01-05 00:14:32 -05:00
parent 227fe601ad
commit 87fee21c04
2 changed files with 25 additions and 13 deletions

View file

@ -7,6 +7,7 @@
#include <wayland-util.h> #include <wayland-util.h>
#include <wlr/util/box.h> #include <wlr/util/box.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#include "common/box.h"
#include "common/edge.h" #include "common/edge.h"
#include "config.h" #include "config.h"
#include "config/types.h" #include "config/types.h"
@ -219,6 +220,14 @@ struct view {
* layout change. False if it was due to user-initiated action. * layout change. False if it was due to user-initiated action.
*/ */
bool adjusted_for_layout_change; bool adjusted_for_layout_change;
/*
* Midpoint of the view's actual geometry prior to adjusting
* for a layout change. For floating views, this will match the
* natural_geometry, but for maximized/tiled/fullscreen views it
* could be on a different output, in which case it is needed to
* restore those views to the correct output.
*/
struct point saved_midpoint;
/* used by xdg-shell views */ /* used by xdg-shell views */
uint32_t pending_configure_serial; uint32_t pending_configure_serial;

View file

@ -446,18 +446,12 @@ view_get_edge_snap_box(struct view *view, struct output *output,
} }
static bool static bool
view_discover_output(struct view *view, struct wlr_box *geometry) view_discover_output(struct view *view, struct point midpoint)
{ {
assert(view); assert(view);
if (!geometry) {
geometry = &view->current;
}
struct output *output = struct output *output =
output_nearest_to(view->server, output_nearest_to(view->server, midpoint.x, midpoint.y);
geometry->x + geometry->width / 2,
geometry->y + geometry->height / 2);
if (output && output != view->output) { if (output && output != view->output) {
view->output = output; view->output = output;
@ -571,7 +565,7 @@ view_moved(struct view *view)
* output when they enter that state. * output when they enter that state.
*/ */
if (view_is_floating(view)) { if (view_is_floating(view)) {
view_discover_output(view, NULL); view_discover_output(view, box_midpoint(&view->current));
} }
view_update_outputs(view); view_update_outputs(view);
ssd_update_geometry(view->ssd); ssd_update_geometry(view->ssd);
@ -1730,9 +1724,7 @@ view_adjust_for_layout_change(struct view *view)
* - Any view without a usable output needs to be repositioned * - Any view without a usable output needs to be repositioned
* - Any fullscreen/tiled/maximized view which was previously * - Any fullscreen/tiled/maximized view which was previously
* handled through this path (i.e. previously lost its output) * handled through this path (i.e. previously lost its output)
* is also checked again. For these, the logic is not quite * should be moved back if the output is reconnected.
* right since the output is chosen based on the natural
* (floating) geometry, but it's better than nothing.
*/ */
if (is_floating || adjusted || !output_is_usable(view->output)) { if (is_floating || adjusted || !output_is_usable(view->output)) {
/* /*
@ -1742,13 +1734,24 @@ view_adjust_for_layout_change(struct view *view)
* the point is to be able to restore to the original * the point is to be able to restore to the original
* location after multiple layout changes (e.g. output * location after multiple layout changes (e.g. output
* disconnected and then reconnected). * disconnected and then reconnected).
*
* Also save midpoint of actual geometry, which is needed
* to restore non-floating views to the correct output.
*/ */
if (!adjusted) { if (!adjusted) {
view_store_natural_geometry(view); view_store_natural_geometry(view);
view->saved_midpoint = box_midpoint(&view->pending);
adjusted = true; adjusted = true;
} }
view_discover_output(view, &view->natural_geometry); /*
* Now try to find the "nearest" output (in terms of
* layout coordinates) to the original position of the
* view. Other strategies might be possible here -- for
* example, trying to match the same physical output the
* view was on previously -- but this is simplest.
*/
view_discover_output(view, view->saved_midpoint);
} }
if (is_floating) { if (is_floating) {