diff --git a/include/view.h b/include/view.h index d2ff5ddf..9c5cd7dc 100644 --- a/include/view.h +++ b/include/view.h @@ -215,11 +215,14 @@ struct view { struct wlr_box natural_geometry; /* * Whenever an output layout change triggers a view relocation, the - * last pending position (or natural geometry) will be saved so the - * view may be restored to its original location on a subsequent layout - * change. + * last pending position will be saved so the view may be restored + * to its original location on a subsequent layout change. */ struct wlr_box last_layout_geometry; + /* Set temporarily when moving view due to layout change */ + bool adjusting_for_layout_change; + /* True if original output was disconnected or disabled */ + bool lost_output_due_to_layout_change; /* used by xdg-shell views */ uint32_t pending_configure_serial; @@ -533,7 +536,6 @@ bool view_titlebar_visible(struct view *view); void view_set_ssd_mode(struct view *view, enum lab_ssd_mode mode); void view_set_decorations(struct view *view, enum lab_ssd_mode mode, bool force_ssd); void view_toggle_fullscreen(struct view *view); -void view_invalidate_last_layout_geometry(struct view *view); void view_adjust_for_layout_change(struct view *view); void view_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_windows); void view_grow_to_edge(struct view *view, enum lab_edge direction); diff --git a/src/interactive.c b/src/interactive.c index 4bbecb9a..339f974f 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -92,9 +92,6 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) /* Store natural geometry at start of move */ view_store_natural_geometry(view); - if (view_is_floating(view)) { - view_invalidate_last_layout_geometry(view); - } /* Prevent region snapping when just moving via A-Left mousebind */ seat->region_prevent_snap = keyboard_get_all_modifiers(seat); @@ -111,12 +108,6 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) return; } - /* - * Resizing overrides any attempt to restore window - * geometries altered by layout changes. - */ - view_invalidate_last_layout_geometry(view); - /* * If tiled or maximized in only one direction, reset * tiled state and un-maximize the relevant axes, but diff --git a/src/view.c b/src/view.c index 20f3bbdc..7ddfc081 100644 --- a/src/view.c +++ b/src/view.c @@ -588,6 +588,21 @@ view_move_resize(struct view *view, struct wlr_box geo) if (view->impl->configure) { view->impl->configure(view, geo); } + + /* + * If the move/resize was user-initiated (rather than due to + * output layout change), then invalidate the saved geometry. + * + * TODO: consider also updating view->output here for floating + * views (based on view->pending) rather than waiting until + * view_moved(). This might eliminate some race conditions with + * view_adjust_for_layout_change(), which uses view->pending. + * Not sure if it might have other side-effects though. + */ + if (!view->adjusting_for_layout_change) { + view->last_layout_geometry = (struct wlr_box){0}; + view->lost_output_due_to_layout_change = false; + } } void @@ -1405,9 +1420,6 @@ view_maximize(struct view *view, enum view_axis axis) * a maximized view. */ interactive_cancel(view); - if (store_natural_geometry && view_is_floating(view)) { - view_invalidate_last_layout_geometry(view); - } } /* @@ -1695,7 +1707,6 @@ view_set_fullscreen(struct view *view, bool fullscreen) */ interactive_cancel(view); view_store_natural_geometry(view); - view_invalidate_last_layout_geometry(view); } set_fullscreen(view, fullscreen); @@ -1713,139 +1724,62 @@ view_set_fullscreen(struct view *view, bool fullscreen) cursor_update_focus(view->server); } -static bool -last_layout_geometry_is_valid(struct view *view) -{ - return view->last_layout_geometry.width > 0 - && view->last_layout_geometry.height > 0; -} - -static void -update_last_layout_geometry(struct view *view) -{ - /* - * Only update an invalid last-layout geometry to prevent a series of - * successive layout changes from continually replacing the "preferred" - * location with whatever location the view currently holds. The - * "preferred" location should be whatever state was set by user - * interaction, not automatic responses to layout changes. - */ - if (last_layout_geometry_is_valid(view)) { - return; - } - - if (view_is_floating(view)) { - view->last_layout_geometry = view->pending; - } else if (!wlr_box_empty(&view->natural_geometry)) { - view->last_layout_geometry = view->natural_geometry; - } else { - /* e.g. initially-maximized window */ - view->last_layout_geometry = - view_get_fallback_natural_geometry(view); - } -} - -static bool -apply_last_layout_geometry(struct view *view, bool force_update) -{ - /* Only apply a valid last-layout geometry */ - if (!last_layout_geometry_is_valid(view)) { - return false; - } - - /* - * Unless forced, the last-layout geometry is only applied - * when the relevant view geometry is distinct. - */ - if (!force_update) { - struct wlr_box *relevant = view_is_floating(view) ? - &view->pending : &view->natural_geometry; - - if (wlr_box_equal(relevant, &view->last_layout_geometry)) { - return false; - } - } - - view->natural_geometry = view->last_layout_geometry; - adjust_floating_geometry(view, &view->natural_geometry, - /* midpoint_visibility */ true); - return true; -} - -void -view_invalidate_last_layout_geometry(struct view *view) -{ - assert(view); - view->last_layout_geometry.width = 0; - view->last_layout_geometry.height = 0; -} - void view_adjust_for_layout_change(struct view *view) { assert(view); bool is_floating = view_is_floating(view); - bool use_natural = false; + view->adjusting_for_layout_change = true; if (!output_is_usable(view->output)) { - /* A view losing an output should have a last-layout geometry */ - update_last_layout_geometry(view); - } - - /* Capture a pointer to the last-layout geometry (only if valid) */ - struct wlr_box *last_geometry = NULL; - if (last_layout_geometry_is_valid(view)) { - last_geometry = &view->last_layout_geometry; + view->lost_output_due_to_layout_change = true; } + /* + * Save the view's geometry if this is the first layout change + * since a user-initiated move/resize. Do not save it again for + * subsequent layout changes, since the point is to be able to + * restore to the original location after multiple changes + * (e.g. output disconnected and then reconnected). + * + * Note that it's important to do this even if an output change + * is not (yet) required, to properly handle cases of multiple + * outputs being disconnected/reconnected in any order. In that + * case, there can be multiple layout change events, and a view + * can be moved first and only later lose its own output. + */ + if (wlr_box_empty(&view->last_layout_geometry)) { + view->last_layout_geometry = view->pending; + } /* * Check if an output change is required: * - Floating views are always mapped to the nearest output - * - Any view without a usable output needs to be repositioned - * - Any view with a valid last-layout geometry might be better - * positioned on another output + * - Any fullscreen/tiled/maximized view which lost its output + * due to a layout change should be moved back if the output + * is reconnected. + * + * If so, try to find the "nearest" output (in terms of layout + * coordinates) to the saved geometry. Other strategies might be + * possible here -- for example, trying to match the same physical + * output the view was on previously -- but this is simplest. */ - if (is_floating || last_geometry || !output_is_usable(view->output)) { - /* Move the view to an appropriate output, if needed */ - bool output_changed = view_discover_output(view, last_geometry); - - /* - * Try to apply the last-layout to the natural geometry - * (adjusting to ensure that it fits on the screen). This is - * forced if the output has changed, but will be done - * opportunistically even on the same output if the last-layout - * geometry is different from the view's governing geometry. - */ - if (apply_last_layout_geometry(view, output_changed)) { - use_natural = true; - } - - /* - * Whether or not the view has moved, the layout has changed. - * Ensure that the view now has a valid last-layout geometry. - */ - update_last_layout_geometry(view); + if (is_floating || view->lost_output_due_to_layout_change) { + view_discover_output(view, &view->last_layout_geometry); } if (!is_floating) { view_apply_special_geometry(view); - } else if (use_natural) { - /* - * Move the window to its natural location, because - * we are trying to restore a prior layout. - */ - view_apply_natural_geometry(view); } else { - /* Otherwise, just ensure the view is on screen. */ - struct wlr_box geometry = view->pending; - if (adjust_floating_geometry(view, &geometry, - /* midpoint_visibility */ true)) { - view_move_resize(view, geometry); - } + /* Restore saved geometry, ensuring view is on-screen */ + struct wlr_box geometry = view->last_layout_geometry; + adjust_floating_geometry(view, &geometry, + /* midpoint_visibility */ true); + view_move_resize(view, geometry); } view_update_outputs(view); + view->adjusting_for_layout_change = false; } void @@ -2134,7 +2068,6 @@ view_snap_to_edge(struct view *view, enum lab_edge edge, } else if (store_natural_geometry) { /* store current geometry as new natural_geometry */ view_store_natural_geometry(view); - view_invalidate_last_layout_geometry(view); } view_set_untiled(view); view_set_output(view, output); @@ -2168,7 +2101,6 @@ view_snap_to_region(struct view *view, struct region *region) } else if (store_natural_geometry) { /* store current geometry as new natural_geometry */ view_store_natural_geometry(view); - view_invalidate_last_layout_geometry(view); } view_set_untiled(view); view->tiled_region = region; @@ -2181,7 +2113,6 @@ view_move_to_output(struct view *view, struct output *output) { assert(view); - view_invalidate_last_layout_geometry(view); view_set_output(view, output); if (view_is_floating(view)) { struct wlr_box output_area = output_usable_area_in_layout_coords(output);