diff --git a/src/core/window-private.h b/src/core/window-private.h index 2a96cc83a..be390ae1d 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 6b5836783..71777aad6 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 fe0daffba..accec6a94 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 f19dd4c15..10a006d52 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) { @@ -277,12 +288,101 @@ 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 bac473d8b..c883c6f46 100644 --- a/src/x11/window-x11.c +++ b/src/x11/window-x11.c @@ -1471,6 +1471,13 @@ meta_window_x11_update_icon (MetaWindow *window, META_MINI_ICON_WIDTH, META_MINI_ICON_HEIGHT); } +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; }