[mutter] wayland: Scale window geometry rects given the main output



commit f6c9261bf669bc1f3f0f8e957843bdeff025daad
Author: Jonas Ådahl <jadahl gmail com>
Date:   Mon Mar 23 21:22:19 2015 +0800

    wayland: Scale window geometry rects given the main output
    
    Since we scale surface actors given what main output their toplevel
    window is on, also scale the window geometry coordinates and sizes
    (window->rect size and window->custom_frame_extents.top/left) in order
    to make the window geometry represent what is being rendered on the
    stage.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=744934

 src/core/window-private.h          |    3 +
 src/core/window.c                  |    9 ++-
 src/wayland/meta-wayland-surface.c |    6 --
 src/wayland/meta-window-wayland.c  |  129 ++++++++++++++++++++++++++++++++++--
 src/x11/window-x11.c               |    8 ++
 5 files changed, 142 insertions(+), 13 deletions(-)
---
diff --git a/src/core/window-private.h b/src/core/window-private.h
index 2a96cc8..be390ae 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -482,6 +482,7 @@ struct _MetaWindowClass
   gboolean (*update_icon)        (MetaWindow       *window,
                                   cairo_surface_t **icon,
                                   cairo_surface_t **mini_icon);
+  void (*update_main_monitor)    (MetaWindow *window);
   void (*main_monitor_changed)   (MetaWindow *window,
                                   const MetaMonitorInfo *old);
 };
@@ -695,4 +696,6 @@ void meta_window_set_alive (MetaWindow *window, gboolean is_alive);
 
 gboolean meta_window_has_pointer (MetaWindow *window);
 
+void meta_window_emit_size_changed (MetaWindow *window);
+
 #endif
diff --git a/src/core/window.c b/src/core/window.c
index 6b58367..71777aa 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -3547,8 +3547,7 @@ meta_window_update_monitor (MetaWindow *window,
   const MetaMonitorInfo *old;
 
   old = window->monitor;
-  window->monitor = meta_screen_calculate_monitor_for_window (window->screen,
-                                                              window);
+  META_WINDOW_GET_CLASS (window)->update_main_monitor (window);
   if (old != window->monitor)
     {
       meta_window_on_all_workspaces_changed (window);
@@ -7855,3 +7854,9 @@ meta_window_grab_op_ended (MetaWindow *window,
 {
   META_WINDOW_GET_CLASS (window)->grab_op_ended (window, op);
 }
+
+void
+meta_window_emit_size_changed (MetaWindow *window)
+{
+  g_signal_emit (window, window_signals[SIZE_CHANGED], 0);
+}
diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c
index fe0daff..accec6a 100644
--- a/src/wayland/meta-wayland-surface.c
+++ b/src/wayland/meta-wayland-surface.c
@@ -2137,12 +2137,6 @@ meta_wayland_surface_configure_notify (MetaWaylandSurface *surface,
       wl_array_init (&states);
       fill_states (&states, surface->window);
 
-      /* new_width and new_height comes from window->rect, which is based on
-       * the buffer size, not the surface size. The configure event requires
-       * surface size. */
-      new_width /= surface->scale;
-      new_height /= surface->scale;
-
       xdg_surface_send_configure (surface->xdg_surface, new_width, new_height, &states, serial);
 
       wl_array_release (&states);
diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c
index f19dd4c..10a006d 100644
--- a/src/wayland/meta-window-wayland.c
+++ b/src/wayland/meta-window-wayland.c
@@ -166,9 +166,20 @@ meta_window_wayland_move_resize_internal (MetaWindow                *window,
 {
   MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window);
   gboolean can_move_now;
+  int configured_width;
+  int configured_height;
+  int monitor_scale;
 
   g_assert (window->frame == NULL);
 
+  /* The scale the window is drawn in might change depending on what monitor it
+   * is mainly on. Scale the configured rectangle to be in logical pixel
+   * coordinate space so that we can have a scale independent size to pass
+   * to the Wayland surface. */
+  monitor_scale = meta_window_wayland_get_main_monitor_scale (window);
+  configured_width = constrained_rect.width / monitor_scale;
+  configured_height = constrained_rect.height / monitor_scale;
+
   /* For wayland clients, the size is completely determined by the client,
    * and while this allows to avoid some trickery with frames and the resulting
    * lagging, we also need to insist a bit when the constraints would apply
@@ -223,8 +234,8 @@ meta_window_wayland_move_resize_internal (MetaWindow                *window,
             return;
 
           meta_wayland_surface_configure_notify (window->surface,
-                                                 constrained_rect.width,
-                                                 constrained_rect.height,
+                                                 configured_width,
+                                                 configured_height,
                                                  &wl_window->pending_configure_serial);
 
           /* We need to wait until the resize completes before we can move */
@@ -238,8 +249,8 @@ meta_window_wayland_move_resize_internal (MetaWindow                *window,
         }
     }
 
-  wl_window->last_sent_width = constrained_rect.width;
-  wl_window->last_sent_height = constrained_rect.height;
+  wl_window->last_sent_width = configured_width;
+  wl_window->last_sent_height = configured_height;
 
   if (can_move_now)
     {
@@ -278,11 +289,100 @@ meta_window_wayland_move_resize_internal (MetaWindow                *window,
 }
 
 static void
+scale_rect_size (MetaRectangle *rect, float scale)
+{
+  rect->width = (int)(rect->width * scale);
+  rect->height = (int)(rect->height * scale);
+}
+
+static void
+meta_window_wayland_update_main_monitor (MetaWindow *window)
+{
+  const MetaMonitorInfo *from;
+  const MetaMonitorInfo *to;
+  const MetaMonitorInfo *scaled_new;
+  float scale;
+  MetaRectangle rect;
+
+  /* Require both the current and the new monitor would be the new main monitor,
+   * even given the resulting scale the window would end up having. This is
+   * needed to avoid jumping back and forth between the new and the old, since
+   * changing main monitor may cause the window to be resized so that it no
+   * longer have that same new main monitor. */
+  from = window->monitor;
+  to = meta_screen_calculate_monitor_for_window (window->screen, window);
+
+  if (from == to)
+    return;
+
+  /* If we are setting the first output, unsetting the output, or the new has
+   * the same scale as the old no need to do any further checking. */
+  if (from == NULL || to == NULL || from->scale == to->scale)
+    {
+      window->monitor = to;
+      return;
+    }
+
+  /* To avoid a window alternating between two main monitors because scaling
+   * changes the main monitor, wait until both the current and the new scale
+   * will result in the same main monitor. */
+  scale = (float)to->scale / from->scale;
+  rect = window->rect;
+  scale_rect_size (&rect, scale);
+  scaled_new = meta_screen_get_monitor_for_rect (window->screen, &rect);
+  if (to != scaled_new)
+    return;
+
+  window->monitor = to;
+}
+
+static void
 meta_window_wayland_main_monitor_changed (MetaWindow *window,
                                           const MetaMonitorInfo *old)
 {
-  MetaWaylandSurface *surface = window->surface;
+  float scale_factor;
+  MetaWaylandSurface *surface;
+
+  /* This function makes sure that window geometry, window actor geometry and
+   * surface actor geometry gets set according the old and current main monitor
+   * scale. If there either is no past or current main monitor, or if the scale
+   * didn't change, there is nothing to do. */
+  if (old == NULL ||
+      window->monitor == NULL ||
+      old->scale == window->monitor->scale)
+    return;
 
+  /* MetaWindow keeps its rectangles in the physical pixel coordinate space.
+   * When the main monitor of a window changes, it can cause the corresponding
+   * window surfaces to be scaled given the monitor scale, so we need to scale
+   * the rectangles in MetaWindow accordingly. */
+
+  scale_factor = (float)window->monitor->scale / old->scale;
+
+  /* Window size. */
+  scale_rect_size (&window->rect, scale_factor);
+
+  /* Window geometry offset (XXX: Need a better place, see
+   * meta_window_wayland_move_resize). */
+  window->custom_frame_extents.left =
+    (int)(scale_factor * window->custom_frame_extents.left);
+  window->custom_frame_extents.top =
+    (int)(scale_factor * window->custom_frame_extents.top);
+
+  /* Buffer rect. */
+  scale_rect_size (&window->buffer_rect, scale_factor);
+  window->buffer_rect.x =
+    window->rect.x - window->custom_frame_extents.left;
+  window->buffer_rect.y =
+    window->rect.y - window->custom_frame_extents.top;
+
+  meta_compositor_sync_window_geometry (window->display->compositor,
+                                        window,
+                                        TRUE);
+
+  /* The surface actor needs to update the scale recursively for itself and all
+   * its subsurfaces */
+  surface = window->surface;
   if (surface)
     {
       MetaSurfaceActorWayland *actor =
@@ -290,6 +390,8 @@ meta_window_wayland_main_monitor_changed (MetaWindow *window,
 
       meta_surface_actor_wayland_sync_state_recursive (actor);
     }
+
+  meta_window_emit_size_changed (window);
 }
 
 static void
@@ -330,6 +432,7 @@ meta_window_wayland_class_init (MetaWindowWaylandClass *klass)
   window_class->grab_op_began = meta_window_wayland_grab_op_began;
   window_class->grab_op_ended = meta_window_wayland_grab_op_ended;
   window_class->move_resize_internal = meta_window_wayland_move_resize_internal;
+  window_class->update_main_monitor = meta_window_wayland_update_main_monitor;
   window_class->main_monitor_changed = meta_window_wayland_main_monitor_changed;
 }
 
@@ -434,6 +537,22 @@ meta_window_wayland_move_resize (MetaWindow        *window,
   int gravity;
   MetaRectangle rect;
   MetaMoveResizeFlags flags;
+  int monitor_scale;
+
+  /* new_geom is in the logical pixel coordinate space, but MetaWindow wants its
+   * rects to represent what in turn will end up on the stage, i.e. we need to
+   * scale new_geom to physical pixels given what buffer scale and texture scale
+   * is in use. */
+  monitor_scale = meta_window_wayland_get_main_monitor_scale (window);
+  new_geom.x *= monitor_scale;
+  new_geom.y *= monitor_scale;
+  new_geom.width *= monitor_scale;
+  new_geom.height *= monitor_scale;
+
+  /* The (dx, dy) offset is also in logical pixel coordinate space and needs
+   * to be scaled in the same way as new_geom. */
+  dx *= monitor_scale;
+  dy *= monitor_scale;
 
   /* XXX: Find a better place to store the window geometry offsets. */
   window->custom_frame_extents.left = new_geom.x;
diff --git a/src/x11/window-x11.c b/src/x11/window-x11.c
index bac473d..c883c6f 100644
--- a/src/x11/window-x11.c
+++ b/src/x11/window-x11.c
@@ -1472,6 +1472,13 @@ meta_window_x11_update_icon (MetaWindow       *window,
 }
 
 static void
+meta_window_x11_update_main_monitor (MetaWindow *window)
+{
+  window->monitor = meta_screen_calculate_monitor_for_window (window->screen,
+                                                              window);
+}
+
+static void
 meta_window_x11_main_monitor_changed (MetaWindow *window,
                                       const MetaMonitorInfo *old)
 {
@@ -1495,6 +1502,7 @@ meta_window_x11_class_init (MetaWindowX11Class *klass)
   window_class->update_struts = meta_window_x11_update_struts;
   window_class->get_default_skip_hints = meta_window_x11_get_default_skip_hints;
   window_class->update_icon = meta_window_x11_update_icon;
+  window_class->update_main_monitor = meta_window_x11_update_main_monitor;
   window_class->main_monitor_changed = meta_window_x11_main_monitor_changed;
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]