]> git.mdlowis.com Git - proto/labwc.git/commitdiff
Adjust views to account for output layout changes
authorJohn Lindgren <john@jlindgren.net>
Fri, 31 Dec 2021 22:30:55 +0000 (17:30 -0500)
committerJohan Malm <johanmalm@users.noreply.github.com>
Sat, 1 Jan 2022 16:20:13 +0000 (16:20 +0000)
labwc currently doesn't handle output layout changes very well:

 - Windows can end up "lost" completely offscreen
 - Maximized/fullscreen windows can end up spanning multiple outputs

Currently, new_output_notify() and output_destroy_notify() contain logic
to update the cursor and force a repaint when outputs are added or
removed.  This logic in fact needs to run on any output layout change,
so consolidate it into a new function, output_update_for_layout_change().

Then add a second new function, view_adjust_for_layout_change(), which
adjusts window placement to account for the new layout.

The behavior is roughly as follows:

 - Normal windows that end up offscreen are centered on the closest
   output (making use of the existing view_center() logic)
 - Maximized windows are re-maximized on the closest output.  Logic is
   also added to the unmaximize step to check that the original
   unmaximized position is still on-screen.
 - Fullscreen windows are re-fullscreened on the same output if
   possible; otherwise they are un-fullscreened.

Minimized windows don't require any special handling.  Their placement
is adjusted just the same, but invisible to the user until they are
later unminimized.

There is some positioning glitch still with un-fullscreening a window
whose output has been disconnected/disabled; it can end up in an
unexpected position (but at least has the correct size and decoration).
I don't think this is due to a bug in my change per se, but perhaps the
change has exposed a bug elsewhere.

Fixes: #177
include/labwc.h
src/output.c
src/view.c

index 78e1b8471f0804319ff3e0c0267210bc0ab3891d..313850401ae6ecdebb264c94ace75d6e8e4b3782 100644 (file)
@@ -372,6 +372,7 @@ void view_toggle_maximize(struct view *view);
 void view_toggle_decorations(struct view *view);
 void view_set_decorations(struct view *view, bool decorations);
 void view_toggle_fullscreen(struct view *view);
+void view_adjust_for_layout_change(struct view *view);
 void view_for_each_surface(struct view *view,
        wlr_surface_iterator_func_t iterator, void *user_data);
 void view_for_each_popup_surface(struct view *view,
index 3f97402c881eb9029dd3e2586695dec3e2b7e1f8..5be2203827d989587f5976bcfd286d9001b97251 100644 (file)
@@ -940,9 +940,6 @@ output_destroy_notify(struct wl_listener *listener, void *data)
        struct output *output = wl_container_of(listener, output, destroy);
        wl_list_remove(&output->link);
        wl_list_remove(&output->destroy.link);
-
-       /* Windows may have moved; redraw all outputs */
-       damage_all_outputs(output->server);
 }
 
 static void
@@ -1029,22 +1026,6 @@ new_output_notify(struct wl_listener *listener, void *data)
                wlr_output_enable_adaptive_sync(wlr_output, true);
        }
        wlr_output_layout_add_auto(server->output_layout, wlr_output);
-
-       /*
-        * Output positions may have changed, so make sure that each
-        * wlr_output_cursor is "moved" (in per-output coordinates) to
-        * align with the seat cursor.  Set a default cursor image so
-        * that the cursor isn't invisible on the new output.
-        *
-        * TODO: remember the most recent cursor image (see cursor.c)
-        * and set that rather than XCURSOR_DEFAULT
-        */
-       wlr_cursor_move(server->seat.cursor, NULL, 0, 0);
-       wlr_xcursor_manager_set_cursor_image(server->seat.xcursor_manager,
-               XCURSOR_DEFAULT, server->seat.cursor);
-
-       /* Windows may have moved; redraw all outputs */
-       damage_all_outputs(server);
 }
 
 void
@@ -1072,6 +1053,31 @@ output_init(struct server *server)
        output_manager_init(server);
 }
 
+static void
+output_update_for_layout_change(struct server *server)
+{
+       /* Adjust window positions/sizes */
+       struct view *view;
+       wl_list_for_each(view, &server->views, link) {
+               view_adjust_for_layout_change(view);
+       }
+
+       /*
+        * "Move" each wlr_output_cursor (in per-output coordinates) to
+        * align with the seat cursor.  Set a default cursor image so
+        * that the cursor isn't invisible on new outputs.
+        *
+        * TODO: remember the most recent cursor image (see cursor.c)
+        * and set that rather than XCURSOR_DEFAULT
+        */
+       wlr_cursor_move(server->seat.cursor, NULL, 0, 0);
+       wlr_xcursor_manager_set_cursor_image(server->seat.xcursor_manager,
+               XCURSOR_DEFAULT, server->seat.cursor);
+
+       /* Redraw everything */
+       damage_all_outputs(server);
+}
+
 static void
 output_config_apply(struct server *server,
                struct wlr_output_configuration_v1 *config)
@@ -1111,6 +1117,7 @@ output_config_apply(struct server *server,
        }
 
        server->pending_output_config = NULL;
+       output_update_for_layout_change(server);
 }
 
 static bool
@@ -1205,6 +1212,7 @@ handle_output_layout_change(struct wl_listener *listener, void *data)
                                arrange_layers(output);
                        }
                }
+               output_update_for_layout_change(server);
        }
 }
 
index e337889016744f08438904f9f6928069ff386fd1..5faa466745f280f90af8991990f6c8e021f121b0 100644 (file)
@@ -115,24 +115,87 @@ view_output(struct view *view)
        return output_from_wlr_output(view->server, wlr_output);
 }
 
-void
-view_center(struct view *view)
+static bool
+view_compute_centered_position(struct view *view, int w, int h, int *x, int *y)
 {
        struct wlr_output *wlr_output = view_wlr_output(view);
        if (!wlr_output) {
-               return;
+               return false;
        }
 
        struct wlr_output_layout *layout = view->server->output_layout;
        struct wlr_output_layout_output *ol_output =
                wlr_output_layout_get(layout, wlr_output);
        if (!ol_output) {
-               return;
+               return false;
+       }
+
+       *x = ol_output->x + wlr_output->width / wlr_output->scale / 2 - w / 2;
+       *y = ol_output->y + wlr_output->height / wlr_output->scale / 2 - h / 2;
+       return true;
+}
+
+void
+view_center(struct view *view)
+{
+       int x, y;
+       if (view_compute_centered_position(view, view->w, view->h, &x, &y)) {
+               view_move(view, x, y);
+       }
+}
+
+static void
+view_apply_fullscreen_geometry(struct view *view, struct wlr_output *wlr_output)
+{
+       struct output *output =
+               output_from_wlr_output(view->server, wlr_output);
+       struct wlr_box box = { 0 };
+       wlr_output_effective_resolution(wlr_output, &box.width, &box.height);
+       double ox = 0, oy = 0;
+       wlr_output_layout_output_coords(output->server->output_layout,
+               output->wlr_output, &ox, &oy);
+       box.x -= ox;
+       box.y -= oy;
+       view_move_resize(view, box);
+}
+
+static void
+view_apply_maximized_geometry(struct view *view)
+{
+       struct output *output = view_output(view);
+       struct wlr_box box = output_usable_area_in_layout_coords(output);
+       if (box.height == output->wlr_output->height && output->wlr_output->scale != 1) {
+               box.height /= output->wlr_output->scale;
+       }
+       if (box.width == output->wlr_output->width && output->wlr_output->scale != 1) {
+               box.width /= output->wlr_output->scale;
+       }
+
+       if (view->ssd.enabled) {
+               struct border border = ssd_thickness(view);
+               box.x += border.left;
+               box.y += border.top;
+               box.width -= border.right + border.left;
+               box.height -= border.top + border.bottom;
        }
+       view_move_resize(view, box);
+}
 
-       int center_x = ol_output->x + wlr_output->width / wlr_output->scale / 2;
-       int center_y = ol_output->y + wlr_output->height / wlr_output->scale / 2;
-       view_move(view, center_x - view->w / 2, center_y - view->h / 2);
+static void
+view_apply_unmaximized_geometry(struct view *view)
+{
+       struct wlr_output_layout *layout = view->server->output_layout;
+       if (wlr_output_layout_intersects(layout, NULL, &view->unmaximized_geometry)) {
+               /* restore to original geometry */
+               view_move_resize(view, view->unmaximized_geometry);
+       } else {
+               /* reposition if original geometry is offscreen */
+               struct wlr_box box = view->unmaximized_geometry;
+               if (view_compute_centered_position(view, box.width, box.height,
+                               &box.x, &box.y)) {
+                       view_move_resize(view, box);
+               }
+       }
 }
 
 void
@@ -155,27 +218,11 @@ view_maximize(struct view *view, bool maximize)
                view->unmaximized_geometry.width = view->w;
                view->unmaximized_geometry.height = view->h;
 
-               struct output *output = view_output(view);
-               struct wlr_box box = output_usable_area_in_layout_coords(output);
-               if (box.height == output->wlr_output->height && output->wlr_output->scale != 1) {
-                       box.height /= output->wlr_output->scale;
-               }
-               if (box.width == output->wlr_output->width && output->wlr_output->scale != 1) {
-                       box.width /= output->wlr_output->scale;
-               }
-
-               if (view->ssd.enabled) {
-                       struct border border = ssd_thickness(view);
-                       box.x += border.left;
-                       box.y += border.top;
-                       box.width -= border.right + border.left;
-                       box.height -= border.top + border.bottom;
-               }
-               view_move_resize(view, box);
+               view_apply_maximized_geometry(view);
                view->maximized = true;
        } else {
                /* unmaximize */
-               view_move_resize(view, view->unmaximized_geometry);
+               view_apply_unmaximized_geometry(view);
                view->maximized = false;
        }
 }
@@ -242,33 +289,42 @@ view_set_fullscreen(struct view *view, bool fullscreen,
                        wlr_output = view_wlr_output(view);
                }
                view->fullscreen = wlr_output;
-               struct output *output =
-                       output_from_wlr_output(view->server, wlr_output);
-               struct wlr_box box = { 0 };
-               wlr_output_effective_resolution(wlr_output, &box.width,
-                                               &box.height);
-               double ox = 0, oy = 0;
-               wlr_output_layout_output_coords(output->server->output_layout,
-                       output->wlr_output, &ox, &oy);
-               box.x -= ox;
-               box.y -= oy;
-               view_move_resize(view, box);
+               view_apply_fullscreen_geometry(view, view->fullscreen);
        } else {
                /* restore to normal */
                if (view->maximized) {
-                       view->maximized = false;
-                       view->x = view->unmaximized_geometry.x;
-                       view->y = view->unmaximized_geometry.y;
-                       view->w = view->unmaximized_geometry.width;
-                       view->h = view->unmaximized_geometry.height;
-                       view_maximize(view, true);
+                       view_apply_maximized_geometry(view);
                } else {
-                       view_move_resize(view, view->unmaximized_geometry);
+                       view_apply_unmaximized_geometry(view);
                }
                view->fullscreen = false;
        }
 }
 
+void
+view_adjust_for_layout_change(struct view *view)
+{
+       struct wlr_output_layout *layout = view->server->output_layout;
+       if (view->fullscreen) {
+               if (wlr_output_layout_get(layout, view->fullscreen)) {
+                       /* recompute fullscreen geometry */
+                       view_apply_fullscreen_geometry(view, view->fullscreen);
+               } else {
+                       /* output is gone, exit fullscreen */
+                       view_set_fullscreen(view, false, NULL);
+               }
+       } else if (view->maximized) {
+               /* recompute maximized geometry */
+               view_apply_maximized_geometry(view);
+       } else {
+               /* reposition view if it's offscreen */
+               struct wlr_box box = { view->x, view->y, view->w, view->h };
+               if (!wlr_output_layout_intersects(layout, NULL, &box)) {
+                       view_center(view);
+               }
+       }
+}
+
 void
 view_for_each_surface(struct view *view, wlr_surface_iterator_func_t iterator,
                void *user_data)