From 63643aba495462ec1a774c6c5ba51a042bc1f68b Mon Sep 17 00:00:00 2001 From: Kyle Gospodnetich Date: Mon, 13 May 2024 15:34:05 -0700 Subject: [PATCH 1/3] chore: Add mutter with xwayland fractional patch --- ...nput-region-from-frame-window-for-de.patch | 163 ++ spec_files/mutter/0001-modified-3329.patch | 145 ++ ...-center-initial-setup-fedora-welcome.patch | 28 + ...tor-Special-case-shaped-Java-windows.patch | 70 + spec_files/mutter/1441.patch | 1868 ++++++++++++++++ spec_files/mutter/3720+3567.patch | 1964 +++++++++++++++++ .../mutter-42.alpha-disable-tegra.patch | 25 + spec_files/mutter/mutter.spec | 223 ++ 8 files changed, 4486 insertions(+) create mode 100644 spec_files/mutter/0001-Revert-x11-Use-input-region-from-frame-window-for-de.patch create mode 100644 spec_files/mutter/0001-modified-3329.patch create mode 100644 spec_files/mutter/0001-place-Always-center-initial-setup-fedora-welcome.patch create mode 100644 spec_files/mutter/0001-window-actor-Special-case-shaped-Java-windows.patch create mode 100644 spec_files/mutter/1441.patch create mode 100644 spec_files/mutter/3720+3567.patch create mode 100644 spec_files/mutter/mutter-42.alpha-disable-tegra.patch create mode 100644 spec_files/mutter/mutter.spec diff --git a/spec_files/mutter/0001-Revert-x11-Use-input-region-from-frame-window-for-de.patch b/spec_files/mutter/0001-Revert-x11-Use-input-region-from-frame-window-for-de.patch new file mode 100644 index 00000000..4124b537 --- /dev/null +++ b/spec_files/mutter/0001-Revert-x11-Use-input-region-from-frame-window-for-de.patch @@ -0,0 +1,163 @@ +From 21680b2f4edb064ff524cb91e9e20ace91deda6d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Sun, 21 Apr 2024 16:54:52 +0200 +Subject: [PATCH 1/2] Revert "x11/window: Update comment and variable name to + reflect current behavior" + +This reverts commit e4763d00e8512aeb408ae118597d753f12217487. +--- + src/x11/window-x11.c | 15 ++++++++------- + 1 file changed, 8 insertions(+), 7 deletions(-) + +diff --git a/src/x11/window-x11.c b/src/x11/window-x11.c +index 6d2016e3ec..f6f7d87dfe 100644 +--- a/src/x11/window-x11.c ++++ b/src/x11/window-x11.c +@@ -2476,20 +2476,21 @@ meta_window_x11_update_input_region (MetaWindow *window) + + if (region != NULL) + { +- MtkRectangle bounding_rect; ++ MtkRectangle client_area; + +- bounding_rect.x = 0; +- bounding_rect.y = 0; +- bounding_rect.width = window->buffer_rect.width; +- bounding_rect.height = window->buffer_rect.height; ++ client_area.x = 0; ++ client_area.y = 0; ++ client_area.width = window->buffer_rect.width; ++ client_area.height = window->buffer_rect.height; + + /* The shape we get back from the client may have coordinates + * outside of the frame. The X SHAPE Extension requires that + * the overall shape the client provides never exceeds the + * "bounding rectangle" of the window -- the shape that the +- * window would have gotten if it was unshaped. ++ * window would have gotten if it was unshaped. In our case, ++ * this is simply the client area. + */ +- mtk_region_intersect_rectangle (region, &bounding_rect); ++ mtk_region_intersect_rectangle (region, &client_area); + } + + meta_window_set_input_region (window, region); +-- +2.44.0 + + +From f1996e67ad8a5a0009e90c38767c8906e015ba70 Mon Sep 17 00:00:00 2001 +From: Adam Williamson +Date: Thu, 5 Oct 2023 13:09:46 -0700 +Subject: [PATCH 2/2] Revert "x11: Use input region from frame window for + decorated windows" + +This reverts commit d991961ae2a5c8cf2e58ff1072239f4902b0f767. It +seems to cause the broken mouse interaction bug reported in +https://bugzilla.redhat.com/show_bug.cgi?id=2239128 . + +Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3068 +--- + src/core/frame.c | 7 ------- + src/x11/window-x11.c | 29 ++++++++++------------------- + 2 files changed, 10 insertions(+), 26 deletions(-) + +diff --git a/src/core/frame.c b/src/core/frame.c +index c74a2e04ec..5feb854805 100644 +--- a/src/core/frame.c ++++ b/src/core/frame.c +@@ -35,7 +35,6 @@ + #include "x11/window-props.h" + + #include +-#include + + #define EVENT_MASK (SubstructureRedirectMask | \ + StructureNotifyMask | SubstructureNotifyMask | \ +@@ -109,9 +108,6 @@ meta_window_x11_set_frame_xwindow (MetaWindow *window, + XChangeWindowAttributes (x11_display->xdisplay, + frame->xwindow, CWEventMask, &attrs); + +- if (META_X11_DISPLAY_HAS_SHAPE (x11_display)) +- XShapeSelectInput (x11_display->xdisplay, frame->xwindow, ShapeNotifyMask); +- + meta_x11_display_register_x_window (x11_display, &frame->xwindow, window); + + if (window->mapped) +@@ -220,9 +216,6 @@ meta_window_destroy_frame (MetaWindow *window) + window->reparents_pending += 1; + } + +- if (META_X11_DISPLAY_HAS_SHAPE (x11_display)) +- XShapeSelectInput (x11_display->xdisplay, frame->xwindow, NoEventMask); +- + XDeleteProperty (x11_display->xdisplay, + meta_window_x11_get_xwindow (window), + x11_display->atom__MUTTER_NEEDS_FRAME); +diff --git a/src/x11/window-x11.c b/src/x11/window-x11.c +index f6f7d87dfe..1bc5c57a1a 100644 +--- a/src/x11/window-x11.c ++++ b/src/x11/window-x11.c +@@ -2082,10 +2082,6 @@ meta_window_x11_constructed (GObject *object) + priv->keys_grabbed = FALSE; + priv->grab_on_frame = FALSE; + +- g_signal_connect (window, "notify::decorated", +- G_CALLBACK (meta_window_x11_update_input_region), +- window); +- + G_OBJECT_CLASS (meta_window_x11_parent_class)->constructed (object); + } + +@@ -2400,21 +2396,16 @@ meta_window_x11_update_input_region (MetaWindow *window) + g_autoptr (MtkRegion) region = NULL; + MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); + MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); +- Window xwindow; + ++ /* Decorated windows don't have an input region, because ++ we don't shape the frame to match the client windows ++ (so the events are blocked by the frame anyway) ++ */ + if (window->decorated) + { +- if (!window->frame) +- { +- if (priv->input_region) +- meta_window_set_input_region (window, NULL); +- return; +- } +- xwindow = window->frame->xwindow; +- } +- else +- { +- xwindow = priv->xwindow; ++ if (priv->input_region) ++ meta_window_set_input_region (window, NULL); ++ return; + } + + if (META_X11_DISPLAY_HAS_SHAPE (x11_display)) +@@ -2426,7 +2417,7 @@ meta_window_x11_update_input_region (MetaWindow *window) + + mtk_x11_error_trap_push (x11_display->xdisplay); + rects = XShapeGetRectangles (x11_display->xdisplay, +- xwindow, ++ priv->xwindow, + ShapeInput, + &n_rects, + &ordering); +@@ -2480,8 +2471,8 @@ meta_window_x11_update_input_region (MetaWindow *window) + + client_area.x = 0; + client_area.y = 0; +- client_area.width = window->buffer_rect.width; +- client_area.height = window->buffer_rect.height; ++ client_area.width = priv->client_rect.width; ++ client_area.height = priv->client_rect.height; + + /* The shape we get back from the client may have coordinates + * outside of the frame. The X SHAPE Extension requires that +-- +2.44.0 + diff --git a/spec_files/mutter/0001-modified-3329.patch b/spec_files/mutter/0001-modified-3329.patch new file mode 100644 index 00000000..54f0ed49 --- /dev/null +++ b/spec_files/mutter/0001-modified-3329.patch @@ -0,0 +1,145 @@ +From e20ebeefa42997fe65008b11ef771c71b697273c Mon Sep 17 00:00:00 2001 +From: Adam Williamson +Date: Fri, 20 Oct 2023 22:12:23 -0700 +Subject: [PATCH] modified 3329 + +Signed-off-by: Adam Williamson +--- + src/compositor/meta-compositor-x11.c | 2 ++ + src/core/display.c | 34 ---------------------------- + src/tests/x11-test.sh | 3 +++ + src/x11/meta-x11-display.c | 30 +++++++++++++++++++++++- + 4 files changed, 34 insertions(+), 35 deletions(-) + +diff --git a/src/compositor/meta-compositor-x11.c b/src/compositor/meta-compositor-x11.c +index 1ad3327dd..ce7bc1945 100644 +--- a/src/compositor/meta-compositor-x11.c ++++ b/src/compositor/meta-compositor-x11.c +@@ -188,6 +188,8 @@ meta_compositor_x11_manage (MetaCompositor *compositor, + + compositor_x11->have_x11_sync_object = meta_sync_ring_init (xdisplay); + ++ meta_x11_display_redirect_windows (x11_display, display); ++ + return TRUE; + } + +diff --git a/src/core/display.c b/src/core/display.c +index 0a191c0fb..b16e50e21 100644 +--- a/src/core/display.c ++++ b/src/core/display.c +@@ -930,9 +930,6 @@ meta_display_new (MetaContext *context, + MetaDisplay *display; + MetaDisplayPrivate *priv; + guint32 timestamp; +-#ifdef HAVE_X11_CLIENT +- Window old_active_xwindow = None; +-#endif + MetaMonitorManager *monitor_manager; + MetaSettings *settings; + MetaInputCapture *input_capture; +@@ -1048,14 +1045,6 @@ meta_display_new (MetaContext *context, + display->last_focus_time = timestamp; + display->last_user_time = timestamp; + +-#ifdef HAVE_X11 +- if (!meta_is_wayland_compositor ()) +- meta_prop_get_window (display->x11_display, +- display->x11_display->xroot, +- display->x11_display->atom__NET_ACTIVE_WINDOW, +- &old_active_xwindow); +-#endif +- + if (!meta_compositor_manage (display->compositor, error)) + { + g_object_unref (display); +@@ -1076,30 +1065,7 @@ meta_display_new (MetaContext *context, + g_signal_connect (display->gesture_tracker, "state-changed", + G_CALLBACK (gesture_tracker_state_changed), display); + +- /* We know that if mutter is running as a Wayland compositor, +- * we start out with no windows. +- */ +-#ifdef HAVE_X11_CLIENT +- if (!meta_is_wayland_compositor ()) +- meta_display_manage_all_xwindows (display); +- +- if (old_active_xwindow != None) +- { +- MetaWindow *old_active_window; +- old_active_window = meta_x11_display_lookup_x_window (display->x11_display, +- old_active_xwindow); +- if (old_active_window) +- meta_window_focus (old_active_window, timestamp); +- else +- meta_display_unset_input_focus (display, timestamp); +- } +- else +- { +- meta_display_unset_input_focus (display, timestamp); +- } +-#else + meta_display_unset_input_focus (display, timestamp); +-#endif + + g_signal_connect (stage, "notify::is-grabbed", + G_CALLBACK (on_is_grabbed_changed), display); +diff --git a/src/tests/x11-test.sh b/src/tests/x11-test.sh +index 59e460fc3..d95b2460f 100755 +--- a/src/tests/x11-test.sh ++++ b/src/tests/x11-test.sh +@@ -34,6 +34,9 @@ echo \# Launched with pid $MUTTER2_PID + MUTTER2_PID=$! + wait $MUTTER1_PID + ++echo \# Waiting for the second mutter to finish loading ++gdbus wait --session org.gnome.Mutter.IdleMonitor ++ + sleep 2 + + echo \# Terminating clients > /dev/stderr +diff --git a/src/x11/meta-x11-display.c b/src/x11/meta-x11-display.c +index 4e98203dd..4ca620410 100644 +--- a/src/x11/meta-x11-display.c ++++ b/src/x11/meta-x11-display.c +@@ -300,8 +300,36 @@ static void + on_x11_display_opened (MetaX11Display *x11_display, + MetaDisplay *display) + { ++ Window old_active_xwindow = None; ++ ++ if (!meta_is_wayland_compositor ()) ++ { ++ meta_prop_get_window (display->x11_display, ++ display->x11_display->xroot, ++ display->x11_display->atom__NET_ACTIVE_WINDOW, ++ &old_active_xwindow); ++ } ++ ++ if (meta_is_wayland_compositor ()) ++ meta_x11_display_redirect_windows (x11_display, display); ++ ++ + meta_display_manage_all_xwindows (display); +- meta_x11_display_redirect_windows (x11_display, display); ++ ++ if (old_active_xwindow != None) ++ { ++ MetaWindow *old_active_window; ++ ++ old_active_window = meta_x11_display_lookup_x_window (x11_display, ++ old_active_xwindow); ++ if (old_active_window) ++ { ++ uint32_t timestamp; ++ ++ timestamp = display->x11_display->timestamp; ++ meta_window_focus (old_active_window, timestamp); ++ } ++ } + } + + static void +-- +2.41.0 + diff --git a/spec_files/mutter/0001-place-Always-center-initial-setup-fedora-welcome.patch b/spec_files/mutter/0001-place-Always-center-initial-setup-fedora-welcome.patch new file mode 100644 index 00000000..078b7912 --- /dev/null +++ b/spec_files/mutter/0001-place-Always-center-initial-setup-fedora-welcome.patch @@ -0,0 +1,28 @@ +From 692546a9701a7b363e6190af441a95385c244907 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 2 Dec 2022 22:49:41 +0100 +Subject: [PATCH] place: Always center initial-setup/fedora-welcome + +--- + src/core/place.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/src/core/place.c b/src/core/place.c +index f9877dfc7..a69a3ebd1 100644 +--- a/src/core/place.c ++++ b/src/core/place.c +@@ -321,6 +321,11 @@ window_place_centered (MetaWindow *window) + + type = window->type; + ++ if (g_strcmp0 (meta_window_get_wm_class (window), "org.gnome.InitialSetup") == 0 || ++ g_strcmp0 (meta_window_get_wm_class (window), "org.fedoraproject.welcome-screen") == 0 || ++ g_strcmp0 (meta_window_get_wm_class (window), "fedora-welcome") == 0) ++ return TRUE; ++ + return (type == META_WINDOW_DIALOG || + type == META_WINDOW_MODAL_DIALOG || + type == META_WINDOW_SPLASHSCREEN || +-- +2.39.2 + diff --git a/spec_files/mutter/0001-window-actor-Special-case-shaped-Java-windows.patch b/spec_files/mutter/0001-window-actor-Special-case-shaped-Java-windows.patch new file mode 100644 index 00000000..51f9ecf1 --- /dev/null +++ b/spec_files/mutter/0001-window-actor-Special-case-shaped-Java-windows.patch @@ -0,0 +1,70 @@ +From b3b5aa01c63aee1df079e0394b0e6372df1838d0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 12 May 2017 13:40:31 +0200 +Subject: [PATCH] window-actor: Special-case shaped Java windows + +OpenJDK wrongly assumes that shaping a window implies no shadows. +They got lucky until commit b975676c changed the fallback case, +but now their compliance tests are broken. Make them happy again +by special-casing shaped Java windows. +--- + src/compositor/meta-window-actor-x11.c | 8 ++++++++ + src/x11/window-x11-private.h | 2 ++ + src/x11/window-x11.c | 9 +++++++++ + 3 files changed, 19 insertions(+) + +diff --git a/src/compositor/meta-window-actor-x11.c b/src/compositor/meta-window-actor-x11.c +index 19827af331..7d5e46ac75 100644 +--- a/src/compositor/meta-window-actor-x11.c ++++ b/src/compositor/meta-window-actor-x11.c +@@ -424,6 +424,14 @@ has_shadow (MetaWindowActorX11 *actor_x11) + */ + if (window->has_custom_frame_extents) + return FALSE; ++ ++ /* ++ * OpenJDK wrongly assumes that shaping a window implies no compositor ++ * shadows; make its compliance tests happy to give it what it wants ... ++ */ ++ if (g_strcmp0 (window->res_name, "sun-awt-X11-XWindowPeer") == 0 && ++ meta_window_x11_is_shaped (window)) ++ return FALSE; + + /* + * Generate shadows for all other windows. +diff --git a/src/x11/window-x11-private.h b/src/x11/window-x11-private.h +index c947744ee5..cb862f0d72 100644 +--- a/src/x11/window-x11-private.h ++++ b/src/x11/window-x11-private.h +@@ -125,6 +125,8 @@ gboolean meta_window_x11_has_pointer (MetaWindow *window); + gboolean meta_window_x11_same_application (MetaWindow *window, + MetaWindow *other_window); + ++gboolean meta_window_x11_is_shaped (MetaWindow *window); ++ + void meta_window_x11_shutdown_group (MetaWindow *window); + + META_EXPORT +diff --git a/src/x11/window-x11.c b/src/x11/window-x11.c +index 745c45db18..83cdd2e420 100644 +--- a/src/x11/window-x11.c ++++ b/src/x11/window-x11.c +@@ -2585,6 +2585,15 @@ meta_window_x11_update_shape_region (MetaWindow *window) + meta_window_set_shape_region (window, region); + } + ++gboolean ++meta_window_x11_is_shaped (MetaWindow *window) ++{ ++ MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); ++ MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); ++ ++ return priv->shape_region != NULL; ++} ++ + /* Generally meta_window_x11_same_application() is a better idea + * of "sameness", since it handles the case where multiple apps + * want to look like the same app or the same app wants to look +-- +2.43.2 + diff --git a/spec_files/mutter/1441.patch b/spec_files/mutter/1441.patch new file mode 100644 index 00000000..9a17ac97 --- /dev/null +++ b/spec_files/mutter/1441.patch @@ -0,0 +1,1868 @@ +diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c +index 93e4c9329..6bd83101e 100644 +--- a/clutter/clutter/clutter-frame-clock.c ++++ b/clutter/clutter/clutter-frame-clock.c +@@ -42,6 +42,15 @@ enum + + static guint signals[N_SIGNALS]; + ++typedef enum ++{ ++ TRIPLE_BUFFERING_MODE_NEVER, ++ TRIPLE_BUFFERING_MODE_AUTO, ++ TRIPLE_BUFFERING_MODE_ALWAYS, ++} TripleBufferingMode; ++ ++static TripleBufferingMode triple_buffering_mode = TRIPLE_BUFFERING_MODE_AUTO; ++ + #define SYNC_DELAY_FALLBACK_FRACTION 0.875 + + #define MINIMUM_REFRESH_RATE 30.f +@@ -70,8 +79,10 @@ typedef enum _ClutterFrameClockState + CLUTTER_FRAME_CLOCK_STATE_IDLE, + CLUTTER_FRAME_CLOCK_STATE_SCHEDULED, + CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW, +- CLUTTER_FRAME_CLOCK_STATE_DISPATCHING, +- CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED, ++ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE, ++ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED, ++ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW, ++ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO, + } ClutterFrameClockState; + + struct _ClutterFrameClock +@@ -92,6 +103,7 @@ struct _ClutterFrameClock + ClutterFrameClockMode mode; + + int64_t last_dispatch_time_us; ++ int64_t prev_last_dispatch_time_us; + int64_t last_dispatch_lateness_us; + int64_t last_presentation_time_us; + int64_t next_update_time_us; +@@ -111,6 +123,9 @@ struct _ClutterFrameClock + int64_t vblank_duration_us; + /* Last KMS buffer submission time. */ + int64_t last_flip_time_us; ++ int64_t prev_last_flip_time_us; ++ ++ ClutterFrameHint last_flip_hints; + + /* Last time we promoted short-term maximum to long-term one */ + int64_t longterm_promotion_us; +@@ -245,10 +260,6 @@ static void + maybe_update_longterm_max_duration_us (ClutterFrameClock *frame_clock, + ClutterFrameInfo *frame_info) + { +- /* Do not update long-term max if there has been no measurement */ +- if (!frame_clock->shortterm_max_update_duration_us) +- return; +- + if ((frame_info->presentation_time - frame_clock->longterm_promotion_us) < + G_USEC_PER_SEC) + return; +@@ -275,6 +286,12 @@ void + clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, + ClutterFrameInfo *frame_info) + { ++#ifdef CLUTTER_ENABLE_DEBUG ++ const char *debug_state = ++ frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO ? ++ "Triple buffering" : "Double buffering"; ++#endif ++ + COGL_TRACE_BEGIN_SCOPED (ClutterFrameClockNotifyPresented, + "Clutter::FrameClock::presented()"); + COGL_TRACE_DESCRIBE (ClutterFrameClockNotifyPresented, +@@ -361,22 +378,52 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, + + frame_clock->got_measurements_last_frame = FALSE; + +- if (frame_info->cpu_time_before_buffer_swap_us != 0 && +- frame_info->has_valid_gpu_rendering_duration) ++ if ((frame_info->cpu_time_before_buffer_swap_us != 0 && ++ frame_info->has_valid_gpu_rendering_duration) || ++ frame_clock->ever_got_measurements) + { + int64_t dispatch_to_swap_us, swap_to_rendering_done_us, swap_to_flip_us; ++ int64_t dispatch_time_us = 0, flip_time_us = 0; + +- dispatch_to_swap_us = +- frame_info->cpu_time_before_buffer_swap_us - +- frame_clock->last_dispatch_time_us; ++ switch (frame_clock->state) ++ { ++ case CLUTTER_FRAME_CLOCK_STATE_INIT: ++ case CLUTTER_FRAME_CLOCK_STATE_IDLE: ++ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: ++ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: ++ g_warn_if_reached (); ++ G_GNUC_FALLTHROUGH; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: ++ dispatch_time_us = frame_clock->last_dispatch_time_us; ++ flip_time_us = frame_clock->last_flip_time_us; ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: ++ dispatch_time_us = frame_clock->prev_last_dispatch_time_us; ++ flip_time_us = frame_clock->prev_last_flip_time_us; ++ break; ++ } ++ ++ if (frame_info->cpu_time_before_buffer_swap_us == 0) ++ { ++ /* Cursor-only updates with no "swap" or "flip" */ ++ dispatch_to_swap_us = 0; ++ swap_to_flip_us = 0; ++ } ++ else ++ { ++ dispatch_to_swap_us = frame_info->cpu_time_before_buffer_swap_us - ++ dispatch_time_us; ++ swap_to_flip_us = flip_time_us - ++ frame_info->cpu_time_before_buffer_swap_us; ++ } + swap_to_rendering_done_us = + frame_info->gpu_rendering_duration_ns / 1000; +- swap_to_flip_us = +- frame_clock->last_flip_time_us - +- frame_info->cpu_time_before_buffer_swap_us; + + CLUTTER_NOTE (FRAME_TIMINGS, +- "update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", ++ "%s: update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", ++ debug_state, + frame_clock->last_dispatch_lateness_us, + dispatch_to_swap_us, + swap_to_rendering_done_us, +@@ -386,7 +433,7 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, + CLAMP (frame_clock->last_dispatch_lateness_us + dispatch_to_swap_us + + MAX (swap_to_rendering_done_us, swap_to_flip_us), + frame_clock->shortterm_max_update_duration_us, +- frame_clock->refresh_interval_us); ++ 2 * frame_clock->refresh_interval_us); + + maybe_update_longterm_max_duration_us (frame_clock, frame_info); + +@@ -395,7 +442,8 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, + } + else + { +- CLUTTER_NOTE (FRAME_TIMINGS, "update2dispatch %ld µs", ++ CLUTTER_NOTE (FRAME_TIMINGS, "%s: update2dispatch %ld µs", ++ debug_state, + frame_clock->last_dispatch_lateness_us); + } + +@@ -413,11 +461,22 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + g_warn_if_reached (); + break; +- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: +- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; + maybe_reschedule_update (frame_clock); + break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; ++ maybe_reschedule_update (frame_clock); ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; ++ maybe_reschedule_update (frame_clock); ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; ++ maybe_reschedule_update (frame_clock); ++ break; + } + } + +@@ -435,26 +494,37 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock) + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + g_warn_if_reached (); + break; +- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: +- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; + maybe_reschedule_update (frame_clock); + break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; ++ maybe_reschedule_update (frame_clock); ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; ++ maybe_reschedule_update (frame_clock); ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; ++ maybe_reschedule_update (frame_clock); ++ break; + } + } + +-static int64_t +-clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) ++static gboolean ++clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock, ++ int64_t *max_render_time_us) + { + int64_t refresh_interval_us; +- int64_t max_render_time_us; + + refresh_interval_us = frame_clock->refresh_interval_us; + + if (!frame_clock->ever_got_measurements || + G_UNLIKELY (clutter_paint_debug_flags & + CLUTTER_DEBUG_DISABLE_DYNAMIC_MAX_RENDER_TIME)) +- return refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION; ++ return FALSE; + + /* Max render time shows how early the frame clock needs to be dispatched + * to make it to the predicted next presentation time. It is an estimate of +@@ -468,15 +538,15 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) + * - The duration of vertical blank. + * - A constant to account for variations in the above estimates. + */ +- max_render_time_us = ++ *max_render_time_us = + MAX (frame_clock->longterm_max_update_duration_us, + frame_clock->shortterm_max_update_duration_us) + + frame_clock->vblank_duration_us + + clutter_max_render_time_constant_us; + +- max_render_time_us = CLAMP (max_render_time_us, 0, refresh_interval_us); ++ *max_render_time_us = CLAMP (*max_render_time_us, 0, 2 * refresh_interval_us); + +- return max_render_time_us; ++ return TRUE; + } + + static void +@@ -491,7 +561,9 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, + int64_t min_render_time_allowed_us; + int64_t max_render_time_allowed_us; + int64_t next_presentation_time_us; ++ int64_t next_smooth_presentation_time_us = 0; + int64_t next_update_time_us; ++ gboolean max_render_time_is_known; + + now_us = g_get_monotonic_time (); + +@@ -511,10 +583,13 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, + } + + min_render_time_allowed_us = refresh_interval_us / 2; +- max_render_time_allowed_us = +- clutter_frame_clock_compute_max_render_time_us (frame_clock); + +- if (min_render_time_allowed_us > max_render_time_allowed_us) ++ max_render_time_is_known = ++ clutter_frame_clock_compute_max_render_time_us (frame_clock, ++ &max_render_time_allowed_us); ++ ++ if (max_render_time_is_known && ++ min_render_time_allowed_us > max_render_time_allowed_us) + min_render_time_allowed_us = max_render_time_allowed_us; + + /* +@@ -535,7 +610,28 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, + * + */ + last_presentation_time_us = frame_clock->last_presentation_time_us; +- next_presentation_time_us = last_presentation_time_us + refresh_interval_us; ++ switch (frame_clock->state) ++ { ++ case CLUTTER_FRAME_CLOCK_STATE_INIT: ++ case CLUTTER_FRAME_CLOCK_STATE_IDLE: ++ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: ++ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: ++ next_smooth_presentation_time_us = last_presentation_time_us + ++ refresh_interval_us; ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: ++ next_smooth_presentation_time_us = last_presentation_time_us + ++ 2 * refresh_interval_us; ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: ++ next_smooth_presentation_time_us = last_presentation_time_us + ++ 3 * refresh_interval_us; ++ break; ++ } ++ ++ next_presentation_time_us = next_smooth_presentation_time_us; + + /* + * However, the last presentation could have happened more than a frame ago. +@@ -601,7 +697,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, + } + } + +- if (next_presentation_time_us != last_presentation_time_us + refresh_interval_us) ++ if (next_presentation_time_us != next_smooth_presentation_time_us) + { + /* There was an idle period since the last presentation, so there seems + * be no constantly updating actor. In this case it's best to start +@@ -613,6 +709,24 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, + } + else + { ++ /* If the max render time isn't known then using the current value of ++ * next_presentation_time_us is suboptimal. Targeting always one frame ++ * prior to that we'd lose the ability to scale up to triple buffering ++ * on late presentation. But targeting two frames prior we would be ++ * always triple buffering even when not required. ++ * So the algorithm for deciding when to scale up to triple buffering ++ * in the absence of render time measurements is to simply target full ++ * frame rate. If we're keeping up then we'll stay double buffering. If ++ * we're not keeping up then this will switch us to triple buffering. ++ */ ++ if (!max_render_time_is_known) ++ { ++ max_render_time_allowed_us = ++ refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION; ++ next_presentation_time_us = ++ last_presentation_time_us + refresh_interval_us; ++ } ++ + while (next_presentation_time_us - min_render_time_allowed_us < now_us) + next_presentation_time_us += refresh_interval_us; + +@@ -644,7 +758,9 @@ calculate_next_variable_update_time_us (ClutterFrameClock *frame_clock, + + refresh_interval_us = frame_clock->refresh_interval_us; + +- if (frame_clock->last_presentation_time_us == 0) ++ if (frame_clock->last_presentation_time_us == 0 || ++ !clutter_frame_clock_compute_max_render_time_us (frame_clock, ++ &max_render_time_allowed_us)) + { + *out_next_update_time_us = + frame_clock->last_dispatch_time_us ? +@@ -657,9 +773,6 @@ calculate_next_variable_update_time_us (ClutterFrameClock *frame_clock, + return; + } + +- max_render_time_allowed_us = +- clutter_frame_clock_compute_max_render_time_us (frame_clock); +- + last_presentation_time_us = frame_clock->last_presentation_time_us; + next_presentation_time_us = last_presentation_time_us + refresh_interval_us; + +@@ -733,8 +846,17 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock) + frame_clock->pending_reschedule_now = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; + break; +- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: +- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ frame_clock->pending_reschedule = TRUE; ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: ++ frame_clock->pending_reschedule = TRUE; ++ frame_clock->pending_reschedule_now = TRUE; ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + break; + } + +@@ -753,6 +875,25 @@ clutter_frame_clock_uninhibit (ClutterFrameClock *frame_clock) + maybe_reschedule_update (frame_clock); + } + ++static gboolean ++want_triple_buffering (ClutterFrameClock *frame_clock) ++{ ++ switch (triple_buffering_mode) ++ { ++ case TRIPLE_BUFFERING_MODE_NEVER: ++ return FALSE; ++ case TRIPLE_BUFFERING_MODE_AUTO: ++ return frame_clock->mode == CLUTTER_FRAME_CLOCK_MODE_FIXED && ++ !(frame_clock->last_flip_hints & ++ CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED); ++ case TRIPLE_BUFFERING_MODE_ALWAYS: ++ return TRUE; ++ } ++ ++ g_assert_not_reached (); ++ return FALSE; ++} ++ + void + clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) + { +@@ -770,11 +911,24 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; + break; + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + return; +- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: +- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ frame_clock->state = ++ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW; ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: ++ if (want_triple_buffering (frame_clock)) ++ { ++ frame_clock->state = ++ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW; ++ break; ++ } ++ G_GNUC_FALLTHROUGH; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + frame_clock->pending_reschedule = TRUE; + frame_clock->pending_reschedule_now = TRUE; + return; +@@ -803,13 +957,17 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) + + frame_clock->next_update_time_us = next_update_time_us; + g_source_set_ready_time (frame_clock->source, next_update_time_us); +- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; + } + + void + clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) + { + int64_t next_update_time_us = -1; ++ TripleBufferingMode current_mode = triple_buffering_mode; ++ ++ if (current_mode == TRIPLE_BUFFERING_MODE_AUTO && ++ !want_triple_buffering (frame_clock)) ++ current_mode = TRIPLE_BUFFERING_MODE_NEVER; + + if (frame_clock->inhibit_count > 0) + { +@@ -825,12 +983,33 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + return; + case CLUTTER_FRAME_CLOCK_STATE_IDLE: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + break; + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + return; +- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: +- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: ++ switch (current_mode) ++ { ++ case TRIPLE_BUFFERING_MODE_NEVER: ++ frame_clock->pending_reschedule = TRUE; ++ return; ++ case TRIPLE_BUFFERING_MODE_AUTO: ++ frame_clock->state = ++ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; ++ break; ++ case TRIPLE_BUFFERING_MODE_ALWAYS: ++ next_update_time_us = g_get_monotonic_time (); ++ frame_clock->next_presentation_time_us = 0; ++ frame_clock->is_next_presentation_time_valid = FALSE; ++ frame_clock->state = ++ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; ++ goto got_update_time; ++ } ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + frame_clock->pending_reschedule = TRUE; + return; + } +@@ -855,11 +1034,11 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) + break; + } + ++got_update_time: + g_warn_if_fail (next_update_time_us != -1); + + frame_clock->next_update_time_us = next_update_time_us; + g_source_set_ready_time (frame_clock->source, next_update_time_us); +- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + } + + void +@@ -875,6 +1054,8 @@ clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock, + { + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + break; + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + frame_clock->pending_reschedule = TRUE; +@@ -885,8 +1066,14 @@ clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock, + frame_clock->pending_reschedule_now = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; + break; +- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: +- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ frame_clock->pending_reschedule = TRUE; ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: ++ frame_clock->pending_reschedule = TRUE; ++ frame_clock->pending_reschedule_now = TRUE; ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + break; + } + +@@ -922,7 +1109,7 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, + frame_clock->refresh_interval_us; + + lateness_us = time_us - ideal_dispatch_time_us; +- if (lateness_us < 0 || lateness_us >= frame_clock->refresh_interval_us) ++ if (lateness_us < 0 || lateness_us >= frame_clock->refresh_interval_us / 4) + frame_clock->last_dispatch_lateness_us = 0; + else + frame_clock->last_dispatch_lateness_us = lateness_us; +@@ -943,10 +1130,27 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, + } + #endif + ++ frame_clock->prev_last_dispatch_time_us = frame_clock->last_dispatch_time_us; + frame_clock->last_dispatch_time_us = time_us; + g_source_set_ready_time (frame_clock->source, -1); + +- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHING; ++ switch (frame_clock->state) ++ { ++ case CLUTTER_FRAME_CLOCK_STATE_INIT: ++ case CLUTTER_FRAME_CLOCK_STATE_IDLE: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: ++ g_warn_if_reached (); ++ return; ++ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: ++ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO; ++ break; ++ } + + frame_count = frame_clock->frame_count++; + +@@ -977,26 +1181,36 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, + result = iface->frame (frame_clock, frame, frame_clock->listener.user_data); + COGL_TRACE_END (ClutterFrameClockFrame); + +- switch (frame_clock->state) ++ switch (result) + { +- case CLUTTER_FRAME_CLOCK_STATE_INIT: +- case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: +- g_warn_if_reached (); ++ case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: + break; +- case CLUTTER_FRAME_CLOCK_STATE_IDLE: +- case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: +- case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: +- break; +- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: +- switch (result) ++ case CLUTTER_FRAME_RESULT_IDLE: ++ /* The frame was aborted; nothing to paint/present */ ++ switch (frame_clock->state) + { +- case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: +- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED; ++ case CLUTTER_FRAME_CLOCK_STATE_INIT: ++ case CLUTTER_FRAME_CLOCK_STATE_IDLE: ++ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: ++ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: ++ g_warn_if_reached (); + break; +- case CLUTTER_FRAME_RESULT_IDLE: ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; + maybe_reschedule_update (frame_clock); + break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; ++ maybe_reschedule_update (frame_clock); ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; ++ maybe_reschedule_update (frame_clock); ++ break; ++ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: ++ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; ++ maybe_reschedule_update (frame_clock); ++ break; + } + break; + } +@@ -1029,21 +1243,31 @@ frame_clock_source_dispatch (GSource *source, + } + + void +-clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock, +- int64_t flip_time_us) ++clutter_frame_clock_record_flip (ClutterFrameClock *frame_clock, ++ int64_t flip_time_us, ++ ClutterFrameHint hints) + { ++ frame_clock->prev_last_flip_time_us = frame_clock->last_flip_time_us; + frame_clock->last_flip_time_us = flip_time_us; ++ frame_clock->last_flip_hints = hints; + } + + GString * + clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clock) + { ++ int64_t max_render_time_us; + int64_t max_update_duration_us; + GString *string; + +- string = g_string_new (NULL); +- g_string_append_printf (string, "Max render time: %ld µs", +- clutter_frame_clock_compute_max_render_time_us (frame_clock)); ++ string = g_string_new ("Max render time: "); ++ if (!clutter_frame_clock_compute_max_render_time_us (frame_clock, ++ &max_render_time_us)) ++ { ++ g_string_append (string, "unknown"); ++ return string; ++ } ++ ++ g_string_append_printf (string, "%ld µs", max_render_time_us); + + if (frame_clock->got_measurements_last_frame) + g_string_append_printf (string, " ="); +@@ -1210,8 +1434,6 @@ clutter_frame_clock_dispose (GObject *object) + { + ClutterFrameClock *frame_clock = CLUTTER_FRAME_CLOCK (object); + +- g_warn_if_fail (frame_clock->state != CLUTTER_FRAME_CLOCK_STATE_DISPATCHING); +- + if (frame_clock->source) + { + g_signal_emit (frame_clock, signals[DESTROY], 0); +@@ -1235,6 +1457,15 @@ static void + clutter_frame_clock_class_init (ClutterFrameClockClass *klass) + { + GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ const char *mode_str; ++ ++ mode_str = g_getenv ("MUTTER_DEBUG_TRIPLE_BUFFERING"); ++ if (!g_strcmp0 (mode_str, "never")) ++ triple_buffering_mode = TRIPLE_BUFFERING_MODE_NEVER; ++ else if (!g_strcmp0 (mode_str, "auto")) ++ triple_buffering_mode = TRIPLE_BUFFERING_MODE_AUTO; ++ else if (!g_strcmp0 (mode_str, "always")) ++ triple_buffering_mode = TRIPLE_BUFFERING_MODE_ALWAYS; + + object_class->dispose = clutter_frame_clock_dispose; + +diff --git a/clutter/clutter/clutter-frame-clock.h b/clutter/clutter/clutter-frame-clock.h +index a7be5ef31..bfc89bde0 100644 +--- a/clutter/clutter/clutter-frame-clock.h ++++ b/clutter/clutter/clutter-frame-clock.h +@@ -33,6 +33,12 @@ typedef enum _ClutterFrameResult + CLUTTER_FRAME_RESULT_IDLE, + } ClutterFrameResult; + ++typedef enum _ClutterFrameHint ++{ ++ CLUTTER_FRAME_HINT_NONE = 0, ++ CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED = 1 << 0, ++} ClutterFrameHint; ++ + #define CLUTTER_TYPE_FRAME_CLOCK (clutter_frame_clock_get_type ()) + CLUTTER_EXPORT + G_DECLARE_FINAL_TYPE (ClutterFrameClock, clutter_frame_clock, +@@ -102,7 +108,8 @@ void clutter_frame_clock_remove_timeline (ClutterFrameClock *frame_clock, + CLUTTER_EXPORT + float clutter_frame_clock_get_refresh_rate (ClutterFrameClock *frame_clock); + +-void clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock, +- int64_t flip_time_us); ++void clutter_frame_clock_record_flip (ClutterFrameClock *frame_clock, ++ int64_t flip_time_us, ++ ClutterFrameHint hints); + + GString * clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clock); +diff --git a/clutter/clutter/clutter-frame-private.h b/clutter/clutter/clutter-frame-private.h +index ef66b874e..ce140560a 100644 +--- a/clutter/clutter/clutter-frame-private.h ++++ b/clutter/clutter/clutter-frame-private.h +@@ -36,6 +36,7 @@ struct _ClutterFrame + + gboolean has_result; + ClutterFrameResult result; ++ ClutterFrameHint hints; + }; + + CLUTTER_EXPORT +diff --git a/clutter/clutter/clutter-frame.c b/clutter/clutter/clutter-frame.c +index 7436f9f18..53c289b2c 100644 +--- a/clutter/clutter/clutter-frame.c ++++ b/clutter/clutter/clutter-frame.c +@@ -115,3 +115,16 @@ clutter_frame_set_result (ClutterFrame *frame, + frame->result = result; + frame->has_result = TRUE; + } ++ ++void ++clutter_frame_set_hint (ClutterFrame *frame, ++ ClutterFrameHint hint) ++{ ++ frame->hints |= hint; ++} ++ ++ClutterFrameHint ++clutter_frame_get_hints (ClutterFrame *frame) ++{ ++ return frame->hints; ++} +diff --git a/clutter/clutter/clutter-frame.h b/clutter/clutter/clutter-frame.h +index 34f0770bd..c7b3d02ac 100644 +--- a/clutter/clutter/clutter-frame.h ++++ b/clutter/clutter/clutter-frame.h +@@ -54,4 +54,11 @@ void clutter_frame_set_result (ClutterFrame *frame, + CLUTTER_EXPORT + gboolean clutter_frame_has_result (ClutterFrame *frame); + ++CLUTTER_EXPORT ++void clutter_frame_set_hint (ClutterFrame *frame, ++ ClutterFrameHint hint); ++ ++CLUTTER_EXPORT ++ClutterFrameHint clutter_frame_get_hints (ClutterFrame *frame); ++ + G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterFrame, clutter_frame_unref) +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index f5188e2ac..d53e37785 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -898,14 +898,21 @@ handle_frame_clock_frame (ClutterFrameClock *frame_clock, + + _clutter_stage_window_redraw_view (stage_window, view, frame); + +- clutter_frame_clock_record_flip_time (frame_clock, +- g_get_monotonic_time ()); ++ clutter_frame_clock_record_flip (frame_clock, ++ g_get_monotonic_time (), ++ clutter_frame_get_hints (frame)); + + clutter_stage_emit_after_paint (stage, view, frame); + + if (_clutter_context_get_show_fps ()) + end_frame_timing_measurement (view); + } ++ else ++ { ++ clutter_frame_clock_record_flip (frame_clock, ++ g_get_monotonic_time (), ++ clutter_frame_get_hints (frame)); ++ } + + _clutter_stage_window_finish_frame (stage_window, view, frame); + +diff --git a/cogl/cogl/cogl-onscreen-private.h b/cogl/cogl/cogl-onscreen-private.h +index 959a60533..86d8ea2d5 100644 +--- a/cogl/cogl/cogl-onscreen-private.h ++++ b/cogl/cogl/cogl-onscreen-private.h +@@ -78,4 +78,7 @@ COGL_EXPORT CoglFrameInfo * + cogl_onscreen_peek_tail_frame_info (CoglOnscreen *onscreen); + + COGL_EXPORT CoglFrameInfo * +-cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen); ++cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen); ++ ++COGL_EXPORT unsigned int ++cogl_onscreen_count_pending_frames (CoglOnscreen *onscreen); +diff --git a/cogl/cogl/cogl-onscreen.c b/cogl/cogl/cogl-onscreen.c +index afb648bcd..086be7ed7 100644 +--- a/cogl/cogl/cogl-onscreen.c ++++ b/cogl/cogl/cogl-onscreen.c +@@ -515,6 +515,14 @@ cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen) + return g_queue_pop_head (&priv->pending_frame_infos); + } + ++unsigned int ++cogl_onscreen_count_pending_frames (CoglOnscreen *onscreen) ++{ ++ CoglOnscreenPrivate *priv = cogl_onscreen_get_instance_private (onscreen); ++ ++ return g_queue_get_length (&priv->pending_frame_infos); ++} ++ + CoglFrameClosure * + cogl_onscreen_add_frame_callback (CoglOnscreen *onscreen, + CoglFrameCallback callback, +diff --git a/src/backends/meta-stage-impl.c b/src/backends/meta-stage-impl.c +index 7aa24439d..727e1a5f3 100644 +--- a/src/backends/meta-stage-impl.c ++++ b/src/backends/meta-stage-impl.c +@@ -774,6 +774,8 @@ meta_stage_impl_redraw_view (ClutterStageWindow *stage_window, + { + g_autoptr (GError) error = NULL; + ++ clutter_frame_set_hint (frame, CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED); ++ + if (meta_stage_impl_scanout_view (stage_impl, + stage_view, + scanout, +diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c +index b15eee14d..05bc89e83 100644 +--- a/src/backends/native/meta-kms-impl-device.c ++++ b/src/backends/native/meta-kms-impl-device.c +@@ -1559,9 +1559,11 @@ meta_kms_impl_device_handle_update (MetaKmsImplDevice *impl_device, + meta_kms_update_merge_from (crtc_frame->pending_update, update); + meta_kms_update_free (update); + update = g_steal_pointer (&crtc_frame->pending_update); +- disarm_crtc_frame_deadline_timer (crtc_frame); + } + ++ if (crtc_frame->deadline.armed) ++ disarm_crtc_frame_deadline_timer (crtc_frame); ++ + meta_kms_device_handle_flush (priv->device, latch_crtc); + + feedback = do_process (impl_device, latch_crtc, update, flags); +diff --git a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c +index 795008b21..70d1e792c 100644 +--- a/src/backends/native/meta-kms.c ++++ b/src/backends/native/meta-kms.c +@@ -63,6 +63,8 @@ struct _MetaKms + int kernel_thread_inhibit_count; + + MetaKmsCursorManager *cursor_manager; ++ ++ gboolean shutting_down; + }; + + G_DEFINE_TYPE (MetaKms, meta_kms, META_TYPE_THREAD) +@@ -354,6 +356,7 @@ static void + on_prepare_shutdown (MetaBackend *backend, + MetaKms *kms) + { ++ kms->shutting_down = TRUE; + meta_kms_run_impl_task_sync (kms, prepare_shutdown_in_impl, NULL, NULL); + meta_thread_flush_callbacks (META_THREAD (kms)); + +@@ -408,6 +411,12 @@ meta_kms_new (MetaBackend *backend, + return kms; + } + ++gboolean ++meta_kms_is_shutting_down (MetaKms *kms) ++{ ++ return kms->shutting_down; ++} ++ + static void + meta_kms_finalize (GObject *object) + { +diff --git a/src/backends/native/meta-kms.h b/src/backends/native/meta-kms.h +index 743401406..f6b19520b 100644 +--- a/src/backends/native/meta-kms.h ++++ b/src/backends/native/meta-kms.h +@@ -60,6 +60,8 @@ MetaKmsDevice * meta_kms_create_device (MetaKms *kms, + MetaKmsDeviceFlag flags, + GError **error); + ++gboolean meta_kms_is_shutting_down (MetaKms *kms); ++ + MetaKms * meta_kms_new (MetaBackend *backend, + MetaKmsFlags flags, + GError **error); +diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c +index 1a31f04a1..9836663d0 100644 +--- a/src/backends/native/meta-onscreen-native.c ++++ b/src/backends/native/meta-onscreen-native.c +@@ -76,7 +76,7 @@ typedef struct _MetaOnscreenNativeSecondaryGpuState + + struct { + MetaDrmBufferDumb *current_dumb_fb; +- MetaDrmBufferDumb *dumb_fbs[2]; ++ MetaDrmBufferDumb *dumb_fbs[3]; + } cpu; + + gboolean noted_primary_gpu_copy_ok; +@@ -98,9 +98,13 @@ struct _MetaOnscreenNative + struct { + struct gbm_surface *surface; + MetaDrmBuffer *current_fb; ++ MetaDrmBuffer *posted_fb; + MetaDrmBuffer *next_fb; ++ MetaDrmBuffer *stalled_fb; + CoglScanout *current_scanout; ++ CoglScanout *posted_scanout; + CoglScanout *next_scanout; ++ CoglScanout *stalled_scanout; + } gbm; + + #ifdef HAVE_EGL_DEVICE +@@ -125,6 +129,16 @@ struct _MetaOnscreenNative + gulong privacy_screen_changed_handler_id; + gulong color_space_changed_handler_id; + gulong hdr_metadata_changed_handler_id; ++ ++ gboolean needs_flush; ++ ++ unsigned int swaps_pending; ++ ++ struct { ++ int *rectangles; /* 4 x n_rectangles */ ++ int n_rectangles; ++ ClutterFrame *frame; ++ } next_post; + }; + + G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native, +@@ -132,44 +146,42 @@ G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native, + + static GQuark blit_source_quark = 0; + ++static void ++try_post_latest_swap (CoglOnscreen *onscreen); ++ ++static void ++post_finish_frame (MetaOnscreenNative *onscreen_native, ++ MetaKmsUpdate *kms_update); ++ + static gboolean + init_secondary_gpu_state (MetaRendererNative *renderer_native, + CoglOnscreen *onscreen, + GError **error); + +-static void +-free_current_bo (CoglOnscreen *onscreen) +-{ +- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); +- +- g_clear_object (&onscreen_native->gbm.current_fb); +- g_clear_object (&onscreen_native->gbm.current_scanout); +-} +- + static void + meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen) + { + MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); + +- if (!onscreen_native->gbm.next_fb) ++ if (!onscreen_native->gbm.posted_fb) + return; + +- free_current_bo (onscreen); ++ g_set_object (&onscreen_native->gbm.current_fb, ++ onscreen_native->gbm.posted_fb); ++ g_clear_object (&onscreen_native->gbm.posted_fb); + +- g_set_object (&onscreen_native->gbm.current_fb, onscreen_native->gbm.next_fb); +- g_clear_object (&onscreen_native->gbm.next_fb); + g_set_object (&onscreen_native->gbm.current_scanout, +- onscreen_native->gbm.next_scanout); +- g_clear_object (&onscreen_native->gbm.next_scanout); ++ onscreen_native->gbm.posted_scanout); ++ g_clear_object (&onscreen_native->gbm.posted_scanout); + } + + static void +-meta_onscreen_native_clear_next_fb (CoglOnscreen *onscreen) ++meta_onscreen_native_clear_posted_fb (CoglOnscreen *onscreen) + { + MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); + +- g_clear_object (&onscreen_native->gbm.next_fb); +- g_clear_object (&onscreen_native->gbm.next_scanout); ++ g_clear_object (&onscreen_native->gbm.posted_fb); ++ g_clear_object (&onscreen_native->gbm.posted_scanout); + } + + static void +@@ -207,7 +219,7 @@ meta_onscreen_native_notify_frame_complete (CoglOnscreen *onscreen) + + info = cogl_onscreen_pop_head_frame_info (onscreen); + +- g_assert (!cogl_onscreen_peek_head_frame_info (onscreen)); ++ g_return_if_fail (info); + + _cogl_onscreen_notify_frame_sync (onscreen, info); + _cogl_onscreen_notify_complete (onscreen, info); +@@ -243,6 +255,7 @@ notify_view_crtc_presented (MetaRendererView *view, + + meta_onscreen_native_notify_frame_complete (onscreen); + meta_onscreen_native_swap_drm_fb (onscreen); ++ try_post_latest_swap (onscreen); + } + + static void +@@ -292,15 +305,13 @@ page_flip_feedback_ready (MetaKmsCrtc *kms_crtc, + CoglFramebuffer *framebuffer = + clutter_stage_view_get_onscreen (CLUTTER_STAGE_VIEW (view)); + CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); +- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); + CoglFrameInfo *frame_info; + + frame_info = cogl_onscreen_peek_head_frame_info (onscreen); + frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; + +- g_warn_if_fail (!onscreen_native->gbm.next_fb); +- + meta_onscreen_native_notify_frame_complete (onscreen); ++ try_post_latest_swap (onscreen); + } + + static void +@@ -350,7 +361,8 @@ page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc, + frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; + + meta_onscreen_native_notify_frame_complete (onscreen); +- meta_onscreen_native_clear_next_fb (onscreen); ++ meta_onscreen_native_clear_posted_fb (onscreen); ++ try_post_latest_swap (onscreen); + } + + static const MetaKmsPageFlipListenerVtable page_flip_listener_vtable = { +@@ -411,18 +423,41 @@ custom_egl_stream_page_flip (gpointer custom_page_flip_data, + } + #endif /* HAVE_EGL_DEVICE */ + +-void +-meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen) ++static void ++drop_stalled_swap (CoglOnscreen *onscreen) + { + CoglFrameInfo *frame_info; ++ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); + +- meta_onscreen_native_swap_drm_fb (onscreen); ++ /* Remember we can't compare stalled_fb because it's not used by ++ * META_RENDERER_NATIVE_MODE_EGL_DEVICE. So we judge stalled to be whenever ++ * swaps_pending > 1. ++ */ ++ if (onscreen_native->swaps_pending <= 1) ++ return; ++ ++ onscreen_native->swaps_pending--; ++ ++ g_clear_object (&onscreen_native->gbm.stalled_fb); ++ g_clear_object (&onscreen_native->gbm.stalled_scanout); + + frame_info = cogl_onscreen_peek_tail_frame_info (onscreen); + frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; + meta_onscreen_native_notify_frame_complete (onscreen); + } + ++void ++meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen) ++{ ++ drop_stalled_swap (onscreen); ++ ++ /* If the monitor just woke up and the shell is fully idle (has nothing ++ * more to swap) then we just woke to an indefinitely black screen. Let's ++ * fix that using the last swap (which is never classified as "stalled"). ++ */ ++ try_post_latest_swap (onscreen); ++} ++ + static void + apply_transform (MetaCrtcKms *crtc_kms, + MetaKmsPlaneAssignment *kms_plane_assignment, +@@ -521,13 +556,21 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, + switch (renderer_gpu_data->mode) + { + case META_RENDERER_NATIVE_MODE_GBM: +- buffer = onscreen_native->gbm.next_fb; ++ g_set_object (&onscreen_native->gbm.posted_fb, ++ onscreen_native->gbm.next_fb); ++ g_clear_object (&onscreen_native->gbm.next_fb); ++ ++ buffer = onscreen_native->gbm.posted_fb; + +- if (onscreen_native->gbm.next_scanout) ++ g_set_object (&onscreen_native->gbm.posted_scanout, ++ onscreen_native->gbm.next_scanout); ++ g_clear_object (&onscreen_native->gbm.next_scanout); ++ ++ if (onscreen_native->gbm.posted_scanout) + { +- cogl_scanout_get_src_rect (onscreen_native->gbm.next_scanout, ++ cogl_scanout_get_src_rect (onscreen_native->gbm.posted_scanout, + &src_rect); +- cogl_scanout_get_dst_rect (onscreen_native->gbm.next_scanout, ++ cogl_scanout_get_dst_rect (onscreen_native->gbm.posted_scanout, + &dst_rect); + } + else +@@ -918,12 +961,17 @@ static MetaDrmBufferDumb * + secondary_gpu_get_next_dumb_buffer (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) + { + MetaDrmBufferDumb *current_dumb_fb; ++ const int n_dumb_fbs = G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); ++ int i; + + current_dumb_fb = secondary_gpu_state->cpu.current_dumb_fb; +- if (current_dumb_fb == secondary_gpu_state->cpu.dumb_fbs[0]) +- return secondary_gpu_state->cpu.dumb_fbs[1]; +- else +- return secondary_gpu_state->cpu.dumb_fbs[0]; ++ for (i = 0; i < n_dumb_fbs; i++) ++ { ++ if (current_dumb_fb == secondary_gpu_state->cpu.dumb_fbs[i]) ++ return secondary_gpu_state->cpu.dumb_fbs[(i + 1) % n_dumb_fbs]; ++ } ++ ++ return secondary_gpu_state->cpu.dumb_fbs[0]; + } + + static MetaDrmBuffer * +@@ -1255,10 +1303,17 @@ swap_buffer_result_feedback (const MetaKmsFeedback *kms_feedback, + g_warning ("Page flip failed: %s", error->message); + + frame_info = cogl_onscreen_peek_head_frame_info (onscreen); +- frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; + +- meta_onscreen_native_notify_frame_complete (onscreen); +- meta_onscreen_native_clear_next_fb (onscreen); ++ /* After resuming from suspend, drop_stalled_swap might have done this ++ * already and emptied the frame_info queue. ++ */ ++ if (frame_info) ++ { ++ frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; ++ meta_onscreen_native_notify_frame_complete (onscreen); ++ } ++ ++ meta_onscreen_native_clear_posted_fb (onscreen); + } + + static const MetaKmsResultListenerVtable swap_buffer_result_listener_vtable = { +@@ -1279,32 +1334,37 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; + MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; + MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; +- MetaRenderer *renderer = META_RENDERER (renderer_native); +- MetaBackend *backend = meta_renderer_get_backend (renderer); +- MetaMonitorManager *monitor_manager = +- meta_backend_get_monitor_manager (backend); + MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); + MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; + MetaGpuKms *render_gpu = onscreen_native->render_gpu; + MetaDeviceFile *render_device_file; + ClutterFrame *frame = user_data; +- MetaFrameNative *frame_native = meta_frame_native_from_frame (frame); +- MetaKmsUpdate *kms_update; + CoglOnscreenClass *parent_class; + gboolean create_timestamp_query = TRUE; + gboolean egl_context_changed = FALSE; +- MetaPowerSave power_save_mode; + g_autoptr (GError) error = NULL; + MetaDrmBufferFlags buffer_flags; + MetaDrmBufferGbm *buffer_gbm; + g_autoptr (MetaDrmBuffer) primary_gpu_fb = NULL; + g_autoptr (MetaDrmBuffer) secondary_gpu_fb = NULL; +- MetaKmsCrtc *kms_crtc; +- MetaKmsDevice *kms_device; ++ size_t rectangles_size; + + COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers, + "Meta::OnscreenNative::swap_buffers_with_damage()"); + ++ if (meta_is_topic_enabled (META_DEBUG_KMS)) ++ { ++ unsigned int frames_pending = ++ cogl_onscreen_count_pending_frames (onscreen); ++ ++ meta_topic (META_DEBUG_KMS, ++ "Swap buffers: %u frames pending (%s-buffering)", ++ frames_pending, ++ frames_pending == 1 ? "double" : ++ frames_pending == 2 ? "triple" : ++ "?"); ++ } ++ + secondary_gpu_fb = + update_secondary_gpu_state_pre_swap_buffers (onscreen, + rectangles, +@@ -1379,7 +1439,17 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + switch (renderer_gpu_data->mode) + { + case META_RENDERER_NATIVE_MODE_GBM: +- g_warn_if_fail (onscreen_native->gbm.next_fb == NULL); ++ if (onscreen_native->gbm.next_fb != NULL) ++ { ++ g_warn_if_fail (onscreen_native->gbm.stalled_fb == NULL); ++ drop_stalled_swap (onscreen); ++ g_assert (onscreen_native->gbm.stalled_fb == NULL); ++ onscreen_native->gbm.stalled_fb = ++ g_steal_pointer (&onscreen_native->gbm.next_fb); ++ onscreen_native->gbm.stalled_scanout = ++ g_steal_pointer (&onscreen_native->gbm.next_scanout); ++ } ++ + if (onscreen_native->secondary_gpu_state) + g_set_object (&onscreen_native->gbm.next_fb, secondary_gpu_fb); + else +@@ -1404,6 +1474,9 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + #endif + } + ++ clutter_frame_set_result (frame, ++ CLUTTER_FRAME_RESULT_PENDING_PRESENTED); ++ + /* + * If we changed EGL context, cogl will have the wrong idea about what is + * current, making it fail to set it when it needs to. Avoid that by making +@@ -1413,12 +1486,78 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + if (egl_context_changed) + _cogl_winsys_egl_ensure_current (cogl_display); + +- kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (onscreen_native->crtc)); +- kms_device = meta_kms_crtc_get_device (kms_crtc); ++ rectangles_size = n_rectangles * 4 * sizeof (int); ++ onscreen_native->next_post.rectangles = ++ g_realloc (onscreen_native->next_post.rectangles, rectangles_size); ++ memcpy (onscreen_native->next_post.rectangles, rectangles, rectangles_size); ++ onscreen_native->next_post.n_rectangles = n_rectangles; ++ ++ g_clear_pointer (&onscreen_native->next_post.frame, clutter_frame_unref); ++ onscreen_native->next_post.frame = clutter_frame_ref (frame); ++ ++ onscreen_native->swaps_pending++; ++ try_post_latest_swap (onscreen); ++} ++ ++static void ++try_post_latest_swap (CoglOnscreen *onscreen) ++{ ++ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); ++ CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); ++ CoglRenderer *cogl_renderer = cogl_context->display->renderer; ++ CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; ++ MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; ++ MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; ++ MetaRenderer *renderer = META_RENDERER (renderer_native); ++ MetaBackend *backend = meta_renderer_get_backend (renderer); ++ MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); ++ MetaKms *kms = meta_backend_native_get_kms (backend_native); ++ MetaMonitorManager *monitor_manager = ++ meta_backend_get_monitor_manager (backend); ++ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); ++ MetaPowerSave power_save_mode; ++ MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); ++ MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); ++ MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); ++ MetaKmsUpdate *kms_update; ++ g_autoptr (MetaKmsFeedback) kms_feedback = NULL; ++ g_autoptr (ClutterFrame) frame = NULL; ++ MetaFrameNative *frame_native; ++ ++ if (onscreen_native->next_post.frame == NULL || ++ onscreen_native->view == NULL || ++ meta_kms_is_shutting_down (kms)) ++ return; + + power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); + if (power_save_mode == META_POWER_SAVE_ON) + { ++ unsigned int frames_pending = ++ cogl_onscreen_count_pending_frames (onscreen); ++ unsigned int posts_pending; ++ ++ g_assert (frames_pending >= onscreen_native->swaps_pending); ++ posts_pending = frames_pending - onscreen_native->swaps_pending; ++ if (posts_pending > 0) ++ return; /* wait for the next frame notification and then try again */ ++ ++ frame = g_steal_pointer (&onscreen_native->next_post.frame); ++ frame_native = meta_frame_native_from_frame (frame); ++ ++ if (onscreen_native->swaps_pending == 0) ++ { ++ if (frame_native) ++ { ++ kms_update = meta_frame_native_steal_kms_update (frame_native); ++ if (kms_update) ++ post_finish_frame (onscreen_native, kms_update); ++ } ++ return; ++ } ++ ++ drop_stalled_swap (onscreen); ++ onscreen_native->swaps_pending--; ++ + kms_update = meta_frame_native_ensure_kms_update (frame_native, + kms_device); + meta_kms_update_add_result_listener (kms_update, +@@ -1433,15 +1572,13 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + onscreen_native->crtc, + kms_update, + META_KMS_ASSIGN_PLANE_FLAG_NONE, +- rectangles, +- n_rectangles); ++ onscreen_native->next_post.rectangles, ++ onscreen_native->next_post.n_rectangles); + } + else + { + meta_renderer_native_queue_power_save_page_flip (renderer_native, + onscreen); +- clutter_frame_set_result (frame, +- CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + return; + } + +@@ -1461,8 +1598,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + kms_update = meta_frame_native_steal_kms_update (frame_native); + meta_renderer_native_queue_mode_set_update (renderer_native, + kms_update); +- clutter_frame_set_result (frame, +- CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + return; + } + else if (meta_renderer_native_has_pending_mode_set (renderer_native)) +@@ -1476,8 +1611,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + + meta_frame_native_steal_kms_update (frame_native); + meta_renderer_native_post_mode_set_updates (renderer_native); +- clutter_frame_set_result (frame, +- CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + return; + } + break; +@@ -1493,8 +1626,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + kms_update); + + meta_renderer_native_post_mode_set_updates (renderer_native); +- clutter_frame_set_result (frame, +- CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + return; + } + break; +@@ -1509,7 +1640,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + kms_update = meta_frame_native_steal_kms_update (frame_native); + meta_kms_device_post_update (kms_device, kms_update, + META_KMS_UPDATE_FLAG_NONE); +- clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + } + + gboolean +@@ -1580,7 +1710,7 @@ scanout_result_feedback (const MetaKmsFeedback *kms_feedback, + + g_warning ("Direct scanout page flip failed: %s", error->message); + +- cogl_scanout_notify_failed (onscreen_native->gbm.next_scanout, ++ cogl_scanout_notify_failed (onscreen_native->gbm.posted_scanout, + onscreen); + clutter_stage_view_add_redraw_clip (view, NULL); + clutter_stage_view_schedule_update_now (view); +@@ -1590,7 +1720,7 @@ scanout_result_feedback (const MetaKmsFeedback *kms_feedback, + frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; + + meta_onscreen_native_notify_frame_complete (onscreen); +- meta_onscreen_native_clear_next_fb (onscreen); ++ meta_onscreen_native_clear_posted_fb (onscreen); + } + + static const MetaKmsResultListenerVtable scanout_result_listener_vtable = { +@@ -1642,6 +1772,18 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, + return FALSE; + } + ++ /* Our direct scanout frame counts as 1, so more than that means we would ++ * be jumping the queue (and post would fail). ++ */ ++ if (cogl_onscreen_count_pending_frames (onscreen) > 1) ++ { ++ g_set_error_literal (error, ++ COGL_SCANOUT_ERROR, ++ COGL_SCANOUT_ERROR_INHIBITED, ++ "Direct scanout is inhibited during triple buffering"); ++ return FALSE; ++ } ++ + renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, + render_gpu); + +@@ -1757,11 +1899,7 @@ meta_onscreen_native_before_redraw (CoglOnscreen *onscreen, + ClutterFrame *frame) + { + MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); +- MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); +- MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); + +- meta_kms_device_await_flush (meta_kms_crtc_get_device (kms_crtc), +- kms_crtc); + maybe_update_frame_sync (onscreen_native, frame); + } + +@@ -1877,22 +2015,79 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, + MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); + MetaFrameNative *frame_native = meta_frame_native_from_frame (frame); + MetaKmsUpdate *kms_update; ++ unsigned int frames_pending = cogl_onscreen_count_pending_frames (onscreen); ++ unsigned int swaps_pending = onscreen_native->swaps_pending; ++ unsigned int posts_pending = frames_pending - swaps_pending; + +- kms_update = meta_frame_native_steal_kms_update (frame_native); +- if (!kms_update) ++ onscreen_native->needs_flush |= meta_kms_device_handle_flush (kms_device, ++ kms_crtc); ++ ++ if (!meta_frame_native_has_kms_update (frame_native)) + { +- if (meta_kms_device_handle_flush (kms_device, kms_crtc)) +- { +- kms_update = meta_kms_update_new (kms_device); +- meta_kms_update_set_flushing (kms_update, kms_crtc); +- } +- else ++ if (!onscreen_native->needs_flush || posts_pending) + { + clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE); + return; + } + } + ++ if (posts_pending && !swaps_pending) ++ { ++ g_return_if_fail (meta_frame_native_has_kms_update (frame_native)); ++ g_warn_if_fail (onscreen_native->next_post.frame == NULL); ++ ++ g_clear_pointer (&onscreen_native->next_post.frame, clutter_frame_unref); ++ onscreen_native->next_post.frame = clutter_frame_ref (frame); ++ clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); ++ return; ++ } ++ ++ kms_update = meta_frame_native_steal_kms_update (frame_native); ++ ++ if (posts_pending && swaps_pending) ++ { ++ MetaFrameNative *older_frame_native; ++ MetaKmsUpdate *older_kms_update; ++ ++ g_return_if_fail (kms_update); ++ g_return_if_fail (onscreen_native->next_post.frame != NULL); ++ ++ older_frame_native = ++ meta_frame_native_from_frame (onscreen_native->next_post.frame); ++ older_kms_update = ++ meta_frame_native_ensure_kms_update (older_frame_native, kms_device); ++ meta_kms_update_merge_from (older_kms_update, kms_update); ++ meta_kms_update_free (kms_update); ++ clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE); ++ return; ++ } ++ ++ if (!kms_update) ++ { ++ kms_update = meta_kms_update_new (kms_device); ++ g_warn_if_fail (onscreen_native->needs_flush); ++ } ++ ++ if (onscreen_native->needs_flush) ++ { ++ meta_kms_update_set_flushing (kms_update, kms_crtc); ++ onscreen_native->needs_flush = FALSE; ++ } ++ ++ post_finish_frame (onscreen_native, kms_update); ++ ++ clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); ++} ++ ++static void ++post_finish_frame (MetaOnscreenNative *onscreen_native, ++ MetaKmsUpdate *kms_update) ++{ ++ MetaCrtc *crtc = onscreen_native->crtc; ++ MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (crtc)); ++ MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); ++ g_autoptr (MetaKmsFeedback) kms_feedback = NULL; ++ + meta_kms_update_add_result_listener (kms_update, + &finish_frame_result_listener_vtable, + NULL, +@@ -1915,7 +2110,19 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, + meta_kms_update_set_flushing (kms_update, kms_crtc); + meta_kms_device_post_update (kms_device, kms_update, + META_KMS_UPDATE_FLAG_NONE); +- clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); ++} ++ ++void ++meta_onscreen_native_discard_pending_swaps (CoglOnscreen *onscreen) ++{ ++ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); ++ ++ onscreen_native->swaps_pending = 0; ++ ++ g_clear_object (&onscreen_native->gbm.stalled_fb); ++ g_clear_object (&onscreen_native->gbm.stalled_scanout); ++ g_clear_object (&onscreen_native->gbm.next_fb); ++ g_clear_object (&onscreen_native->gbm.next_scanout); + } + + static gboolean +@@ -2830,8 +3037,11 @@ meta_onscreen_native_dispose (GObject *object) + { + case META_RENDERER_NATIVE_MODE_GBM: + g_clear_object (&onscreen_native->gbm.next_fb); ++ g_clear_object (&onscreen_native->gbm.posted_fb); ++ g_clear_object (&onscreen_native->gbm.current_fb); + g_clear_object (&onscreen_native->gbm.next_scanout); +- free_current_bo (onscreen); ++ g_clear_object (&onscreen_native->gbm.posted_scanout); ++ g_clear_object (&onscreen_native->gbm.current_scanout); + break; + case META_RENDERER_NATIVE_MODE_SURFACELESS: + g_assert_not_reached (); +@@ -2865,6 +3075,10 @@ meta_onscreen_native_dispose (GObject *object) + + g_clear_object (&onscreen_native->output); + g_clear_object (&onscreen_native->crtc); ++ ++ g_clear_pointer (&onscreen_native->next_post.rectangles, g_free); ++ g_clear_pointer (&onscreen_native->next_post.frame, clutter_frame_unref); ++ onscreen_native->next_post.n_rectangles = 0; + } + + static void +diff --git a/src/backends/native/meta-onscreen-native.h b/src/backends/native/meta-onscreen-native.h +index 0e1193325..e30357d19 100644 +--- a/src/backends/native/meta-onscreen-native.h ++++ b/src/backends/native/meta-onscreen-native.h +@@ -48,6 +48,8 @@ void meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen); + gboolean meta_onscreen_native_is_buffer_scanout_compatible (CoglOnscreen *onscreen, + CoglScanout *scanout); + ++void meta_onscreen_native_discard_pending_swaps (CoglOnscreen *onscreen); ++ + void meta_onscreen_native_set_view (CoglOnscreen *onscreen, + MetaRendererView *view); + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index aa76d018c..3c22b4e86 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -731,12 +731,18 @@ static gboolean + dummy_power_save_page_flip_cb (gpointer user_data) + { + MetaRendererNative *renderer_native = user_data; ++ GList *old_list = ++ g_steal_pointer (&renderer_native->power_save_page_flip_onscreens); + +- g_list_foreach (renderer_native->power_save_page_flip_onscreens, ++ g_list_foreach (old_list, + (GFunc) meta_onscreen_native_dummy_power_save_page_flip, + NULL); +- g_clear_list (&renderer_native->power_save_page_flip_onscreens, ++ g_clear_list (&old_list, + g_object_unref); ++ ++ if (renderer_native->power_save_page_flip_onscreens != NULL) ++ return G_SOURCE_CONTINUE; ++ + renderer_native->power_save_page_flip_source_id = 0; + + return G_SOURCE_REMOVE; +@@ -748,6 +754,9 @@ meta_renderer_native_queue_power_save_page_flip (MetaRendererNative *renderer_na + { + const unsigned int timeout_ms = 100; + ++ if (g_list_find (renderer_native->power_save_page_flip_onscreens, onscreen)) ++ return; ++ + if (!renderer_native->power_save_page_flip_source_id) + { + renderer_native->power_save_page_flip_source_id = +@@ -1529,6 +1538,26 @@ detach_onscreens (MetaRenderer *renderer) + } + } + ++static void ++discard_pending_swaps (MetaRenderer *renderer) ++{ ++ GList *views = meta_renderer_get_views (renderer);; ++ GList *l; ++ ++ for (l = views; l; l = l->next) ++ { ++ ClutterStageView *stage_view = l->data; ++ CoglFramebuffer *fb = clutter_stage_view_get_onscreen (stage_view); ++ CoglOnscreen *onscreen; ++ ++ if (!COGL_IS_ONSCREEN (fb)) ++ continue; ++ ++ onscreen = COGL_ONSCREEN (fb); ++ meta_onscreen_native_discard_pending_swaps (onscreen); ++ } ++} ++ + static void + meta_renderer_native_rebuild_views (MetaRenderer *renderer) + { +@@ -1539,6 +1568,7 @@ meta_renderer_native_rebuild_views (MetaRenderer *renderer) + MetaRendererClass *parent_renderer_class = + META_RENDERER_CLASS (meta_renderer_native_parent_class); + ++ discard_pending_swaps (renderer); + meta_kms_discard_pending_page_flips (kms); + g_hash_table_remove_all (renderer_native->mode_set_updates); + +diff --git a/src/tests/native-kms-render.c b/src/tests/native-kms-render.c +index f5ebc23fe..2f870fdc3 100644 +--- a/src/tests/native-kms-render.c ++++ b/src/tests/native-kms-render.c +@@ -39,6 +39,8 @@ + #include "tests/meta-wayland-test-driver.h" + #include "tests/meta-wayland-test-utils.h" + ++#define N_FRAMES_PER_TEST 30 ++ + typedef struct + { + int number_of_frames_left; +@@ -46,12 +48,15 @@ typedef struct + + struct { + int n_paints; +- uint32_t fb_id; ++ int n_presentations; ++ int n_direct_scanouts; ++ GList *fb_ids; + } scanout; + + gboolean wait_for_scanout; + + struct { ++ int scanouts_attempted; + gboolean scanout_sabotaged; + gboolean fallback_painted; + guint repaint_guard_id; +@@ -101,7 +106,7 @@ meta_test_kms_render_basic (void) + gulong handler_id; + + test = (KmsRenderingTest) { +- .number_of_frames_left = 10, ++ .number_of_frames_left = N_FRAMES_PER_TEST, + .loop = g_main_loop_new (NULL, FALSE), + }; + handler_id = g_signal_connect (stage, "after-update", +@@ -123,7 +128,6 @@ on_scanout_before_update (ClutterStage *stage, + KmsRenderingTest *test) + { + test->scanout.n_paints = 0; +- test->scanout.fb_id = 0; + } + + static void +@@ -135,6 +139,7 @@ on_scanout_before_paint (ClutterStage *stage, + CoglScanout *scanout; + CoglScanoutBuffer *scanout_buffer; + MetaDrmBuffer *buffer; ++ uint32_t fb_id; + + scanout = clutter_stage_view_peek_scanout (stage_view); + if (!scanout) +@@ -143,8 +148,13 @@ on_scanout_before_paint (ClutterStage *stage, + scanout_buffer = cogl_scanout_get_buffer (scanout); + g_assert_true (META_IS_DRM_BUFFER (scanout_buffer)); + buffer = META_DRM_BUFFER (scanout_buffer); +- test->scanout.fb_id = meta_drm_buffer_get_fb_id (buffer); +- g_assert_cmpuint (test->scanout.fb_id, >, 0); ++ fb_id = meta_drm_buffer_get_fb_id (buffer); ++ g_assert_cmpuint (fb_id, >, 0); ++ test->scanout.fb_ids = g_list_append (test->scanout.fb_ids, ++ GUINT_TO_POINTER (fb_id)); ++ ++ /* Triple buffering, but no higher */ ++ g_assert_cmpuint (g_list_length (test->scanout.fb_ids), <=, 2); + } + + static void +@@ -173,12 +183,12 @@ on_scanout_presented (ClutterStage *stage, + MetaDeviceFile *device_file; + GError *error = NULL; + drmModeCrtc *drm_crtc; ++ uint32_t first_fb_id_expected; + +- if (test->wait_for_scanout && test->scanout.n_paints > 0) ++ if (test->wait_for_scanout && test->scanout.fb_ids == NULL) + return; + +- if (test->wait_for_scanout && test->scanout.fb_id == 0) +- return; ++ test->scanout.n_presentations++; + + device_pool = meta_backend_native_get_device_pool (backend_native); + +@@ -197,15 +207,41 @@ on_scanout_presented (ClutterStage *stage, + drm_crtc = drmModeGetCrtc (meta_device_file_get_fd (device_file), + meta_kms_crtc_get_id (kms_crtc)); + g_assert_nonnull (drm_crtc); +- if (test->scanout.fb_id == 0) +- g_assert_cmpuint (drm_crtc->buffer_id, !=, test->scanout.fb_id); ++ ++ if (test->scanout.fb_ids) ++ { ++ test->scanout.n_direct_scanouts++; ++ first_fb_id_expected = GPOINTER_TO_UINT (test->scanout.fb_ids->data); ++ test->scanout.fb_ids = g_list_delete_link (test->scanout.fb_ids, ++ test->scanout.fb_ids); ++ } + else +- g_assert_cmpuint (drm_crtc->buffer_id, ==, test->scanout.fb_id); ++ { ++ first_fb_id_expected = 0; ++ } ++ ++ /* The buffer ID won't match on the first frame because switching from ++ * triple buffered compositing to double buffered direct scanout takes ++ * an extra frame to drain the queue. Thereafter we are in direct scanout ++ * mode and expect the buffer IDs to match. ++ */ ++ if (test->scanout.n_presentations > 1) ++ { ++ if (first_fb_id_expected == 0) ++ g_assert_cmpuint (drm_crtc->buffer_id, !=, first_fb_id_expected); ++ else ++ g_assert_cmpuint (drm_crtc->buffer_id, ==, first_fb_id_expected); ++ } ++ + drmModeFreeCrtc (drm_crtc); + + meta_device_file_release (device_file); + +- g_main_loop_quit (test->loop); ++ test->number_of_frames_left--; ++ if (test->number_of_frames_left <= 0) ++ g_main_loop_quit (test->loop); ++ else ++ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + } + + typedef enum +@@ -244,7 +280,9 @@ meta_test_kms_render_client_scanout (void) + g_assert_nonnull (wayland_test_client); + + test = (KmsRenderingTest) { ++ .number_of_frames_left = N_FRAMES_PER_TEST, + .loop = g_main_loop_new (NULL, FALSE), ++ .scanout = {0}, + .wait_for_scanout = TRUE, + }; + +@@ -270,7 +308,8 @@ meta_test_kms_render_client_scanout (void) + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + g_main_loop_run (test.loop); + +- g_assert_cmpuint (test.scanout.fb_id, >, 0); ++ g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST); ++ g_assert_cmpint (test.scanout.n_direct_scanouts, ==, N_FRAMES_PER_TEST); + + g_debug ("Unmake fullscreen"); + window = meta_find_window_from_title (test_context, "dma-buf-scanout-test"); +@@ -292,10 +331,15 @@ meta_test_kms_render_client_scanout (void) + g_assert_cmpint (buffer_rect.y, ==, 10); + + test.wait_for_scanout = FALSE; ++ test.number_of_frames_left = N_FRAMES_PER_TEST; ++ test.scanout.n_presentations = 0; ++ test.scanout.n_direct_scanouts = 0; ++ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + g_main_loop_run (test.loop); + +- g_assert_cmpuint (test.scanout.fb_id, ==, 0); ++ g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST); ++ g_assert_cmpint (test.scanout.n_direct_scanouts, ==, 0); + + g_debug ("Moving back to 0, 0"); + meta_window_move_frame (window, TRUE, 0, 0); +@@ -307,10 +351,15 @@ meta_test_kms_render_client_scanout (void) + g_assert_cmpint (buffer_rect.y, ==, 0); + + test.wait_for_scanout = TRUE; ++ test.number_of_frames_left = N_FRAMES_PER_TEST; ++ test.scanout.n_presentations = 0; ++ test.scanout.n_direct_scanouts = 0; ++ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + g_main_loop_run (test.loop); + +- g_assert_cmpuint (test.scanout.fb_id, >, 0); ++ g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST); ++ g_assert_cmpint (test.scanout.n_direct_scanouts, ==, N_FRAMES_PER_TEST); + + g_signal_handler_disconnect (stage, before_update_handler_id); + g_signal_handler_disconnect (stage, before_paint_handler_id); +@@ -364,6 +413,15 @@ on_scanout_fallback_before_paint (ClutterStage *stage, + if (!scanout) + return; + ++ test->scanout_fallback.scanouts_attempted++; ++ ++ /* The first scanout candidate frame will get composited due to triple ++ * buffering draining the queue to drop to double buffering. So don't ++ * sabotage that first frame. ++ */ ++ if (test->scanout_fallback.scanouts_attempted < 2) ++ return; ++ + g_assert_false (test->scanout_fallback.scanout_sabotaged); + + if (is_atomic_mode_setting (kms_device)) +@@ -401,6 +459,15 @@ on_scanout_fallback_paint_view (ClutterStage *stage, + g_clear_handle_id (&test->scanout_fallback.repaint_guard_id, + g_source_remove); + test->scanout_fallback.fallback_painted = TRUE; ++ test->scanout_fallback.scanout_sabotaged = FALSE; ++ } ++ else if (test->scanout_fallback.scanouts_attempted == 1) ++ { ++ /* Now that we've seen the first scanout attempt that was inhibited by ++ * triple buffering, try a second frame. The second one should scanout ++ * and will be sabotaged. ++ */ ++ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + } + } + +@@ -410,11 +477,11 @@ on_scanout_fallback_presented (ClutterStage *stage, + ClutterFrameInfo *frame_info, + KmsRenderingTest *test) + { +- if (!test->scanout_fallback.scanout_sabotaged) +- return; ++ if (test->scanout_fallback.fallback_painted) ++ g_main_loop_quit (test->loop); + +- g_assert_true (test->scanout_fallback.fallback_painted); +- g_main_loop_quit (test->loop); ++ test->number_of_frames_left--; ++ g_assert_cmpint (test->number_of_frames_left, >, 0); + } + + static void +@@ -443,6 +510,7 @@ meta_test_kms_render_client_scanout_fallback (void) + g_assert_nonnull (wayland_test_client); + + test = (KmsRenderingTest) { ++ .number_of_frames_left = N_FRAMES_PER_TEST, + .loop = g_main_loop_new (NULL, FALSE), + }; + diff --git a/spec_files/mutter/3720+3567.patch b/spec_files/mutter/3720+3567.patch new file mode 100644 index 00000000..27439329 --- /dev/null +++ b/spec_files/mutter/3720+3567.patch @@ -0,0 +1,1964 @@ +diff --git a/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml b/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml +index b05337d74..7294c57a8 100644 +--- a/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml ++++ b/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml +@@ -426,10 +426,6 @@ + always use the same scale. Absence of + this means logical monitor scales can + differ. +- * "legacy-ui-scaling-factor" (i): The legacy scaling factor traditionally +- used to scale X11 clients (commonly +- communicated via the +- Gdk/WindowScalingFactor XSetting entry). + --> + + +diff --git a/data/dbus-interfaces/org.gnome.Mutter.X11.xml b/data/dbus-interfaces/org.gnome.Mutter.X11.xml +new file mode 100644 +index 000000000..3d3c8a42f +--- /dev/null ++++ b/data/dbus-interfaces/org.gnome.Mutter.X11.xml +@@ -0,0 +1,8 @@ ++ ++ ++ ++ ++ ++ +diff --git a/data/org.gnome.mutter.gschema.xml.in b/data/org.gnome.mutter.gschema.xml.in +index 92c97b12e..18abd8d51 100644 +--- a/data/org.gnome.mutter.gschema.xml.in ++++ b/data/org.gnome.mutter.gschema.xml.in +@@ -5,6 +5,7 @@ + + + ++ + + + + + +diff --git a/src/backends/meta-monitor-manager-private.h b/src/backends/meta-monitor-manager-private.h +index 0760a341a..6ed3fc2c3 100644 +--- a/src/backends/meta-monitor-manager-private.h ++++ b/src/backends/meta-monitor-manager-private.h +@@ -436,3 +436,5 @@ gboolean meta_monitor_manager_apply_monitors_config (MetaMonitorManager * + MetaMonitorsConfig *config, + MetaMonitorsConfigMethod method, + GError **error); ++ ++MetaLogicalMonitorLayoutMode meta_monitor_manager_get_layout_mode (MetaMonitorManager *manager); +diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c +index 77743bc72..45033d966 100644 +--- a/src/backends/meta-monitor-manager.c ++++ b/src/backends/meta-monitor-manager.c +@@ -2051,14 +2051,12 @@ meta_monitor_manager_handle_get_current_state (MetaDBusDisplayConfig *skeleton, + GDBusMethodInvocation *invocation, + MetaMonitorManager *manager) + { +- MetaSettings *settings = meta_backend_get_settings (manager->backend); + GVariantBuilder monitors_builder; + GVariantBuilder logical_monitors_builder; + GVariantBuilder properties_builder; + GList *l; + int i; + MetaMonitorManagerCapability capabilities; +- int ui_scaling_factor; + int max_screen_width, max_screen_height; + + g_variant_builder_init (&monitors_builder, +@@ -2261,11 +2259,6 @@ meta_monitor_manager_handle_get_current_state (MetaDBusDisplayConfig *skeleton, + g_variant_new_boolean (TRUE)); + } + +- ui_scaling_factor = meta_settings_get_ui_scaling_factor (settings); +- g_variant_builder_add (&properties_builder, "{sv}", +- "legacy-ui-scaling-factor", +- g_variant_new_int32 (ui_scaling_factor)); +- + if (meta_monitor_manager_get_max_screen_size (manager, + &max_screen_width, + &max_screen_height)) +@@ -4123,3 +4116,9 @@ meta_monitor_manager_get_virtual_monitors (MetaMonitorManager *manager) + + return priv->virtual_monitors; + } ++ ++MetaLogicalMonitorLayoutMode ++meta_monitor_manager_get_layout_mode (MetaMonitorManager *manager) ++{ ++ return manager->layout_mode; ++} +diff --git a/src/backends/meta-settings-private.h b/src/backends/meta-settings-private.h +index afbba054a..2081a81b1 100644 +--- a/src/backends/meta-settings-private.h ++++ b/src/backends/meta-settings-private.h +@@ -32,6 +32,7 @@ typedef enum _MetaExperimentalFeature + META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS = (1 << 1), + META_EXPERIMENTAL_FEATURE_AUTOCLOSE_XWAYLAND = (1 << 2), + META_EXPERIMENTAL_FEATURE_VARIABLE_REFRESH_RATE = (1 << 3), ++ META_EXPERIMENTAL_FEATURE_XWAYLAND_NATIVE_SCALING = (1 << 4), + } MetaExperimentalFeature; + + typedef enum _MetaXwaylandExtension +diff --git a/src/backends/meta-settings.c b/src/backends/meta-settings.c +index 3703b23b0..1ae59d636 100644 +--- a/src/backends/meta-settings.c ++++ b/src/backends/meta-settings.c +@@ -296,6 +296,8 @@ experimental_features_handler (GVariant *features_variant, + feature = META_EXPERIMENTAL_FEATURE_AUTOCLOSE_XWAYLAND; + else if (g_str_equal (feature_str, "variable-refresh-rate")) + feature = META_EXPERIMENTAL_FEATURE_VARIABLE_REFRESH_RATE; ++ else if (g_str_equal (feature_str, "xwayland-native-scaling")) ++ feature = META_EXPERIMENTAL_FEATURE_XWAYLAND_NATIVE_SCALING; + + if (feature) + g_message ("Enabling experimental feature '%s'", feature_str); +diff --git a/src/compositor/meta-window-actor-x11.c b/src/compositor/meta-window-actor-x11.c +index 7d5e46ac7..577ed2760 100644 +--- a/src/compositor/meta-window-actor-x11.c ++++ b/src/compositor/meta-window-actor-x11.c +@@ -696,11 +696,23 @@ meta_window_actor_x11_process_damage (MetaWindowActorX11 *actor_x11, + + surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11)); + if (surface) +- meta_surface_actor_process_damage (surface, +- event->area.x, +- event->area.y, +- event->area.width, +- event->area.height); ++ { ++ MetaWindow *window = ++ meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); ++ MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); ++ MtkRectangle damage; ++ ++ meta_window_x11_protocol_to_stage (window_x11, ++ event->area.x, event->area.y, ++ event->area.width, event->area.height, ++ &damage.x, &damage.y, ++ &damage.width, &damage.height); ++ meta_surface_actor_process_damage (surface, ++ damage.x, ++ damage.y, ++ damage.width, ++ damage.height); ++ } + + meta_window_actor_notify_damaged (META_WINDOW_ACTOR (actor_x11)); + } +diff --git a/src/core/frame.c b/src/core/frame.c +index 5feb85480..6f0f3ab83 100644 +--- a/src/core/frame.c ++++ b/src/core/frame.c +@@ -33,6 +33,7 @@ + #include "x11/meta-x11-display-private.h" + #include "x11/window-x11-private.h" + #include "x11/window-props.h" ++#include "x11/window-x11.h" + + #include + +@@ -65,6 +66,7 @@ meta_window_x11_set_frame_xwindow (MetaWindow *window, + XSetWindowAttributes attrs; + gulong create_serial = 0; + MetaFrame *frame; ++ int child_x, child_y; + + if (window->frame) + return; +@@ -123,11 +125,19 @@ meta_window_x11_set_frame_xwindow (MetaWindow *window, + meta_stack_tracker_record_remove (window->display->stack_tracker, + meta_window_x11_get_xwindow (window), + XNextRequest (x11_display->xdisplay)); ++ meta_window_x11_stage_to_protocol (META_WINDOW_X11 (window), ++ frame->child_x, ++ frame->child_y, ++ 0, 0, ++ &child_x, ++ &child_y, ++ NULL, NULL); ++ + XReparentWindow (x11_display->xdisplay, + meta_window_x11_get_xwindow (window), + frame->xwindow, +- frame->child_x, +- frame->child_y); ++ child_x, ++ child_y); + window->reparents_pending += 1; + /* FIXME handle this error */ + mtk_x11_error_trap_pop (x11_display->xdisplay); +@@ -197,6 +207,8 @@ meta_window_destroy_frame (MetaWindow *window) + + if (!x11_display->closing) + { ++ int child_x, child_y; ++ + if (!window->unmanaging) + { + meta_stack_tracker_record_add (window->display->stack_tracker, +@@ -204,6 +216,14 @@ meta_window_destroy_frame (MetaWindow *window) + XNextRequest (x11_display->xdisplay)); + } + ++ meta_window_x11_stage_to_protocol (META_WINDOW_X11 (window), ++ window->frame->rect.x + borders.invisible.left, ++ window->frame->rect.y + borders.invisible.top, ++ 0, 0, ++ &child_x, ++ &child_y, ++ NULL, NULL); ++ + XReparentWindow (x11_display->xdisplay, + meta_window_x11_get_xwindow (window), + x11_display->xroot, +@@ -211,8 +231,7 @@ meta_window_destroy_frame (MetaWindow *window) + * coordinates here means we'll need to ensure a configure + * notify event is sent; see bug 399552. + */ +- window->frame->rect.x + borders.invisible.left, +- window->frame->rect.y + borders.invisible.top); ++ child_x, child_y); + window->reparents_pending += 1; + } + +@@ -263,6 +282,7 @@ meta_frame_query_borders (MetaFrame *frame, + MetaFrameBorders *borders) + { + MetaWindow *window = frame->window; ++ MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); + MetaX11Display *x11_display = window->display->x11_display; + int format, res; + Atom type; +@@ -286,12 +306,22 @@ meta_frame_query_borders (MetaFrame *frame, + if (mtk_x11_error_trap_pop_with_return (x11_display->xdisplay) == Success && + res == Success && nitems == 4) + { +- borders->invisible = (MetaFrameBorder) { +- ((long *) data)[0], +- ((long *) data)[1], +- ((long *) data)[2], +- ((long *) data)[3], +- }; ++ int left, right, top, bottom; ++ ++ meta_window_x11_protocol_to_stage (window_x11, ++ ((long *) data)[0], ++ ((long *) data)[1], ++ ((long *) data)[2], ++ ((long *) data)[3], ++ &left, ++ &right, ++ &top, ++ &bottom); ++ ++ borders->invisible.left = left; ++ borders->invisible.right = right; ++ borders->invisible.top = top; ++ borders->invisible.bottom = bottom; + } + else + { +@@ -314,12 +344,21 @@ meta_frame_query_borders (MetaFrame *frame, + if (mtk_x11_error_trap_pop_with_return (x11_display->xdisplay) == Success && + res == Success && nitems == 4) + { +- borders->visible = (MetaFrameBorder) { +- ((long *) data)[0], +- ((long *) data)[1], +- ((long *) data)[2], +- ((long *) data)[3], +- }; ++ int left, right, top, bottom; ++ ++ meta_window_x11_protocol_to_stage (window_x11, ++ ((long *) data)[0], ++ ((long *) data)[1], ++ ((long *) data)[2], ++ ((long *) data)[3], ++ &left, ++ &right, ++ &top, ++ &bottom); ++ borders->visible.left = left; ++ borders->visible.right = right; ++ borders->visible.top = top; ++ borders->visible.bottom = bottom; + } + else + { +@@ -367,7 +406,9 @@ meta_frame_sync_to_window (MetaFrame *frame, + gboolean need_resize) + { + MetaWindow *window = frame->window; ++ MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); + MetaX11Display *x11_display = window->display->x11_display; ++ MtkRectangle rect; + + meta_topic (META_DEBUG_GEOMETRY, + "Syncing frame geometry %d,%d %dx%d (SE: %d,%d)", +@@ -378,12 +419,22 @@ meta_frame_sync_to_window (MetaFrame *frame, + + mtk_x11_error_trap_push (x11_display->xdisplay); + ++ meta_window_x11_stage_to_protocol (window_x11, ++ frame->rect.x, ++ frame->rect.y, ++ frame->rect.width, ++ frame->rect.height, ++ &rect.x, ++ &rect.y, ++ &rect.width, ++ &rect.height); ++ + XMoveResizeWindow (x11_display->xdisplay, + frame->xwindow, +- frame->rect.x, +- frame->rect.y, +- frame->rect.width, +- frame->rect.height); ++ rect.x, ++ rect.y, ++ rect.width, ++ rect.height); + + mtk_x11_error_trap_pop (x11_display->xdisplay); + +@@ -420,6 +471,7 @@ static void + send_configure_notify (MetaFrame *frame) + { + MetaX11Display *x11_display = frame->window->display->x11_display; ++ MetaWindowX11 *window_x11 = META_WINDOW_X11 (frame->window); + XEvent event = { 0 }; + + /* We never get told by the frames client, just reassert the +@@ -429,10 +481,16 @@ send_configure_notify (MetaFrame *frame) + event.xconfigure.display = x11_display->xdisplay; + event.xconfigure.event = frame->xwindow; + event.xconfigure.window = frame->xwindow; +- event.xconfigure.x = frame->rect.x; +- event.xconfigure.y = frame->rect.y; +- event.xconfigure.width = frame->rect.width; +- event.xconfigure.height = frame->rect.height; ++ ++ meta_window_x11_stage_to_protocol (window_x11, ++ frame->rect.x, ++ frame->rect.y, ++ frame->rect.width, ++ frame->rect.height, ++ &event.xconfigure.x, ++ &event.xconfigure.y, ++ &event.xconfigure.width, ++ &event.xconfigure.height); + event.xconfigure.border_width = 0; + event.xconfigure.above = None; + event.xconfigure.override_redirect = False; +diff --git a/src/meson.build b/src/meson.build +index 05df3bfd2..e658f98ca 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -949,6 +949,11 @@ dbus_interfaces = [ + 'interface': 'org.gnome.Mutter.DebugControl.xml', + 'prefix': 'org.gnome.Mutter', + }, ++ { ++ 'name': 'meta-dbus-x11', ++ 'interface': 'org.gnome.Mutter.X11.xml', ++ 'prefix': 'org.gnome.Mutter', ++ }, + ] + + if have_profiler +diff --git a/src/meta/meta-context.h b/src/meta/meta-context.h +index ef36bd2c3..2adb9b07e 100644 +--- a/src/meta/meta-context.h ++++ b/src/meta/meta-context.h +@@ -101,3 +101,8 @@ gboolean meta_context_raise_rlimit_nofile (MetaContext *context, + META_EXPORT + gboolean meta_context_restore_rlimit_nofile (MetaContext *context, + GError **error); ++ ++#ifdef HAVE_WAYLAND ++META_EXPORT ++MetaWaylandCompositor * meta_context_get_wayland_compositor (MetaContext *context); ++#endif +diff --git a/src/meta/meta-wayland-compositor.h b/src/meta/meta-wayland-compositor.h +index 7f4a50705..3df92fda5 100644 +--- a/src/meta/meta-wayland-compositor.h ++++ b/src/meta/meta-wayland-compositor.h +@@ -31,9 +31,6 @@ G_DECLARE_FINAL_TYPE (MetaWaylandCompositor, + META, WAYLAND_COMPOSITOR, + GObject) + +-META_EXPORT +-MetaWaylandCompositor *meta_context_get_wayland_compositor (MetaContext *context); +- + META_EXPORT + struct wl_display *meta_wayland_compositor_get_wayland_display (MetaWaylandCompositor *compositor); + +diff --git a/src/meta/types.h b/src/meta/types.h +index cbe2a9a3d..8fba4a839 100644 +--- a/src/meta/types.h ++++ b/src/meta/types.h +@@ -38,3 +38,7 @@ typedef struct _MetaSettings MetaSettings; + + typedef struct _MetaWorkspaceManager MetaWorkspaceManager; + typedef struct _MetaSelection MetaSelection; ++ ++#ifdef HAVE_WAYLAND ++typedef struct _MetaWaylandCompositor MetaWaylandCompositor; ++#endif +diff --git a/src/wayland/meta-wayland-cursor-surface.c b/src/wayland/meta-wayland-cursor-surface.c +index 87a8895c8..5a16ce7d8 100644 +--- a/src/wayland/meta-wayland-cursor-surface.c ++++ b/src/wayland/meta-wayland-cursor-surface.c +@@ -92,37 +92,29 @@ cursor_sprite_prepare_at (MetaCursorSprite *cursor_sprite, + { + MetaWaylandSurfaceRole *role = META_WAYLAND_SURFACE_ROLE (cursor_surface); + MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (role); +- +- if (!meta_wayland_surface_is_xwayland (surface)) ++ MetaContext *context = ++ meta_wayland_compositor_get_context (surface->compositor); ++ MetaBackend *backend = meta_context_get_backend (context); ++ MetaMonitorManager *monitor_manager = ++ meta_backend_get_monitor_manager (backend); ++ MetaLogicalMonitor *logical_monitor; ++ ++ logical_monitor = ++ meta_monitor_manager_get_logical_monitor_at (monitor_manager, x, y); ++ if (logical_monitor) + { +- MetaWaylandSurfaceRole *surface_role = +- META_WAYLAND_SURFACE_ROLE (cursor_surface); +- MetaWaylandSurface *surface = +- meta_wayland_surface_role_get_surface (surface_role); +- MetaContext *context = +- meta_wayland_compositor_get_context (surface->compositor); +- MetaBackend *backend = meta_context_get_backend (context); +- MetaMonitorManager *monitor_manager = +- meta_backend_get_monitor_manager (backend); +- MetaLogicalMonitor *logical_monitor; +- +- logical_monitor = +- meta_monitor_manager_get_logical_monitor_at (monitor_manager, x, y); +- if (logical_monitor) +- { +- int surface_scale = surface->applied_state.scale; +- float texture_scale; +- +- if (meta_backend_is_stage_views_scaled (backend)) +- texture_scale = 1.0 / surface_scale; +- else +- texture_scale = (meta_logical_monitor_get_scale (logical_monitor) / +- surface_scale); +- +- meta_cursor_sprite_set_texture_scale (cursor_sprite, texture_scale); +- meta_cursor_sprite_set_texture_transform (cursor_sprite, +- surface->buffer_transform); +- } ++ int surface_scale = surface->applied_state.scale; ++ float texture_scale; ++ ++ if (meta_backend_is_stage_views_scaled (backend)) ++ texture_scale = 1.0 / surface_scale; ++ else ++ texture_scale = (meta_logical_monitor_get_scale (logical_monitor) / ++ surface_scale); ++ ++ meta_cursor_sprite_set_texture_scale (cursor_sprite, texture_scale); ++ meta_cursor_sprite_set_texture_transform (cursor_sprite, ++ surface->buffer_transform); + } + + meta_wayland_surface_update_outputs (surface); +diff --git a/src/wayland/meta-wayland-outputs.c b/src/wayland/meta-wayland-outputs.c +index 89ae86445..f957bc339 100644 +--- a/src/wayland/meta-wayland-outputs.c ++++ b/src/wayland/meta-wayland-outputs.c +@@ -31,6 +31,10 @@ + #include "backends/meta-monitor-manager-private.h" + #include "wayland/meta-wayland-private.h" + ++#ifdef HAVE_XWAYLAND ++#include "wayland/meta-xwayland.h" ++#endif ++ + #include "xdg-output-unstable-v1-server-protocol.h" + + /* Wayland protocol headers list new additions, not deprecations */ +@@ -50,6 +54,8 @@ struct _MetaWaylandOutput + { + GObject parent; + ++ MetaWaylandCompositor *compositor; ++ + struct wl_global *global; + GList *resources; + GList *xdg_output_resources; +@@ -422,6 +428,7 @@ meta_wayland_output_new (MetaWaylandCompositor *compositor, + MetaWaylandOutput *wayland_output; + + wayland_output = g_object_new (META_TYPE_WAYLAND_OUTPUT, NULL); ++ wayland_output->compositor = compositor; + wayland_output->global = wl_global_create (compositor->wayland_display, + &wl_output_interface, + META_WL_OUTPUT_VERSION, +@@ -596,6 +603,37 @@ static const struct zxdg_output_v1_interface + meta_xdg_output_destroy, + }; + ++#ifdef HAVE_XWAYLAND ++static gboolean ++is_xwayland_resource (MetaWaylandOutput *wayland_output, ++ struct wl_resource *resource) ++{ ++ MetaXWaylandManager *manager = &wayland_output->compositor->xwayland_manager; ++ ++ return resource && wl_resource_get_client (resource) == manager->client; ++} ++#endif ++ ++static void ++maybe_scale_for_xwayland (MetaWaylandOutput *wayland_output, ++ struct wl_resource *resource, ++ int *x, ++ int *y) ++{ ++#ifdef HAVE_XWAYLAND ++ if (is_xwayland_resource (wayland_output, resource)) ++ { ++ MetaXWaylandManager *xwayland_manager = ++ &wayland_output->compositor->xwayland_manager; ++ int xwayland_scale; ++ ++ xwayland_scale = meta_xwayland_get_effective_scale (xwayland_manager); ++ *x *= xwayland_scale; ++ *y *= xwayland_scale; ++ } ++#endif ++} ++ + static void + send_xdg_output_events (struct wl_resource *resource, + MetaWaylandOutput *wayland_output, +@@ -616,6 +654,7 @@ send_xdg_output_events (struct wl_resource *resource, + if (need_all_events || + old_layout.x != layout.x || old_layout.y != layout.y) + { ++ maybe_scale_for_xwayland (wayland_output, resource, &layout.x, &layout.y); + zxdg_output_v1_send_logical_position (resource, layout.x, layout.y); + need_done = TRUE; + } +@@ -623,6 +662,7 @@ send_xdg_output_events (struct wl_resource *resource, + if (need_all_events || + old_layout.width != layout.width || old_layout.height != layout.height) + { ++ maybe_scale_for_xwayland (wayland_output, resource, &layout.width, &layout.height); + zxdg_output_v1_send_logical_size (resource, layout.width, layout.height); + need_done = TRUE; + } +@@ -745,7 +785,7 @@ meta_wayland_outputs_init (MetaWaylandCompositor *compositor) + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + +- g_signal_connect (monitor_manager, "monitors-changed-internal", ++ g_signal_connect (monitor_manager, "monitors-changed", + G_CALLBACK (on_monitors_changed), compositor); + + compositor->outputs = +diff --git a/src/wayland/meta-wayland-pointer.c b/src/wayland/meta-wayland-pointer.c +index 88b27f84d..324092970 100644 +--- a/src/wayland/meta-wayland-pointer.c ++++ b/src/wayland/meta-wayland-pointer.c +@@ -1244,6 +1244,20 @@ pointer_set_cursor (struct wl_client *client, + cursor_surface = META_WAYLAND_CURSOR_SURFACE (surface->role); + meta_wayland_cursor_surface_set_renderer (cursor_surface, + cursor_renderer); ++ ++#ifdef HAVE_XWAYLAND ++ if (meta_wayland_surface_is_xwayland (surface)) ++ { ++ MetaXWaylandManager *xwayland_manager = ++ &surface->compositor->xwayland_manager; ++ int scale; ++ ++ scale = meta_xwayland_get_effective_scale (xwayland_manager); ++ hot_x = round (hot_x / (double) scale); ++ hot_y = round (hot_y / (double) scale); ++ } ++#endif ++ + meta_wayland_cursor_surface_set_hotspot (cursor_surface, + hot_x, hot_y); + +diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h +index e8d442c03..834753ffd 100644 +--- a/src/wayland/meta-wayland-private.h ++++ b/src/wayland/meta-wayland-private.h +@@ -77,6 +77,8 @@ struct _MetaXWaylandManager + int rr_error_base; + + gboolean should_enable_ei_portal; ++ ++ double highest_monitor_scale; + }; + + struct _MetaWaylandCompositor +diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c +index 6dc5006b7..81ee47bbd 100644 +--- a/src/wayland/meta-wayland-surface.c ++++ b/src/wayland/meta-wayland-surface.c +@@ -781,8 +781,19 @@ meta_wayland_surface_apply_state (MetaWaylandSurface *surface, + state->buffer->type != META_WAYLAND_BUFFER_TYPE_SINGLE_PIXEL)); + } + +- if (state->scale > 0) +- surface->applied_state.scale = state->scale; ++ if (meta_wayland_surface_is_xwayland (surface)) ++ { ++#ifdef HAVE_XWAYLAND ++ MetaXWaylandManager *xwayland_manager = ++ &surface->compositor->xwayland_manager; ++ ++ surface->applied_state.scale = meta_xwayland_get_effective_scale (xwayland_manager); ++#endif ++ } ++ else if (state->scale > 0) ++ { ++ surface->applied_state.scale = state->scale; ++ } + + if (state->has_new_buffer_transform) + surface->buffer_transform = state->buffer_transform; +@@ -977,8 +988,9 @@ meta_wayland_surface_commit (MetaWaylandSurface *surface) + MetaMultiTexture *committed_texture = surface->committed_state.texture; + int committed_scale = surface->committed_state.scale; + +- if ((meta_multi_texture_get_width (committed_texture) % committed_scale != 0) || +- (meta_multi_texture_get_height (committed_texture) % committed_scale != 0)) ++ if (((meta_multi_texture_get_width (committed_texture) % committed_scale != 0) || ++ (meta_multi_texture_get_height (committed_texture) % committed_scale != 0)) && ++ !meta_wayland_surface_is_xwayland (surface)) + { + if (!surface->role || !META_IS_WAYLAND_CURSOR_SURFACE (surface->role)) + { +@@ -1506,6 +1518,16 @@ meta_wayland_surface_update_outputs (MetaWaylandSurface *surface) + g_hash_table_foreach (surface->compositor->outputs, + update_surface_output_state, + surface); ++ ++ if (meta_wayland_surface_is_xwayland (surface)) ++ { ++#ifdef HAVE_XWAYLAND ++ MetaXWaylandManager *xwayland_manager = ++ &surface->compositor->xwayland_manager; ++ ++ surface->applied_state.scale = meta_xwayland_get_effective_scale (xwayland_manager); ++#endif ++ } + } + + void +diff --git a/src/wayland/meta-window-xwayland.c b/src/wayland/meta-window-xwayland.c +index 1299a351c..5fb006962 100644 +--- a/src/wayland/meta-window-xwayland.c ++++ b/src/wayland/meta-window-xwayland.c +@@ -27,8 +27,9 @@ + #include "x11/window-x11-private.h" + #include "x11/xprops.h" + #include "wayland/meta-window-xwayland.h" +-#include "wayland/meta-wayland.h" ++#include "wayland/meta-wayland-private.h" + #include "wayland/meta-wayland-surface-private.h" ++#include "wayland/meta-xwayland.h" + + enum + { +@@ -315,6 +316,72 @@ meta_window_xwayland_process_property_notify (MetaWindow *window, + meta_window_queue (window, META_QUEUE_MOVE_RESIZE); + } + ++static void ++meta_window_xwayland_stage_to_protocol (MetaWindowX11 *window_x11, ++ int stage_x, ++ int stage_y, ++ int stage_width, ++ int stage_height, ++ int *protocol_x, ++ int *protocol_y, ++ int *protocol_width, ++ int *protocol_height) ++{ ++ MetaDisplay *display = meta_window_get_display (META_WINDOW (window_x11)); ++ MetaContext *context = meta_display_get_context (display); ++ MetaWaylandCompositor *wayland_compositor = ++ meta_context_get_wayland_compositor (context); ++ MetaXWaylandManager *xwayland_manager = &wayland_compositor->xwayland_manager; ++ int scale; ++ ++ scale = meta_xwayland_get_effective_scale (xwayland_manager); ++ if (protocol_x) ++ *protocol_x = stage_x * scale; ++ if (protocol_y) ++ *protocol_y = stage_y * scale; ++ if (protocol_width) ++ *protocol_width = stage_width * scale; ++ if (protocol_height) ++ *protocol_height = stage_height * scale; ++} ++ ++static void ++meta_window_xwayland_protocol_to_stage (MetaWindowX11 *window_x11, ++ int protocol_x, ++ int protocol_y, ++ int protocol_width, ++ int protocol_height, ++ int *stage_x, ++ int *stage_y, ++ int *stage_width, ++ int *stage_height) ++{ ++ MetaDisplay *display = meta_window_get_display (META_WINDOW (window_x11)); ++ MetaContext *context = meta_display_get_context (display); ++ MetaWaylandCompositor *wayland_compositor = ++ meta_context_get_wayland_compositor (context); ++ MetaXWaylandManager *xwayland_manager = &wayland_compositor->xwayland_manager; ++ MtkRectangle rect; ++ int scale; ++ ++ rect.x = protocol_x; ++ rect.y = protocol_y; ++ rect.width = protocol_width; ++ rect.height = protocol_height; ++ ++ scale = meta_xwayland_get_effective_scale (xwayland_manager); ++ mtk_rectangle_scale_double (&rect, 1.0 / scale, MTK_ROUNDING_STRATEGY_GROW, &rect); ++ ++ if (stage_x) ++ *stage_x = rect.x; ++ if (stage_y) ++ *stage_y = rect.y; ++ if (stage_width) ++ *stage_width = rect.width; ++ if (stage_height) ++ *stage_height = rect.height; ++} ++ + static void + meta_window_xwayland_class_init (MetaWindowXwaylandClass *klass) + { +@@ -331,6 +398,8 @@ meta_window_xwayland_class_init (MetaWindowXwaylandClass *klass) + window_x11_class->thaw_commits = meta_window_xwayland_thaw_commits; + window_x11_class->always_update_shape = meta_window_xwayland_always_update_shape; + window_x11_class->process_property_notify = meta_window_xwayland_process_property_notify; ++ window_x11_class->stage_to_protocol = meta_window_xwayland_stage_to_protocol; ++ window_x11_class->protocol_to_stage = meta_window_xwayland_protocol_to_stage; + + gobject_class->get_property = meta_window_xwayland_get_property; + gobject_class->set_property = meta_window_xwayland_set_property; +diff --git a/src/wayland/meta-xwayland-private.h b/src/wayland/meta-xwayland-private.h +index 7a9cb73fd..9e06f0315 100644 +--- a/src/wayland/meta-xwayland-private.h ++++ b/src/wayland/meta-xwayland-private.h +@@ -20,6 +20,7 @@ + #include + + #include "wayland/meta-wayland-private.h" ++#include "wayland/meta-xwayland.h" + + gboolean + meta_xwayland_init (MetaXWaylandManager *manager, +diff --git a/src/wayland/meta-xwayland-surface.c b/src/wayland/meta-xwayland-surface.c +index 8fa1c72a9..c6daf9b26 100644 +--- a/src/wayland/meta-xwayland-surface.c ++++ b/src/wayland/meta-xwayland-surface.c +@@ -163,13 +163,19 @@ meta_xwayland_surface_get_relative_coordinates (MetaWaylandSurfaceRole *surface_ + float *out_sy) + { + MetaXwaylandSurface *xwayland_surface = META_XWAYLAND_SURFACE (surface_role); ++ MetaWaylandSurface *surface = ++ meta_wayland_surface_role_get_surface (surface_role); ++ MetaWaylandCompositor *compositor = ++ meta_wayland_surface_get_compositor (surface); + MtkRectangle window_rect = { 0 }; ++ int xwayland_scale; + + if (xwayland_surface->window) + meta_window_get_buffer_rect (xwayland_surface->window, &window_rect); + +- *out_sx = abs_x - window_rect.x; +- *out_sy = abs_y - window_rect.y; ++ xwayland_scale = meta_xwayland_get_effective_scale (&compositor->xwayland_manager); ++ *out_sx = (abs_x - window_rect.x) * xwayland_scale; ++ *out_sy = (abs_y - window_rect.y) * xwayland_scale; + } + + static MetaWaylandSurface * +diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c +index ea9c27d74..828e6f64e 100644 +--- a/src/wayland/meta-xwayland.c ++++ b/src/wayland/meta-xwayland.c +@@ -1051,6 +1051,29 @@ meta_xwayland_shutdown (MetaWaylandCompositor *compositor) + } + } + ++static void ++update_highest_monitor_scale (MetaXWaylandManager *manager) ++{ ++ MetaWaylandCompositor *compositor = manager->compositor; ++ MetaContext *context = meta_wayland_compositor_get_context (compositor); ++ MetaBackend *backend = meta_context_get_backend (context); ++ MetaMonitorManager *monitor_manager = ++ meta_backend_get_monitor_manager (backend); ++ GList *logical_monitors; ++ GList *l; ++ double scale = 1.0; ++ ++ logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); ++ for (l = logical_monitors; l; l = l->next) ++ { ++ MetaLogicalMonitor *logical_monitor = l->data; ++ ++ scale = MAX (scale, meta_logical_monitor_get_scale (logical_monitor)); ++ } ++ ++ manager->highest_monitor_scale = scale; ++} ++ + gboolean + meta_xwayland_init (MetaXWaylandManager *manager, + MetaWaylandCompositor *compositor, +@@ -1058,6 +1081,9 @@ meta_xwayland_init (MetaXWaylandManager *manager, + GError **error) + { + MetaContext *context = compositor->context; ++ MetaBackend *backend = meta_context_get_backend (context); ++ MetaMonitorManager *monitor_manager = ++ meta_backend_get_monitor_manager (backend); + MetaX11DisplayPolicy policy; + int display = 0; + +@@ -1121,6 +1147,10 @@ meta_xwayland_init (MetaXWaylandManager *manager, + /* Xwayland specific protocol, needs to be filtered out for all other clients */ + meta_xwayland_grab_keyboard_init (compositor); + ++ g_signal_connect_swapped (monitor_manager, "monitors-changed-internal", ++ G_CALLBACK (update_highest_monitor_scale), manager); ++ update_highest_monitor_scale (manager); ++ + return TRUE; + } + +@@ -1312,3 +1342,29 @@ meta_xwayland_set_should_enable_ei_portal (MetaXWaylandManager *manager, + { + manager->should_enable_ei_portal = should_enable_ei_portal; + } ++ ++int ++meta_xwayland_get_effective_scale (MetaXWaylandManager *manager) ++{ ++ MetaWaylandCompositor *compositor = manager->compositor; ++ MetaContext *context = meta_wayland_compositor_get_context (compositor); ++ MetaBackend *backend = meta_context_get_backend (context); ++ MetaMonitorManager *monitor_manager = ++ meta_backend_get_monitor_manager (backend); ++ MetaSettings *settings = meta_backend_get_settings (backend); ++ ++ switch (meta_monitor_manager_get_layout_mode (monitor_manager)) ++ { ++ case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: ++ break; ++ ++ case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: ++ if (meta_settings_is_experimental_feature_enabled (settings, ++ META_EXPERIMENTAL_FEATURE_XWAYLAND_NATIVE_SCALING) && ++ meta_settings_is_experimental_feature_enabled (settings, ++ META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER)) ++ return ceil (manager->highest_monitor_scale); ++ } ++ ++ return 1; ++} +diff --git a/src/wayland/meta-xwayland.h b/src/wayland/meta-xwayland.h +index daf9d1abb..ae7a06977 100644 +--- a/src/wayland/meta-xwayland.h ++++ b/src/wayland/meta-xwayland.h +@@ -48,3 +48,5 @@ META_EXPORT_TEST + gboolean meta_xwayland_signal (MetaXWaylandManager *manager, + int signum, + GError **error); ++ ++int meta_xwayland_get_effective_scale (MetaXWaylandManager *manager); +diff --git a/src/x11/meta-x11-display.c b/src/x11/meta-x11-display.c +index 5c0760daa..f67e9e427 100644 +--- a/src/x11/meta-x11-display.c ++++ b/src/x11/meta-x11-display.c +@@ -70,7 +70,7 @@ + #include "wayland/meta-xwayland-private.h" + #endif + +-G_DEFINE_TYPE (MetaX11Display, meta_x11_display, G_TYPE_OBJECT) ++#include "meta-dbus-x11.h" + + static GQuark quark_x11_display_logical_monitor_data = 0; + +@@ -89,6 +89,14 @@ typedef struct _MetaX11DisplayLogicalMonitorData + int xinerama_index; + } MetaX11DisplayLogicalMonitorData; + ++typedef struct _MetaX11DisplayPrivate ++{ ++ MetaDBusX11 *dbus_api; ++ guint dbus_name_id; ++} MetaX11DisplayPrivate; ++ ++G_DEFINE_TYPE_WITH_PRIVATE (MetaX11Display, meta_x11_display, G_TYPE_OBJECT) ++ + static char *get_screen_name (Display *xdisplay, + int number); + +@@ -122,6 +130,42 @@ backend_from_x11_display (MetaX11Display *x11_display) + return meta_context_get_backend (context); + } + ++static void ++stage_to_protocol (MetaX11Display *x11_display, ++ int stage_x, ++ int stage_y, ++ int *protocol_x, ++ int *protocol_y) ++{ ++ MetaDisplay *display = meta_x11_display_get_display (x11_display); ++ MetaContext *context = meta_display_get_context (display); ++ int scale = 1; ++ ++ switch (meta_context_get_compositor_type (context)) ++ { ++ case META_COMPOSITOR_TYPE_WAYLAND: ++ { ++#ifdef HAVE_XWAYLAND ++ MetaWaylandCompositor *wayland_compositor = ++ meta_context_get_wayland_compositor (context); ++ MetaXWaylandManager *xwayland_manager = ++ &wayland_compositor->xwayland_manager; ++ ++ scale = meta_xwayland_get_effective_scale (xwayland_manager); ++#endif ++ break; ++ } ++ ++ case META_COMPOSITOR_TYPE_X11: ++ break; ++ } ++ ++ if (protocol_x) ++ *protocol_x = stage_x * scale; ++ if (protocol_y) ++ *protocol_y = stage_y * scale; ++} ++ + static void + meta_x11_display_unmanage_windows (MetaX11Display *x11_display) + { +@@ -151,13 +195,68 @@ meta_x11_event_filter_free (MetaX11EventFilter *filter) + g_free (filter); + } + ++static void ++on_bus_acquired (GDBusConnection *connection, ++ const char *name, ++ gpointer user_data) ++{ ++ MetaX11Display *x11_display = user_data; ++ MetaX11DisplayPrivate *priv = ++ meta_x11_display_get_instance_private (x11_display); ++ ++ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->dbus_api), ++ connection, ++ "/org/gnome/Mutter/X11", ++ NULL); ++} ++ ++static void ++update_ui_scaling_factor (MetaX11Display *x11_display) ++{ ++ MetaX11DisplayPrivate *priv = ++ meta_x11_display_get_instance_private (x11_display); ++ MetaBackend *backend = backend_from_x11_display (x11_display); ++ MetaContext *context = meta_backend_get_context (backend); ++ int ui_scaling_factor = 1; ++ ++ switch (meta_context_get_compositor_type (context)) ++ { ++ case META_COMPOSITOR_TYPE_WAYLAND: ++ { ++#ifdef HAVE_XWAYLAND ++ MetaWaylandCompositor *wayland_compositor = ++ meta_context_get_wayland_compositor (context); ++ MetaXWaylandManager *xwayland_manager = ++ &wayland_compositor->xwayland_manager; ++ ++ ui_scaling_factor = meta_xwayland_get_effective_scale (xwayland_manager); ++#endif ++ break; ++ } ++ case META_COMPOSITOR_TYPE_X11: ++ { ++ MetaSettings *settings = meta_backend_get_settings (backend); ++ ++ ui_scaling_factor = meta_settings_get_ui_scaling_factor (settings); ++ break; ++ } ++ } ++ ++ meta_dbus_x11_set_ui_scaling_factor (priv->dbus_api, ui_scaling_factor); ++} ++ + static void + meta_x11_display_dispose (GObject *object) + { + MetaX11Display *x11_display = META_X11_DISPLAY (object); ++ MetaX11DisplayPrivate *priv = ++ meta_x11_display_get_instance_private (x11_display); + + x11_display->closing = TRUE; + ++ g_clear_handle_id (&priv->dbus_name_id, g_bus_unown_name); ++ g_clear_object (&priv->dbus_api); ++ + g_clear_pointer (&x11_display->alarm_filters, g_ptr_array_unref); + + g_clear_list (&x11_display->event_funcs, +@@ -600,6 +699,9 @@ set_desktop_geometry_hint (MetaX11Display *x11_display) + return; + + meta_display_get_size (x11_display->display, &monitor_width, &monitor_height); ++ stage_to_protocol (x11_display, ++ monitor_width, monitor_height, ++ &monitor_width, &monitor_height); + + data[0] = monitor_width; + data[1] = monitor_height; +@@ -1009,14 +1111,22 @@ set_workspace_work_area_hint (MetaWorkspace *workspace, + + for (l = logical_monitors; l; l = l->next) + { +- MtkRectangle area; ++ MtkRectangle stage_area; ++ MtkRectangle protocol_area; + +- meta_workspace_get_work_area_for_logical_monitor (workspace, l->data, &area); ++ meta_workspace_get_work_area_for_logical_monitor (workspace, l->data, ++ &stage_area); + +- tmp[0] = area.x; +- tmp[1] = area.y; +- tmp[2] = area.width; +- tmp[3] = area.height; ++ stage_to_protocol (x11_display, ++ stage_area.x, stage_area.y, ++ &protocol_area.x, &protocol_area.y); ++ stage_to_protocol (x11_display, ++ stage_area.width, stage_area.height, ++ &protocol_area.width, &protocol_area.height); ++ tmp[0] = protocol_area.x; ++ tmp[1] = protocol_area.y; ++ tmp[2] = protocol_area.width; ++ tmp[3] = protocol_area.height; + + tmp += 4; + } +@@ -1045,7 +1155,6 @@ set_work_area_hint (MetaDisplay *display, + int num_workspaces; + GList *l; + unsigned long *data, *tmp; +- MtkRectangle area; + + num_workspaces = meta_workspace_manager_get_n_workspaces (workspace_manager); + data = g_new (unsigned long, num_workspaces * 4); +@@ -1054,14 +1163,22 @@ set_work_area_hint (MetaDisplay *display, + for (l = workspace_manager->workspaces; l; l = l->next) + { + MetaWorkspace *workspace = l->data; ++ MtkRectangle stage_area; ++ MtkRectangle protocol_area; + +- meta_workspace_get_work_area_all_monitors (workspace, &area); ++ meta_workspace_get_work_area_all_monitors (workspace, &stage_area); + set_workspace_work_area_hint (workspace, x11_display); + +- tmp[0] = area.x; +- tmp[1] = area.y; +- tmp[2] = area.width; +- tmp[3] = area.height; ++ stage_to_protocol (x11_display, ++ stage_area.x, stage_area.y, ++ &protocol_area.x, &protocol_area.y); ++ stage_to_protocol (x11_display, ++ stage_area.width, stage_area.height, ++ &protocol_area.width, &protocol_area.height); ++ tmp[0] = protocol_area.x; ++ tmp[1] = protocol_area.y; ++ tmp[2] = protocol_area.width; ++ tmp[3] = protocol_area.height; + + tmp += 4; + } +@@ -1224,6 +1341,58 @@ meta_x11_display_init_frames_client (MetaX11Display *x11_display) + on_frames_client_died, x11_display); + } + ++static void ++initialize_dbus_interface (MetaX11Display *x11_display) ++{ ++ MetaX11DisplayPrivate *priv = ++ meta_x11_display_get_instance_private (x11_display); ++ ++ priv->dbus_api = meta_dbus_x11_skeleton_new (); ++ priv->dbus_name_id = ++ g_bus_own_name (G_BUS_TYPE_SESSION, ++ "org.gnome.Mutter.X11", ++ G_BUS_NAME_OWNER_FLAGS_NONE, ++ on_bus_acquired, ++ NULL, NULL, ++ x11_display, NULL); ++ update_ui_scaling_factor (x11_display); ++} ++ ++static void ++experimental_features_changed (MetaSettings *settings, ++ MetaExperimentalFeature old_experimental_features, ++ MetaX11Display *x11_display) ++{ ++ gboolean was_xwayland_native_scaling; ++ gboolean was_stage_views_scaled; ++ gboolean is_xwayland_native_scaling; ++ gboolean is_stage_views_scaled; ++ ++ was_xwayland_native_scaling = ++ !!(old_experimental_features & ++ META_EXPERIMENTAL_FEATURE_XWAYLAND_NATIVE_SCALING); ++ was_stage_views_scaled = ++ !!(old_experimental_features & ++ META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER); ++ ++ is_xwayland_native_scaling = ++ meta_settings_is_experimental_feature_enabled ( ++ settings, ++ META_EXPERIMENTAL_FEATURE_XWAYLAND_NATIVE_SCALING); ++ is_stage_views_scaled = ++ meta_settings_is_experimental_feature_enabled ( ++ settings, ++ META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER); ++ ++ if (is_xwayland_native_scaling != was_xwayland_native_scaling || ++ is_stage_views_scaled != was_stage_views_scaled) ++ { ++ update_ui_scaling_factor (x11_display); ++ set_desktop_geometry_hint (x11_display); ++ set_work_area_hint (x11_display->display, x11_display); ++ } ++} ++ + /** + * meta_x11_display_new: + * +@@ -1242,6 +1411,7 @@ meta_x11_display_new (MetaDisplay *display, + MetaBackend *backend = meta_context_get_backend (context); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); ++ MetaSettings *settings = meta_backend_get_settings (backend); + g_autoptr (MetaX11Display) x11_display = NULL; + Display *xdisplay; + Screen *xscreen; +@@ -1318,6 +1488,8 @@ meta_x11_display_new (MetaDisplay *display, + x11_display = g_object_new (META_TYPE_X11_DISPLAY, NULL); + x11_display->display = display; + ++ initialize_dbus_interface (x11_display); ++ + /* here we use XDisplayName which is what the user + * probably put in, vs. DisplayString(display) which is + * canonicalized by XOpenDisplay() +@@ -1410,7 +1582,7 @@ meta_x11_display_new (MetaDisplay *display, + "monitors-changed-internal", + G_CALLBACK (on_monitors_changed_internal), + x11_display, +- 0); ++ G_CONNECT_AFTER); + + init_leader_window (x11_display, ×tamp); + x11_display->timestamp = timestamp; +@@ -1503,6 +1675,11 @@ meta_x11_display_new (MetaDisplay *display, + + meta_prefs_add_listener (prefs_changed_callback, x11_display); + ++ g_signal_connect_object (settings, ++ "experimental-features-changed", ++ G_CALLBACK (experimental_features_changed), ++ x11_display, 0); ++ + set_work_area_hint (display, x11_display); + + g_signal_connect_object (display, "workareas-changed", +@@ -1711,16 +1888,12 @@ meta_x11_display_reload_cursor (MetaX11Display *x11_display) + } + + static void +-set_cursor_theme (Display *xdisplay, +- MetaBackend *backend) ++set_cursor_theme (Display *xdisplay, ++ const char *theme, ++ int size) + { +- MetaSettings *settings = meta_backend_get_settings (backend); +- int scale; +- +- scale = meta_settings_get_ui_scaling_factor (settings); +- XcursorSetTheme (xdisplay, meta_prefs_get_cursor_theme ()); +- XcursorSetDefaultSize (xdisplay, +- meta_prefs_get_cursor_size () * scale); ++ XcursorSetTheme (xdisplay, theme); ++ XcursorSetDefaultSize (xdisplay, size); + } + + static void +@@ -1772,8 +1945,37 @@ static void + update_cursor_theme (MetaX11Display *x11_display) + { + MetaBackend *backend = backend_from_x11_display (x11_display); ++ MetaContext *context = meta_backend_get_context (backend); ++ MetaSettings *settings = meta_backend_get_settings (backend); ++ int scale = 1; ++ int size; ++ const char *theme; ++ ++ switch (meta_context_get_compositor_type (context)) ++ { ++ case META_COMPOSITOR_TYPE_WAYLAND: ++ { ++#ifdef HAVE_XWAYLAND ++ MetaWaylandCompositor *wayland_compositor = ++ meta_context_get_wayland_compositor (context); ++ MetaXWaylandManager *xwayland_manager = ++ &wayland_compositor->xwayland_manager; + +- set_cursor_theme (x11_display->xdisplay, backend); ++ scale = meta_xwayland_get_effective_scale (xwayland_manager); ++#endif ++ break; ++ } ++ ++ case META_COMPOSITOR_TYPE_X11: ++ scale = meta_settings_get_ui_scaling_factor (settings); ++ break; ++ } ++ ++ size = meta_prefs_get_cursor_size () * scale; ++ ++ theme = meta_prefs_get_cursor_theme (); ++ ++ set_cursor_theme (x11_display->xdisplay, theme, size); + schedule_reload_x11_cursor (x11_display); + + if (META_IS_BACKEND_X11 (backend)) +@@ -1781,7 +1983,7 @@ update_cursor_theme (MetaX11Display *x11_display) + MetaBackendX11 *backend_x11 = META_BACKEND_X11 (backend); + Display *xdisplay = meta_backend_x11_get_xdisplay (backend_x11); + +- set_cursor_theme (xdisplay, backend); ++ set_cursor_theme (xdisplay, theme, size); + meta_backend_x11_reload_cursor (backend_x11); + } + } +@@ -1974,6 +2176,8 @@ on_monitors_changed_internal (MetaMonitorManager *monitor_manager, + } + + x11_display->has_xinerama_indices = FALSE; ++ ++ update_ui_scaling_factor (x11_display); + } + + static Bool +diff --git a/src/x11/window-props.c b/src/x11/window-props.c +index c18b3eab5..494fbe843 100644 +--- a/src/x11/window-props.c ++++ b/src/x11/window-props.c +@@ -305,10 +305,15 @@ reload_icon_geometry (MetaWindow *window, + { + MtkRectangle geometry; + +- geometry.x = (int)value->v.cardinal_list.cardinals[0]; +- geometry.y = (int)value->v.cardinal_list.cardinals[1]; +- geometry.width = (int)value->v.cardinal_list.cardinals[2]; +- geometry.height = (int)value->v.cardinal_list.cardinals[3]; ++ meta_window_x11_protocol_to_stage (META_WINDOW_X11 (window), ++ value->v.cardinal_list.cardinals[0], ++ value->v.cardinal_list.cardinals[1], ++ value->v.cardinal_list.cardinals[2], ++ value->v.cardinal_list.cardinals[3], ++ &geometry.x, ++ &geometry.y, ++ &geometry.width, ++ &geometry.height); + + meta_window_set_icon_geometry (window, &geometry); + } +@@ -370,11 +375,24 @@ reload_gtk_frame_extents (MetaWindow *window, + } + else + { ++ int left, right, top, bottom; + MetaFrameBorder extents; +- extents.left = (int)value->v.cardinal_list.cardinals[0]; +- extents.right = (int)value->v.cardinal_list.cardinals[1]; +- extents.top = (int)value->v.cardinal_list.cardinals[2]; +- extents.bottom = (int)value->v.cardinal_list.cardinals[3]; ++ ++ meta_window_x11_protocol_to_stage (META_WINDOW_X11 (window), ++ value->v.cardinal_list.cardinals[0], ++ value->v.cardinal_list.cardinals[1], ++ value->v.cardinal_list.cardinals[2], ++ value->v.cardinal_list.cardinals[3], ++ &left, ++ &right, ++ &top, ++ &bottom); ++ ++ extents.left = left; ++ extents.right = right; ++ extents.top = top; ++ extents.bottom = bottom; ++ + meta_window_set_custom_frame_extents (window, &extents, initial); + } + } +@@ -678,10 +696,16 @@ reload_opaque_region (MetaWindow *window, + { + MtkRectangle *rect = &rects[rect_index]; + +- rect->x = region[i++]; +- rect->y = region[i++]; +- rect->width = region[i++]; +- rect->height = region[i++]; ++ meta_window_x11_protocol_to_stage (META_WINDOW_X11 (window), ++ region[i + 0], ++ region[i + 1], ++ region[i + 2], ++ region[i + 3], ++ &rect->x, ++ &rect->y, ++ &rect->width, ++ &rect->height); ++ i += 4; + + rect_index++; + } +@@ -1245,9 +1269,65 @@ meta_set_normal_hints (MetaWindow *window, + * as if flags were zero + */ + if (hints) +- window->size_hints = *(MetaSizeHints*)(hints); ++ { ++ MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); ++ ++ window->size_hints = *(MetaSizeHints *) hints; ++ ++ meta_window_x11_protocol_to_stage (window_x11, ++ hints->x, hints->y, ++ hints->width, hints->height, ++ &window->size_hints.x, ++ &window->size_hints.y, ++ &window->size_hints.width, ++ &window->size_hints.height); ++ ++ meta_window_x11_protocol_to_stage (window_x11, ++ hints->min_width, hints->min_height, ++ 0, 0, ++ &window->size_hints.min_width, ++ &window->size_hints.min_height, ++ NULL, NULL); ++ ++ meta_window_x11_protocol_to_stage (window_x11, ++ hints->max_width, hints->max_height, ++ 0, 0, ++ &window->size_hints.max_width, ++ &window->size_hints.max_height, ++ NULL, NULL); ++ ++ meta_window_x11_protocol_to_stage (window_x11, ++ hints->width_inc, hints->height_inc, ++ 0, 0, ++ &window->size_hints.width_inc, ++ &window->size_hints.height_inc, ++ NULL, NULL); ++ ++ meta_window_x11_protocol_to_stage (window_x11, ++ hints->min_aspect.x, hints->min_aspect.y, ++ 0, 0, ++ &window->size_hints.min_aspect.x, ++ &window->size_hints.min_aspect.y, ++ NULL, NULL); ++ ++ meta_window_x11_protocol_to_stage (window_x11, ++ hints->max_aspect.x, hints->max_aspect.y, ++ 0, 0, ++ &window->size_hints.max_aspect.x, ++ &window->size_hints.max_aspect.y, ++ NULL, NULL); ++ ++ meta_window_x11_protocol_to_stage (window_x11, ++ hints->base_width, hints->base_height, ++ 0, 0, ++ &window->size_hints.base_width, ++ &window->size_hints.base_height, ++ NULL, NULL); ++ } + else +- window->size_hints.flags = 0; ++ { ++ window->size_hints.flags = 0; ++ } + + /* Put back saved ConfigureRequest. */ + window->size_hints.x = x; +diff --git a/src/x11/window-x11.c b/src/x11/window-x11.c +index b15ddfb5d..fd1a8ec1b 100644 +--- a/src/x11/window-x11.c ++++ b/src/x11/window-x11.c +@@ -110,6 +110,113 @@ meta_window_x11_get_private (MetaWindowX11 *window_x11) + return meta_window_x11_get_instance_private (window_x11); + } + ++static void ++meta_window_x11_real_stage_to_protocol (MetaWindowX11 *window_x11, ++ int stage_x, ++ int stage_y, ++ int stage_width, ++ int stage_height, ++ int *protocol_x, ++ int *protocol_y, ++ int *protocol_width, ++ int *protocol_height) ++{ ++ if (protocol_x) ++ *protocol_x = stage_x; ++ if (protocol_y) ++ *protocol_y = stage_y; ++ if (protocol_width) ++ *protocol_width = stage_width; ++ if (protocol_height) ++ *protocol_height = stage_height; ++} ++ ++static void ++meta_window_x11_real_protocol_to_stage (MetaWindowX11 *window_x11, ++ int protocol_x, ++ int protocol_y, ++ int protocol_width, ++ int protocol_height, ++ int *stage_x, ++ int *stage_y, ++ int *stage_width, ++ int *stage_height) ++{ ++ if (stage_x) ++ *stage_x = protocol_x; ++ if (stage_y) ++ *stage_y = protocol_y; ++ if (stage_width) ++ *stage_width = protocol_width; ++ if (stage_height) ++ *stage_height = protocol_height; ++} ++ ++void ++meta_window_x11_stage_to_protocol (MetaWindowX11 *window_x11, ++ int stage_x, ++ int stage_y, ++ int stage_width, ++ int stage_height, ++ int *protocol_x, ++ int *protocol_y, ++ int *protocol_width, ++ int *protocol_height) ++{ ++ MetaWindowX11Class *klass = META_WINDOW_X11_GET_CLASS (window_x11); ++ ++ klass->stage_to_protocol (window_x11, ++ stage_x, stage_y, ++ stage_width, stage_height, ++ protocol_x, protocol_y, ++ protocol_width, protocol_height); ++} ++ ++void ++meta_window_x11_protocol_to_stage (MetaWindowX11 *window_x11, ++ int protocol_x, ++ int protocol_y, ++ int protocol_width, ++ int protocol_height, ++ int *stage_x, ++ int *stage_y, ++ int *stage_width, ++ int *stage_height) ++{ ++ MetaWindowX11Class *klass = META_WINDOW_X11_GET_CLASS (window_x11); ++ ++ klass->protocol_to_stage (window_x11, ++ protocol_x, protocol_y, ++ protocol_width, protocol_height, ++ stage_x, stage_y, ++ stage_width, stage_height); ++} ++ ++static MtkRegion * ++region_protocol_to_stage (MtkRegion *region, ++ MetaWindowX11 *window_x11) ++{ ++ int n_rects, i; ++ MtkRectangle *rects; ++ MtkRegion *scaled_region; ++ ++ n_rects = mtk_region_num_rectangles (region); ++ MTK_RECTANGLE_CREATE_ARRAY_SCOPED (n_rects, rects); ++ for (i = 0; i < n_rects; i++) ++ { ++ rects[i] = mtk_region_get_rectangle (region, i); ++ meta_window_x11_protocol_to_stage (window_x11, ++ rects[i].x, rects[i].y, ++ rects[i].width, rects[i].height, ++ &rects[i].x, &rects[i].y, ++ &rects[i].width, &rects[i].height); ++ } ++ ++ scaled_region = mtk_region_create_rectangles (rects, n_rects); ++ ++ return scaled_region; ++} ++ + static void + send_icccm_message (MetaWindow *window, + Atom atom, +@@ -254,8 +361,13 @@ send_configure_notify (MetaWindow *window) + event.xconfigure.display = x11_display->xdisplay; + event.xconfigure.event = priv->xwindow; + event.xconfigure.window = priv->xwindow; +- event.xconfigure.x = priv->client_rect.x - priv->border_width; +- event.xconfigure.y = priv->client_rect.y - priv->border_width; ++ meta_window_x11_stage_to_protocol (window_x11, ++ priv->client_rect.x - priv->border_width, ++ priv->client_rect.y - priv->border_width, ++ 0, 0, ++ &event.xconfigure.x, ++ &event.xconfigure.y, ++ NULL, NULL); + if (window->frame) + { + if (window->withdrawn) +@@ -267,19 +379,42 @@ send_configure_notify (MetaWindow *window) + + meta_frame_calc_borders (window->frame, &borders); + +- event.xconfigure.x = window->frame->rect.x + borders.invisible.left; +- event.xconfigure.y = window->frame->rect.y + borders.invisible.top; ++ meta_window_x11_stage_to_protocol (window_x11, ++ window->frame->rect.x + borders.invisible.left, ++ window->frame->rect.y + borders.invisible.top, ++ 0, 0, ++ &event.xconfigure.x, ++ &event.xconfigure.y, ++ NULL, NULL); + } + else + { ++ int dx, dy; ++ + /* Need to be in root window coordinates */ +- event.xconfigure.x += window->frame->rect.x; +- event.xconfigure.y += window->frame->rect.y; ++ meta_window_x11_stage_to_protocol (window_x11, ++ window->frame->rect.x, ++ window->frame->rect.y, ++ 0, 0, ++ &dx, ++ &dy, ++ NULL, NULL); ++ event.xconfigure.x += dx; ++ event.xconfigure.y += dy; + } + } +- event.xconfigure.width = priv->client_rect.width; +- event.xconfigure.height = priv->client_rect.height; +- event.xconfigure.border_width = priv->border_width; /* requested not actual */ ++ meta_window_x11_stage_to_protocol (window_x11, ++ priv->client_rect.width, ++ priv->client_rect.height, ++ 0, 0, ++ &event.xconfigure.width, ++ &event.xconfigure.height, ++ NULL, NULL); ++ meta_window_x11_stage_to_protocol (window_x11, ++ priv->border_width, ++ 0, 0, 0, ++ &event.xconfigure.border_width, ++ NULL, NULL, NULL); + event.xconfigure.above = None; /* FIXME */ + event.xconfigure.override_redirect = False; + +@@ -1137,20 +1272,26 @@ static void + update_net_frame_extents (MetaWindow *window) + { + MetaX11Display *x11_display = window->display->x11_display; +- ++ int left, right, top, bottom; + unsigned long data[4]; + MetaFrameBorders borders; + Window xwindow = meta_window_x11_get_xwindow (window); + + meta_frame_calc_borders (window->frame, &borders); +- /* Left */ +- data[0] = borders.visible.left; +- /* Right */ +- data[1] = borders.visible.right; +- /* Top */ +- data[2] = borders.visible.top; +- /* Bottom */ +- data[3] = borders.visible.bottom; ++ meta_window_x11_stage_to_protocol (META_WINDOW_X11 (window), ++ borders.visible.left, ++ borders.visible.right, ++ borders.visible.top, ++ borders.visible.bottom, ++ &left, ++ &right, ++ &top, ++ &bottom); ++ ++ data[0] = left; ++ data[1] = right; ++ data[2] = top; ++ data[3] = bottom; + + meta_topic (META_DEBUG_GEOMETRY, + "Setting _NET_FRAME_EXTENTS on managed window 0x%lx " +@@ -1482,10 +1623,11 @@ meta_window_x11_move_resize_internal (MetaWindow *window, + configure_frame_first = size_dx + size_dy >= 0; + + values.border_width = 0; +- values.x = client_rect.x; +- values.y = client_rect.y; +- values.width = client_rect.width; +- values.height = client_rect.height; ++ meta_window_x11_stage_to_protocol (window_x11, ++ client_rect.x, client_rect.y, ++ client_rect.width, client_rect.height, ++ &values.x, &values.y, ++ &values.width, &values.height); + + mask = 0; + if (is_configure_request && priv->border_width != 0) +@@ -1591,6 +1733,10 @@ meta_window_x11_update_struts (MetaWindow *window) + strut_begin = struts[4+(i*2)]; + strut_end = struts[4+(i*2)+1]; + ++ meta_window_x11_protocol_to_stage (META_WINDOW_X11 (window), ++ strut_begin, strut_end, thickness, 0, ++ &strut_begin, &strut_end, &thickness, NULL); ++ + temp = g_new0 (MetaStrut, 1); + temp->side = 1 << i; /* See MetaSide def. Matches nicely, eh? */ + meta_display_get_size (window->display, +@@ -1655,6 +1801,10 @@ meta_window_x11_update_struts (MetaWindow *window) + if (thickness == 0) + continue; + ++ meta_window_x11_protocol_to_stage (META_WINDOW_X11 (window), ++ thickness, 0, 0, 0, ++ &thickness, NULL, NULL, NULL); ++ + temp = g_new0 (MetaStrut, 1); + temp->side = 1 << i; + meta_display_get_size (window->display, +@@ -2040,9 +2190,10 @@ static void + meta_window_x11_constructed (GObject *object) + { + MetaWindow *window = META_WINDOW (object); +- MetaWindowX11 *x11_window = META_WINDOW_X11 (object); +- MetaWindowX11Private *priv = meta_window_x11_get_instance_private (x11_window); ++ MetaWindowX11 *window_x11 = META_WINDOW_X11 (object); ++ MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); + XWindowAttributes attrs = priv->attributes; ++ MtkRectangle rect; + + meta_verbose ("attrs->map_state = %d (%s)", + attrs.map_state, +@@ -2057,16 +2208,17 @@ meta_window_x11_constructed (GObject *object) + window->client_type = META_WINDOW_CLIENT_TYPE_X11; + window->override_redirect = attrs.override_redirect; + +- window->rect.x = attrs.x; +- window->rect.y = attrs.y; +- window->rect.width = attrs.width; +- window->rect.height = attrs.height; ++ meta_window_x11_protocol_to_stage (window_x11, ++ attrs.x, attrs.y, attrs.width, attrs.height, ++ &rect.x, &rect.y, &rect.width, &rect.height); ++ ++ window->rect = rect; + + /* size_hints are the "request" */ +- window->size_hints.x = attrs.x; +- window->size_hints.y = attrs.y; +- window->size_hints.width = attrs.width; +- window->size_hints.height = attrs.height; ++ window->size_hints.x = rect.x; ++ window->size_hints.y = rect.y; ++ window->size_hints.width = rect.width; ++ window->size_hints.height = rect.height; + + window->depth = attrs.depth; + priv->xvisual = attrs.visual; +@@ -2076,11 +2228,11 @@ meta_window_x11_constructed (GObject *object) + + window->decorated = TRUE; + window->hidden = FALSE; +- priv->border_width = attrs.border_width; + priv->xclient_leader = None; + +- priv->keys_grabbed = FALSE; +- priv->grab_on_frame = FALSE; ++ meta_window_x11_protocol_to_stage (window_x11, ++ attrs.border_width, 0, 0, 0, ++ &priv->border_width, NULL, NULL, NULL); + + G_OBJECT_CLASS (meta_window_x11_parent_class)->constructed (object); + } +@@ -2188,6 +2340,8 @@ meta_window_x11_class_init (MetaWindowX11Class *klass) + klass->thaw_commits = meta_window_x11_impl_thaw_commits; + klass->always_update_shape = meta_window_x11_impl_always_update_shape; + klass->process_property_notify = meta_window_x11_impl_process_property_notify; ++ klass->stage_to_protocol = meta_window_x11_real_stage_to_protocol; ++ klass->protocol_to_stage = meta_window_x11_real_protocol_to_stage; + + obj_props[PROP_ATTRIBUTES] = + g_param_spec_pointer ("attributes", NULL, NULL, +@@ -2473,7 +2627,10 @@ meta_window_x11_update_input_region (MetaWindow *window) + else + { + /* Window has a custom shape. */ +- region = region_create_from_x_rectangles (rects, n_rects); ++ g_autoptr (MtkRegion) protocol_region = NULL; ++ ++ protocol_region = region_create_from_x_rectangles (rects, n_rects); ++ region = region_protocol_to_stage (protocol_region, window_x11); + } + + meta_XFree (rects); +@@ -2550,7 +2707,10 @@ meta_window_x11_update_shape_region (MetaWindow *window) + + if (rects) + { +- region = region_create_from_x_rectangles (rects, n_rects); ++ g_autoptr (MtkRegion) protocol_region = NULL; ++ ++ protocol_region = region_create_from_x_rectangles (rects, n_rects); ++ region = region_protocol_to_stage (protocol_region, window_x11); + XFree (rects); + } + } +@@ -2828,6 +2988,7 @@ meta_window_x11_configure_request (MetaWindow *window, + { + MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); + MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); ++ int new_x, new_y, new_width, new_height; + + /* Note that x, y is the corner of the window border, + * and width, height is the size of the window inside +@@ -2836,15 +2997,25 @@ meta_window_x11_configure_request (MetaWindow *window, + * requested border here. + */ + if (event->xconfigurerequest.value_mask & CWBorderWidth) +- priv->border_width = event->xconfigurerequest.border_width; ++ { ++ meta_window_x11_protocol_to_stage (window_x11, ++ event->xconfigurerequest.border_width, 0, 0, 0, ++ &priv->border_width, NULL, NULL, NULL); ++ } + +- meta_window_move_resize_request(window, +- event->xconfigurerequest.value_mask, +- window->size_hints.win_gravity, +- event->xconfigurerequest.x, +- event->xconfigurerequest.y, +- event->xconfigurerequest.width, +- event->xconfigurerequest.height); ++ meta_window_x11_protocol_to_stage (window_x11, ++ event->xconfigurerequest.x, event->xconfigurerequest.y, ++ event->xconfigurerequest.width, event->xconfigurerequest.height, ++ &new_x, &new_y, ++ &new_width, &new_height); ++ ++ meta_window_move_resize_request (window, ++ event->xconfigurerequest.value_mask, ++ window->size_hints.win_gravity, ++ new_x, ++ new_y, ++ new_width, ++ new_height); + + /* Handle stacking. We only handle raises/lowers, mostly because + * stack.c really can't deal with anything else. I guess we'll fix +@@ -3339,8 +3510,13 @@ meta_window_x11_client_message (MetaWindow *window, + guint32 timestamp; + MetaWindowDrag *window_drag; + +- x_root = event->xclient.data.l[0]; +- y_root = event->xclient.data.l[1]; ++ meta_window_x11_protocol_to_stage (window_x11, ++ event->xclient.data.l[0], ++ event->xclient.data.l[1], ++ 0, 0, ++ &x_root, ++ &y_root, ++ NULL, NULL); + action = event->xclient.data.l[2]; + button = event->xclient.data.l[3]; + +@@ -3504,6 +3680,7 @@ meta_window_x11_client_message (MetaWindow *window, + { + MetaGravity gravity; + guint value_mask; ++ int x, y, width, height; + + gravity = (MetaGravity) (event->xclient.data.l[0] & 0xff); + value_mask = (event->xclient.data.l[0] & 0xf00) >> 8; +@@ -3512,13 +3689,20 @@ meta_window_x11_client_message (MetaWindow *window, + if (gravity == 0) + gravity = window->size_hints.win_gravity; + ++ meta_window_x11_protocol_to_stage (window_x11, ++ event->xclient.data.l[1], ++ event->xclient.data.l[2], ++ event->xclient.data.l[3], ++ event->xclient.data.l[4], ++ &x, &y, &width, &height); ++ + meta_window_move_resize_request(window, + value_mask, + gravity, +- event->xclient.data.l[1], /* x */ +- event->xclient.data.l[2], /* y */ +- event->xclient.data.l[3], /* width */ +- event->xclient.data.l[4]); /* height */ ++ x, ++ y, ++ width, ++ height); + } + else if (event->xclient.message_type == + x11_display->atom__NET_ACTIVE_WINDOW && +@@ -3575,11 +3759,15 @@ meta_window_x11_client_message (MetaWindow *window, + else if (event->xclient.message_type == + x11_display->atom__GTK_SHOW_WINDOW_MENU) + { +- gulong x, y; ++ int x, y; + + /* l[0] is device_id, which we don't use */ +- x = event->xclient.data.l[1]; +- y = event->xclient.data.l[2]; ++ meta_window_x11_protocol_to_stage (window_x11, ++ event->xclient.data.l[1], ++ event->xclient.data.l[2], ++ 0, 0, ++ &x, &y, ++ NULL, NULL); + + meta_window_show_menu (window, META_WINDOW_MENU_WM, x, y); + } +@@ -4101,10 +4289,11 @@ meta_window_x11_configure_notify (MetaWindow *window, + g_assert (window->override_redirect); + g_assert (window->frame == NULL); + +- window->rect.x = event->x; +- window->rect.y = event->y; +- window->rect.width = event->width; +- window->rect.height = event->height; ++ meta_window_x11_protocol_to_stage (window_x11, ++ event->x, event->y, ++ event->width, event->height, ++ &window->rect.x, &window->rect.y, ++ &window->rect.width, &window->rect.height); + + priv->client_rect = window->rect; + window->buffer_rect = window->rect; +diff --git a/src/x11/window-x11.h b/src/x11/window-x11.h +index 205eaaa63..fa3fbea6a 100644 +--- a/src/x11/window-x11.h ++++ b/src/x11/window-x11.h +@@ -45,6 +45,24 @@ struct _MetaWindowX11Class + gboolean (*always_update_shape) (MetaWindow *window); + void (*process_property_notify) (MetaWindow *window, + XPropertyEvent *event); ++ void (*stage_to_protocol) (MetaWindowX11 *window_x11, ++ int stage_x, ++ int stage_y, ++ int stage_width, ++ int stage_height, ++ int *protocol_x, ++ int *protocol_y, ++ int *protocol_width, ++ int *protocol_height); ++ void (*protocol_to_stage) (MetaWindowX11 *window_x11, ++ int protocol_x, ++ int protocol_y, ++ int protocol_width, ++ int protocol_height, ++ int *stage_x, ++ int *stage_y, ++ int *stage_width, ++ int *stage_height); + }; + + MetaWindow * meta_window_x11_new (MetaDisplay *display, +@@ -112,3 +130,23 @@ gboolean meta_window_x11_has_alpha_channel (MetaWindow *window); + + META_EXPORT + Window meta_window_x11_get_xwindow (MetaWindow *window); ++ ++void meta_window_x11_stage_to_protocol (MetaWindowX11 *window_x11, ++ int stage_x, ++ int stage_y, ++ int stage_width, ++ int stage_heigth, ++ int *protocol_x, ++ int *protocol_y, ++ int *protocol_width, ++ int *protocol_height); ++ ++void meta_window_x11_protocol_to_stage (MetaWindowX11 *window_x11, ++ int protocol_x, ++ int protocol_y, ++ int protocol_width, ++ int protocol_height, ++ int *stage_x, ++ int *stage_y, ++ int *stage_width, ++ int *stage_heigth); diff --git a/spec_files/mutter/mutter-42.alpha-disable-tegra.patch b/spec_files/mutter/mutter-42.alpha-disable-tegra.patch new file mode 100644 index 00000000..3225c8f5 --- /dev/null +++ b/spec_files/mutter/mutter-42.alpha-disable-tegra.patch @@ -0,0 +1,25 @@ +From a5c67e0debaa89f7a73452560664cdc5c581ab95 Mon Sep 17 00:00:00 2001 +From: Adam Williamson +Date: Tue, 9 Mar 2021 17:21:59 -0800 +Subject: [PATCH] Test: deny atomic KMS for "tegra" (RHBZ #1936991) + +Signed-off-by: Adam Williamson +--- + src/backends/native/meta-kms-impl-device-atomic.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/backends/native/meta-kms-impl-device-atomic.c b/src/backends/native/meta-kms-impl-device-atomic.c +index 35837f7429..ffff7b8e23 100644 +--- a/src/backends/native/meta-kms-impl-device-atomic.c ++++ b/src/backends/native/meta-kms-impl-device-atomic.c +@@ -1352,6 +1352,7 @@ is_atomic_allowed (const char *driver_name) + { + const char *atomic_driver_deny_list[] = { + "xlnx", ++ "tegra", + NULL, + }; + +-- +2.43.0 + diff --git a/spec_files/mutter/mutter.spec b/spec_files/mutter/mutter.spec new file mode 100644 index 00000000..81a97ee5 --- /dev/null +++ b/spec_files/mutter/mutter.spec @@ -0,0 +1,223 @@ +%global glib_version 2.75.1 +%global gtk3_version 3.19.8 +%global gtk4_version 4.0.0 +%global gsettings_desktop_schemas_version 40~alpha +%global json_glib_version 0.12.0 +%global libinput_version 1.19.0 +%global pipewire_version 0.3.33 +%global lcms2_version 2.6 +%global colord_version 1.4.5 +%global libei_version 1.0.0 +%global mutter_api_version 14 + +%global gnome_major_version 46 +%global gnome_version %{gnome_major_version}.1 +%global tarball_version %%(echo %{gnome_version} | tr '~' '.') +%global _default_patch_fuzz 2 + +Name: mutter +Version: %{gnome_version}.ublue.{{{ git_dir_version }}} +Release: 2%{?dist} +Summary: Window and compositing manager based on Clutter + +License: GPLv2+ +URL: http://www.gnome.org +Source0: https://download.gnome.org/sources/%{name}/%{gnome_major_version}/%{name}-%{tarball_version}.tar.xz + +# Work-around for OpenJDK's compliance test +Patch0: 0001-window-actor-Special-case-shaped-Java-windows.patch + +# https://bugzilla.redhat.com/show_bug.cgi?id=1936991 +Patch1: mutter-42.alpha-disable-tegra.patch + +# https://pagure.io/fedora-workstation/issue/79 +Patch2: 0001-place-Always-center-initial-setup-fedora-welcome.patch + +# https://bugzilla.redhat.com/show_bug.cgi?id=2239128 +# https://gitlab.gnome.org/GNOME/mutter/-/issues/3068 +# not upstreamed because for upstream we'd really want to find a way +# to fix *both* problems +Patch3: 0001-Revert-x11-Use-input-region-from-frame-window-for-de.patch + +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3329 +# Modified to add the change from +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3329#note_1874837 +# which solves the problems reported with #3329 alone +Patch4: 0001-modified-3329.patch + +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1441 +Patch5: 1441.patch + +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3567 +Patch6: 3720+3567.patch + +BuildRequires: pkgconfig(gobject-introspection-1.0) >= 1.41.0 +BuildRequires: pkgconfig(sm) +BuildRequires: pkgconfig(libwacom) +BuildRequires: pkgconfig(x11) +BuildRequires: pkgconfig(xdamage) +BuildRequires: pkgconfig(xext) +BuildRequires: pkgconfig(xfixes) +BuildRequires: pkgconfig(xi) +BuildRequires: pkgconfig(xrandr) +BuildRequires: pkgconfig(xrender) +BuildRequires: pkgconfig(xcursor) +BuildRequires: pkgconfig(xcomposite) +BuildRequires: pkgconfig(x11-xcb) +BuildRequires: pkgconfig(xkbcommon) +BuildRequires: pkgconfig(xkbcommon-x11) +BuildRequires: pkgconfig(xkbfile) +BuildRequires: pkgconfig(xtst) +BuildRequires: mesa-libEGL-devel +BuildRequires: mesa-libGLES-devel +BuildRequires: mesa-libGL-devel +BuildRequires: mesa-libgbm-devel +BuildRequires: pkgconfig(glesv2) +BuildRequires: pkgconfig(graphene-gobject-1.0) +BuildRequires: pam-devel +BuildRequires: pkgconfig(libdisplay-info) +BuildRequires: pkgconfig(libpipewire-0.3) >= %{pipewire_version} +BuildRequires: pkgconfig(sysprof-capture-4) +BuildRequires: sysprof-devel +BuildRequires: pkgconfig(libsystemd) +BuildRequires: xorg-x11-server-Xorg +BuildRequires: xorg-x11-server-Xvfb +BuildRequires: pkgconfig(xkeyboard-config) +BuildRequires: desktop-file-utils +# Bootstrap requirements +BuildRequires: gettext-devel git-core +BuildRequires: pkgconfig(libcanberra) +BuildRequires: pkgconfig(gsettings-desktop-schemas) >= %{gsettings_desktop_schemas_version} +BuildRequires: pkgconfig(gnome-settings-daemon) +BuildRequires: meson +BuildRequires: pkgconfig(gbm) +BuildRequires: pkgconfig(gnome-desktop-4) +BuildRequires: pkgconfig(gudev-1.0) +BuildRequires: pkgconfig(libdrm) +BuildRequires: pkgconfig(libstartup-notification-1.0) +BuildRequires: pkgconfig(wayland-eglstream) +BuildRequires: pkgconfig(wayland-protocols) +BuildRequires: pkgconfig(wayland-server) +BuildRequires: pkgconfig(lcms2) >= %{lcms2_version} +BuildRequires: pkgconfig(colord) >= %{colord_version} +BuildRequires: pkgconfig(libei-1.0) >= %{libei_version} +BuildRequires: pkgconfig(libeis-1.0) >= %{libei_version} + +BuildRequires: pkgconfig(json-glib-1.0) >= %{json_glib_version} +BuildRequires: pkgconfig(libinput) >= %{libinput_version} +BuildRequires: pkgconfig(xwayland) + +BuildRequires: python3-dbusmock + +Requires: control-center-filesystem +Requires: gsettings-desktop-schemas%{?_isa} >= %{gsettings_desktop_schemas_version} +Requires: gnome-settings-daemon +Requires: gtk4%{?_isa} >= %{gtk4_version} +Requires: json-glib%{?_isa} >= %{json_glib_version} +Requires: libinput%{?_isa} >= %{libinput_version} +Requires: pipewire%{_isa} >= %{pipewire_version} +Requires: startup-notification +Requires: dbus + +# Need common +Requires: %{name}-common = %{version}-%{release} + +Recommends: mesa-dri-drivers%{?_isa} + +Provides: firstboot(windowmanager) = mutter + +# Cogl and Clutter were forked at these versions, but have diverged +# significantly since then. +Provides: bundled(cogl) = 1.22.0 +Provides: bundled(clutter) = 1.26.0 + +Provides: mutter = %{gnome_version}-%{release} + +Conflicts: mutter < 45~beta.1-2 + +# Make sure dnf updates gnome-shell together with this package; otherwise we +# might end up with broken gnome-shell installations due to mutter ABI changes. +Conflicts: gnome-shell < 45~rc + +%description +Mutter is a window and compositing manager that displays and manages +your desktop via OpenGL. Mutter combines a sophisticated display engine +using the Clutter toolkit with solid window-management logic inherited +from the Metacity window manager. + +While Mutter can be used stand-alone, it is primarily intended to be +used as the display core of a larger system such as GNOME Shell. For +this reason, Mutter is very extensible via plugins, which are used both +to add fancy visual effects and to rework the window management +behaviors to meet the needs of the environment. + +%package common +Summary: Common files used by %{name} and forks of %{name} +BuildArch: noarch +Conflicts: mutter < 45~beta.1-2 +Provides: mutter-common = %{gnome_version}-%{release} + +%description common +Common files used by Mutter and soft forks of Mutter + +%package devel +Summary: Development package for %{name} +Requires: %{name}%{?_isa} = %{version}-%{release} +# for EGL/eglmesaext.h that's included from public cogl-egl-defines.h header +Requires: mesa-libEGL-devel + +%description devel +Header files and libraries for developing Mutter plugins. Also includes +utilities for testing Metacity/Mutter themes. + +%package tests +Summary: Tests for the %{name} package +Requires: %{name}-devel%{?_isa} = %{version}-%{release} +Requires: %{name}%{?_isa} = %{version}-%{release} +Requires: gtk3%{?_isa} >= %{gtk3_version} + +%description tests +The %{name}-tests package contains tests that can be used to verify +the functionality of the installed %{name} package. + +%prep +%autosetup -S git -n %{name}-%{tarball_version} + +%build +%meson -Degl_device=true -Dwayland_eglstream=true +%meson_build + +%install +%meson_install + +%find_lang %{name} + +%files -f %{name}.lang +%license COPYING +%doc NEWS +%{_bindir}/mutter +%{_libdir}/lib*.so.* +%{_libdir}/mutter-%{mutter_api_version}/ +%{_libexecdir}/mutter-restart-helper +%{_libexecdir}/mutter-x11-frames +%{_mandir}/man1/mutter.1* + +%files common +%{_datadir}/GConf/gsettings/mutter-schemas.convert +%{_datadir}/glib-2.0/schemas/org.gnome.mutter.gschema.xml +%{_datadir}/glib-2.0/schemas/org.gnome.mutter.wayland.gschema.xml +%{_datadir}/gnome-control-center/keybindings/50-mutter-*.xml +%{_udevrulesdir}/61-mutter.rules + +%files devel +%{_includedir}/* +%{_libdir}/lib*.so +%{_libdir}/pkgconfig/* + +%files tests +%{_libexecdir}/installed-tests/mutter-%{mutter_api_version} +%{_datadir}/installed-tests/mutter-%{mutter_api_version} +%{_datadir}/mutter-%{mutter_api_version}/tests + +%changelog +%autochangelog From f47c23856ad7f9ad5604c3aeba3684ff0bb4e2b6 Mon Sep 17 00:00:00 2001 From: Kyle Gospodnetich Date: Mon, 13 May 2024 17:26:42 -0700 Subject: [PATCH 2/3] chore: Remove some unneeded patches --- ...nput-region-from-frame-window-for-de.patch | 163 -- spec_files/mutter/0001-modified-3329.patch | 145 -- spec_files/mutter/3720+3567.patch | 1994 +---------------- spec_files/mutter/mutter.spec | 16 +- 4 files changed, 43 insertions(+), 2275 deletions(-) delete mode 100644 spec_files/mutter/0001-Revert-x11-Use-input-region-from-frame-window-for-de.patch delete mode 100644 spec_files/mutter/0001-modified-3329.patch diff --git a/spec_files/mutter/0001-Revert-x11-Use-input-region-from-frame-window-for-de.patch b/spec_files/mutter/0001-Revert-x11-Use-input-region-from-frame-window-for-de.patch deleted file mode 100644 index 4124b537..00000000 --- a/spec_files/mutter/0001-Revert-x11-Use-input-region-from-frame-window-for-de.patch +++ /dev/null @@ -1,163 +0,0 @@ -From 21680b2f4edb064ff524cb91e9e20ace91deda6d Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Florian=20M=C3=BCllner?= -Date: Sun, 21 Apr 2024 16:54:52 +0200 -Subject: [PATCH 1/2] Revert "x11/window: Update comment and variable name to - reflect current behavior" - -This reverts commit e4763d00e8512aeb408ae118597d753f12217487. ---- - src/x11/window-x11.c | 15 ++++++++------- - 1 file changed, 8 insertions(+), 7 deletions(-) - -diff --git a/src/x11/window-x11.c b/src/x11/window-x11.c -index 6d2016e3ec..f6f7d87dfe 100644 ---- a/src/x11/window-x11.c -+++ b/src/x11/window-x11.c -@@ -2476,20 +2476,21 @@ meta_window_x11_update_input_region (MetaWindow *window) - - if (region != NULL) - { -- MtkRectangle bounding_rect; -+ MtkRectangle client_area; - -- bounding_rect.x = 0; -- bounding_rect.y = 0; -- bounding_rect.width = window->buffer_rect.width; -- bounding_rect.height = window->buffer_rect.height; -+ client_area.x = 0; -+ client_area.y = 0; -+ client_area.width = window->buffer_rect.width; -+ client_area.height = window->buffer_rect.height; - - /* The shape we get back from the client may have coordinates - * outside of the frame. The X SHAPE Extension requires that - * the overall shape the client provides never exceeds the - * "bounding rectangle" of the window -- the shape that the -- * window would have gotten if it was unshaped. -+ * window would have gotten if it was unshaped. In our case, -+ * this is simply the client area. - */ -- mtk_region_intersect_rectangle (region, &bounding_rect); -+ mtk_region_intersect_rectangle (region, &client_area); - } - - meta_window_set_input_region (window, region); --- -2.44.0 - - -From f1996e67ad8a5a0009e90c38767c8906e015ba70 Mon Sep 17 00:00:00 2001 -From: Adam Williamson -Date: Thu, 5 Oct 2023 13:09:46 -0700 -Subject: [PATCH 2/2] Revert "x11: Use input region from frame window for - decorated windows" - -This reverts commit d991961ae2a5c8cf2e58ff1072239f4902b0f767. It -seems to cause the broken mouse interaction bug reported in -https://bugzilla.redhat.com/show_bug.cgi?id=2239128 . - -Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3068 ---- - src/core/frame.c | 7 ------- - src/x11/window-x11.c | 29 ++++++++++------------------- - 2 files changed, 10 insertions(+), 26 deletions(-) - -diff --git a/src/core/frame.c b/src/core/frame.c -index c74a2e04ec..5feb854805 100644 ---- a/src/core/frame.c -+++ b/src/core/frame.c -@@ -35,7 +35,6 @@ - #include "x11/window-props.h" - - #include --#include - - #define EVENT_MASK (SubstructureRedirectMask | \ - StructureNotifyMask | SubstructureNotifyMask | \ -@@ -109,9 +108,6 @@ meta_window_x11_set_frame_xwindow (MetaWindow *window, - XChangeWindowAttributes (x11_display->xdisplay, - frame->xwindow, CWEventMask, &attrs); - -- if (META_X11_DISPLAY_HAS_SHAPE (x11_display)) -- XShapeSelectInput (x11_display->xdisplay, frame->xwindow, ShapeNotifyMask); -- - meta_x11_display_register_x_window (x11_display, &frame->xwindow, window); - - if (window->mapped) -@@ -220,9 +216,6 @@ meta_window_destroy_frame (MetaWindow *window) - window->reparents_pending += 1; - } - -- if (META_X11_DISPLAY_HAS_SHAPE (x11_display)) -- XShapeSelectInput (x11_display->xdisplay, frame->xwindow, NoEventMask); -- - XDeleteProperty (x11_display->xdisplay, - meta_window_x11_get_xwindow (window), - x11_display->atom__MUTTER_NEEDS_FRAME); -diff --git a/src/x11/window-x11.c b/src/x11/window-x11.c -index f6f7d87dfe..1bc5c57a1a 100644 ---- a/src/x11/window-x11.c -+++ b/src/x11/window-x11.c -@@ -2082,10 +2082,6 @@ meta_window_x11_constructed (GObject *object) - priv->keys_grabbed = FALSE; - priv->grab_on_frame = FALSE; - -- g_signal_connect (window, "notify::decorated", -- G_CALLBACK (meta_window_x11_update_input_region), -- window); -- - G_OBJECT_CLASS (meta_window_x11_parent_class)->constructed (object); - } - -@@ -2400,21 +2396,16 @@ meta_window_x11_update_input_region (MetaWindow *window) - g_autoptr (MtkRegion) region = NULL; - MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); - MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); -- Window xwindow; - -+ /* Decorated windows don't have an input region, because -+ we don't shape the frame to match the client windows -+ (so the events are blocked by the frame anyway) -+ */ - if (window->decorated) - { -- if (!window->frame) -- { -- if (priv->input_region) -- meta_window_set_input_region (window, NULL); -- return; -- } -- xwindow = window->frame->xwindow; -- } -- else -- { -- xwindow = priv->xwindow; -+ if (priv->input_region) -+ meta_window_set_input_region (window, NULL); -+ return; - } - - if (META_X11_DISPLAY_HAS_SHAPE (x11_display)) -@@ -2426,7 +2417,7 @@ meta_window_x11_update_input_region (MetaWindow *window) - - mtk_x11_error_trap_push (x11_display->xdisplay); - rects = XShapeGetRectangles (x11_display->xdisplay, -- xwindow, -+ priv->xwindow, - ShapeInput, - &n_rects, - &ordering); -@@ -2480,8 +2471,8 @@ meta_window_x11_update_input_region (MetaWindow *window) - - client_area.x = 0; - client_area.y = 0; -- client_area.width = window->buffer_rect.width; -- client_area.height = window->buffer_rect.height; -+ client_area.width = priv->client_rect.width; -+ client_area.height = priv->client_rect.height; - - /* The shape we get back from the client may have coordinates - * outside of the frame. The X SHAPE Extension requires that --- -2.44.0 - diff --git a/spec_files/mutter/0001-modified-3329.patch b/spec_files/mutter/0001-modified-3329.patch deleted file mode 100644 index 54f0ed49..00000000 --- a/spec_files/mutter/0001-modified-3329.patch +++ /dev/null @@ -1,145 +0,0 @@ -From e20ebeefa42997fe65008b11ef771c71b697273c Mon Sep 17 00:00:00 2001 -From: Adam Williamson -Date: Fri, 20 Oct 2023 22:12:23 -0700 -Subject: [PATCH] modified 3329 - -Signed-off-by: Adam Williamson ---- - src/compositor/meta-compositor-x11.c | 2 ++ - src/core/display.c | 34 ---------------------------- - src/tests/x11-test.sh | 3 +++ - src/x11/meta-x11-display.c | 30 +++++++++++++++++++++++- - 4 files changed, 34 insertions(+), 35 deletions(-) - -diff --git a/src/compositor/meta-compositor-x11.c b/src/compositor/meta-compositor-x11.c -index 1ad3327dd..ce7bc1945 100644 ---- a/src/compositor/meta-compositor-x11.c -+++ b/src/compositor/meta-compositor-x11.c -@@ -188,6 +188,8 @@ meta_compositor_x11_manage (MetaCompositor *compositor, - - compositor_x11->have_x11_sync_object = meta_sync_ring_init (xdisplay); - -+ meta_x11_display_redirect_windows (x11_display, display); -+ - return TRUE; - } - -diff --git a/src/core/display.c b/src/core/display.c -index 0a191c0fb..b16e50e21 100644 ---- a/src/core/display.c -+++ b/src/core/display.c -@@ -930,9 +930,6 @@ meta_display_new (MetaContext *context, - MetaDisplay *display; - MetaDisplayPrivate *priv; - guint32 timestamp; --#ifdef HAVE_X11_CLIENT -- Window old_active_xwindow = None; --#endif - MetaMonitorManager *monitor_manager; - MetaSettings *settings; - MetaInputCapture *input_capture; -@@ -1048,14 +1045,6 @@ meta_display_new (MetaContext *context, - display->last_focus_time = timestamp; - display->last_user_time = timestamp; - --#ifdef HAVE_X11 -- if (!meta_is_wayland_compositor ()) -- meta_prop_get_window (display->x11_display, -- display->x11_display->xroot, -- display->x11_display->atom__NET_ACTIVE_WINDOW, -- &old_active_xwindow); --#endif -- - if (!meta_compositor_manage (display->compositor, error)) - { - g_object_unref (display); -@@ -1076,30 +1065,7 @@ meta_display_new (MetaContext *context, - g_signal_connect (display->gesture_tracker, "state-changed", - G_CALLBACK (gesture_tracker_state_changed), display); - -- /* We know that if mutter is running as a Wayland compositor, -- * we start out with no windows. -- */ --#ifdef HAVE_X11_CLIENT -- if (!meta_is_wayland_compositor ()) -- meta_display_manage_all_xwindows (display); -- -- if (old_active_xwindow != None) -- { -- MetaWindow *old_active_window; -- old_active_window = meta_x11_display_lookup_x_window (display->x11_display, -- old_active_xwindow); -- if (old_active_window) -- meta_window_focus (old_active_window, timestamp); -- else -- meta_display_unset_input_focus (display, timestamp); -- } -- else -- { -- meta_display_unset_input_focus (display, timestamp); -- } --#else - meta_display_unset_input_focus (display, timestamp); --#endif - - g_signal_connect (stage, "notify::is-grabbed", - G_CALLBACK (on_is_grabbed_changed), display); -diff --git a/src/tests/x11-test.sh b/src/tests/x11-test.sh -index 59e460fc3..d95b2460f 100755 ---- a/src/tests/x11-test.sh -+++ b/src/tests/x11-test.sh -@@ -34,6 +34,9 @@ echo \# Launched with pid $MUTTER2_PID - MUTTER2_PID=$! - wait $MUTTER1_PID - -+echo \# Waiting for the second mutter to finish loading -+gdbus wait --session org.gnome.Mutter.IdleMonitor -+ - sleep 2 - - echo \# Terminating clients > /dev/stderr -diff --git a/src/x11/meta-x11-display.c b/src/x11/meta-x11-display.c -index 4e98203dd..4ca620410 100644 ---- a/src/x11/meta-x11-display.c -+++ b/src/x11/meta-x11-display.c -@@ -300,8 +300,36 @@ static void - on_x11_display_opened (MetaX11Display *x11_display, - MetaDisplay *display) - { -+ Window old_active_xwindow = None; -+ -+ if (!meta_is_wayland_compositor ()) -+ { -+ meta_prop_get_window (display->x11_display, -+ display->x11_display->xroot, -+ display->x11_display->atom__NET_ACTIVE_WINDOW, -+ &old_active_xwindow); -+ } -+ -+ if (meta_is_wayland_compositor ()) -+ meta_x11_display_redirect_windows (x11_display, display); -+ -+ - meta_display_manage_all_xwindows (display); -- meta_x11_display_redirect_windows (x11_display, display); -+ -+ if (old_active_xwindow != None) -+ { -+ MetaWindow *old_active_window; -+ -+ old_active_window = meta_x11_display_lookup_x_window (x11_display, -+ old_active_xwindow); -+ if (old_active_window) -+ { -+ uint32_t timestamp; -+ -+ timestamp = display->x11_display->timestamp; -+ meta_window_focus (old_active_window, timestamp); -+ } -+ } - } - - static void --- -2.41.0 - diff --git a/spec_files/mutter/3720+3567.patch b/spec_files/mutter/3720+3567.patch index 27439329..a0d3ba31 100644 --- a/spec_files/mutter/3720+3567.patch +++ b/spec_files/mutter/3720+3567.patch @@ -1,1964 +1,52 @@ -diff --git a/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml b/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml -index b05337d74..7294c57a8 100644 ---- a/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml -+++ b/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml -@@ -426,10 +426,6 @@ - always use the same scale. Absence of - this means logical monitor scales can - differ. -- * "legacy-ui-scaling-factor" (i): The legacy scaling factor traditionally -- used to scale X11 clients (commonly -- communicated via the -- Gdk/WindowScalingFactor XSetting entry). - --> - - -diff --git a/data/dbus-interfaces/org.gnome.Mutter.X11.xml b/data/dbus-interfaces/org.gnome.Mutter.X11.xml -new file mode 100644 -index 000000000..3d3c8a42f ---- /dev/null -+++ b/data/dbus-interfaces/org.gnome.Mutter.X11.xml -@@ -0,0 +1,8 @@ -+ -+ -+ -+ -+ -+ -diff --git a/data/org.gnome.mutter.gschema.xml.in b/data/org.gnome.mutter.gschema.xml.in -index 92c97b12e..18abd8d51 100644 ---- a/data/org.gnome.mutter.gschema.xml.in -+++ b/data/org.gnome.mutter.gschema.xml.in -@@ -5,6 +5,7 @@ - - - -+ - - - - - -diff --git a/src/backends/meta-monitor-manager-private.h b/src/backends/meta-monitor-manager-private.h -index 0760a341a..6ed3fc2c3 100644 ---- a/src/backends/meta-monitor-manager-private.h -+++ b/src/backends/meta-monitor-manager-private.h -@@ -436,3 +436,5 @@ gboolean meta_monitor_manager_apply_monitors_config (MetaMonitorManager * - MetaMonitorsConfig *config, - MetaMonitorsConfigMethod method, - GError **error); -+ -+MetaLogicalMonitorLayoutMode meta_monitor_manager_get_layout_mode (MetaMonitorManager *manager); -diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c -index 77743bc72..45033d966 100644 ---- a/src/backends/meta-monitor-manager.c -+++ b/src/backends/meta-monitor-manager.c -@@ -2051,14 +2051,12 @@ meta_monitor_manager_handle_get_current_state (MetaDBusDisplayConfig *skeleton, - GDBusMethodInvocation *invocation, - MetaMonitorManager *manager) - { -- MetaSettings *settings = meta_backend_get_settings (manager->backend); - GVariantBuilder monitors_builder; - GVariantBuilder logical_monitors_builder; - GVariantBuilder properties_builder; - GList *l; - int i; - MetaMonitorManagerCapability capabilities; -- int ui_scaling_factor; - int max_screen_width, max_screen_height; - - g_variant_builder_init (&monitors_builder, -@@ -2261,11 +2259,6 @@ meta_monitor_manager_handle_get_current_state (MetaDBusDisplayConfig *skeleton, - g_variant_new_boolean (TRUE)); - } - -- ui_scaling_factor = meta_settings_get_ui_scaling_factor (settings); -- g_variant_builder_add (&properties_builder, "{sv}", -- "legacy-ui-scaling-factor", -- g_variant_new_int32 (ui_scaling_factor)); -- - if (meta_monitor_manager_get_max_screen_size (manager, - &max_screen_width, - &max_screen_height)) -@@ -4123,3 +4116,9 @@ meta_monitor_manager_get_virtual_monitors (MetaMonitorManager *manager) - - return priv->virtual_monitors; - } -+ -+MetaLogicalMonitorLayoutMode -+meta_monitor_manager_get_layout_mode (MetaMonitorManager *manager) -+{ -+ return manager->layout_mode; -+} -diff --git a/src/backends/meta-settings-private.h b/src/backends/meta-settings-private.h -index afbba054a..2081a81b1 100644 ---- a/src/backends/meta-settings-private.h -+++ b/src/backends/meta-settings-private.h -@@ -32,6 +32,7 @@ typedef enum _MetaExperimentalFeature - META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS = (1 << 1), - META_EXPERIMENTAL_FEATURE_AUTOCLOSE_XWAYLAND = (1 << 2), - META_EXPERIMENTAL_FEATURE_VARIABLE_REFRESH_RATE = (1 << 3), -+ META_EXPERIMENTAL_FEATURE_XWAYLAND_NATIVE_SCALING = (1 << 4), - } MetaExperimentalFeature; - - typedef enum _MetaXwaylandExtension -diff --git a/src/backends/meta-settings.c b/src/backends/meta-settings.c -index 3703b23b0..1ae59d636 100644 ---- a/src/backends/meta-settings.c -+++ b/src/backends/meta-settings.c -@@ -296,6 +296,8 @@ experimental_features_handler (GVariant *features_variant, - feature = META_EXPERIMENTAL_FEATURE_AUTOCLOSE_XWAYLAND; - else if (g_str_equal (feature_str, "variable-refresh-rate")) - feature = META_EXPERIMENTAL_FEATURE_VARIABLE_REFRESH_RATE; -+ else if (g_str_equal (feature_str, "xwayland-native-scaling")) -+ feature = META_EXPERIMENTAL_FEATURE_XWAYLAND_NATIVE_SCALING; - - if (feature) - g_message ("Enabling experimental feature '%s'", feature_str); -diff --git a/src/compositor/meta-window-actor-x11.c b/src/compositor/meta-window-actor-x11.c -index 7d5e46ac7..577ed2760 100644 ---- a/src/compositor/meta-window-actor-x11.c -+++ b/src/compositor/meta-window-actor-x11.c -@@ -696,11 +696,23 @@ meta_window_actor_x11_process_damage (MetaWindowActorX11 *actor_x11, - - surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11)); - if (surface) -- meta_surface_actor_process_damage (surface, -- event->area.x, -- event->area.y, -- event->area.width, -- event->area.height); -+ { -+ MetaWindow *window = -+ meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); -+ MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); -+ MtkRectangle damage; -+ -+ meta_window_x11_protocol_to_stage (window_x11, -+ event->area.x, event->area.y, -+ event->area.width, event->area.height, -+ &damage.x, &damage.y, -+ &damage.width, &damage.height); -+ meta_surface_actor_process_damage (surface, -+ damage.x, -+ damage.y, -+ damage.width, -+ damage.height); -+ } - - meta_window_actor_notify_damaged (META_WINDOW_ACTOR (actor_x11)); - } -diff --git a/src/core/frame.c b/src/core/frame.c -index 5feb85480..6f0f3ab83 100644 ---- a/src/core/frame.c -+++ b/src/core/frame.c -@@ -33,6 +33,7 @@ - #include "x11/meta-x11-display-private.h" - #include "x11/window-x11-private.h" - #include "x11/window-props.h" -+#include "x11/window-x11.h" - - #include - -@@ -65,6 +66,7 @@ meta_window_x11_set_frame_xwindow (MetaWindow *window, - XSetWindowAttributes attrs; - gulong create_serial = 0; - MetaFrame *frame; -+ int child_x, child_y; - - if (window->frame) - return; -@@ -123,11 +125,19 @@ meta_window_x11_set_frame_xwindow (MetaWindow *window, - meta_stack_tracker_record_remove (window->display->stack_tracker, - meta_window_x11_get_xwindow (window), - XNextRequest (x11_display->xdisplay)); -+ meta_window_x11_stage_to_protocol (META_WINDOW_X11 (window), -+ frame->child_x, -+ frame->child_y, -+ 0, 0, -+ &child_x, -+ &child_y, -+ NULL, NULL); -+ - XReparentWindow (x11_display->xdisplay, - meta_window_x11_get_xwindow (window), - frame->xwindow, -- frame->child_x, -- frame->child_y); -+ child_x, -+ child_y); - window->reparents_pending += 1; - /* FIXME handle this error */ - mtk_x11_error_trap_pop (x11_display->xdisplay); -@@ -197,6 +207,8 @@ meta_window_destroy_frame (MetaWindow *window) - - if (!x11_display->closing) - { -+ int child_x, child_y; -+ - if (!window->unmanaging) - { - meta_stack_tracker_record_add (window->display->stack_tracker, -@@ -204,6 +216,14 @@ meta_window_destroy_frame (MetaWindow *window) - XNextRequest (x11_display->xdisplay)); - } - -+ meta_window_x11_stage_to_protocol (META_WINDOW_X11 (window), -+ window->frame->rect.x + borders.invisible.left, -+ window->frame->rect.y + borders.invisible.top, -+ 0, 0, -+ &child_x, -+ &child_y, -+ NULL, NULL); -+ - XReparentWindow (x11_display->xdisplay, - meta_window_x11_get_xwindow (window), - x11_display->xroot, -@@ -211,8 +231,7 @@ meta_window_destroy_frame (MetaWindow *window) - * coordinates here means we'll need to ensure a configure - * notify event is sent; see bug 399552. - */ -- window->frame->rect.x + borders.invisible.left, -- window->frame->rect.y + borders.invisible.top); -+ child_x, child_y); - window->reparents_pending += 1; - } - -@@ -263,6 +282,7 @@ meta_frame_query_borders (MetaFrame *frame, - MetaFrameBorders *borders) - { - MetaWindow *window = frame->window; -+ MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); - MetaX11Display *x11_display = window->display->x11_display; - int format, res; - Atom type; -@@ -286,12 +306,22 @@ meta_frame_query_borders (MetaFrame *frame, - if (mtk_x11_error_trap_pop_with_return (x11_display->xdisplay) == Success && - res == Success && nitems == 4) - { -- borders->invisible = (MetaFrameBorder) { -- ((long *) data)[0], -- ((long *) data)[1], -- ((long *) data)[2], -- ((long *) data)[3], -- }; -+ int left, right, top, bottom; -+ -+ meta_window_x11_protocol_to_stage (window_x11, -+ ((long *) data)[0], -+ ((long *) data)[1], -+ ((long *) data)[2], -+ ((long *) data)[3], -+ &left, -+ &right, -+ &top, -+ &bottom); -+ -+ borders->invisible.left = left; -+ borders->invisible.right = right; -+ borders->invisible.top = top; -+ borders->invisible.bottom = bottom; - } - else - { -@@ -314,12 +344,21 @@ meta_frame_query_borders (MetaFrame *frame, - if (mtk_x11_error_trap_pop_with_return (x11_display->xdisplay) == Success && - res == Success && nitems == 4) - { -- borders->visible = (MetaFrameBorder) { -- ((long *) data)[0], -- ((long *) data)[1], -- ((long *) data)[2], -- ((long *) data)[3], -- }; -+ int left, right, top, bottom; -+ -+ meta_window_x11_protocol_to_stage (window_x11, -+ ((long *) data)[0], -+ ((long *) data)[1], -+ ((long *) data)[2], -+ ((long *) data)[3], -+ &left, -+ &right, -+ &top, -+ &bottom); -+ borders->visible.left = left; -+ borders->visible.right = right; -+ borders->visible.top = top; -+ borders->visible.bottom = bottom; - } - else - { -@@ -367,7 +406,9 @@ meta_frame_sync_to_window (MetaFrame *frame, - gboolean need_resize) - { - MetaWindow *window = frame->window; -+ MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); - MetaX11Display *x11_display = window->display->x11_display; -+ MtkRectangle rect; - - meta_topic (META_DEBUG_GEOMETRY, - "Syncing frame geometry %d,%d %dx%d (SE: %d,%d)", -@@ -378,12 +419,22 @@ meta_frame_sync_to_window (MetaFrame *frame, - - mtk_x11_error_trap_push (x11_display->xdisplay); - -+ meta_window_x11_stage_to_protocol (window_x11, -+ frame->rect.x, -+ frame->rect.y, -+ frame->rect.width, -+ frame->rect.height, -+ &rect.x, -+ &rect.y, -+ &rect.width, -+ &rect.height); -+ - XMoveResizeWindow (x11_display->xdisplay, - frame->xwindow, -- frame->rect.x, -- frame->rect.y, -- frame->rect.width, -- frame->rect.height); -+ rect.x, -+ rect.y, -+ rect.width, -+ rect.height); - - mtk_x11_error_trap_pop (x11_display->xdisplay); - -@@ -420,6 +471,7 @@ static void - send_configure_notify (MetaFrame *frame) - { - MetaX11Display *x11_display = frame->window->display->x11_display; -+ MetaWindowX11 *window_x11 = META_WINDOW_X11 (frame->window); - XEvent event = { 0 }; - - /* We never get told by the frames client, just reassert the -@@ -429,10 +481,16 @@ send_configure_notify (MetaFrame *frame) - event.xconfigure.display = x11_display->xdisplay; - event.xconfigure.event = frame->xwindow; - event.xconfigure.window = frame->xwindow; -- event.xconfigure.x = frame->rect.x; -- event.xconfigure.y = frame->rect.y; -- event.xconfigure.width = frame->rect.width; -- event.xconfigure.height = frame->rect.height; -+ -+ meta_window_x11_stage_to_protocol (window_x11, -+ frame->rect.x, -+ frame->rect.y, -+ frame->rect.width, -+ frame->rect.height, -+ &event.xconfigure.x, -+ &event.xconfigure.y, -+ &event.xconfigure.width, -+ &event.xconfigure.height); - event.xconfigure.border_width = 0; - event.xconfigure.above = None; - event.xconfigure.override_redirect = False; -diff --git a/src/meson.build b/src/meson.build -index 05df3bfd2..e658f98ca 100644 ---- a/src/meson.build -+++ b/src/meson.build -@@ -949,6 +949,11 @@ dbus_interfaces = [ - 'interface': 'org.gnome.Mutter.DebugControl.xml', - 'prefix': 'org.gnome.Mutter', - }, -+ { -+ 'name': 'meta-dbus-x11', -+ 'interface': 'org.gnome.Mutter.X11.xml', -+ 'prefix': 'org.gnome.Mutter', -+ }, - ] - - if have_profiler -diff --git a/src/meta/meta-context.h b/src/meta/meta-context.h -index ef36bd2c3..2adb9b07e 100644 ---- a/src/meta/meta-context.h -+++ b/src/meta/meta-context.h -@@ -101,3 +101,8 @@ gboolean meta_context_raise_rlimit_nofile (MetaContext *context, - META_EXPORT - gboolean meta_context_restore_rlimit_nofile (MetaContext *context, - GError **error); -+ -+#ifdef HAVE_WAYLAND -+META_EXPORT -+MetaWaylandCompositor * meta_context_get_wayland_compositor (MetaContext *context); -+#endif -diff --git a/src/meta/meta-wayland-compositor.h b/src/meta/meta-wayland-compositor.h -index 7f4a50705..3df92fda5 100644 ---- a/src/meta/meta-wayland-compositor.h -+++ b/src/meta/meta-wayland-compositor.h -@@ -31,9 +31,6 @@ G_DECLARE_FINAL_TYPE (MetaWaylandCompositor, - META, WAYLAND_COMPOSITOR, - GObject) - --META_EXPORT --MetaWaylandCompositor *meta_context_get_wayland_compositor (MetaContext *context); -- - META_EXPORT - struct wl_display *meta_wayland_compositor_get_wayland_display (MetaWaylandCompositor *compositor); - -diff --git a/src/meta/types.h b/src/meta/types.h -index cbe2a9a3d..8fba4a839 100644 ---- a/src/meta/types.h -+++ b/src/meta/types.h -@@ -38,3 +38,7 @@ typedef struct _MetaSettings MetaSettings; - - typedef struct _MetaWorkspaceManager MetaWorkspaceManager; - typedef struct _MetaSelection MetaSelection; -+ -+#ifdef HAVE_WAYLAND -+typedef struct _MetaWaylandCompositor MetaWaylandCompositor; -+#endif -diff --git a/src/wayland/meta-wayland-cursor-surface.c b/src/wayland/meta-wayland-cursor-surface.c -index 87a8895c8..5a16ce7d8 100644 ---- a/src/wayland/meta-wayland-cursor-surface.c -+++ b/src/wayland/meta-wayland-cursor-surface.c -@@ -92,37 +92,29 @@ cursor_sprite_prepare_at (MetaCursorSprite *cursor_sprite, - { - MetaWaylandSurfaceRole *role = META_WAYLAND_SURFACE_ROLE (cursor_surface); - MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (role); -- -- if (!meta_wayland_surface_is_xwayland (surface)) -+ MetaContext *context = -+ meta_wayland_compositor_get_context (surface->compositor); -+ MetaBackend *backend = meta_context_get_backend (context); -+ MetaMonitorManager *monitor_manager = -+ meta_backend_get_monitor_manager (backend); -+ MetaLogicalMonitor *logical_monitor; -+ -+ logical_monitor = -+ meta_monitor_manager_get_logical_monitor_at (monitor_manager, x, y); -+ if (logical_monitor) - { -- MetaWaylandSurfaceRole *surface_role = -- META_WAYLAND_SURFACE_ROLE (cursor_surface); -- MetaWaylandSurface *surface = -- meta_wayland_surface_role_get_surface (surface_role); -- MetaContext *context = -- meta_wayland_compositor_get_context (surface->compositor); -- MetaBackend *backend = meta_context_get_backend (context); -- MetaMonitorManager *monitor_manager = -- meta_backend_get_monitor_manager (backend); -- MetaLogicalMonitor *logical_monitor; -- -- logical_monitor = -- meta_monitor_manager_get_logical_monitor_at (monitor_manager, x, y); -- if (logical_monitor) -- { -- int surface_scale = surface->applied_state.scale; -- float texture_scale; -- -- if (meta_backend_is_stage_views_scaled (backend)) -- texture_scale = 1.0 / surface_scale; -- else -- texture_scale = (meta_logical_monitor_get_scale (logical_monitor) / -- surface_scale); -- -- meta_cursor_sprite_set_texture_scale (cursor_sprite, texture_scale); -- meta_cursor_sprite_set_texture_transform (cursor_sprite, -- surface->buffer_transform); -- } -+ int surface_scale = surface->applied_state.scale; -+ float texture_scale; -+ -+ if (meta_backend_is_stage_views_scaled (backend)) -+ texture_scale = 1.0 / surface_scale; -+ else -+ texture_scale = (meta_logical_monitor_get_scale (logical_monitor) / -+ surface_scale); -+ -+ meta_cursor_sprite_set_texture_scale (cursor_sprite, texture_scale); -+ meta_cursor_sprite_set_texture_transform (cursor_sprite, -+ surface->buffer_transform); - } - - meta_wayland_surface_update_outputs (surface); -diff --git a/src/wayland/meta-wayland-outputs.c b/src/wayland/meta-wayland-outputs.c -index 89ae86445..f957bc339 100644 ---- a/src/wayland/meta-wayland-outputs.c -+++ b/src/wayland/meta-wayland-outputs.c -@@ -31,6 +31,10 @@ - #include "backends/meta-monitor-manager-private.h" - #include "wayland/meta-wayland-private.h" - -+#ifdef HAVE_XWAYLAND -+#include "wayland/meta-xwayland.h" -+#endif -+ - #include "xdg-output-unstable-v1-server-protocol.h" - - /* Wayland protocol headers list new additions, not deprecations */ -@@ -50,6 +54,8 @@ struct _MetaWaylandOutput - { - GObject parent; - -+ MetaWaylandCompositor *compositor; -+ - struct wl_global *global; - GList *resources; - GList *xdg_output_resources; -@@ -422,6 +428,7 @@ meta_wayland_output_new (MetaWaylandCompositor *compositor, - MetaWaylandOutput *wayland_output; - - wayland_output = g_object_new (META_TYPE_WAYLAND_OUTPUT, NULL); -+ wayland_output->compositor = compositor; - wayland_output->global = wl_global_create (compositor->wayland_display, - &wl_output_interface, - META_WL_OUTPUT_VERSION, -@@ -596,6 +603,37 @@ static const struct zxdg_output_v1_interface - meta_xdg_output_destroy, - }; - -+#ifdef HAVE_XWAYLAND -+static gboolean -+is_xwayland_resource (MetaWaylandOutput *wayland_output, -+ struct wl_resource *resource) -+{ -+ MetaXWaylandManager *manager = &wayland_output->compositor->xwayland_manager; -+ -+ return resource && wl_resource_get_client (resource) == manager->client; -+} -+#endif -+ -+static void -+maybe_scale_for_xwayland (MetaWaylandOutput *wayland_output, -+ struct wl_resource *resource, -+ int *x, -+ int *y) -+{ -+#ifdef HAVE_XWAYLAND -+ if (is_xwayland_resource (wayland_output, resource)) -+ { -+ MetaXWaylandManager *xwayland_manager = -+ &wayland_output->compositor->xwayland_manager; -+ int xwayland_scale; -+ -+ xwayland_scale = meta_xwayland_get_effective_scale (xwayland_manager); -+ *x *= xwayland_scale; -+ *y *= xwayland_scale; -+ } -+#endif -+} -+ - static void - send_xdg_output_events (struct wl_resource *resource, - MetaWaylandOutput *wayland_output, -@@ -616,6 +654,7 @@ send_xdg_output_events (struct wl_resource *resource, - if (need_all_events || - old_layout.x != layout.x || old_layout.y != layout.y) - { -+ maybe_scale_for_xwayland (wayland_output, resource, &layout.x, &layout.y); - zxdg_output_v1_send_logical_position (resource, layout.x, layout.y); - need_done = TRUE; - } -@@ -623,6 +662,7 @@ send_xdg_output_events (struct wl_resource *resource, - if (need_all_events || - old_layout.width != layout.width || old_layout.height != layout.height) - { -+ maybe_scale_for_xwayland (wayland_output, resource, &layout.width, &layout.height); - zxdg_output_v1_send_logical_size (resource, layout.width, layout.height); - need_done = TRUE; - } -@@ -745,7 +785,7 @@ meta_wayland_outputs_init (MetaWaylandCompositor *compositor) - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - -- g_signal_connect (monitor_manager, "monitors-changed-internal", -+ g_signal_connect (monitor_manager, "monitors-changed", - G_CALLBACK (on_monitors_changed), compositor); - - compositor->outputs = -diff --git a/src/wayland/meta-wayland-pointer.c b/src/wayland/meta-wayland-pointer.c -index 88b27f84d..324092970 100644 ---- a/src/wayland/meta-wayland-pointer.c -+++ b/src/wayland/meta-wayland-pointer.c -@@ -1244,6 +1244,20 @@ pointer_set_cursor (struct wl_client *client, - cursor_surface = META_WAYLAND_CURSOR_SURFACE (surface->role); - meta_wayland_cursor_surface_set_renderer (cursor_surface, - cursor_renderer); -+ -+#ifdef HAVE_XWAYLAND -+ if (meta_wayland_surface_is_xwayland (surface)) -+ { -+ MetaXWaylandManager *xwayland_manager = -+ &surface->compositor->xwayland_manager; -+ int scale; -+ -+ scale = meta_xwayland_get_effective_scale (xwayland_manager); -+ hot_x = round (hot_x / (double) scale); -+ hot_y = round (hot_y / (double) scale); -+ } -+#endif -+ - meta_wayland_cursor_surface_set_hotspot (cursor_surface, - hot_x, hot_y); - -diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h -index e8d442c03..834753ffd 100644 ---- a/src/wayland/meta-wayland-private.h -+++ b/src/wayland/meta-wayland-private.h -@@ -77,6 +77,8 @@ struct _MetaXWaylandManager - int rr_error_base; - - gboolean should_enable_ei_portal; -+ -+ double highest_monitor_scale; - }; - - struct _MetaWaylandCompositor -diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c -index 6dc5006b7..81ee47bbd 100644 ---- a/src/wayland/meta-wayland-surface.c -+++ b/src/wayland/meta-wayland-surface.c -@@ -781,8 +781,19 @@ meta_wayland_surface_apply_state (MetaWaylandSurface *surface, - state->buffer->type != META_WAYLAND_BUFFER_TYPE_SINGLE_PIXEL)); - } - -- if (state->scale > 0) -- surface->applied_state.scale = state->scale; -+ if (meta_wayland_surface_is_xwayland (surface)) -+ { -+#ifdef HAVE_XWAYLAND -+ MetaXWaylandManager *xwayland_manager = -+ &surface->compositor->xwayland_manager; -+ -+ surface->applied_state.scale = meta_xwayland_get_effective_scale (xwayland_manager); -+#endif -+ } -+ else if (state->scale > 0) -+ { -+ surface->applied_state.scale = state->scale; -+ } - - if (state->has_new_buffer_transform) - surface->buffer_transform = state->buffer_transform; -@@ -977,8 +988,9 @@ meta_wayland_surface_commit (MetaWaylandSurface *surface) - MetaMultiTexture *committed_texture = surface->committed_state.texture; - int committed_scale = surface->committed_state.scale; - -- if ((meta_multi_texture_get_width (committed_texture) % committed_scale != 0) || -- (meta_multi_texture_get_height (committed_texture) % committed_scale != 0)) -+ if (((meta_multi_texture_get_width (committed_texture) % committed_scale != 0) || -+ (meta_multi_texture_get_height (committed_texture) % committed_scale != 0)) && -+ !meta_wayland_surface_is_xwayland (surface)) - { - if (!surface->role || !META_IS_WAYLAND_CURSOR_SURFACE (surface->role)) - { -@@ -1506,6 +1518,16 @@ meta_wayland_surface_update_outputs (MetaWaylandSurface *surface) - g_hash_table_foreach (surface->compositor->outputs, - update_surface_output_state, - surface); -+ -+ if (meta_wayland_surface_is_xwayland (surface)) -+ { -+#ifdef HAVE_XWAYLAND -+ MetaXWaylandManager *xwayland_manager = -+ &surface->compositor->xwayland_manager; -+ -+ surface->applied_state.scale = meta_xwayland_get_effective_scale (xwayland_manager); -+#endif -+ } - } - - void -diff --git a/src/wayland/meta-window-xwayland.c b/src/wayland/meta-window-xwayland.c -index 1299a351c..5fb006962 100644 ---- a/src/wayland/meta-window-xwayland.c -+++ b/src/wayland/meta-window-xwayland.c -@@ -27,8 +27,9 @@ - #include "x11/window-x11-private.h" - #include "x11/xprops.h" - #include "wayland/meta-window-xwayland.h" --#include "wayland/meta-wayland.h" -+#include "wayland/meta-wayland-private.h" - #include "wayland/meta-wayland-surface-private.h" -+#include "wayland/meta-xwayland.h" - - enum - { -@@ -315,6 +316,72 @@ meta_window_xwayland_process_property_notify (MetaWindow *window, - meta_window_queue (window, META_QUEUE_MOVE_RESIZE); - } - -+static void -+meta_window_xwayland_stage_to_protocol (MetaWindowX11 *window_x11, -+ int stage_x, -+ int stage_y, -+ int stage_width, -+ int stage_height, -+ int *protocol_x, -+ int *protocol_y, -+ int *protocol_width, -+ int *protocol_height) -+{ -+ MetaDisplay *display = meta_window_get_display (META_WINDOW (window_x11)); -+ MetaContext *context = meta_display_get_context (display); -+ MetaWaylandCompositor *wayland_compositor = -+ meta_context_get_wayland_compositor (context); -+ MetaXWaylandManager *xwayland_manager = &wayland_compositor->xwayland_manager; -+ int scale; -+ -+ scale = meta_xwayland_get_effective_scale (xwayland_manager); -+ if (protocol_x) -+ *protocol_x = stage_x * scale; -+ if (protocol_y) -+ *protocol_y = stage_y * scale; -+ if (protocol_width) -+ *protocol_width = stage_width * scale; -+ if (protocol_height) -+ *protocol_height = stage_height * scale; -+} -+ -+static void -+meta_window_xwayland_protocol_to_stage (MetaWindowX11 *window_x11, -+ int protocol_x, -+ int protocol_y, -+ int protocol_width, -+ int protocol_height, -+ int *stage_x, -+ int *stage_y, -+ int *stage_width, -+ int *stage_height) -+{ -+ MetaDisplay *display = meta_window_get_display (META_WINDOW (window_x11)); -+ MetaContext *context = meta_display_get_context (display); -+ MetaWaylandCompositor *wayland_compositor = -+ meta_context_get_wayland_compositor (context); -+ MetaXWaylandManager *xwayland_manager = &wayland_compositor->xwayland_manager; -+ MtkRectangle rect; -+ int scale; -+ -+ rect.x = protocol_x; -+ rect.y = protocol_y; -+ rect.width = protocol_width; -+ rect.height = protocol_height; -+ -+ scale = meta_xwayland_get_effective_scale (xwayland_manager); -+ mtk_rectangle_scale_double (&rect, 1.0 / scale, MTK_ROUNDING_STRATEGY_GROW, &rect); -+ -+ if (stage_x) -+ *stage_x = rect.x; -+ if (stage_y) -+ *stage_y = rect.y; -+ if (stage_width) -+ *stage_width = rect.width; -+ if (stage_height) -+ *stage_height = rect.height; -+} -+ - static void - meta_window_xwayland_class_init (MetaWindowXwaylandClass *klass) - { -@@ -331,6 +398,8 @@ meta_window_xwayland_class_init (MetaWindowXwaylandClass *klass) - window_x11_class->thaw_commits = meta_window_xwayland_thaw_commits; - window_x11_class->always_update_shape = meta_window_xwayland_always_update_shape; - window_x11_class->process_property_notify = meta_window_xwayland_process_property_notify; -+ window_x11_class->stage_to_protocol = meta_window_xwayland_stage_to_protocol; -+ window_x11_class->protocol_to_stage = meta_window_xwayland_protocol_to_stage; - - gobject_class->get_property = meta_window_xwayland_get_property; - gobject_class->set_property = meta_window_xwayland_set_property; -diff --git a/src/wayland/meta-xwayland-private.h b/src/wayland/meta-xwayland-private.h -index 7a9cb73fd..9e06f0315 100644 ---- a/src/wayland/meta-xwayland-private.h -+++ b/src/wayland/meta-xwayland-private.h -@@ -20,6 +20,7 @@ - #include - - #include "wayland/meta-wayland-private.h" -+#include "wayland/meta-xwayland.h" - - gboolean - meta_xwayland_init (MetaXWaylandManager *manager, -diff --git a/src/wayland/meta-xwayland-surface.c b/src/wayland/meta-xwayland-surface.c -index 8fa1c72a9..c6daf9b26 100644 ---- a/src/wayland/meta-xwayland-surface.c -+++ b/src/wayland/meta-xwayland-surface.c -@@ -163,13 +163,19 @@ meta_xwayland_surface_get_relative_coordinates (MetaWaylandSurfaceRole *surface_ - float *out_sy) - { - MetaXwaylandSurface *xwayland_surface = META_XWAYLAND_SURFACE (surface_role); -+ MetaWaylandSurface *surface = -+ meta_wayland_surface_role_get_surface (surface_role); -+ MetaWaylandCompositor *compositor = -+ meta_wayland_surface_get_compositor (surface); - MtkRectangle window_rect = { 0 }; -+ int xwayland_scale; - - if (xwayland_surface->window) - meta_window_get_buffer_rect (xwayland_surface->window, &window_rect); - -- *out_sx = abs_x - window_rect.x; -- *out_sy = abs_y - window_rect.y; -+ xwayland_scale = meta_xwayland_get_effective_scale (&compositor->xwayland_manager); -+ *out_sx = (abs_x - window_rect.x) * xwayland_scale; -+ *out_sy = (abs_y - window_rect.y) * xwayland_scale; - } - - static MetaWaylandSurface * -diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c -index ea9c27d74..828e6f64e 100644 ---- a/src/wayland/meta-xwayland.c -+++ b/src/wayland/meta-xwayland.c -@@ -1051,6 +1051,29 @@ meta_xwayland_shutdown (MetaWaylandCompositor *compositor) - } - } - -+static void -+update_highest_monitor_scale (MetaXWaylandManager *manager) -+{ -+ MetaWaylandCompositor *compositor = manager->compositor; -+ MetaContext *context = meta_wayland_compositor_get_context (compositor); -+ MetaBackend *backend = meta_context_get_backend (context); -+ MetaMonitorManager *monitor_manager = -+ meta_backend_get_monitor_manager (backend); -+ GList *logical_monitors; -+ GList *l; -+ double scale = 1.0; -+ -+ logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); -+ for (l = logical_monitors; l; l = l->next) -+ { -+ MetaLogicalMonitor *logical_monitor = l->data; -+ -+ scale = MAX (scale, meta_logical_monitor_get_scale (logical_monitor)); -+ } -+ -+ manager->highest_monitor_scale = scale; -+} -+ - gboolean - meta_xwayland_init (MetaXWaylandManager *manager, - MetaWaylandCompositor *compositor, -@@ -1058,6 +1081,9 @@ meta_xwayland_init (MetaXWaylandManager *manager, - GError **error) - { - MetaContext *context = compositor->context; -+ MetaBackend *backend = meta_context_get_backend (context); -+ MetaMonitorManager *monitor_manager = -+ meta_backend_get_monitor_manager (backend); - MetaX11DisplayPolicy policy; - int display = 0; - -@@ -1121,6 +1147,10 @@ meta_xwayland_init (MetaXWaylandManager *manager, - /* Xwayland specific protocol, needs to be filtered out for all other clients */ - meta_xwayland_grab_keyboard_init (compositor); - -+ g_signal_connect_swapped (monitor_manager, "monitors-changed-internal", -+ G_CALLBACK (update_highest_monitor_scale), manager); -+ update_highest_monitor_scale (manager); -+ - return TRUE; - } - -@@ -1312,3 +1342,29 @@ meta_xwayland_set_should_enable_ei_portal (MetaXWaylandManager *manager, - { - manager->should_enable_ei_portal = should_enable_ei_portal; - } -+ -+int -+meta_xwayland_get_effective_scale (MetaXWaylandManager *manager) -+{ -+ MetaWaylandCompositor *compositor = manager->compositor; -+ MetaContext *context = meta_wayland_compositor_get_context (compositor); -+ MetaBackend *backend = meta_context_get_backend (context); -+ MetaMonitorManager *monitor_manager = -+ meta_backend_get_monitor_manager (backend); -+ MetaSettings *settings = meta_backend_get_settings (backend); -+ -+ switch (meta_monitor_manager_get_layout_mode (monitor_manager)) -+ { -+ case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: -+ break; -+ -+ case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: -+ if (meta_settings_is_experimental_feature_enabled (settings, -+ META_EXPERIMENTAL_FEATURE_XWAYLAND_NATIVE_SCALING) && -+ meta_settings_is_experimental_feature_enabled (settings, -+ META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER)) -+ return ceil (manager->highest_monitor_scale); -+ } -+ -+ return 1; -+} -diff --git a/src/wayland/meta-xwayland.h b/src/wayland/meta-xwayland.h -index daf9d1abb..ae7a06977 100644 ---- a/src/wayland/meta-xwayland.h -+++ b/src/wayland/meta-xwayland.h -@@ -48,3 +48,5 @@ META_EXPORT_TEST - gboolean meta_xwayland_signal (MetaXWaylandManager *manager, - int signum, - GError **error); -+ -+int meta_xwayland_get_effective_scale (MetaXWaylandManager *manager); -diff --git a/src/x11/meta-x11-display.c b/src/x11/meta-x11-display.c -index 5c0760daa..f67e9e427 100644 ---- a/src/x11/meta-x11-display.c -+++ b/src/x11/meta-x11-display.c -@@ -70,7 +70,7 @@ - #include "wayland/meta-xwayland-private.h" - #endif - --G_DEFINE_TYPE (MetaX11Display, meta_x11_display, G_TYPE_OBJECT) -+#include "meta-dbus-x11.h" - - static GQuark quark_x11_display_logical_monitor_data = 0; - -@@ -89,6 +89,14 @@ typedef struct _MetaX11DisplayLogicalMonitorData - int xinerama_index; - } MetaX11DisplayLogicalMonitorData; - -+typedef struct _MetaX11DisplayPrivate -+{ -+ MetaDBusX11 *dbus_api; -+ guint dbus_name_id; -+} MetaX11DisplayPrivate; -+ -+G_DEFINE_TYPE_WITH_PRIVATE (MetaX11Display, meta_x11_display, G_TYPE_OBJECT) -+ - static char *get_screen_name (Display *xdisplay, - int number); - -@@ -122,6 +130,42 @@ backend_from_x11_display (MetaX11Display *x11_display) - return meta_context_get_backend (context); - } - -+static void -+stage_to_protocol (MetaX11Display *x11_display, -+ int stage_x, -+ int stage_y, -+ int *protocol_x, -+ int *protocol_y) -+{ -+ MetaDisplay *display = meta_x11_display_get_display (x11_display); -+ MetaContext *context = meta_display_get_context (display); -+ int scale = 1; -+ -+ switch (meta_context_get_compositor_type (context)) -+ { -+ case META_COMPOSITOR_TYPE_WAYLAND: -+ { -+#ifdef HAVE_XWAYLAND -+ MetaWaylandCompositor *wayland_compositor = -+ meta_context_get_wayland_compositor (context); -+ MetaXWaylandManager *xwayland_manager = -+ &wayland_compositor->xwayland_manager; -+ -+ scale = meta_xwayland_get_effective_scale (xwayland_manager); -+#endif -+ break; -+ } -+ -+ case META_COMPOSITOR_TYPE_X11: -+ break; -+ } -+ -+ if (protocol_x) -+ *protocol_x = stage_x * scale; -+ if (protocol_y) -+ *protocol_y = stage_y * scale; -+} -+ - static void - meta_x11_display_unmanage_windows (MetaX11Display *x11_display) - { -@@ -151,13 +195,68 @@ meta_x11_event_filter_free (MetaX11EventFilter *filter) - g_free (filter); - } - -+static void -+on_bus_acquired (GDBusConnection *connection, -+ const char *name, -+ gpointer user_data) -+{ -+ MetaX11Display *x11_display = user_data; -+ MetaX11DisplayPrivate *priv = -+ meta_x11_display_get_instance_private (x11_display); -+ -+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->dbus_api), -+ connection, -+ "/org/gnome/Mutter/X11", -+ NULL); -+} -+ -+static void -+update_ui_scaling_factor (MetaX11Display *x11_display) -+{ -+ MetaX11DisplayPrivate *priv = -+ meta_x11_display_get_instance_private (x11_display); -+ MetaBackend *backend = backend_from_x11_display (x11_display); -+ MetaContext *context = meta_backend_get_context (backend); -+ int ui_scaling_factor = 1; -+ -+ switch (meta_context_get_compositor_type (context)) -+ { -+ case META_COMPOSITOR_TYPE_WAYLAND: -+ { -+#ifdef HAVE_XWAYLAND -+ MetaWaylandCompositor *wayland_compositor = -+ meta_context_get_wayland_compositor (context); -+ MetaXWaylandManager *xwayland_manager = -+ &wayland_compositor->xwayland_manager; -+ -+ ui_scaling_factor = meta_xwayland_get_effective_scale (xwayland_manager); -+#endif -+ break; -+ } -+ case META_COMPOSITOR_TYPE_X11: -+ { -+ MetaSettings *settings = meta_backend_get_settings (backend); -+ -+ ui_scaling_factor = meta_settings_get_ui_scaling_factor (settings); -+ break; -+ } -+ } -+ -+ meta_dbus_x11_set_ui_scaling_factor (priv->dbus_api, ui_scaling_factor); -+} -+ - static void - meta_x11_display_dispose (GObject *object) - { - MetaX11Display *x11_display = META_X11_DISPLAY (object); -+ MetaX11DisplayPrivate *priv = -+ meta_x11_display_get_instance_private (x11_display); - - x11_display->closing = TRUE; - -+ g_clear_handle_id (&priv->dbus_name_id, g_bus_unown_name); -+ g_clear_object (&priv->dbus_api); -+ - g_clear_pointer (&x11_display->alarm_filters, g_ptr_array_unref); - - g_clear_list (&x11_display->event_funcs, -@@ -600,6 +699,9 @@ set_desktop_geometry_hint (MetaX11Display *x11_display) - return; - - meta_display_get_size (x11_display->display, &monitor_width, &monitor_height); -+ stage_to_protocol (x11_display, -+ monitor_width, monitor_height, -+ &monitor_width, &monitor_height); - - data[0] = monitor_width; - data[1] = monitor_height; -@@ -1009,14 +1111,22 @@ set_workspace_work_area_hint (MetaWorkspace *workspace, - - for (l = logical_monitors; l; l = l->next) - { -- MtkRectangle area; -+ MtkRectangle stage_area; -+ MtkRectangle protocol_area; - -- meta_workspace_get_work_area_for_logical_monitor (workspace, l->data, &area); -+ meta_workspace_get_work_area_for_logical_monitor (workspace, l->data, -+ &stage_area); - -- tmp[0] = area.x; -- tmp[1] = area.y; -- tmp[2] = area.width; -- tmp[3] = area.height; -+ stage_to_protocol (x11_display, -+ stage_area.x, stage_area.y, -+ &protocol_area.x, &protocol_area.y); -+ stage_to_protocol (x11_display, -+ stage_area.width, stage_area.height, -+ &protocol_area.width, &protocol_area.height); -+ tmp[0] = protocol_area.x; -+ tmp[1] = protocol_area.y; -+ tmp[2] = protocol_area.width; -+ tmp[3] = protocol_area.height; - - tmp += 4; - } -@@ -1045,7 +1155,6 @@ set_work_area_hint (MetaDisplay *display, - int num_workspaces; - GList *l; - unsigned long *data, *tmp; -- MtkRectangle area; - - num_workspaces = meta_workspace_manager_get_n_workspaces (workspace_manager); - data = g_new (unsigned long, num_workspaces * 4); -@@ -1054,14 +1163,22 @@ set_work_area_hint (MetaDisplay *display, - for (l = workspace_manager->workspaces; l; l = l->next) - { - MetaWorkspace *workspace = l->data; -+ MtkRectangle stage_area; -+ MtkRectangle protocol_area; - -- meta_workspace_get_work_area_all_monitors (workspace, &area); -+ meta_workspace_get_work_area_all_monitors (workspace, &stage_area); - set_workspace_work_area_hint (workspace, x11_display); - -- tmp[0] = area.x; -- tmp[1] = area.y; -- tmp[2] = area.width; -- tmp[3] = area.height; -+ stage_to_protocol (x11_display, -+ stage_area.x, stage_area.y, -+ &protocol_area.x, &protocol_area.y); -+ stage_to_protocol (x11_display, -+ stage_area.width, stage_area.height, -+ &protocol_area.width, &protocol_area.height); -+ tmp[0] = protocol_area.x; -+ tmp[1] = protocol_area.y; -+ tmp[2] = protocol_area.width; -+ tmp[3] = protocol_area.height; - - tmp += 4; - } -@@ -1224,6 +1341,58 @@ meta_x11_display_init_frames_client (MetaX11Display *x11_display) - on_frames_client_died, x11_display); - } - -+static void -+initialize_dbus_interface (MetaX11Display *x11_display) -+{ -+ MetaX11DisplayPrivate *priv = -+ meta_x11_display_get_instance_private (x11_display); -+ -+ priv->dbus_api = meta_dbus_x11_skeleton_new (); -+ priv->dbus_name_id = -+ g_bus_own_name (G_BUS_TYPE_SESSION, -+ "org.gnome.Mutter.X11", -+ G_BUS_NAME_OWNER_FLAGS_NONE, -+ on_bus_acquired, -+ NULL, NULL, -+ x11_display, NULL); -+ update_ui_scaling_factor (x11_display); -+} -+ -+static void -+experimental_features_changed (MetaSettings *settings, -+ MetaExperimentalFeature old_experimental_features, -+ MetaX11Display *x11_display) -+{ -+ gboolean was_xwayland_native_scaling; -+ gboolean was_stage_views_scaled; -+ gboolean is_xwayland_native_scaling; -+ gboolean is_stage_views_scaled; -+ -+ was_xwayland_native_scaling = -+ !!(old_experimental_features & -+ META_EXPERIMENTAL_FEATURE_XWAYLAND_NATIVE_SCALING); -+ was_stage_views_scaled = -+ !!(old_experimental_features & -+ META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER); -+ -+ is_xwayland_native_scaling = -+ meta_settings_is_experimental_feature_enabled ( -+ settings, -+ META_EXPERIMENTAL_FEATURE_XWAYLAND_NATIVE_SCALING); -+ is_stage_views_scaled = -+ meta_settings_is_experimental_feature_enabled ( -+ settings, -+ META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER); -+ -+ if (is_xwayland_native_scaling != was_xwayland_native_scaling || -+ is_stage_views_scaled != was_stage_views_scaled) -+ { -+ update_ui_scaling_factor (x11_display); -+ set_desktop_geometry_hint (x11_display); -+ set_work_area_hint (x11_display->display, x11_display); -+ } -+} -+ - /** - * meta_x11_display_new: - * -@@ -1242,6 +1411,7 @@ meta_x11_display_new (MetaDisplay *display, - MetaBackend *backend = meta_context_get_backend (context); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); -+ MetaSettings *settings = meta_backend_get_settings (backend); - g_autoptr (MetaX11Display) x11_display = NULL; - Display *xdisplay; - Screen *xscreen; -@@ -1318,6 +1488,8 @@ meta_x11_display_new (MetaDisplay *display, - x11_display = g_object_new (META_TYPE_X11_DISPLAY, NULL); - x11_display->display = display; - -+ initialize_dbus_interface (x11_display); -+ - /* here we use XDisplayName which is what the user - * probably put in, vs. DisplayString(display) which is - * canonicalized by XOpenDisplay() -@@ -1410,7 +1582,7 @@ meta_x11_display_new (MetaDisplay *display, - "monitors-changed-internal", - G_CALLBACK (on_monitors_changed_internal), - x11_display, -- 0); -+ G_CONNECT_AFTER); - - init_leader_window (x11_display, ×tamp); - x11_display->timestamp = timestamp; -@@ -1503,6 +1675,11 @@ meta_x11_display_new (MetaDisplay *display, - - meta_prefs_add_listener (prefs_changed_callback, x11_display); - -+ g_signal_connect_object (settings, -+ "experimental-features-changed", -+ G_CALLBACK (experimental_features_changed), -+ x11_display, 0); -+ - set_work_area_hint (display, x11_display); - - g_signal_connect_object (display, "workareas-changed", -@@ -1711,16 +1888,12 @@ meta_x11_display_reload_cursor (MetaX11Display *x11_display) - } - - static void --set_cursor_theme (Display *xdisplay, -- MetaBackend *backend) -+set_cursor_theme (Display *xdisplay, -+ const char *theme, -+ int size) - { -- MetaSettings *settings = meta_backend_get_settings (backend); -- int scale; -- -- scale = meta_settings_get_ui_scaling_factor (settings); -- XcursorSetTheme (xdisplay, meta_prefs_get_cursor_theme ()); -- XcursorSetDefaultSize (xdisplay, -- meta_prefs_get_cursor_size () * scale); -+ XcursorSetTheme (xdisplay, theme); -+ XcursorSetDefaultSize (xdisplay, size); - } - - static void -@@ -1772,8 +1945,37 @@ static void - update_cursor_theme (MetaX11Display *x11_display) - { - MetaBackend *backend = backend_from_x11_display (x11_display); -+ MetaContext *context = meta_backend_get_context (backend); -+ MetaSettings *settings = meta_backend_get_settings (backend); -+ int scale = 1; -+ int size; -+ const char *theme; -+ -+ switch (meta_context_get_compositor_type (context)) -+ { -+ case META_COMPOSITOR_TYPE_WAYLAND: -+ { -+#ifdef HAVE_XWAYLAND -+ MetaWaylandCompositor *wayland_compositor = -+ meta_context_get_wayland_compositor (context); -+ MetaXWaylandManager *xwayland_manager = -+ &wayland_compositor->xwayland_manager; - -- set_cursor_theme (x11_display->xdisplay, backend); -+ scale = meta_xwayland_get_effective_scale (xwayland_manager); -+#endif -+ break; -+ } -+ -+ case META_COMPOSITOR_TYPE_X11: -+ scale = meta_settings_get_ui_scaling_factor (settings); -+ break; -+ } -+ -+ size = meta_prefs_get_cursor_size () * scale; -+ -+ theme = meta_prefs_get_cursor_theme (); -+ -+ set_cursor_theme (x11_display->xdisplay, theme, size); - schedule_reload_x11_cursor (x11_display); - - if (META_IS_BACKEND_X11 (backend)) -@@ -1781,7 +1983,7 @@ update_cursor_theme (MetaX11Display *x11_display) - MetaBackendX11 *backend_x11 = META_BACKEND_X11 (backend); - Display *xdisplay = meta_backend_x11_get_xdisplay (backend_x11); - -- set_cursor_theme (xdisplay, backend); -+ set_cursor_theme (xdisplay, theme, size); - meta_backend_x11_reload_cursor (backend_x11); - } - } -@@ -1974,6 +2176,8 @@ on_monitors_changed_internal (MetaMonitorManager *monitor_manager, - } - - x11_display->has_xinerama_indices = FALSE; -+ -+ update_ui_scaling_factor (x11_display); - } - - static Bool -diff --git a/src/x11/window-props.c b/src/x11/window-props.c -index c18b3eab5..494fbe843 100644 ---- a/src/x11/window-props.c -+++ b/src/x11/window-props.c -@@ -305,10 +305,15 @@ reload_icon_geometry (MetaWindow *window, - { - MtkRectangle geometry; - -- geometry.x = (int)value->v.cardinal_list.cardinals[0]; -- geometry.y = (int)value->v.cardinal_list.cardinals[1]; -- geometry.width = (int)value->v.cardinal_list.cardinals[2]; -- geometry.height = (int)value->v.cardinal_list.cardinals[3]; -+ meta_window_x11_protocol_to_stage (META_WINDOW_X11 (window), -+ value->v.cardinal_list.cardinals[0], -+ value->v.cardinal_list.cardinals[1], -+ value->v.cardinal_list.cardinals[2], -+ value->v.cardinal_list.cardinals[3], -+ &geometry.x, -+ &geometry.y, -+ &geometry.width, -+ &geometry.height); - - meta_window_set_icon_geometry (window, &geometry); - } -@@ -370,11 +375,24 @@ reload_gtk_frame_extents (MetaWindow *window, - } - else - { -+ int left, right, top, bottom; - MetaFrameBorder extents; -- extents.left = (int)value->v.cardinal_list.cardinals[0]; -- extents.right = (int)value->v.cardinal_list.cardinals[1]; -- extents.top = (int)value->v.cardinal_list.cardinals[2]; -- extents.bottom = (int)value->v.cardinal_list.cardinals[3]; -+ -+ meta_window_x11_protocol_to_stage (META_WINDOW_X11 (window), -+ value->v.cardinal_list.cardinals[0], -+ value->v.cardinal_list.cardinals[1], -+ value->v.cardinal_list.cardinals[2], -+ value->v.cardinal_list.cardinals[3], -+ &left, -+ &right, -+ &top, -+ &bottom); -+ -+ extents.left = left; -+ extents.right = right; -+ extents.top = top; -+ extents.bottom = bottom; -+ - meta_window_set_custom_frame_extents (window, &extents, initial); - } - } -@@ -678,10 +696,16 @@ reload_opaque_region (MetaWindow *window, - { - MtkRectangle *rect = &rects[rect_index]; - -- rect->x = region[i++]; -- rect->y = region[i++]; -- rect->width = region[i++]; -- rect->height = region[i++]; -+ meta_window_x11_protocol_to_stage (META_WINDOW_X11 (window), -+ region[i + 0], -+ region[i + 1], -+ region[i + 2], -+ region[i + 3], -+ &rect->x, -+ &rect->y, -+ &rect->width, -+ &rect->height); -+ i += 4; - - rect_index++; - } -@@ -1245,9 +1269,65 @@ meta_set_normal_hints (MetaWindow *window, - * as if flags were zero - */ - if (hints) -- window->size_hints = *(MetaSizeHints*)(hints); -+ { -+ MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); -+ -+ window->size_hints = *(MetaSizeHints *) hints; -+ -+ meta_window_x11_protocol_to_stage (window_x11, -+ hints->x, hints->y, -+ hints->width, hints->height, -+ &window->size_hints.x, -+ &window->size_hints.y, -+ &window->size_hints.width, -+ &window->size_hints.height); -+ -+ meta_window_x11_protocol_to_stage (window_x11, -+ hints->min_width, hints->min_height, -+ 0, 0, -+ &window->size_hints.min_width, -+ &window->size_hints.min_height, -+ NULL, NULL); -+ -+ meta_window_x11_protocol_to_stage (window_x11, -+ hints->max_width, hints->max_height, -+ 0, 0, -+ &window->size_hints.max_width, -+ &window->size_hints.max_height, -+ NULL, NULL); -+ -+ meta_window_x11_protocol_to_stage (window_x11, -+ hints->width_inc, hints->height_inc, -+ 0, 0, -+ &window->size_hints.width_inc, -+ &window->size_hints.height_inc, -+ NULL, NULL); -+ -+ meta_window_x11_protocol_to_stage (window_x11, -+ hints->min_aspect.x, hints->min_aspect.y, -+ 0, 0, -+ &window->size_hints.min_aspect.x, -+ &window->size_hints.min_aspect.y, -+ NULL, NULL); -+ -+ meta_window_x11_protocol_to_stage (window_x11, -+ hints->max_aspect.x, hints->max_aspect.y, -+ 0, 0, -+ &window->size_hints.max_aspect.x, -+ &window->size_hints.max_aspect.y, -+ NULL, NULL); -+ -+ meta_window_x11_protocol_to_stage (window_x11, -+ hints->base_width, hints->base_height, -+ 0, 0, -+ &window->size_hints.base_width, -+ &window->size_hints.base_height, -+ NULL, NULL); -+ } - else -- window->size_hints.flags = 0; -+ { -+ window->size_hints.flags = 0; -+ } - - /* Put back saved ConfigureRequest. */ - window->size_hints.x = x; diff --git a/src/x11/window-x11.c b/src/x11/window-x11.c -index b15ddfb5d..fd1a8ec1b 100644 +index 6d2016e3e..6ba8988c9 100644 --- a/src/x11/window-x11.c +++ b/src/x11/window-x11.c -@@ -110,6 +110,113 @@ meta_window_x11_get_private (MetaWindowX11 *window_x11) - return meta_window_x11_get_instance_private (window_x11); - } - -+static void -+meta_window_x11_real_stage_to_protocol (MetaWindowX11 *window_x11, -+ int stage_x, -+ int stage_y, -+ int stage_width, -+ int stage_height, -+ int *protocol_x, -+ int *protocol_y, -+ int *protocol_width, -+ int *protocol_height) -+{ -+ if (protocol_x) -+ *protocol_x = stage_x; -+ if (protocol_y) -+ *protocol_y = stage_y; -+ if (protocol_width) -+ *protocol_width = stage_width; -+ if (protocol_height) -+ *protocol_height = stage_height; -+} -+ -+static void -+meta_window_x11_real_protocol_to_stage (MetaWindowX11 *window_x11, -+ int protocol_x, -+ int protocol_y, -+ int protocol_width, -+ int protocol_height, -+ int *stage_x, -+ int *stage_y, -+ int *stage_width, -+ int *stage_height) -+{ -+ if (stage_x) -+ *stage_x = protocol_x; -+ if (stage_y) -+ *stage_y = protocol_y; -+ if (stage_width) -+ *stage_width = protocol_width; -+ if (stage_height) -+ *stage_height = protocol_height; -+} -+ -+void -+meta_window_x11_stage_to_protocol (MetaWindowX11 *window_x11, -+ int stage_x, -+ int stage_y, -+ int stage_width, -+ int stage_height, -+ int *protocol_x, -+ int *protocol_y, -+ int *protocol_width, -+ int *protocol_height) -+{ -+ MetaWindowX11Class *klass = META_WINDOW_X11_GET_CLASS (window_x11); -+ -+ klass->stage_to_protocol (window_x11, -+ stage_x, stage_y, -+ stage_width, stage_height, -+ protocol_x, protocol_y, -+ protocol_width, protocol_height); -+} -+ -+void -+meta_window_x11_protocol_to_stage (MetaWindowX11 *window_x11, -+ int protocol_x, -+ int protocol_y, -+ int protocol_width, -+ int protocol_height, -+ int *stage_x, -+ int *stage_y, -+ int *stage_width, -+ int *stage_height) -+{ -+ MetaWindowX11Class *klass = META_WINDOW_X11_GET_CLASS (window_x11); -+ -+ klass->protocol_to_stage (window_x11, -+ protocol_x, protocol_y, -+ protocol_width, protocol_height, -+ stage_x, stage_y, -+ stage_width, stage_height); -+} -+ -+static MtkRegion * -+region_protocol_to_stage (MtkRegion *region, -+ MetaWindowX11 *window_x11) -+{ -+ int n_rects, i; -+ MtkRectangle *rects; -+ MtkRegion *scaled_region; -+ -+ n_rects = mtk_region_num_rectangles (region); -+ MTK_RECTANGLE_CREATE_ARRAY_SCOPED (n_rects, rects); -+ for (i = 0; i < n_rects; i++) -+ { -+ rects[i] = mtk_region_get_rectangle (region, i); -+ meta_window_x11_protocol_to_stage (window_x11, -+ rects[i].x, rects[i].y, -+ rects[i].width, rects[i].height, -+ &rects[i].x, &rects[i].y, -+ &rects[i].width, &rects[i].height); -+ } -+ -+ scaled_region = mtk_region_create_rectangles (rects, n_rects); -+ -+ return scaled_region; -+} -+ - static void - send_icccm_message (MetaWindow *window, - Atom atom, -@@ -254,8 +361,13 @@ send_configure_notify (MetaWindow *window) - event.xconfigure.display = x11_display->xdisplay; - event.xconfigure.event = priv->xwindow; - event.xconfigure.window = priv->xwindow; -- event.xconfigure.x = priv->client_rect.x - priv->border_width; -- event.xconfigure.y = priv->client_rect.y - priv->border_width; -+ meta_window_x11_stage_to_protocol (window_x11, -+ priv->client_rect.x - priv->border_width, -+ priv->client_rect.y - priv->border_width, -+ 0, 0, -+ &event.xconfigure.x, -+ &event.xconfigure.y, -+ NULL, NULL); - if (window->frame) - { - if (window->withdrawn) -@@ -267,19 +379,42 @@ send_configure_notify (MetaWindow *window) - - meta_frame_calc_borders (window->frame, &borders); - -- event.xconfigure.x = window->frame->rect.x + borders.invisible.left; -- event.xconfigure.y = window->frame->rect.y + borders.invisible.top; -+ meta_window_x11_stage_to_protocol (window_x11, -+ window->frame->rect.x + borders.invisible.left, -+ window->frame->rect.y + borders.invisible.top, -+ 0, 0, -+ &event.xconfigure.x, -+ &event.xconfigure.y, -+ NULL, NULL); - } - else - { -+ int dx, dy; -+ - /* Need to be in root window coordinates */ -- event.xconfigure.x += window->frame->rect.x; -- event.xconfigure.y += window->frame->rect.y; -+ meta_window_x11_stage_to_protocol (window_x11, -+ window->frame->rect.x, -+ window->frame->rect.y, -+ 0, 0, -+ &dx, -+ &dy, -+ NULL, NULL); -+ event.xconfigure.x += dx; -+ event.xconfigure.y += dy; - } - } -- event.xconfigure.width = priv->client_rect.width; -- event.xconfigure.height = priv->client_rect.height; -- event.xconfigure.border_width = priv->border_width; /* requested not actual */ -+ meta_window_x11_stage_to_protocol (window_x11, -+ priv->client_rect.width, -+ priv->client_rect.height, -+ 0, 0, -+ &event.xconfigure.width, -+ &event.xconfigure.height, -+ NULL, NULL); -+ meta_window_x11_stage_to_protocol (window_x11, -+ priv->border_width, -+ 0, 0, 0, -+ &event.xconfigure.border_width, -+ NULL, NULL, NULL); - event.xconfigure.above = None; /* FIXME */ - event.xconfigure.override_redirect = False; - -@@ -1137,20 +1272,26 @@ static void - update_net_frame_extents (MetaWindow *window) - { - MetaX11Display *x11_display = window->display->x11_display; -- -+ int left, right, top, bottom; - unsigned long data[4]; - MetaFrameBorders borders; - Window xwindow = meta_window_x11_get_xwindow (window); - - meta_frame_calc_borders (window->frame, &borders); -- /* Left */ -- data[0] = borders.visible.left; -- /* Right */ -- data[1] = borders.visible.right; -- /* Top */ -- data[2] = borders.visible.top; -- /* Bottom */ -- data[3] = borders.visible.bottom; -+ meta_window_x11_stage_to_protocol (META_WINDOW_X11 (window), -+ borders.visible.left, -+ borders.visible.right, -+ borders.visible.top, -+ borders.visible.bottom, -+ &left, -+ &right, -+ &top, -+ &bottom); -+ -+ data[0] = left; -+ data[1] = right; -+ data[2] = top; -+ data[3] = bottom; - - meta_topic (META_DEBUG_GEOMETRY, - "Setting _NET_FRAME_EXTENTS on managed window 0x%lx " -@@ -1482,10 +1623,11 @@ meta_window_x11_move_resize_internal (MetaWindow *window, - configure_frame_first = size_dx + size_dy >= 0; - - values.border_width = 0; -- values.x = client_rect.x; -- values.y = client_rect.y; -- values.width = client_rect.width; -- values.height = client_rect.height; -+ meta_window_x11_stage_to_protocol (window_x11, -+ client_rect.x, client_rect.y, -+ client_rect.width, client_rect.height, -+ &values.x, &values.y, -+ &values.width, &values.height); - - mask = 0; - if (is_configure_request && priv->border_width != 0) -@@ -1591,6 +1733,10 @@ meta_window_x11_update_struts (MetaWindow *window) - strut_begin = struts[4+(i*2)]; - strut_end = struts[4+(i*2)+1]; - -+ meta_window_x11_protocol_to_stage (META_WINDOW_X11 (window), -+ strut_begin, strut_end, thickness, 0, -+ &strut_begin, &strut_end, &thickness, NULL); -+ - temp = g_new0 (MetaStrut, 1); - temp->side = 1 << i; /* See MetaSide def. Matches nicely, eh? */ - meta_display_get_size (window->display, -@@ -1655,6 +1801,10 @@ meta_window_x11_update_struts (MetaWindow *window) - if (thickness == 0) - continue; - -+ meta_window_x11_protocol_to_stage (META_WINDOW_X11 (window), -+ thickness, 0, 0, 0, -+ &thickness, NULL, NULL, NULL); -+ - temp = g_new0 (MetaStrut, 1); - temp->side = 1 << i; - meta_display_get_size (window->display, -@@ -2040,9 +2190,10 @@ static void - meta_window_x11_constructed (GObject *object) - { - MetaWindow *window = META_WINDOW (object); -- MetaWindowX11 *x11_window = META_WINDOW_X11 (object); -- MetaWindowX11Private *priv = meta_window_x11_get_instance_private (x11_window); -+ MetaWindowX11 *window_x11 = META_WINDOW_X11 (object); -+ MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); - XWindowAttributes attrs = priv->attributes; -+ MtkRectangle rect; - - meta_verbose ("attrs->map_state = %d (%s)", - attrs.map_state, -@@ -2057,16 +2208,17 @@ meta_window_x11_constructed (GObject *object) - window->client_type = META_WINDOW_CLIENT_TYPE_X11; - window->override_redirect = attrs.override_redirect; - -- window->rect.x = attrs.x; -- window->rect.y = attrs.y; -- window->rect.width = attrs.width; -- window->rect.height = attrs.height; -+ meta_window_x11_protocol_to_stage (window_x11, -+ attrs.x, attrs.y, attrs.width, attrs.height, -+ &rect.x, &rect.y, &rect.width, &rect.height); -+ -+ window->rect = rect; - - /* size_hints are the "request" */ -- window->size_hints.x = attrs.x; -- window->size_hints.y = attrs.y; -- window->size_hints.width = attrs.width; -- window->size_hints.height = attrs.height; -+ window->size_hints.x = rect.x; -+ window->size_hints.y = rect.y; -+ window->size_hints.width = rect.width; -+ window->size_hints.height = rect.height; - - window->depth = attrs.depth; - priv->xvisual = attrs.visual; -@@ -2076,11 +2228,11 @@ meta_window_x11_constructed (GObject *object) - - window->decorated = TRUE; - window->hidden = FALSE; -- priv->border_width = attrs.border_width; - priv->xclient_leader = None; - -- priv->keys_grabbed = FALSE; -- priv->grab_on_frame = FALSE; -+ meta_window_x11_protocol_to_stage (window_x11, -+ attrs.border_width, 0, 0, 0, -+ &priv->border_width, NULL, NULL, NULL); - - G_OBJECT_CLASS (meta_window_x11_parent_class)->constructed (object); - } -@@ -2188,6 +2340,8 @@ meta_window_x11_class_init (MetaWindowX11Class *klass) - klass->thaw_commits = meta_window_x11_impl_thaw_commits; - klass->always_update_shape = meta_window_x11_impl_always_update_shape; - klass->process_property_notify = meta_window_x11_impl_process_property_notify; -+ klass->stage_to_protocol = meta_window_x11_real_stage_to_protocol; -+ klass->protocol_to_stage = meta_window_x11_real_protocol_to_stage; - - obj_props[PROP_ATTRIBUTES] = - g_param_spec_pointer ("attributes", NULL, NULL, -@@ -2473,7 +2627,10 @@ meta_window_x11_update_input_region (MetaWindow *window) - else - { - /* Window has a custom shape. */ -- region = region_create_from_x_rectangles (rects, n_rects); -+ g_autoptr (MtkRegion) protocol_region = NULL; -+ -+ protocol_region = region_create_from_x_rectangles (rects, n_rects); -+ region = region_protocol_to_stage (protocol_region, window_x11); - } - - meta_XFree (rects); -@@ -2550,7 +2707,10 @@ meta_window_x11_update_shape_region (MetaWindow *window) - - if (rects) - { -- region = region_create_from_x_rectangles (rects, n_rects); -+ g_autoptr (MtkRegion) protocol_region = NULL; -+ -+ protocol_region = region_create_from_x_rectangles (rects, n_rects); -+ region = region_protocol_to_stage (protocol_region, window_x11); - XFree (rects); - } - } -@@ -2828,6 +2988,7 @@ meta_window_x11_configure_request (MetaWindow *window, - { +@@ -2400,6 +2400,7 @@ meta_window_x11_update_input_region (MetaWindow *window) + g_autoptr (MtkRegion) region = NULL; MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); -+ int new_x, new_y, new_width, new_height; ++ MtkRectangle bounding_rect = { 0 }; + Window xwindow; - /* Note that x, y is the corner of the window border, - * and width, height is the size of the window inside -@@ -2836,15 +2997,25 @@ meta_window_x11_configure_request (MetaWindow *window, - * requested border here. - */ - if (event->xconfigurerequest.value_mask & CWBorderWidth) -- priv->border_width = event->xconfigurerequest.border_width; -+ { -+ meta_window_x11_protocol_to_stage (window_x11, -+ event->xconfigurerequest.border_width, 0, 0, 0, -+ &priv->border_width, NULL, NULL, NULL); -+ } - -- meta_window_move_resize_request(window, -- event->xconfigurerequest.value_mask, -- window->size_hints.win_gravity, -- event->xconfigurerequest.x, -- event->xconfigurerequest.y, -- event->xconfigurerequest.width, -- event->xconfigurerequest.height); -+ meta_window_x11_protocol_to_stage (window_x11, -+ event->xconfigurerequest.x, event->xconfigurerequest.y, -+ event->xconfigurerequest.width, event->xconfigurerequest.height, -+ &new_x, &new_y, -+ &new_width, &new_height); -+ -+ meta_window_move_resize_request (window, -+ event->xconfigurerequest.value_mask, -+ window->size_hints.win_gravity, -+ new_x, -+ new_y, -+ new_width, -+ new_height); - - /* Handle stacking. We only handle raises/lowers, mostly because - * stack.c really can't deal with anything else. I guess we'll fix -@@ -3339,8 +3510,13 @@ meta_window_x11_client_message (MetaWindow *window, - guint32 timestamp; - MetaWindowDrag *window_drag; - -- x_root = event->xclient.data.l[0]; -- y_root = event->xclient.data.l[1]; -+ meta_window_x11_protocol_to_stage (window_x11, -+ event->xclient.data.l[0], -+ event->xclient.data.l[1], -+ 0, 0, -+ &x_root, -+ &y_root, -+ NULL, NULL); - action = event->xclient.data.l[2]; - button = event->xclient.data.l[3]; - -@@ -3504,6 +3680,7 @@ meta_window_x11_client_message (MetaWindow *window, - { - MetaGravity gravity; - guint value_mask; -+ int x, y, width, height; - - gravity = (MetaGravity) (event->xclient.data.l[0] & 0xff); - value_mask = (event->xclient.data.l[0] & 0xf00) >> 8; -@@ -3512,13 +3689,20 @@ meta_window_x11_client_message (MetaWindow *window, - if (gravity == 0) - gravity = window->size_hints.win_gravity; - -+ meta_window_x11_protocol_to_stage (window_x11, -+ event->xclient.data.l[1], -+ event->xclient.data.l[2], -+ event->xclient.data.l[3], -+ event->xclient.data.l[4], -+ &x, &y, &width, &height); -+ - meta_window_move_resize_request(window, - value_mask, - gravity, -- event->xclient.data.l[1], /* x */ -- event->xclient.data.l[2], /* y */ -- event->xclient.data.l[3], /* width */ -- event->xclient.data.l[4]); /* height */ -+ x, -+ y, -+ width, -+ height); + if (window->decorated) +@@ -2411,10 +2412,14 @@ meta_window_x11_update_input_region (MetaWindow *window) + return; + } + xwindow = window->frame->xwindow; ++ bounding_rect.width = window->buffer_rect.width; ++ bounding_rect.height = window->buffer_rect.height; } - else if (event->xclient.message_type == - x11_display->atom__NET_ACTIVE_WINDOW && -@@ -3575,11 +3759,15 @@ meta_window_x11_client_message (MetaWindow *window, - else if (event->xclient.message_type == - x11_display->atom__GTK_SHOW_WINDOW_MENU) + else { -- gulong x, y; -+ int x, y; - - /* l[0] is device_id, which we don't use */ -- x = event->xclient.data.l[1]; -- y = event->xclient.data.l[2]; -+ meta_window_x11_protocol_to_stage (window_x11, -+ event->xclient.data.l[1], -+ event->xclient.data.l[2], -+ 0, 0, -+ &x, &y, -+ NULL, NULL); - - meta_window_show_menu (window, META_WINDOW_MENU_WM, x, y); + xwindow = priv->xwindow; ++ bounding_rect.width = priv->client_rect.width; ++ bounding_rect.height = priv->client_rect.height; } -@@ -4101,10 +4289,11 @@ meta_window_x11_configure_notify (MetaWindow *window, - g_assert (window->override_redirect); - g_assert (window->frame == NULL); -- window->rect.x = event->x; -- window->rect.y = event->y; -- window->rect.width = event->width; -- window->rect.height = event->height; -+ meta_window_x11_protocol_to_stage (window_x11, -+ event->x, event->y, -+ event->width, event->height, -+ &window->rect.x, &window->rect.y, -+ &window->rect.width, &window->rect.height); + if (META_X11_DISPLAY_HAS_SHAPE (x11_display)) +@@ -2458,8 +2463,8 @@ meta_window_x11_update_input_region (MetaWindow *window) + else if (n_rects == 1 && + (rects[0].x == 0 && + rects[0].y == 0 && +- rects[0].width == window->buffer_rect.width && +- rects[0].height == window->buffer_rect.height)) ++ rects[0].width == bounding_rect.width && ++ rects[0].height == bounding_rect.height)) + { + /* This is the bounding region case. Keep the + * region as NULL. */ +@@ -2476,13 +2481,6 @@ meta_window_x11_update_input_region (MetaWindow *window) - priv->client_rect = window->rect; - window->buffer_rect = window->rect; -diff --git a/src/x11/window-x11.h b/src/x11/window-x11.h -index 205eaaa63..fa3fbea6a 100644 ---- a/src/x11/window-x11.h -+++ b/src/x11/window-x11.h -@@ -45,6 +45,24 @@ struct _MetaWindowX11Class - gboolean (*always_update_shape) (MetaWindow *window); - void (*process_property_notify) (MetaWindow *window, - XPropertyEvent *event); -+ void (*stage_to_protocol) (MetaWindowX11 *window_x11, -+ int stage_x, -+ int stage_y, -+ int stage_width, -+ int stage_height, -+ int *protocol_x, -+ int *protocol_y, -+ int *protocol_width, -+ int *protocol_height); -+ void (*protocol_to_stage) (MetaWindowX11 *window_x11, -+ int protocol_x, -+ int protocol_y, -+ int protocol_width, -+ int protocol_height, -+ int *stage_x, -+ int *stage_y, -+ int *stage_width, -+ int *stage_height); - }; - - MetaWindow * meta_window_x11_new (MetaDisplay *display, -@@ -112,3 +130,23 @@ gboolean meta_window_x11_has_alpha_channel (MetaWindow *window); - - META_EXPORT - Window meta_window_x11_get_xwindow (MetaWindow *window); -+ -+void meta_window_x11_stage_to_protocol (MetaWindowX11 *window_x11, -+ int stage_x, -+ int stage_y, -+ int stage_width, -+ int stage_heigth, -+ int *protocol_x, -+ int *protocol_y, -+ int *protocol_width, -+ int *protocol_height); -+ -+void meta_window_x11_protocol_to_stage (MetaWindowX11 *window_x11, -+ int protocol_x, -+ int protocol_y, -+ int protocol_width, -+ int protocol_height, -+ int *stage_x, -+ int *stage_y, -+ int *stage_width, -+ int *stage_heigth); + if (region != NULL) + { +- MtkRectangle bounding_rect; +- +- bounding_rect.x = 0; +- bounding_rect.y = 0; +- bounding_rect.width = window->buffer_rect.width; +- bounding_rect.height = window->buffer_rect.height; +- + /* The shape we get back from the client may have coordinates + * outside of the frame. The X SHAPE Extension requires that + * the overall shape the client provides never exceeds the diff --git a/spec_files/mutter/mutter.spec b/spec_files/mutter/mutter.spec index 81a97ee5..87d6e252 100644 --- a/spec_files/mutter/mutter.spec +++ b/spec_files/mutter/mutter.spec @@ -33,23 +33,11 @@ Patch1: mutter-42.alpha-disable-tegra.patch # https://pagure.io/fedora-workstation/issue/79 Patch2: 0001-place-Always-center-initial-setup-fedora-welcome.patch -# https://bugzilla.redhat.com/show_bug.cgi?id=2239128 -# https://gitlab.gnome.org/GNOME/mutter/-/issues/3068 -# not upstreamed because for upstream we'd really want to find a way -# to fix *both* problems -Patch3: 0001-Revert-x11-Use-input-region-from-frame-window-for-de.patch - -# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3329 -# Modified to add the change from -# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3329#note_1874837 -# which solves the problems reported with #3329 alone -Patch4: 0001-modified-3329.patch - # https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1441 -Patch5: 1441.patch +Patch3: 1441.patch # https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3567 -Patch6: 3720+3567.patch +Patch4: 3720+3567.patch BuildRequires: pkgconfig(gobject-introspection-1.0) >= 1.41.0 BuildRequires: pkgconfig(sm) From 7c90cc359ba499288f2fb4de5540bef94d6816d1 Mon Sep 17 00:00:00 2001 From: Kyle Gospodnetich Date: Mon, 13 May 2024 19:21:31 -0700 Subject: [PATCH 3/3] chore: Update to 3.14.14 --- spec_files/gamescope/gamescope.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec_files/gamescope/gamescope.spec b/spec_files/gamescope/gamescope.spec index 0f6519db..39ebf059 100644 --- a/spec_files/gamescope/gamescope.spec +++ b/spec_files/gamescope/gamescope.spec @@ -2,7 +2,7 @@ %global _default_patch_fuzz 2 %global build_timestamp %(date +"%Y%m%d") -%global gamescope_tag 3.14.13 +%global gamescope_tag 3.14.14 Name: gamescope Version: 100.%{gamescope_tag}