On Mon, 14 Jul 2025 at 14:16:57 +0100, Simon McVittie wrote:
 [x] attach debdiff against the package in testing

Now really attached.

    smcv
diffstat for mutter-48.3.1 mutter-48.4

 NEWS                                       |   18 ++++++++
 cogl/cogl/cogl-pipeline.c                  |    7 ++-
 debian/changelog                           |   62 ++++++++++++++++++++++++++++
 debian/control                             |    2 
 debian/gbp.conf                            |    4 -
 debian/watch                               |    2 
 doc/reference/clutter/clutter.toml.in      |    4 -
 doc/reference/cogl/cogl.toml.in            |    4 -
 doc/reference/meta/meta.toml.in            |    4 -
 doc/reference/mtk/mtk.toml.in              |    4 -
 doc/website/index.html                     |    1 
 meson.build                                |    2 
 src/backends/native/meta-gpu-kms.c         |    2 
 src/backends/native/meta-kms-impl-device.c |   10 ++++
 src/backends/native/meta-kms.c             |    2 
 src/backends/native/meta-onscreen-native.c |    2 
 src/backends/native/meta-output-kms.c      |   11 ++++
 src/backends/native/meta-output-kms.h      |    2 
 src/backends/native/meta-seat-impl.c       |    4 -
 src/compositor/compositor-private.h        |    6 +-
 src/compositor/compositor.c                |   10 ++--
 src/core/bell.c                            |   64 ++++++++++++++++++++++++-----
 src/core/display-private.h                 |    7 +--
 src/core/meta-context-main.c               |    2 
 src/core/window.c                          |    7 +++
 src/tests/test-runner.c                    |    2 
 src/wayland/meta-wayland-data-device.c     |   59 ++++----------------------
 src/wayland/meta-wayland-data-source.c     |   59 ++++++++++++++++++++++++++
 src/wayland/meta-wayland-data-source.h     |    3 +
 src/wayland/meta-wayland-seat.c            |    4 -
 src/x11/meta-x11-display.c                 |    8 +++
 31 files changed, 286 insertions(+), 92 deletions(-)

diff -Nru mutter-48.3.1/cogl/cogl/cogl-pipeline.c mutter-48.4/cogl/cogl/cogl-pipeline.c
--- mutter-48.3.1/cogl/cogl/cogl-pipeline.c	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/cogl/cogl/cogl-pipeline.c	2025-06-29 11:00:20.000000000 +0100
@@ -121,11 +121,14 @@
 {
 
   for (CoglPipeline *child = pipeline->first_child;
-       child != NULL;
-       child = child->next_sibling)
+       child != NULL;)
     {
+      CoglPipeline *next = child->next_sibling;
+
       if (!callback (child, user_data))
         break;
+
+      child = next;
     }
 }
 
diff -Nru mutter-48.3.1/debian/changelog mutter-48.4/debian/changelog
--- mutter-48.3.1/debian/changelog	2025-06-14 10:07:10.000000000 +0100
+++ mutter-48.4/debian/changelog	2025-07-13 12:27:01.000000000 +0100
@@ -1,3 +1,65 @@
+mutter (48.4-2) unstable; urgency=medium
+
+  * Team upload
+  * d/gbp.conf, d/control: Switch packaging branch for trixie
+  * Expand previous changelog entry to document all upstream changes
+  * Upload to unstable
+
+ -- Simon McVittie <s...@debian.org>  Sun, 13 Jul 2025 12:27:01 +0100
+
+mutter (48.4-1) experimental; urgency=medium
+
+  [ Jeremy Bícha ]
+  * New upstream bugfix release
+    - Unlink outputs from unused connectors in update_outputs, avoiding a
+      use-after-free when a monitor is disconnected
+      (mutter!4474 upstream)
+    - Fix a crash caused by a use-after-free when clearing many
+      accumulated notifications
+      (mutter#3970 upstream, LP: #2102063)
+    - Fix a non-reproducible crash caused by a use-after-free when dragging
+      an epiphany-browser tab to create a new window
+      (mutter#4157 upstream)
+    - Fix the logic for when to disable the --no-x11 option
+      (mutter!4485 upstream, not relevant to how we configure it in Debian)
+    - Rate-limit visual alerts to 2 per second, to comply with the European
+      Accessibility Act and avoid potentially triggering photosensitive
+      seizures
+      (mutter!4487 upstream)
+    - Fix a rare crash with a NULL pointer dereference
+      (mutter#4147 upstream, LP: #2100243)
+    - Avoid reopening an inactive Nvidia GPU when not needed
+      (mutter#3550 upstream)
+    - Fix a NULL dereference in the tests
+      (mutter!4495 upstream)
+    - Fix stacking order for windows that get activated before mapped
+      (mutter!4462 upstream)
+    - Fix incorrect cursor over Xwayland clients
+      (mutter#4033 upstream, LP: #2111502)
+    - Improve Wayland spec compliance by sending Wayland seat name before
+      its capabilities
+      (mutter#4119 upstream)
+    - Improve X11 compatibility (especially in Wine) by moving the X11 focus
+      to a shared dummy window when the Wayland focus is really on any
+      non-X11 window
+      (mutter!4447 upstream)
+    - Wait longer for hotplugged monitors to detect their input sources,
+      improving compatibility with e.g. MSI PRO MP275Q
+      (mutter!4442 upstream)
+    - Make sure the cursor is updated after modesetting, even if there is no
+      application window to redraw
+      (mutter!4452 upstream)
+    - Fix high resolution scroll events getting converted to discrete events
+      that intermittently switch direction
+      (mutter#4128 upstream)
+    - Documentation updates
+
+  [ Simon McVittie ]
+  * d/gbp.conf: Use upstream/48.x branch for trixie
+  * d/watch: Only watch for 48.x releases
+
+ -- Jeremy Bícha <jbi...@ubuntu.com>  Thu, 03 Jul 2025 08:48:17 -0400
+
 mutter (48.3.1-2) unstable; urgency=medium
 
   * Team upload
diff -Nru mutter-48.3.1/debian/control mutter-48.4/debian/control
--- mutter-48.3.1/debian/control	2025-06-14 10:07:10.000000000 +0100
+++ mutter-48.4/debian/control	2025-07-13 12:27:01.000000000 +0100
@@ -102,7 +102,7 @@
 Rules-Requires-Root: no
 Standards-Version: 4.7.0
 Homepage: https://mutter.gnome.org/
-Vcs-Git: https://salsa.debian.org/gnome-team/mutter.git
+Vcs-Git: https://salsa.debian.org/gnome-team/mutter.git -b debian/trixie
 Vcs-Browser: https://salsa.debian.org/gnome-team/mutter
 
 Package: mutter
diff -Nru mutter-48.3.1/debian/gbp.conf mutter-48.4/debian/gbp.conf
--- mutter-48.3.1/debian/gbp.conf	2025-06-14 10:07:10.000000000 +0100
+++ mutter-48.4/debian/gbp.conf	2025-07-13 12:27:01.000000000 +0100
@@ -1,7 +1,7 @@
 [DEFAULT]
 pristine-tar = True
-debian-branch = debian/latest
-upstream-branch = upstream/latest
+debian-branch = debian/trixie
+upstream-branch = upstream/48.x
 
 [buildpackage]
 sign-tags = True
diff -Nru mutter-48.3.1/debian/watch mutter-48.4/debian/watch
--- mutter-48.3.1/debian/watch	2025-06-14 10:07:10.000000000 +0100
+++ mutter-48.4/debian/watch	2025-07-13 12:27:01.000000000 +0100
@@ -1,4 +1,4 @@
 version=4
 opts="searchmode=plain, uversionmangle=s/\.(alpha|beta|rc)/~$1/, downloadurlmangle=s|cache.json||" \
 https://download.gnome.org/sources/@PACKAGE@/cache.json \
-	[\d.]+/@PACKAGE@@ANY_VERSION@@ARCHIVE_EXT@
+	48/@PACKAGE@-([\d.]+)@ARCHIVE_EXT@
diff -Nru mutter-48.3.1/doc/reference/clutter/clutter.toml.in mutter-48.4/doc/reference/clutter/clutter.toml.in
--- mutter-48.3.1/doc/reference/clutter/clutter.toml.in	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/doc/reference/clutter/clutter.toml.in	2025-06-29 11:00:20.000000000 +0100
@@ -2,8 +2,8 @@
 version = "@version@"
 browse_url = "https://gitlab.gnome.org/GNOME/mutter/";
 repository_url = "https://gitlab.gnome.org/GNOME/mutter.git";
-website_url = "https://blogs.gnome.org/shell-dev/";
-docs_url = "https://mutter.gnome.org/";
+website_url = "https://mutter.gnome.org";
+docs_url = "https://mutter.gnome.org/clutter";
 logo_url = "logo.svg"
 authors = "Mutter Development Team"
 license = "GPL-2.0-or-later"
diff -Nru mutter-48.3.1/doc/reference/cogl/cogl.toml.in mutter-48.4/doc/reference/cogl/cogl.toml.in
--- mutter-48.3.1/doc/reference/cogl/cogl.toml.in	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/doc/reference/cogl/cogl.toml.in	2025-06-29 11:00:20.000000000 +0100
@@ -2,8 +2,8 @@
 version = "@version@"
 browse_url = "https://gitlab.gnome.org/GNOME/mutter/";
 repository_url = "https://gitlab.gnome.org/GNOME/mutter.git";
-website_url = "https://blogs.gnome.org/shell-dev/";
-docs_url = "https://mutter.gnome.org/";
+website_url = "https://mutter.gnome.org";
+docs_url = "https://mutter.gnome.org/cogl";
 logo_url = "logo.svg"
 authors = "Mutter Development Team"
 license = "GPL-2.0-or-later"
diff -Nru mutter-48.3.1/doc/reference/meta/meta.toml.in mutter-48.4/doc/reference/meta/meta.toml.in
--- mutter-48.3.1/doc/reference/meta/meta.toml.in	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/doc/reference/meta/meta.toml.in	2025-06-29 11:00:20.000000000 +0100
@@ -2,8 +2,8 @@
 version = "@version@"
 browse_url = "https://gitlab.gnome.org/GNOME/mutter/";
 repository_url = "https://gitlab.gnome.org/GNOME/mutter.git";
-website_url = "https://blogs.gnome.org/shell-dev/";
-docs_url = "https://mutter.gnome.org/";
+website_url = "https://mutter.gnome.org";
+docs_url = "https://mutter.gnome.org/meta";
 logo_url = "logo.svg"
 authors = "Mutter Development Team"
 license = "GPL-2.0-or-later"
diff -Nru mutter-48.3.1/doc/reference/mtk/mtk.toml.in mutter-48.4/doc/reference/mtk/mtk.toml.in
--- mutter-48.3.1/doc/reference/mtk/mtk.toml.in	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/doc/reference/mtk/mtk.toml.in	2025-06-29 11:00:20.000000000 +0100
@@ -2,8 +2,8 @@
 version = "@version@"
 browse_url = "https://gitlab.gnome.org/GNOME/mutter/";
 repository_url = "https://gitlab.gnome.org/GNOME/mutter.git";
-website_url = "https://blogs.gnome.org/shell-dev/";
-docs_url = "https://mutter.gnome.org/";
+website_url = "https://mutter.gnome.org";
+docs_url = "https://mutter.gnome.org/mtk";
 logo_url = "logo.svg"
 authors = "Mutter Development Team"
 license = "GPL-2.0-or-later"
diff -Nru mutter-48.3.1/doc/website/index.html mutter-48.4/doc/website/index.html
--- mutter-48.3.1/doc/website/index.html	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/doc/website/index.html	2025-06-29 11:00:20.000000000 +0100
@@ -126,6 +126,7 @@
       <li><a
           href="https://gitlab.gnome.org/GNOME/mutter/-/jobs/artifacts/main/file/coveragereport/index.html?job=coverage";>Code
           Coverage Report</a></li>
+      <li><a href="https://blogs.gnome.org/shell-dev/";>Development blog</a></li>
     </ul>
 
   </div>
diff -Nru mutter-48.3.1/meson.build mutter-48.4/meson.build
--- mutter-48.3.1/meson.build	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/meson.build	2025-06-29 11:00:20.000000000 +0100
@@ -1,5 +1,5 @@
 project('mutter', 'c',
-  version: '48.3.1',
+  version: '48.4',
   meson_version: '>= 1.3.0',
   license: 'GPL-2.0-or-later',
 )
diff -Nru mutter-48.3.1/NEWS mutter-48.4/NEWS
--- mutter-48.3.1/NEWS	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/NEWS	2025-06-29 11:00:20.000000000 +0100
@@ -1,3 +1,21 @@
+48.4
+====
+* Unlink outputs from unused connectors in update_outputs [Michel; !4474]
+* Add speed limit to Visual alerts [Sergio; !4487]
+* Fix behavior of windows that get activated before mapped [Alessandro; !4462]
+* Fix incorrect cursor over Xwayland clients [Carlos; !4433]
+* Fix hi-res scroll events getting converted to flip-flopping discrete events
+  [Peter; !4459]
+* Fixed crash [Daniel; !4303]
+* Misc. bug fixes and cleanups [Alessandro, Jonas, Jordan, Alessandro, Corentin,
+  Daniel, Lukáš, Rémi, Michel; !4481, !4482, !4485, !4492, !4496, !4495, !4444,
+  !4447, !4442, !4452]
+
+Contributors:
+  Alessandro Astone, Rémi Bernon, Sergio Costas Rodriguez, Michel Dänzer,
+  Carlos Garnacho, Peter Hutterer, Corentin Noël, Jordan Petridis,
+  Lukáš Tyrychtr, Daniel van Vugt, Jonas Ådahl
+
 48.3.1
 ======
 * Fix Xwayland windows becoming unresponsive to events [Jonas; !4475]
diff -Nru mutter-48.3.1/src/backends/native/meta-gpu-kms.c mutter-48.4/src/backends/native/meta-gpu-kms.c
--- mutter-48.3.1/src/backends/native/meta-gpu-kms.c	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/backends/native/meta-gpu-kms.c	2025-06-29 11:00:20.000000000 +0100
@@ -351,6 +351,8 @@
       MetaOutput *old_output;
       GError *error = NULL;
 
+      meta_unlink_kms_connector (kms_connector);
+
       if (!meta_kms_connector_get_current_state (kms_connector))
         continue;
 
diff -Nru mutter-48.3.1/src/backends/native/meta-kms.c mutter-48.4/src/backends/native/meta-kms.c
--- mutter-48.3.1/src/backends/native/meta-kms.c	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/backends/native/meta-kms.c	2025-06-29 11:00:20.000000000 +0100
@@ -373,7 +373,7 @@
     }
 
   ensure_hotplug_timeout_source (kms);
-  g_source_set_ready_time (kms->hotplug_timeout, now + 2 * G_USEC_PER_SEC);
+  g_source_set_ready_time (kms->hotplug_timeout, now + 3 * G_USEC_PER_SEC);
 
   g_hash_table_insert (kms->hotplug_events, g_steal_pointer (&hotplug_event),
                        NULL);
diff -Nru mutter-48.3.1/src/backends/native/meta-kms-impl-device.c mutter-48.4/src/backends/native/meta-kms-impl-device.c
--- mutter-48.3.1/src/backends/native/meta-kms-impl-device.c	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/backends/native/meta-kms-impl-device.c	2025-06-29 11:00:20.000000000 +0100
@@ -1131,11 +1131,13 @@
   drmModeRes *drm_resources;
   MetaKmsResourceChanges changes;
   GList *l;
+  gboolean had_fd_open;
 
   meta_assert_in_kms_impl (meta_kms_impl_get_kms (priv->impl));
 
   meta_topic (META_DEBUG_KMS, "Updating device state for %s", priv->path);
 
+  had_fd_open = !!priv->device_file;
   if (!ensure_device_file (impl_device, &error))
     {
       g_warning ("Failed to reopen '%s': %s", priv->path, error->message);
@@ -1172,6 +1174,9 @@
 
   drmModeFreeResources (drm_resources);
 
+  if (changes == META_KMS_RESOURCE_CHANGE_NONE && !had_fd_open)
+    clear_latched_fd_hold (impl_device);
+
   return changes;
 
 err:
@@ -1781,11 +1786,14 @@
   crtc_frame = get_crtc_frame (impl_device, latch_crtc);
   if (!crtc_frame)
     {
+      const MetaKmsCrtcState *crtc_state =
+        meta_kms_crtc_get_current_state (latch_crtc);
+
       crtc_frame = g_new0 (CrtcFrame, 1);
       crtc_frame->impl_device = impl_device;
       crtc_frame->crtc = latch_crtc;
       crtc_frame->deadline.timer_fd = -1;
-      crtc_frame->await_flush = TRUE;
+      crtc_frame->await_flush = !crtc_state->is_active;
       g_hash_table_insert (priv->crtc_frames, latch_crtc, crtc_frame);
     }
 
diff -Nru mutter-48.3.1/src/backends/native/meta-onscreen-native.c mutter-48.4/src/backends/native/meta-onscreen-native.c
--- mutter-48.3.1/src/backends/native/meta-onscreen-native.c	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/backends/native/meta-onscreen-native.c	2025-06-29 11:00:20.000000000 +0100
@@ -1833,7 +1833,7 @@
       g_warning ("Direct scanout page flip failed: %s", error->message);
 
       cogl_scanout_notify_failed (scanout, onscreen);
-      if (onscreen_native->next_frame == NULL)
+      if (onscreen_native->next_frame == NULL && view != NULL)
         {
           clutter_stage_view_add_redraw_clip (view, NULL);
           clutter_stage_view_schedule_update_now (view);
diff -Nru mutter-48.3.1/src/backends/native/meta-output-kms.c mutter-48.4/src/backends/native/meta-output-kms.c
--- mutter-48.3.1/src/backends/native/meta-output-kms.c	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/backends/native/meta-output-kms.c	2025-06-29 11:00:20.000000000 +0100
@@ -99,6 +99,17 @@
                              kms_connector_output_kms_quark);
 }
 
+void
+meta_unlink_kms_connector (MetaKmsConnector *connector)
+{
+  if (!kms_connector_output_kms_quark)
+    return;
+
+  g_object_set_qdata (G_OBJECT (connector),
+                      kms_connector_output_kms_quark,
+                      NULL);
+}
+
 static GBytes *
 meta_output_kms_read_edid (MetaOutputNative *output_native)
 {
diff -Nru mutter-48.3.1/src/backends/native/meta-output-kms.h mutter-48.4/src/backends/native/meta-output-kms.h
--- mutter-48.3.1/src/backends/native/meta-output-kms.h	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/backends/native/meta-output-kms.h	2025-06-29 11:00:20.000000000 +0100
@@ -41,6 +41,8 @@
 
 MetaOutputKms * meta_output_kms_from_kms_connector (MetaKmsConnector *connector);
 
+void meta_unlink_kms_connector (MetaKmsConnector *connector);
+
 MetaOutputKms * meta_output_kms_new (MetaGpuKms        *gpu_kms,
                                      MetaKmsConnector  *kms_connector,
                                      MetaOutput        *old_output,
diff -Nru mutter-48.3.1/src/backends/native/meta-seat-impl.c mutter-48.4/src/backends/native/meta-seat-impl.c
--- mutter-48.3.1/src/backends/native/meta-seat-impl.c	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/backends/native/meta-seat-impl.c	2025-06-29 11:00:20.000000000 +0100
@@ -1218,7 +1218,7 @@
   evdev_device->value120.acc_dx += (int32_t) dx_value120;
   evdev_device->value120.acc_dy += (int32_t) dy_value120;
 
-  if (abs (evdev_device->value120.acc_dx) >= 60)
+  if (dx_value120 != 0 && abs (evdev_device->value120.acc_dx) >= 60)
     {
       low_res_value = (evdev_device->value120.acc_dx / 120);
       if (low_res_value == 0)
@@ -1230,7 +1230,7 @@
       evdev_device->value120.acc_dx -= (low_res_value * 120);
     }
 
-  if (abs (evdev_device->value120.acc_dy) >= 60)
+  if (dy_value120 != 0 && abs (evdev_device->value120.acc_dy) >= 60)
     {
       low_res_value = (evdev_device->value120.acc_dy / 120);
       if (low_res_value == 0)
diff -Nru mutter-48.3.1/src/compositor/compositor.c mutter-48.4/src/compositor/compositor.c
--- mutter-48.3.1/src/compositor/compositor.c	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/compositor/compositor.c	2025-06-29 11:00:20.000000000 +0100
@@ -1282,7 +1282,8 @@
 
 void
 meta_compositor_flash_display (MetaCompositor *compositor,
-                               MetaDisplay    *display)
+                               MetaDisplay    *display,
+                               int             n_flashes)
 {
   MetaBackend *backend;
   ClutterActor *stage;
@@ -1308,7 +1309,7 @@
 
   transition = clutter_actor_get_transition (flash, "opacity");
   clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE);
-  clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), 2);
+  clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), n_flashes);
 
   g_signal_connect (transition, "stopped",
                     G_CALLBACK (flash_out_completed), flash);
@@ -1327,7 +1328,8 @@
 
 void
 meta_compositor_flash_window (MetaCompositor *compositor,
-                              MetaWindow     *window)
+                              MetaWindow     *window,
+                              int             n_flashes)
 {
   ClutterActor *window_actor =
     CLUTTER_ACTOR (meta_window_actor_from_window (window));
@@ -1356,7 +1358,7 @@
   if (transition)
     {
       clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE);
-      clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), 2);
+      clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), n_flashes);
 
       g_signal_connect (transition, "stopped",
                         G_CALLBACK (window_flash_out_completed), flash);
diff -Nru mutter-48.3.1/src/compositor/compositor-private.h mutter-48.4/src/compositor/compositor-private.h
--- mutter-48.3.1/src/compositor/compositor-private.h	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/compositor/compositor-private.h	2025-06-29 11:00:20.000000000 +0100
@@ -52,7 +52,8 @@
                                                             int64_t         monotonic_time_us);
 
 void meta_compositor_flash_window (MetaCompositor *compositor,
-                                   MetaWindow     *window);
+                                   MetaWindow     *window,
+                                   int             n_flashes);
 
 MetaCloseDialog * meta_compositor_create_close_dialog (MetaCompositor *compositor,
                                                        MetaWindow     *window);
@@ -141,7 +142,8 @@
                                  GList          *stack);
 
 void meta_compositor_flash_display (MetaCompositor *compositor,
-                                    MetaDisplay    *display);
+                                    MetaDisplay    *display,
+                                    int             n_flashes);
 
 void meta_compositor_show_tile_preview (MetaCompositor *compositor,
                                         MetaWindow     *window,
diff -Nru mutter-48.3.1/src/core/bell.c mutter-48.4/src/core/bell.c
--- mutter-48.3.1/src/core/bell.c	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/core/bell.c	2025-06-29 11:00:20.000000000 +0100
@@ -54,6 +54,11 @@
 #include "core/util-private.h"
 #include "core/window-private.h"
 #include "meta/compositor.h"
+#include "mtk/mtk.h"
+
+/* Time limits to prevent Photosensitive Seizures */
+#define MIN_TIME_BETWEEN_VISUAL_ALERTS_MS 500
+#define MIN_TIME_BETWEEN_DOUBLE_VISUAL_ALERT_MS 3000
 
 G_DEFINE_TYPE (MetaBell, meta_bell, G_TYPE_OBJECT)
 
@@ -120,7 +125,7 @@
 /**
  * bell_flash_fullscreen:
  * @display: The display the event came in on
- * @xkb_ev: The bell event
+ * @n_flashes: The number of times to flash the screen
  *
  * Flashes one screen, or all screens, in response to a bell event.
  * If the event is on a particular window, flash the screen that
@@ -129,15 +134,17 @@
  * If the configure script found we had no XKB, this does not exist.
  */
 static void
-bell_flash_fullscreen (MetaDisplay *display)
+bell_flash_fullscreen (MetaDisplay *display,
+                       int          n_flashes)
 {
-  meta_compositor_flash_display (display->compositor, display);
+  meta_compositor_flash_display (display->compositor, display, n_flashes);
 }
 
 static void
-bell_flash_window (MetaWindow *window)
+bell_flash_window (MetaWindow *window,
+                   int         n_flashes)
 {
-  meta_compositor_flash_window (window->display->compositor, window);
+  meta_compositor_flash_window (window->display->compositor, window, n_flashes);
 }
 
 /**
@@ -150,12 +157,13 @@
  */
 static void
 bell_flash_frame (MetaDisplay *display,
-                  MetaWindow  *window)
+                  MetaWindow  *window,
+                  int          n_flashes)
 {
   if (window)
-    bell_flash_window (window);
+    bell_flash_window (window, n_flashes);
   else
-    bell_flash_fullscreen (display);
+    bell_flash_fullscreen (display, n_flashes);
 }
 
 /**
@@ -170,13 +178,49 @@
 bell_visual_notify (MetaDisplay *display,
                     MetaWindow  *window)
 {
+  /*
+   * The European Accessibility Act (EAA), in the Annex I, Section I, 2.J,
+   * specifies that products "shall avoid triggering photosensitive seizures".
+   *
+   * According to the Web Content Accessibility Guidelines (WCAG), any
+   * element that flashes in the screen must have a maximum period of
+   * 3Hz to avoid the risk of Photosensitivity Seizures.
+   *
+   * If several alarm bells are sent fast enough, the Visual alerts could
+   * flash the screen or the window at speeds about 8-9Hz (tested with a
+   * simple BASH script), which is greater than the currently accepted
+   * limit of 3Hz.
+   *
+   * To avoid this, a timeout is added to ensure that no visual alerts are
+   * sent with less than 500ms of difference, to set a maximum flash speed
+   * of 2Hz.
+   *
+   * A property in display is used to keep the last time a visual alert has been
+   * sent because not only a "single flash zone" can trigger a seizure, but also
+   * slower patterns combined. So a global timeout for all the desktop is the
+   * safest approach.
+   */
+  int64_t now_us;
+  int64_t time_difference_ms;
+  int n_flashes;
+
+  now_us = g_get_monotonic_time ();
+  time_difference_ms = us2ms (now_us - display->last_visual_bell_time_us);
+
+  if (time_difference_ms < MIN_TIME_BETWEEN_VISUAL_ALERTS_MS)
+    return;
+
+  display->last_visual_bell_time_us = now_us;
+
+  n_flashes = (time_difference_ms < MIN_TIME_BETWEEN_DOUBLE_VISUAL_ALERT_MS) ? 1 : 2;
+
   switch (meta_prefs_get_visual_bell_type ())
     {
     case G_DESKTOP_VISUAL_BELL_FULLSCREEN_FLASH:
-      bell_flash_fullscreen (display);
+      bell_flash_fullscreen (display, n_flashes);
       break;
     case G_DESKTOP_VISUAL_BELL_FRAME_FLASH:
-      bell_flash_frame (display, window);
+      bell_flash_frame (display, window, n_flashes);
       break;
     }
 }
diff -Nru mutter-48.3.1/src/core/display-private.h mutter-48.4/src/core/display-private.h
--- mutter-48.3.1/src/core/display-private.h	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/core/display-private.h	2025-06-29 11:00:20.000000000 +0100
@@ -120,11 +120,11 @@
   GSList     *pending_pings;
 
   /* Pending focus change */
-  guint       focus_timeout_id;
+  guint focus_timeout_id;
 
   /* Pending autoraise */
-  guint       autoraise_timeout_id;
-  MetaWindow* autoraise_window;
+  guint autoraise_timeout_id;
+  MetaWindow *autoraise_window;
 
   MetaKeyBindingManager key_binding_manager;
 
@@ -157,6 +157,7 @@
   guint check_fullscreen_later;
 
   MetaBell *bell;
+  int64_t last_visual_bell_time_us;
   MetaWorkspaceManager *workspace_manager;
 
   MetaSoundPlayer *sound_player;
diff -Nru mutter-48.3.1/src/core/meta-context-main.c mutter-48.4/src/core/meta-context-main.c
--- mutter-48.3.1/src/core/meta-context-main.c	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/core/meta-context-main.c	2025-06-29 11:00:20.000000000 +0100
@@ -666,6 +666,8 @@
       N_("Run as a nested compositor"),
       NULL
     },
+#endif
+#ifdef HAVE_XWAYLAND
     {
       "no-x11", 0, 0, G_OPTION_ARG_NONE,
       &context_main->options.no_x11,
diff -Nru mutter-48.3.1/src/core/window.c mutter-48.4/src/core/window.c
--- mutter-48.3.1/src/core/window.c	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/core/window.c	2025-06-29 11:00:20.000000000 +0100
@@ -5181,6 +5181,13 @@
 
   g_return_if_fail (!window->override_redirect);
 
+  /* Flush pending visible state now.
+   * It is important that this runs before meta_stack_raise() because
+   * showing a window may overwrite its stacking order based on the
+   * stacking rules for newly shown windows.
+   */
+  meta_window_flush_calc_showing (window);
+
   ancestor = meta_window_find_root_ancestor (window);
 
   meta_topic (META_DEBUG_WINDOW_OPS,
diff -Nru mutter-48.3.1/src/tests/test-runner.c mutter-48.4/src/tests/test-runner.c
--- mutter-48.3.1/src/tests/test-runner.c	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/tests/test-runner.c	2025-06-29 11:00:20.000000000 +0100
@@ -296,7 +296,7 @@
       if ((filter & STACK_FILTER_SHOWING) && window && window->hidden)
         continue;
 
-      if (workspace && !meta_window_located_on_workspace (window, workspace))
+      if (window && workspace && !meta_window_located_on_workspace (window, workspace))
         continue;
 
       if (window != NULL && window->title)
diff -Nru mutter-48.3.1/src/wayland/meta-wayland-data-device.c mutter-48.4/src/wayland/meta-wayland-data-device.c
--- mutter-48.3.1/src/wayland/meta-wayland-data-device.c	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/wayland/meta-wayland-data-device.c	2025-06-29 11:00:20.000000000 +0100
@@ -26,8 +26,6 @@
 
 #include "wayland/meta-wayland-data-device.h"
 
-#include <gio/gunixoutputstream.h>
-#include <glib-unix.h>
 #include <glib.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -235,6 +233,12 @@
   g_autoptr (MetaCursorSprite) cursor_sprite = NULL;
   MetaCursorRenderer *cursor_renderer;
 
+#ifdef HAVE_X11_CLIENT
+  /* X11 DnD lets the drag source client drive pointer cursor updates */
+  if (META_IS_WAYLAND_DATA_SOURCE_XWAYLAND (drag_grab->drag_data_source))
+    return;
+#endif
+
   cursor_sprite =
     META_CURSOR_SPRITE (meta_cursor_sprite_xcursor_new (cursor, cursor_tracker));
   cursor_renderer =
@@ -276,10 +280,7 @@
 on_data_source_action_changed (MetaWaylandDataSource *source,
                                MetaWaylandDragGrab   *drag_grab)
 {
-#ifdef HAVE_X11_CLIENT
-  if (!META_IS_WAYLAND_DATA_SOURCE_XWAYLAND (source))
-#endif
-    meta_wayland_drag_grab_update_cursor (drag_grab);
+  meta_wayland_drag_grab_update_cursor (drag_grab);
 }
 
 static void
@@ -453,6 +454,8 @@
   MetaDisplay *display = display_from_data_device (data_device);
   MetaCompositor *compositor = meta_display_get_compositor (display);
 
+  meta_wayland_drag_grab_set_cursor (drag_grab, META_CURSOR_DEFAULT);
+
   meta_wayland_drag_grab_set_source (drag_grab, NULL);
   meta_wayland_drag_grab_set_focus (drag_grab, NULL);
 
@@ -485,55 +488,11 @@
       drag_grab->handler = NULL;
     }
 
-  meta_wayland_drag_grab_set_cursor (drag_grab, META_CURSOR_DEFAULT);
   meta_dnd_wayland_handle_end_modal (compositor);
 
   g_free (drag_grab);
 }
 
-static gboolean
-on_fake_read_hup (GIOChannel   *channel,
-                  GIOCondition  condition,
-                  gpointer      data)
-{
-  MetaWaylandDataSource *source = data;
-
-  meta_wayland_data_source_notify_finish (source);
-  g_io_channel_shutdown (channel, FALSE, NULL);
-  g_io_channel_unref (channel);
-
-  return G_SOURCE_REMOVE;
-}
-
-static void
-meta_wayland_data_source_fake_read (MetaWaylandDataSource *source,
-                                    const gchar           *mimetype)
-{
-  GIOChannel *channel;
-  int p[2];
-
-  if (!g_unix_open_pipe (p, FD_CLOEXEC, NULL))
-    {
-      meta_wayland_data_source_notify_finish (source);
-      return;
-    }
-
-  if (!g_unix_set_fd_nonblocking (p[0], TRUE, NULL) ||
-      !g_unix_set_fd_nonblocking (p[1], TRUE, NULL))
-    {
-      meta_wayland_data_source_notify_finish (source);
-      close (p[0]);
-      close (p[1]);
-      return;
-    }
-
-  meta_wayland_data_source_send (source, mimetype, p[1]);
-  close (p[1]);
-  channel = g_io_channel_unix_new (p[0]);
-  g_io_channel_set_close_on_unref (channel, TRUE);
-  g_io_add_watch (channel, G_IO_HUP, on_fake_read_hup, source);
-}
-
 static MetaWaylandSurface *
 drag_grab_get_focus_surface (MetaWaylandEventHandler *handler,
                              ClutterInputDevice      *device,
diff -Nru mutter-48.3.1/src/wayland/meta-wayland-data-source.c mutter-48.4/src/wayland/meta-wayland-data-source.c
--- mutter-48.3.1/src/wayland/meta-wayland-data-source.c	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/wayland/meta-wayland-data-source.c	2025-06-29 11:00:20.000000000 +0100
@@ -23,6 +23,8 @@
 
 #include "config.h"
 
+#include <gio/gunixoutputstream.h>
+#include <glib-unix.h>
 #include <unistd.h>
 
 #include "wayland/meta-wayland-data-source.h"
@@ -46,6 +48,10 @@
   enum wl_data_device_manager_dnd_action current_dnd_action;
   MetaWaylandSeat *seat;
   MetaWaylandToplevelDrag *toplevel_drag;
+
+  GIOChannel *fake_read_channel;
+  guint fake_read_watch_id;
+
   guint actions_set : 1;
   guint in_ask : 1;
   guint drop_performed : 1;
@@ -160,6 +166,9 @@
     meta_wayland_data_source_get_instance_private (source);
   char **pos;
 
+  g_clear_handle_id (&priv->fake_read_watch_id, g_source_remove);
+  g_clear_pointer (&priv->fake_read_channel, g_io_channel_unref);
+
   wl_array_for_each (pos, &priv->mime_types)
     g_free (*pos);
   wl_array_release (&priv->mime_types);
@@ -582,6 +591,56 @@
   META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->drag_finished (source);
 }
 
+static gboolean
+on_fake_read_hup (GIOChannel   *channel,
+                  GIOCondition  condition,
+                  gpointer      user_data)
+{
+  MetaWaylandDataSource *source = META_WAYLAND_DATA_SOURCE (user_data);
+  MetaWaylandDataSourcePrivate *priv =
+    meta_wayland_data_source_get_instance_private (source);
+
+  priv->fake_read_watch_id = 0;
+  meta_wayland_data_source_notify_finish (source);
+  g_io_channel_shutdown (channel, FALSE, NULL);
+  g_clear_pointer (&priv->fake_read_channel, g_io_channel_unref);
+
+  return G_SOURCE_REMOVE;
+}
+
+void
+meta_wayland_data_source_fake_read (MetaWaylandDataSource *source,
+                                    const char            *mimetype)
+{
+  MetaWaylandDataSourcePrivate *priv =
+    meta_wayland_data_source_get_instance_private (source);
+  GIOChannel *channel;
+  int p[2];
+
+  if (!g_unix_open_pipe (p, FD_CLOEXEC, NULL))
+    {
+      meta_wayland_data_source_notify_finish (source);
+      return;
+    }
+
+  if (!g_unix_set_fd_nonblocking (p[0], TRUE, NULL) ||
+      !g_unix_set_fd_nonblocking (p[1], TRUE, NULL))
+    {
+      meta_wayland_data_source_notify_finish (source);
+      close (p[0]);
+      close (p[1]);
+      return;
+    }
+
+  meta_wayland_data_source_send (source, mimetype, p[1]);
+  close (p[1]);
+  channel = g_io_channel_unix_new (p[0]);
+  g_io_channel_set_close_on_unref (channel, TRUE);
+  priv->fake_read_channel = channel;
+  priv->fake_read_watch_id =
+    g_io_add_watch (channel, G_IO_HUP, on_fake_read_hup, source);
+}
+
 gboolean
 meta_wayland_data_source_add_mime_type (MetaWaylandDataSource *source,
                                         const char            *mime_type)
diff -Nru mutter-48.3.1/src/wayland/meta-wayland-data-source.h mutter-48.4/src/wayland/meta-wayland-data-source.h
--- mutter-48.3.1/src/wayland/meta-wayland-data-source.h	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/wayland/meta-wayland-data-source.h	2025-06-29 11:00:20.000000000 +0100
@@ -111,6 +111,9 @@
 void meta_wayland_data_source_notify_drop_performed (MetaWaylandDataSource *source);
 void meta_wayland_data_source_notify_finish (MetaWaylandDataSource *source);
 
+void meta_wayland_data_source_fake_read (MetaWaylandDataSource *source,
+                                         const char            *mimetype);
+
 void
 meta_wayland_data_source_set_toplevel_drag (MetaWaylandDataSource   *source,
                                             MetaWaylandToplevelDrag *toplevel_drag);
diff -Nru mutter-48.3.1/src/wayland/meta-wayland-seat.c mutter-48.4/src/wayland/meta-wayland-seat.c
--- mutter-48.3.1/src/wayland/meta-wayland-seat.c	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/wayland/meta-wayland-seat.c	2025-06-29 11:00:20.000000000 +0100
@@ -98,10 +98,10 @@
   wl_resource_set_implementation (resource, &seat_interface, seat, unbind_resource);
   wl_list_insert (&seat->base_resource_list, wl_resource_get_link (resource));
 
-  wl_seat_send_capabilities (resource, seat->capabilities);
-
   if (version >= WL_SEAT_NAME_SINCE_VERSION)
     wl_seat_send_name (resource, "seat0");
+
+  wl_seat_send_capabilities (resource, seat->capabilities);
 }
 
 static uint32_t
diff -Nru mutter-48.3.1/src/x11/meta-x11-display.c mutter-48.4/src/x11/meta-x11-display.c
--- mutter-48.3.1/src/x11/meta-x11-display.c	2025-06-02 23:01:18.000000000 +0100
+++ mutter-48.4/src/x11/meta-x11-display.c	2025-06-29 11:00:20.000000000 +0100
@@ -2261,6 +2261,14 @@
 
   if (focus_window)
     data[0] = meta_window_x11_get_xwindow (focus_window);
+#ifdef HAVE_XWAYLAND
+  else if (x11_display->focus_xwindow && meta_is_wayland_compositor ())
+    /* On Wayland, when a Wayland window is focused, indicate that an
+     * actual window is focused rather than None, as None is otherwise
+     * also used during transient focus changes.
+     */
+    data[0] = x11_display->no_focus_window;
+#endif
   else
     data[0] = None;
 

Reply via email to