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

Bug#1032450: unblock (pre-approval): gtk+3.0/3.24.37-2



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock
X-Debbugs-Cc: gtk+3.0@packages.debian.org
Control: affects -1 + src:gtk+3.0

gtk+3.0_3.24.37 is in experimental at the moment, and we'd like to
include it in bookworm if possible.

[ Reason ]
Resync with upstream release. What we have in bookworm at the moment
is GTK 3.24.36 and something like half of 3.24.37; rebasing on 3.24.37
seems a better basis for a stable release.

We could apply selected changes from 3.24.37 as patches, but at some
point we're just reconstructing 3.24.37 with extra steps.

[ Impact ]
Updating to the version in experimental will give us:
- support for transparent drag-and-drop of files between sandboxed
  Flatpak GTK 3/GTK 4 apps (hopefully also Qt in future), and
  non-sandboxed GTK 3 apps from Debian
- the upstream fix for an annoying regression where spurious startup
  notifications appear in the GNOME top bar and newly-launched GTK 3 apps
  don't reliably appear in Alt+Tab, replacing our previous
  upstream-rejected workaround which didn't completely solve this
- various smaller bug fixes, some of which resolve potential crashes

Also, the patches added in 3.24.36-3 and 3.24.36-4 are now part of the
upstream source, giving users a better picture of what version we're
actually providing.

[ Tests ]
Installed in a GNOME desktop for manual testing with
no obvious regressions, and I can no longer reproduce
https://gitlab.gnome.org/GNOME/gtk/-/issues/5386.

I was also able to drag-and-drop a file from Nautilus onto the
org.gtk.Demo4 Flatpak app from <https://wiki.gnome.org/Apps/Nightly>
(specifically the Clipboard window), where it appears as an
xdg-document-portal filename when dropped on the bottom half of the demo.

autopkgtest reports one apparent regression for gnome-photos, but I think
it might be spurious (the test getting stuck). I'll try to investigate it
if it turns out to be reproducible.

[ Risks ]
I wouldn't normally be adding the file transfer portal (drag-and-drop
to/from sandboxes) at this stage in the release cycle, but it's a good
feature to have if we want users to be able to run more sandboxed apps
over the next 2 years, and surprisingly little code. It does have one
potential interop issue (using a non-standard MIME type) which I've
reported upstream. I hope fixing that should be a simple change
(< 10 lines).

Similarly I wouldn't normally be adding the changes in gdk/broadway/ at
this stage in the release cycle, but hardly anyone uses the GTK broadway
backend (it's a mechanism for displaying apps via a web browser) and
patching them out seems like it would be more disruptive than helpful.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

The attached diff is between the patched trees, excluding the actual
patches and some Windows- and macOS-specific changes to avoid noise.
I normally upload using dgit, so the contents of git and the debdiff
will match exactly.
diff --git a/NEWS b/NEWS
index fdd44e6e77..c4191123a9 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,25 @@
+Overview of Changes in GTK+ 3.24.37, 02-03-2023
+===============================================
+
+* Support the file transfer portal for copy-paste and DND
+
+* Treat XKB_MODE_NAME_LODO as super key
+
+* Refactor startup notification handling to be in sync with GTK 4
+
+* GL: Synchronie when calling MakeCurrent
+
+* CSS: Fix a problem with stopping animations
+
+* Wayland: Drop the legacy text input module
+
+* Windows: Set the default file extension in the native file chooser
+
+* Translation updates:
+ Abkhazian
+ Turkish
+
+
 Overview of Changes in GTK+ 3.24.36, 12-22-2022
 ===============================================
 
diff --git a/debian/changelog b/debian/changelog
index c7c69ef1c0..d115481e26 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,43 @@
+gtk+3.0 (3.24.37-2) UNRELEASED; urgency=medium
+
+  * d/copyright: Remove gtk-text-input.xml.
+    This file is no longer present in the source package.
+  * Remove Lintian overrides for lintian/lintian!452, no longer necessary
+
+ -- Simon McVittie <smcv@debian.org>  Tue, 07 Mar 2023 00:07:09 +0000
+
+gtk+3.0 (3.24.37-1) experimental; urgency=medium
+
+  * New upstream release
+    - Add support for xdg-desktop-portal file transfer API, allowing
+      copy/paste and drag-and-drop of files where one of the apps involved
+      is sandboxed by Flatpak, Snap or similar (GNOME/gtk!5554)
+    - Fix a regression for startup notifications (GNOME/gtk!5581)
+    - Fix potential invalid pointer dereference when checking whether a
+      list store iterator is valid
+    - Simple input method: Silence a warning from newer GLib by correctly
+      returning a value from a task
+    - Search engine: Reduce severity of the warning when Tracker
+      initialization failed, avoiding crashes when run with
+      G_DEBUG=fatal-criticals and without Tracker (GNOME/gtk!5490)
+    - Wayland backend: Drop support for an obsolete text input protocol,
+      which was only used by GNOME versions prior to Debian 10
+    - Broadway backend: Implement modal dialogs, preventing issues
+      with focus loss in complex UIs (GNOME/gtk!3990)
+    - Tests: Stop using GTestDBus, avoiding test timeouts if a dependency
+      leaks a bus connection
+    - Windows and macOS fixes not directly relevant to Debian
+  * d/patches: Drop patches that were applied upstream
+  * d/p/gdk-wayland-save-custom-xdg-activation-startup_id.patch,
+    d/p/window-focus-on-window-show-for-wayland-activation.patch:
+    Drop temporary fixes for GNOME/gtk#5386 (see 3.24.36-2, 3.24.35-3
+    changelogs). These were rejected upstream, and the change from
+    GNOME/gtk!5581 included in 3.24.37 should fix the issue more
+    correctly.
+  * Upload to experimental for further testing
+
+ -- Simon McVittie <smcv@debian.org>  Fri, 03 Mar 2023 20:27:28 +0000
+
 gtk+3.0 (3.24.36-4) unstable; urgency=medium
 
   * Team upload.
diff --git a/debian/copyright b/debian/copyright
index aad40ca787..215606a578 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -461,7 +461,6 @@ Copyright: Copyright (C) 1992-1994 The Regents of the University of California.
 License: LGPL-2+ or SWL
 
 Files:
- modules/input/gtk-text-input.xml
  modules/input/text-input-unstable-v3.xml
 Copyright:
  2012-2013 Intel Corporation
diff --git a/debian/libgtk-3-dev.lintian-overrides b/debian/libgtk-3-dev.lintian-overrides
deleted file mode 100644
index 570f53b902..0000000000
diff --git a/debian/patches/Fix-build_gir-logic.patch b/debian/patches/Fix-build_gir-logic.patch
diff --git a/debian/patches/Fix-build_gir-logic.patch b/debian/patches/Fix-build_gir-logic.patch
deleted file mode 100644
index 221f07691a..0000000000
diff --git a/debian/patches/Fixes-incorrect-grabbing-behaviour-causing-subsequent-rej.patch b/debian/patches/Fixes-incorrect-grabbing-behaviour-causing-subsequent-rej.patch
diff --git a/debian/patches/Fixes-incorrect-grabbing-behaviour-causing-subsequent-rej.patch b/debian/patches/Fixes-incorrect-grabbing-behaviour-causing-subsequent-rej.patch
deleted file mode 100644
index 0b9f3dc23a..0000000000
diff --git a/debian/patches/Mark-surface-as-dirty-before-flushing-it.patch b/debian/patches/Mark-surface-as-dirty-before-flushing-it.patch
diff --git a/debian/patches/Mark-surface-as-dirty-before-flushing-it.patch b/debian/patches/Mark-surface-as-dirty-before-flushing-it.patch
deleted file mode 100644
index c0c97625b2..0000000000
diff --git a/debian/patches/Remove-warning.patch b/debian/patches/Remove-warning.patch
diff --git a/debian/patches/Remove-warning.patch b/debian/patches/Remove-warning.patch
deleted file mode 100644
index 557c93f3d9..0000000000
diff --git a/debian/patches/Take-UI-scale-into-acount-when-loading-image-missing-fall.patch b/debian/patches/Take-UI-scale-into-acount-when-loading-image-missing-fall.patch
diff --git a/debian/patches/Take-UI-scale-into-acount-when-loading-image-missing-fall.patch b/debian/patches/Take-UI-scale-into-acount-when-loading-image-missing-fall.patch
deleted file mode 100644
index 25f187035a..0000000000
diff --git a/debian/patches/Treat-XKB_MOD_NAME_LOGO-as-super-key.patch b/debian/patches/Treat-XKB_MOD_NAME_LOGO-as-super-key.patch
diff --git a/debian/patches/Treat-XKB_MOD_NAME_LOGO-as-super-key.patch b/debian/patches/Treat-XKB_MOD_NAME_LOGO-as-super-key.patch
deleted file mode 100644
index c3e79db53d..0000000000
diff --git a/debian/patches/Update-Abkhazian-translation-1.patch b/debian/patches/Update-Abkhazian-translation-1.patch
diff --git a/debian/patches/Update-Abkhazian-translation-1.patch b/debian/patches/Update-Abkhazian-translation-1.patch
deleted file mode 100644
index a1f9b65352..0000000000
diff --git a/debian/patches/Update-Abkhazian-translation.patch b/debian/patches/Update-Abkhazian-translation.patch
diff --git a/debian/patches/Update-Abkhazian-translation.patch b/debian/patches/Update-Abkhazian-translation.patch
deleted file mode 100644
index 8a6e2b5c0f..0000000000
diff --git a/debian/patches/Update-Lithuanian-translation.patch b/debian/patches/Update-Lithuanian-translation.patch
diff --git a/debian/patches/Update-Lithuanian-translation.patch b/debian/patches/Update-Lithuanian-translation.patch
deleted file mode 100644
index d675b6efc6..0000000000
diff --git a/debian/patches/Update-Turkish-translation.patch b/debian/patches/Update-Turkish-translation.patch
diff --git a/debian/patches/Update-Turkish-translation.patch b/debian/patches/Update-Turkish-translation.patch
deleted file mode 100644
index 1fb517febd..0000000000
diff --git a/debian/patches/gdk-wayland-Perform-xdg_activation-on-gdk_window_set_star.patch b/debian/patches/gdk-wayland-Perform-xdg_activation-on-gdk_window_set_star.patch
diff --git a/debian/patches/gdk-wayland-Perform-xdg_activation-on-gdk_window_set_star.patch b/debian/patches/gdk-wayland-Perform-xdg_activation-on-gdk_window_set_star.patch
deleted file mode 100644
index 2082fc190d..0000000000
diff --git a/debian/patches/gdk-wayland-save-custom-xdg-activation-startup_id.patch b/debian/patches/gdk-wayland-save-custom-xdg-activation-startup_id.patch
diff --git a/debian/patches/gdk-wayland-save-custom-xdg-activation-startup_id.patch b/debian/patches/gdk-wayland-save-custom-xdg-activation-startup_id.patch
deleted file mode 100644
index 0764693706..0000000000
diff --git a/debian/patches/gdk-wayland-set-requesting-surface-to-xdg-activation.patch b/debian/patches/gdk-wayland-set-requesting-surface-to-xdg-activation.patch
diff --git a/debian/patches/gdk-wayland-set-requesting-surface-to-xdg-activation.patch b/debian/patches/gdk-wayland-set-requesting-surface-to-xdg-activation.patch
deleted file mode 100644
index 63a5062878..0000000000
diff --git a/debian/patches/gdk-x11-Emit-remove-s-n-message-from-gdk_toplevel_set_sta.patch b/debian/patches/gdk-x11-Emit-remove-s-n-message-from-gdk_toplevel_set_sta.patch
diff --git a/debian/patches/gdk-x11-Emit-remove-s-n-message-from-gdk_toplevel_set_sta.patch b/debian/patches/gdk-x11-Emit-remove-s-n-message-from-gdk_toplevel_set_sta.patch
deleted file mode 100644
index f00f4e2b72..0000000000
diff --git a/debian/patches/gl-Synchronize-when-calling-MakeCurrent.patch b/debian/patches/gl-Synchronize-when-calling-MakeCurrent.patch
diff --git a/debian/patches/gl-Synchronize-when-calling-MakeCurrent.patch b/debian/patches/gl-Synchronize-when-calling-MakeCurrent.patch
deleted file mode 100644
index 43336ee28c..0000000000
diff --git a/debian/patches/gtkapplication-Do-not-call-gdk_display_notify_startup_com.patch b/debian/patches/gtkapplication-Do-not-call-gdk_display_notify_startup_com.patch
diff --git a/debian/patches/gtkapplication-Do-not-call-gdk_display_notify_startup_com.patch b/debian/patches/gtkapplication-Do-not-call-gdk_display_notify_startup_com.patch
deleted file mode 100644
index 341777c1ee..0000000000
diff --git a/debian/patches/gtkcssanimatedstyle-Fix-return-of-new_advance.patch b/debian/patches/gtkcssanimatedstyle-Fix-return-of-new_advance.patch
diff --git a/debian/patches/gtkcssanimatedstyle-Fix-return-of-new_advance.patch b/debian/patches/gtkcssanimatedstyle-Fix-return-of-new_advance.patch
deleted file mode 100644
index 387ee9e7d6..0000000000
diff --git a/debian/patches/gtkwindow-Minor-refactor.patch b/debian/patches/gtkwindow-Minor-refactor.patch
diff --git a/debian/patches/gtkwindow-Minor-refactor.patch b/debian/patches/gtkwindow-Minor-refactor.patch
deleted file mode 100644
index 6493ef7952..0000000000
diff --git a/debian/patches/gtkwindow-Shuffle-gdk_window_set_startup_id-calls.patch b/debian/patches/gtkwindow-Shuffle-gdk_window_set_startup_id-calls.patch
diff --git a/debian/patches/gtkwindow-Shuffle-gdk_window_set_startup_id-calls.patch b/debian/patches/gtkwindow-Shuffle-gdk_window_set_startup_id-calls.patch
deleted file mode 100644
index 95514796d6..0000000000
diff --git a/debian/patches/series b/debian/patches/series
diff --git a/debian/patches/series b/debian/patches/series
index 324fadbe64..75e2a23964 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,26 +1,5 @@
-Fix-build_gir-logic.patch
-tests-make-accel-tests-easier-to-debug.patch
-Treat-XKB_MOD_NAME_LOGO-as-super-key.patch
-Update-Abkhazian-translation.patch
-Update-Abkhazian-translation-1.patch
-Update-Turkish-translation.patch
-gtkcssanimatedstyle-Fix-return-of-new_advance.patch
-Update-Lithuanian-translation.patch
-Take-UI-scale-into-acount-when-loading-image-missing-fall.patch
-gtkwindow-Minor-refactor.patch
-gdk-wayland-Perform-xdg_activation-on-gdk_window_set_star.patch
-gdk-x11-Emit-remove-s-n-message-from-gdk_toplevel_set_sta.patch
-gtkwindow-Shuffle-gdk_window_set_startup_id-calls.patch
-gtkapplication-Do-not-call-gdk_display_notify_startup_com.patch
-gl-Synchronize-when-calling-MakeCurrent.patch
-gdk-wayland-set-requesting-surface-to-xdg-activation.patch
-Fixes-incorrect-grabbing-behaviour-causing-subsequent-rej.patch
-Remove-warning.patch
 016_no_offscreen_widgets_grabbing.patch
 017_no_offscreen_device_grabbing.patch
 060_ignore-random-icons.patch
 reftest_compare_surfaces-Report-how-much-the-images-diffe.patch
 reftests-Allow-minor-differences-to-be-tolerated.patch
-window-focus-on-window-show-for-wayland-activation.patch
-gdk-wayland-save-custom-xdg-activation-startup_id.patch
-Mark-surface-as-dirty-before-flushing-it.patch
diff --git a/debian/patches/tests-make-accel-tests-easier-to-debug.patch b/debian/patches/tests-make-accel-tests-easier-to-debug.patch
deleted file mode 100644
index b2b33d2183..0000000000
diff --git a/debian/patches/window-focus-on-window-show-for-wayland-activation.patch b/debian/patches/window-focus-on-window-show-for-wayland-activation.patch
diff --git a/debian/patches/window-focus-on-window-show-for-wayland-activation.patch b/debian/patches/window-focus-on-window-show-for-wayland-activation.patch
deleted file mode 100644
index 4ad8445a22..0000000000
diff --git a/debian/source/lintian-overrides b/debian/source/lintian-overrides
diff --git a/debian/source/lintian-overrides b/debian/source/lintian-overrides
deleted file mode 100644
index 1b38d3611c..0000000000
diff --git a/gdk/broadway/broadway-protocol.h b/gdk/broadway/broadway-protocol.h
diff --git a/gdk/broadway/broadway-protocol.h b/gdk/broadway/broadway-protocol.h
index 0e82e94297..2573e9083a 100644
--- a/gdk/broadway/broadway-protocol.h
+++ b/gdk/broadway/broadway-protocol.h
@@ -157,7 +157,8 @@ typedef enum {
   BROADWAY_REQUEST_GRAB_POINTER,
   BROADWAY_REQUEST_UNGRAB_POINTER,
   BROADWAY_REQUEST_FOCUS_WINDOW,
-  BROADWAY_REQUEST_SET_SHOW_KEYBOARD
+  BROADWAY_REQUEST_SET_SHOW_KEYBOARD,
+  BROADWAY_REQUEST_SET_MODAL_HINT
 } BroadwayRequestType;
 
 typedef struct {
@@ -231,6 +232,12 @@ typedef struct {
   guint32 show_keyboard;
 } BroadwayRequestSetShowKeyboard;
 
+typedef struct {
+  BroadwayRequestBase base;
+  guint32 id;
+  gboolean modal_hint;
+} BroadwayRequestSetModalHint;
+
 typedef union {
   BroadwayRequestBase base;
   BroadwayRequestNewWindow new_window;
@@ -248,6 +255,7 @@ typedef union {
   BroadwayRequestTranslate translate;
   BroadwayRequestFocusWindow focus_window;
   BroadwayRequestSetShowKeyboard set_show_keyboard;
+  BroadwayRequestSetModalHint set_modal_hint;
 } BroadwayRequest;
 
 typedef enum {
diff --git a/gdk/broadway/broadway-server.c b/gdk/broadway/broadway-server.c
index b53f4edf12..9b99dd66de 100644
--- a/gdk/broadway/broadway-server.c
+++ b/gdk/broadway/broadway-server.c
@@ -113,6 +113,7 @@ struct BroadwayWindow {
   gboolean is_temp;
   gboolean visible;
   gint32 transient_for;
+  gboolean modal_hint;
 
   BroadwayBuffer *buffer;
   gboolean buffer_synced;
@@ -276,6 +277,14 @@ update_event_state (BroadwayServer *server,
       {
 	window->x = message->configure_notify.x;
 	window->y = message->configure_notify.y;
+
+	if (server->focused_window_id != message->configure_notify.id &&
+	    server->pointer_grab_window_id == -1 && window->modal_hint)
+	{
+	  broadway_server_window_raise (server, message->configure_notify.id);
+	  broadway_server_focus_window (server, message->configure_notify.id);
+	  broadway_server_flush (server);
+	}
       }
     break;
   case BROADWAY_EVENT_DELETE_NOTIFY:
@@ -1435,6 +1444,7 @@ broadway_server_destroy_window (BroadwayServer *server,
 				gint id)
 {
   BroadwayWindow *window;
+  gint transient_for = -1;
 
   if (server->mouse_in_toplevel_id == id)
     {
@@ -1453,6 +1463,9 @@ broadway_server_destroy_window (BroadwayServer *server,
 				GINT_TO_POINTER (id));
   if (window != NULL)
     {
+      if (server->focused_window_id == id)
+	transient_for = window->transient_for;
+
       server->toplevels = g_list_remove (server->toplevels, window);
       g_hash_table_remove (server->id_ht,
 			   GINT_TO_POINTER (id));
@@ -1463,6 +1476,17 @@ broadway_server_destroy_window (BroadwayServer *server,
 
       g_free (window);
     }
+
+  if (transient_for != -1)
+    {
+      window = g_hash_table_lookup (server->id_ht,
+				    GINT_TO_POINTER (transient_for));
+      if (window != NULL)
+        {
+	  broadway_server_focus_window (server, transient_for);
+	  broadway_server_flush (server);
+	}
+    }
 }
 
 gboolean
@@ -1588,6 +1612,20 @@ broadway_server_window_set_transient_for (BroadwayServer *server,
     }
 }
 
+void
+broadway_server_window_set_modal_hint (BroadwayServer *server,
+				       gint id, gboolean modal_hint)
+{
+  BroadwayWindow *window;
+
+  window = g_hash_table_lookup (server->id_ht,
+				GINT_TO_POINTER (id));
+  if (window == NULL)
+    return;
+
+  window->modal_hint = modal_hint;
+}
+
 gboolean
 broadway_server_has_client (BroadwayServer *server)
 {
diff --git a/gdk/broadway/broadway-server.h b/gdk/broadway/broadway-server.h
index b5d319133a..d266685820 100644
--- a/gdk/broadway/broadway-server.h
+++ b/gdk/broadway/broadway-server.h
@@ -95,5 +95,8 @@ cairo_surface_t * broadway_server_open_surface (BroadwayServer *server,
 						char *name,
 						int width,
 						int height);
+void                broadway_server_window_set_modal_hint (BroadwayServer   *server,
+							   gint              id,
+							   gboolean          modal_hint);
 
 #endif /* __BROADWAY_SERVER__ */
diff --git a/gdk/broadway/broadwayd.c b/gdk/broadway/broadwayd.c
index 84a9c3f3c1..90f4d3206b 100644
--- a/gdk/broadway/broadwayd.c
+++ b/gdk/broadway/broadwayd.c
@@ -301,6 +301,11 @@ client_handle_request (BroadwayClient *client,
     case BROADWAY_REQUEST_SET_SHOW_KEYBOARD:
       broadway_server_set_show_keyboard (server, request->set_show_keyboard.show_keyboard);
       break;
+    case BROADWAY_REQUEST_SET_MODAL_HINT:
+      broadway_server_window_set_modal_hint (server,
+					     request->set_modal_hint.id,
+					     request->set_modal_hint.modal_hint);
+      break;
     default:
       g_warning ("Unknown request of type %d", request->base.type);
     }
diff --git a/gdk/broadway/gdkbroadway-server.c b/gdk/broadway/gdkbroadway-server.c
index 2aa98abfab..9398428857 100644
--- a/gdk/broadway/gdkbroadway-server.c
+++ b/gdk/broadway/gdkbroadway-server.c
@@ -523,6 +523,18 @@ _gdk_broadway_server_window_set_transient_for (GdkBroadwayServer *server,
 				    BROADWAY_REQUEST_SET_TRANSIENT_FOR);
 }
 
+void
+_gdk_broadway_server_window_set_modal_hint (GdkBroadwayServer *server,
+					    gint id, gboolean modal_hint)
+{
+  BroadwayRequestSetModalHint msg;
+
+  msg.id = id;
+  msg.modal_hint = modal_hint;
+  gdk_broadway_server_send_message (server, msg,
+				    BROADWAY_REQUEST_SET_MODAL_HINT);
+}
+
 static void *
 map_named_shm (char *name, gsize size, gboolean *is_shm)
 {
diff --git a/gdk/broadway/gdkbroadway-server.h b/gdk/broadway/gdkbroadway-server.h
index 89a3076992..a961bb0358 100644
--- a/gdk/broadway/gdkbroadway-server.h
+++ b/gdk/broadway/gdkbroadway-server.h
@@ -71,5 +71,8 @@ gboolean           _gdk_broadway_server_window_move_resize       (GdkBroadwaySer
 								  int                 y,
 								  int                 width,
 								  int                 height);
+void               _gdk_broadway_server_window_set_modal_hint    (GdkBroadwayServer  *server,
+								  gint                id,
+								  gboolean            modal_hint);
 
 #endif /* __GDK_BROADWAY_SERVER__ */
diff --git a/gdk/broadway/gdkeventsource.c b/gdk/broadway/gdkeventsource.c
index e946778483..07c74f6d43 100644
--- a/gdk/broadway/gdkeventsource.c
+++ b/gdk/broadway/gdkeventsource.c
@@ -89,6 +89,24 @@ gdk_event_source_check (GSource *source)
   return retval;
 }
 
+static void
+handle_focus_change (GdkEventCrossing *event)
+{
+  gboolean focus_in = (event->type != GDK_ENTER_NOTIFY);
+  GdkEvent *focus_event;
+
+  if (event->window->parent) {
+      focus_event = gdk_event_new (GDK_FOCUS_CHANGE);
+      focus_event->focus_change.window = g_object_ref (event->window->parent);
+      focus_event->focus_change.send_event = FALSE;
+      focus_event->focus_change.in = focus_in;
+      gdk_event_set_device (focus_event, gdk_event_get_device ((GdkEvent *) event));
+
+      gdk_event_put (focus_event);
+      gdk_event_free (focus_event);
+  }
+}
+
 void
 _gdk_broadway_events_got_input (BroadwayInputMsg *message)
 {
@@ -160,6 +178,8 @@ _gdk_broadway_events_got_input (BroadwayInputMsg *message)
 	gdk_event_set_device (event, device_manager->core_pointer);
 	gdk_event_set_seat (event, gdk_device_get_seat (device_manager->core_pointer));
 
+	handle_focus_change (&event->crossing);
+
 	node = _gdk_event_queue_append (display, event);
 	_gdk_windowing_got_event (display, node, event, message->base.serial);
       }
diff --git a/gdk/broadway/gdkwindow-broadway.c b/gdk/broadway/gdkwindow-broadway.c
index 32d1a0771c..39ef2ee353 100644
--- a/gdk/broadway/gdkwindow-broadway.c
+++ b/gdk/broadway/gdkwindow-broadway.c
@@ -584,6 +584,15 @@ static void
 gdk_broadway_window_set_modal_hint (GdkWindow *window,
 				    gboolean   modal)
 {
+  GdkBroadwayDisplay *display;
+  GdkWindowImplBroadway *impl;
+
+  impl = GDK_WINDOW_IMPL_BROADWAY (window->impl);
+
+  impl->modal_hint = modal;
+
+  display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (impl->wrapper));
+  _gdk_broadway_server_window_set_modal_hint (display->server, impl->id, impl->modal_hint);
 }
 
 static void
diff --git a/gdk/broadway/gdkwindow-broadway.h b/gdk/broadway/gdkwindow-broadway.h
index f6a9ec1c65..ce1b3f1019 100644
--- a/gdk/broadway/gdkwindow-broadway.h
+++ b/gdk/broadway/gdkwindow-broadway.h
@@ -73,6 +73,7 @@ struct _GdkWindowImplBroadway
 
   GdkGeometry geometry_hints;
   GdkWindowHints geometry_hints_mask;
+  gboolean modal_hint;
 };
 
 struct _GdkWindowImplBroadwayClass
diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c
index da366d7235..55c0388ec6 100644
--- a/gdk/wayland/gdkdisplay-wayland.c
+++ b/gdk/wayland/gdkdisplay-wayland.c
@@ -952,13 +952,7 @@ gdk_wayland_display_notify_startup_complete (GdkDisplay  *display,
 #ifdef HAVE_XDG_ACTIVATION
   /* Will be signaled with focus activation */
   if (display_wayland->xdg_activation)
-    {
-      if (startup_id != NULL)
-        {
-          display_wayland->startup_notification_id = g_strdup (startup_id);
-        }
-      return;
-    }
+    return;
 #endif
 
   if (startup_id == NULL)
diff --git a/gtk/filetransferportal.c b/gtk/filetransferportal.c
new file mode 100644
index 0000000000..30afaeed97
--- /dev/null
+++ b/gtk/filetransferportal.c
@@ -0,0 +1,498 @@
+/*
+ * Copyright (C) 2018 Matthias Clasen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <gio/gio.h>
+
+#ifdef G_OS_UNIX
+
+#include <gio/gunixfdlist.h>
+
+#ifndef O_PATH
+#define O_PATH 0
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#else
+#define HAVE_O_CLOEXEC 1
+#endif
+
+#include "filetransferportalprivate.h"
+
+static GDBusProxy *file_transfer_proxy = NULL;
+
+typedef struct {
+  GTask *task;
+  char **files;
+  int len;
+  int start;
+} AddFileData;
+
+static void
+free_add_file_data (gpointer data)
+{
+  AddFileData *afd = data;
+
+  g_object_unref (afd->task);
+  g_free (afd->files);
+  g_free (afd);
+}
+
+static void add_files (GDBusProxy  *proxy,
+                       AddFileData *afd);
+
+static void
+add_files_done (GObject      *object,
+                GAsyncResult *result,
+                gpointer      data)
+{
+  GDBusProxy *proxy = G_DBUS_PROXY (object);
+  AddFileData *afd = data;
+  GError *error = NULL;
+  GVariant *ret;
+
+  ret = g_dbus_proxy_call_with_unix_fd_list_finish (proxy, NULL, result, &error);
+  if (ret == NULL)
+    {
+      g_task_return_error (afd->task, error);
+      free_add_file_data (afd);
+      return;
+    }
+
+  g_variant_unref (ret);
+
+  if (afd->start >= afd->len)
+    {
+      g_task_return_boolean (afd->task, TRUE);
+      free_add_file_data (afd);
+      return;
+    }
+
+  add_files (proxy, afd);
+}
+
+/* We call AddFiles in chunks of 16 to avoid running into
+ * the per-message fd limit of the bus.
+ */
+static void
+add_files (GDBusProxy  *proxy,
+           AddFileData *afd)
+{
+  GUnixFDList *fd_list;
+  GVariantBuilder fds, options;
+  int i;
+  char *key;
+
+  g_variant_builder_init (&fds, G_VARIANT_TYPE ("ah"));
+  fd_list = g_unix_fd_list_new ();
+
+  for (i = 0; afd->files[afd->start + i]; i++)
+    {
+      int fd;
+      int fd_in;
+      GError *error = NULL;
+
+      if (i == 16)
+        break;
+
+      fd = open (afd->files[afd->start + i], O_PATH | O_CLOEXEC);
+      if (fd == -1)
+        {
+          g_task_return_new_error (afd->task, G_IO_ERROR, g_io_error_from_errno (errno),
+                                   "Failed to open %s", afd->files[afd->start + i]);
+          free_add_file_data (afd);
+          g_object_unref (fd_list);
+          return;
+         }
+
+#ifndef HAVE_O_CLOEXEC
+      fcntl (fd, F_SETFD, FD_CLOEXEC);
+#endif
+      fd_in = g_unix_fd_list_append (fd_list, fd, &error);
+      close (fd);
+
+      if (fd_in == -1)
+        {
+          g_task_return_error (afd->task, error);
+          free_add_file_data (afd);
+          g_object_unref (fd_list);
+          return;
+        }
+
+      g_variant_builder_add (&fds, "h", fd_in);
+    }
+
+   afd->start += 16;
+
+  key = (char *)g_object_get_data (G_OBJECT (afd->task), "key");
+
+  g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
+  g_dbus_proxy_call_with_unix_fd_list (proxy,
+                                       "AddFiles",
+                                       g_variant_new ("(saha{sv})", key, &fds, &options),
+                                       0, -1,
+                                       fd_list,
+                                       NULL,
+                                       add_files_done, afd);
+
+  g_object_unref (fd_list);
+}
+
+static void
+start_session_done (GObject      *object,
+                    GAsyncResult *result,
+                    gpointer      data)
+{
+  GDBusProxy *proxy = G_DBUS_PROXY (object);
+  AddFileData *afd = data;
+  GError *error = NULL;
+  GVariant *ret;
+  const char *key;
+
+  ret = g_dbus_proxy_call_finish (proxy, result, &error);
+  if (ret == NULL)
+    {
+      g_task_return_error (afd->task, error);
+      free_add_file_data (afd);
+      return;
+    }
+
+  g_variant_get (ret, "(&s)", &key);
+
+  g_object_set_data_full (G_OBJECT (afd->task), "key", g_strdup (key), g_free);
+
+  g_variant_unref (ret);
+
+  add_files (proxy, afd);
+}
+
+void
+file_transfer_portal_register_files (const char          **files,
+                                     gboolean              writable,
+                                     GAsyncReadyCallback   callback,
+                                     gpointer              data)
+{
+  GTask *task;
+  AddFileData *afd;
+  GVariantBuilder options;
+
+  task = g_task_new (NULL, NULL, callback, data);
+
+  if (file_transfer_proxy == NULL)
+    {
+      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                               "No portal found");
+      g_object_unref (task);
+      return;
+    }
+
+  afd = g_new (AddFileData, 1);
+  afd->task = task;
+  afd->files = g_strdupv ((char **)files);
+  afd->len = g_strv_length (afd->files);
+  afd->start = 0;
+
+  g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
+  g_variant_builder_add (&options, "{sv}", "writable", g_variant_new_boolean (writable));
+  g_variant_builder_add (&options, "{sv}", "autostop", g_variant_new_boolean (TRUE));
+
+  g_dbus_proxy_call (file_transfer_proxy, "StartTransfer",
+                     g_variant_new ("(a{sv})", &options),
+                     0, -1, NULL, start_session_done, afd);
+}
+
+gboolean
+file_transfer_portal_register_files_finish (GAsyncResult  *result,
+                                            char         **key,
+                                            GError       **error)
+{
+  if (g_task_propagate_boolean (G_TASK (result), error))
+    {
+      *key = g_strdup (g_object_get_data (G_OBJECT (result), "key"));
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+char *
+file_transfer_portal_register_files_sync (const char **files,
+                                          gboolean     writable,
+                                          GError     **error)
+{
+  const char *value;
+  char *key;
+  GUnixFDList *fd_list;
+  GVariantBuilder fds, options;
+  int i;
+  GVariant *ret;
+
+  g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
+  ret = g_dbus_proxy_call_sync (file_transfer_proxy,
+                                "StartTransfer",
+                                g_variant_new ("(a{sv})", &options),
+                                0,
+                                -1,
+                                NULL,
+                                error);
+  if (ret == NULL)
+    return NULL;
+
+  g_variant_get (ret, "(&s)", &value);
+  key = g_strdup (value);
+  g_variant_unref (ret);
+
+  fd_list = NULL;
+
+  for (i = 0; files[i]; i++)
+    {
+      int fd;
+      int fd_in;
+
+      if (fd_list == NULL)
+        {
+          g_variant_builder_init (&fds, G_VARIANT_TYPE ("ah"));
+          fd_list = g_unix_fd_list_new ();
+        }
+
+      fd = open (files[i], O_PATH | O_CLOEXEC);
+      if (fd == -1)
+        {
+          g_set_error (error,
+                       G_IO_ERROR, g_io_error_from_errno (errno),
+                       "Failed to open %s", files[i]);
+          g_variant_builder_clear (&fds);
+          g_object_unref (fd_list);
+          g_free (key);
+          return NULL;
+         }
+
+#ifndef HAVE_O_CLOEXEC
+      fcntl (fd, F_SETFD, FD_CLOEXEC);
+#endif
+      fd_in = g_unix_fd_list_append (fd_list, fd, error);
+      close (fd);
+
+      if (fd_in == -1)
+        {
+          g_variant_builder_clear (&fds);
+          g_object_unref (fd_list);
+          g_free (key);
+          return NULL;
+        }
+
+      g_variant_builder_add (&fds, "h", fd_in);
+
+      if ((i + 1) % 16 == 0 || files[i + 1] == NULL)
+        {
+          g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
+          ret = g_dbus_proxy_call_with_unix_fd_list_sync (file_transfer_proxy,
+                                                          "AddFiles",
+                                                          g_variant_new ("(saha{sv})",
+                                                                         key,
+                                                                         &fds,
+                                                                         &options),
+                                                          0,
+                                                          -1,
+                                                          fd_list,
+                                                          NULL,
+                                                          NULL,
+                                                          error);
+          g_clear_object (&fd_list);
+
+          if (ret == NULL)
+            {
+              g_free (key);
+              return NULL;
+            }
+
+          g_variant_unref (ret);
+        }
+    }
+
+  return key;
+}
+
+static void
+retrieve_files_done (GObject      *object,
+                     GAsyncResult *result,
+                     gpointer      data)
+{
+  GDBusProxy *proxy = G_DBUS_PROXY (object);
+  GTask *task = data;
+  GError *error = NULL;
+  GVariant *ret;
+  char **files;
+
+  ret = g_dbus_proxy_call_finish (proxy, result, &error);
+  if (ret == NULL)
+    {
+      g_task_return_error (task, error);
+      g_object_unref (task);
+      return;
+    }
+
+  g_variant_get (ret, "(^a&s)", &files);
+
+  g_object_set_data_full (G_OBJECT (task), "files", g_strdupv (files), (GDestroyNotify)g_strfreev);
+
+  g_variant_unref (ret);
+
+  g_task_return_boolean (task, TRUE);
+}
+
+void
+file_transfer_portal_retrieve_files (const char          *key,
+                                     GAsyncReadyCallback  callback,
+                                     gpointer             data)
+{
+  GTask *task;
+  GVariantBuilder options;
+
+  task = g_task_new (NULL, NULL, callback, data);
+
+  if (file_transfer_proxy == NULL)
+    {
+      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                               "No portal found");
+      g_object_unref (task);
+      return;
+    }
+
+  g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
+  g_dbus_proxy_call (file_transfer_proxy,
+                     "RetrieveFiles",
+                     g_variant_new ("(sa{sv})", key, &options),
+                     0, -1, NULL,
+                     retrieve_files_done, task);
+}
+
+gboolean
+file_transfer_portal_retrieve_files_finish (GAsyncResult   *result,
+                                            char         ***files,
+                                            GError        **error)
+{
+  if (g_task_propagate_boolean (G_TASK (result), error))
+    {
+      *files = g_strdupv (g_object_get_data (G_OBJECT (result), "files"));
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+char **
+file_transfer_portal_retrieve_files_sync (const char  *key,
+                                          GError     **error)
+{
+  GVariantBuilder options;
+  GVariant *ret;
+  char **files = NULL;
+
+  g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
+  ret = g_dbus_proxy_call_sync (file_transfer_proxy,
+                                "RetrieveFiles",
+                                g_variant_new ("(sa{sv})", key, &options),
+                                0, -1, NULL,
+                                error);
+  if (ret)
+    {
+      const char **value;
+      g_variant_get (ret, "(^a&s)", &value);
+      files = g_strdupv ((char **)value);
+      g_variant_unref (ret);
+    }
+
+  return files;
+}
+
+static void
+connection_closed (GDBusConnection *connection,
+                   gboolean         remote_peer_vanished,
+                   GError          *error)
+{
+  g_clear_object (&file_transfer_proxy);
+}
+
+static void
+finish_registration (void)
+{
+  /* Free the singleton when the connection closes, important for test */
+  g_signal_connect (g_dbus_proxy_get_connection (G_DBUS_PROXY (file_transfer_proxy)),
+                    "closed", G_CALLBACK (connection_closed), NULL);
+}
+
+static gboolean
+proxy_has_owner (GDBusProxy *proxy)
+{
+  char *owner;
+
+  owner = g_dbus_proxy_get_name_owner (proxy);
+  if (owner)
+    {
+      g_free (owner);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+void
+file_transfer_portal_register (void)
+{
+  static gboolean called;
+
+  if (!called)
+    {
+      called = TRUE;
+
+      file_transfer_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+                                G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
+                                | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS
+                                | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+                                NULL,
+                                "org.freedesktop.portal.Documents",
+                                "/org/freedesktop/portal/documents",
+                                "org.freedesktop.portal.FileTransfer",
+                                NULL,
+                                NULL);
+
+      if (file_transfer_proxy && !proxy_has_owner (file_transfer_proxy))
+        g_clear_object (&file_transfer_proxy);
+
+      if (file_transfer_proxy)
+        finish_registration ();
+    }
+}
+
+gboolean
+file_transfer_portal_supported (void)
+{
+  file_transfer_portal_register ();
+
+  return file_transfer_proxy != NULL;
+}
+
+#endif /* G_OS_UNIX */
diff --git a/gtk/filetransferportalprivate.h b/gtk/filetransferportalprivate.h
new file mode 100644
index 0000000000..d136b53345
--- /dev/null
+++ b/gtk/filetransferportalprivate.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 Matthias Clasen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __FILE_TRANSFER_PROTOCOL_H__
+#define __FILE_TRANSFER_PROTOCOL_H__
+
+
+void     file_transfer_portal_register              (void);
+
+void     file_transfer_portal_register_files        (const char           **files,
+                                                     gboolean               writable,
+                                                     GAsyncReadyCallback    callback,
+                                                     gpointer               data);
+gboolean file_transfer_portal_register_files_finish (GAsyncResult          *result,
+                                                     char                 **key,
+                                                     GError               **error);
+
+void     file_transfer_portal_retrieve_files        (const char            *key,
+                                                     GAsyncReadyCallback    callback,
+                                                     gpointer               data);
+gboolean file_transfer_portal_retrieve_files_finish (GAsyncResult          *result,
+                                                     char                ***files,
+                                                     GError               **error);
+
+
+char *   file_transfer_portal_register_files_sync   (const char           **files,
+                                                     gboolean               writable,
+                                                     GError               **error);
+
+char **  file_transfer_portal_retrieve_files_sync   (const char            *key,
+                                                     GError               **error);
+
+gboolean file_transfer_portal_supported             (void);
+
+#endif
diff --git a/gtk/gtkfilechoosernativewin32.c b/gtk/gtkfilechoosernativewin32.c
index bec7ca4be3..d2f1769210 100644
--- a/gtk/gtkfilechoosernativewin32.c
+++ b/gtk/gtkfilechoosernativewin32.c
@@ -610,6 +610,10 @@ filechooser_win32_thread (gpointer _data)
       if (FAILED (hr))
         g_warning_hr ("Can't set file types", hr);
 
+      hr = IFileDialog_SetDefaultExtension (pfd, L"");
+      if (FAILED (hr))
+        g_warning_hr ("Can't set default extension", hr);
+
       if (data->self->current_filter)
         {
           GSList *filters = gtk_file_chooser_list_filters (GTK_FILE_CHOOSER (data->self));
diff --git a/gtk/gtkimcontextsimple.c b/gtk/gtkimcontextsimple.c
index a788426ceb..ccf9687ba6 100644
--- a/gtk/gtkimcontextsimple.c
+++ b/gtk/gtkimcontextsimple.c
@@ -253,6 +253,8 @@ init_compose_table_thread_cb (GTask            *task,
     return;
 
   gtk_im_context_simple_init_compose_table ();
+
+  g_task_return_boolean (task, TRUE);
 }
 
 static void
diff --git a/gtk/gtkliststore.c b/gtk/gtkliststore.c
index e4c50e6aae..7fac96ad72 100644
--- a/gtk/gtkliststore.c
+++ b/gtk/gtkliststore.c
@@ -1454,10 +1454,34 @@ gboolean
 gtk_list_store_iter_is_valid (GtkListStore *list_store,
                               GtkTreeIter  *iter)
 {
+  GtkListStorePrivate *priv;
+  GSequenceIter *seq_iter;
+
   g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), FALSE);
   g_return_val_if_fail (iter != NULL, FALSE);
 
-  return iter_is_valid (iter, list_store);
+  /* can't use iter_is_valid() here, because iter might point
+   * to random memory.
+   *
+   * We MUST NOT dereference it.
+   */
+
+  priv = list_store->priv;
+
+  if (iter == NULL ||
+      iter->user_data == NULL ||
+      priv->stamp != iter->stamp)
+    return FALSE;
+
+  for (seq_iter = g_sequence_get_begin_iter (priv->seq);
+       !g_sequence_iter_is_end (seq_iter);
+       seq_iter = g_sequence_iter_next (seq_iter))
+    {
+      if (seq_iter == iter->user_data)
+        return TRUE;
+    }
+
+  return FALSE;
 }
 
 static gboolean real_gtk_list_store_row_draggable (GtkTreeDragSource *drag_source,
diff --git a/gtk/gtksearchenginetracker3.c b/gtk/gtksearchenginetracker3.c
index bbc2a7752d..3f054110b5 100644
--- a/gtk/gtksearchenginetracker3.c
+++ b/gtk/gtksearchenginetracker3.c
@@ -100,8 +100,11 @@ finalize (GObject *object)
   g_clear_object (&engine->search_query);
   g_clear_object (&engine->search_location_query);
   g_clear_object (&engine->file_check_query);
-  tracker_sparql_connection_close (engine->sparql_conn);
-  g_clear_object (&engine->sparql_conn);
+  if (engine->sparql_conn != NULL)
+    {
+      tracker_sparql_connection_close (engine->sparql_conn);
+      g_clear_object (&engine->sparql_conn);
+    }
 
   G_OBJECT_CLASS (gtk_search_engine_tracker3_parent_class)->finalize (object);
 }
@@ -391,8 +394,8 @@ gtk_search_engine_tracker3_new (void)
                            NULL, &error, NULL);
   if (!engine)
     {
-      g_critical ("Could not init tracker3 search engine: %s",
-                  error->message);
+      g_warning ("Could not init tracker3 search engine: %s",
+                 error->message);
       g_error_free (error);
     }
 
diff --git a/gtk/gtkselection.c b/gtk/gtkselection.c
index 32d9de88bf..048b4ad496 100644
--- a/gtk/gtkselection.c
+++ b/gtk/gtkselection.c
@@ -116,6 +116,10 @@
 #include "broadway/gdkbroadway.h"
 #endif
 
+#ifndef G_OS_WIN32
+#include "filetransferportalprivate.h"
+#endif
+
 #undef DEBUG_SELECTION
 
 /* Maximum size of a sent chunk, in bytes. Also the default size of
@@ -338,6 +342,7 @@ static GdkAtom text_plain_atom;
 static GdkAtom text_plain_utf8_atom;
 static GdkAtom text_plain_locale_atom;
 static GdkAtom text_uri_list_atom;
+static GdkAtom portal_files_atom;
 
 static void 
 init_atoms (void)
@@ -358,6 +363,7 @@ init_atoms (void)
       g_free (tmp);
 
       text_uri_list_atom = gdk_atom_intern_static_string ("text/uri-list");
+      portal_files_atom = gdk_atom_intern_static_string ("application/vnd.portal.files");
     }
 }
 
@@ -502,6 +508,10 @@ gtk_target_list_add_image_targets (GtkTargetList *list,
  * Appends the URI targets supported by #GtkSelectionData to
  * the target list. All targets are added with the same @info.
  * 
+ * Since 3.24.37, this includes the application/vnd.portal.files
+ * target when possible, to allow sending files between sandboxed
+ * apps via the FileTransfer portal.
+ *
  * Since: 2.6
  **/
 void 
@@ -512,7 +522,12 @@ gtk_target_list_add_uri_targets (GtkTargetList *list,
   
   init_atoms ();
 
-  gtk_target_list_add (list, text_uri_list_atom, 0, info);  
+  gtk_target_list_add (list, text_uri_list_atom, 0, info);
+
+#ifndef G_OS_WIN32
+  if (file_transfer_portal_supported ())
+    gtk_target_list_add (list, portal_files_atom, 0, info);
+#endif
 }
 
 /**
@@ -1835,6 +1850,9 @@ gtk_selection_data_get_pixbuf (const GtkSelectionData *selection_data)
  * Sets the contents of the selection from a list of URIs.
  * The string is converted to the form determined by
  * @selection_data->target.
+ *
+ * Since 3.24.37, this may involve using the FileTransfer
+ * portal to send files between sandboxed apps.
  * 
  * Returns: %TRUE if the selection was successfully set,
  *   otherwise %FALSE.
@@ -1880,6 +1898,57 @@ gtk_selection_data_set_uris (GtkSelectionData  *selection_data,
 	  return TRUE;
 	}
     }
+#ifndef G_OS_WIN32
+  else if (selection_data->target == portal_files_atom &&
+           file_transfer_portal_supported ())
+    {
+      GPtrArray *a;
+      char **files;
+      char *key;
+      GError *error = NULL;
+
+      a = g_ptr_array_new ();
+
+      for (int i = 0; uris[i]; i++)
+        {
+          GFile *file;
+          char *path;
+
+          file = g_file_new_for_uri (uris[i]);
+          path = g_file_get_path (file);
+          g_object_unref (file);
+
+          if (path == NULL)
+            {
+              g_ptr_array_unref (a);
+              return FALSE;
+            }
+
+          g_ptr_array_add (a, path);
+        }
+
+      g_ptr_array_add (a, NULL);
+      files = (char **) g_ptr_array_free (a, FALSE);
+
+      key = file_transfer_portal_register_files_sync ((const char **)files, TRUE, &error);
+      if (key == NULL)
+        {
+          g_strfreev (files);
+          g_warning ("%s", error->message);
+          g_error_free (error);
+          return FALSE;
+        }
+
+      gtk_selection_data_set (selection_data,
+                              portal_files_atom,
+                              8, (guchar *)key, strlen (key));
+
+      g_strfreev (files);
+      g_free (key);
+
+      return TRUE;
+    }
+#endif
 
   return FALSE;
 }
@@ -1890,6 +1959,9 @@ gtk_selection_data_set_uris (GtkSelectionData  *selection_data,
  * 
  * Gets the contents of the selection data as array of URIs.
  *
+ * Since 3.24.37, this may involve using the FileTransfer
+ * portal to send files between sandboxed apps.
+ *
  * Returns:  (array zero-terminated=1) (element-type utf8) (transfer full): if
  *   the selection data contains a list of
  *   URIs, a newly allocated %NULL-terminated string array
@@ -1922,6 +1994,40 @@ gtk_selection_data_get_uris (const GtkSelectionData *selection_data)
       
       g_strfreev (list);
     }
+#ifndef G_OS_WIN32
+  else if (selection_data->length >= 0 &&
+           selection_data->type == portal_files_atom &&
+           file_transfer_portal_supported ())
+    {
+      char *key;
+      GError *error = NULL;
+      char **files;
+
+      key = g_strndup ((char *) selection_data->data, selection_data->length);
+      files = file_transfer_portal_retrieve_files_sync (key, &error);
+      if (error)
+        {
+          g_warning ("%s", error->message);
+          g_error_free (error);
+        }
+      g_free (key);
+
+      if (files)
+        {
+          GPtrArray *uris = g_ptr_array_new ();
+
+          for (int i = 0; files[i]; i++)
+            {
+              GFile *file = g_file_new_for_path (files[i]);
+              g_ptr_array_add (uris, g_file_get_uri (file));
+              g_object_unref (file);
+            }
+
+          g_ptr_array_add (uris, NULL);
+          result = (char **) g_ptr_array_free (uris, FALSE);
+        }
+    }
+#endif
 
   return result;
 }
@@ -2246,7 +2352,8 @@ gtk_targets_include_uri (GdkAtom *targets,
 
   for (i = 0; i < n_targets; i++)
     {
-      if (targets[i] == text_uri_list_atom)
+      if (targets[i] == text_uri_list_atom ||
+          targets[i] == portal_files_atom)
 	{
 	  result = TRUE;
 	  break;
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index ded0a8e23b..39b01eeb66 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -6227,16 +6227,6 @@ gtk_window_show (GtkWidget *widget)
   
   if (priv->modal)
     gtk_grab_add (widget);
-
-#ifdef GDK_WINDOWING_WAYLAND
-  if (GDK_IS_WAYLAND_WINDOW (gtk_widget_get_window (widget)))
-    {
-      // Submits the activation token / startup id to the compositor
-      gdk_window_focus (gtk_widget_get_window (widget), priv->initial_timestamp);
-      // Use gtk_window_present's timestamp only once
-      priv->initial_timestamp = GDK_CURRENT_TIME;
-    }
-#endif
 }
 
 static void
@@ -6297,19 +6287,23 @@ gtk_window_notify_startup (GtkWindow *window)
       !GTK_IS_OFFSCREEN_WINDOW (window) &&
       priv->type != GTK_WINDOW_POPUP)
     {
+      GdkWindow *gdk_window;
+
+      gdk_window = _gtk_widget_get_window (GTK_WIDGET (window));
+
       /* Do we have a custom startup-notification id? */
       if (priv->startup_id != NULL)
         {
           /* Make sure we have a "real" id */
           if (!startup_id_is_fake (priv->startup_id))
-            gdk_notify_startup_complete_with_id (priv->startup_id);
+            gdk_window_set_startup_id (gdk_window, priv->startup_id);
 
           g_free (priv->startup_id);
           priv->startup_id = NULL;
         }
       else
         {
-          gdk_notify_startup_complete ();
+          gdk_window_set_startup_id (gdk_window, NULL);
         }
     }
 }
diff --git a/gtk/meson.build b/gtk/meson.build
index 21540dd93b..4b7a453e11 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -659,6 +659,10 @@ if os_unix and tracker3_enabled
   gtk_unix_sources += 'gtksearchenginetracker3.c'
 endif
 
+if os_unix
+  gtk_unix_sources += 'filetransferportal.c'
+endif
+
 if os_unix
   gtk_sources += gtk_unix_sources
 endif
diff --git a/meson.build b/meson.build
index 6c711e9e9a..5444fa112e 100644
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,5 @@
 project('gtk+', 'c',
-  version: '3.24.36',
+  version: '3.24.37',
   default_options: [
     'buildtype=debugoptimized',
     'warning_level=1'
@@ -706,7 +706,6 @@ endif
 
 proto_sources = [
   'text-input-unstable-v3',
-  'gtk-text-input',
 ]
 proto_sources_outputs = []
 
@@ -760,7 +759,6 @@ immodules = [
   [ 'ime', files([ immodule_srcdir + 'gtkimcontextime.c', immodule_srcdir + 'imime.c' ]), win32_enabled ],
   [ 'quartz', files([ immodule_srcdir + 'imquartz.c' ]), quartz_enabled, [ '-xobjective-c' ] ],
   [ 'wayland', files([ immodule_srcdir + 'imwayland.c' ]) + proto_sources_outputs[0], wayland_enabled ],
-  [ 'waylandgtk', files([ immodule_srcdir + 'imwaylandgtk.c'])  + proto_sources_outputs[1], wayland_enabled ],
   [ 'xim', files([ immodule_srcdir + 'gtkimcontextxim.c', immodule_srcdir + 'imxim.c' ]), x11_enabled ],
 ]
 
diff --git a/modules/input/gtk-text-input.xml b/modules/input/gtk-text-input.xml
deleted file mode 100644
index a134a19f61..0000000000
diff --git a/modules/input/imwaylandgtk.c b/modules/input/imwaylandgtk.c
diff --git a/modules/input/imwaylandgtk.c b/modules/input/imwaylandgtk.c
deleted file mode 100644
index 207891d502..0000000000
diff --git a/po-properties/POTFILES.in b/po-properties/POTFILES.in
diff --git a/po-properties/POTFILES.in b/po-properties/POTFILES.in
index 8dae6a5f76..3773004d9b 100644
--- a/po-properties/POTFILES.in
+++ b/po-properties/POTFILES.in
@@ -344,7 +344,6 @@ modules/input/imti-er.c
 modules/input/imti-et.c
 modules/input/imviqr.c
 modules/input/imwayland.c
-modules/input/imwaylandgtk.c
 modules/input/imxim.c
 modules/printbackends/cups/gtkprintbackendcups.c
 modules/printbackends/cups/gtkprintercups.c
diff --git a/po/POTFILES.in b/po/POTFILES.in
index cb09da8767..5183c57c27 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -395,7 +395,6 @@ modules/input/imti-er.c
 modules/input/imti-et.c
 modules/input/imviqr.c
 modules/input/imwayland.c
-modules/input/imwaylandgtk.c
 modules/input/imxim.c
 modules/printbackends/cups/gtkprintbackendcups.c
 modules/printbackends/cups/gtkprintercups.c
diff --git a/tests/meson.build b/tests/meson.build
index 586fe2f45e..6ff249f8a1 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -36,7 +36,7 @@ gtk_tests = [
   ['testcombo'],
   ['testcombochange'],
   ['testdialog'],
-  ['testdnd'],
+  ['testdnd2'],
   ['testellipsise'],
   ['testemblems'],
   ['testentrycompletion'],
@@ -45,6 +45,7 @@ gtk_tests = [
   ['testexpander'],
   ['testfilechooserbutton'],
   ['testfilechooser'],
+  ['testfileportal'],
   ['testflowbox'],
   ['testfontchooser'],
   ['testfontoptions'],
diff --git a/tests/testfileportal.c b/tests/testfileportal.c
new file mode 100644
index 0000000000..0918af1d1b
--- /dev/null
+++ b/tests/testfileportal.c
@@ -0,0 +1,130 @@
+/* simple.c
+ * Copyright (C) 1997  Red Hat, Inc
+ * Author: Elliot Lee
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "config.h"
+#include <gtk/gtk.h>
+
+static void
+drag_begin_cb (GtkWidget      *widget,
+               GdkDragContext *context,
+               gpointer        data)
+{
+  char **uris;
+  char *cwd;
+
+  cwd = g_get_current_dir ();
+  uris = g_new0 (char *, 2);
+  uris[0] = g_strconcat ("file://", cwd, "/README.md", NULL);
+  g_free (cwd);
+
+  g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL);
+  gtk_drag_set_icon_default (context);
+
+  g_object_set_data_full (G_OBJECT (widget), "uris", g_strdupv ((char **)uris), (GDestroyNotify) g_strfreev);
+}
+
+static void
+drag_data_get (GtkWidget        *widget,
+               GdkDragContext   *context,
+               GtkSelectionData *selection,
+               unsigned int      target_info,
+               unsigned int      time,
+               gpointer          data)
+{
+  char **uris = (char **)g_object_get_data (G_OBJECT (widget), "uris");
+
+  gtk_selection_data_set_uris (selection, uris);
+
+  g_object_set_data (G_OBJECT (widget), "uris", NULL);
+}
+
+static void
+drag_data_received (GtkWidget        *widget,
+                    GdkDragContext   *context,
+                    int               x,
+                    int               y,
+                    GtkSelectionData *selection_data,
+                    unsigned int      info,
+                    unsigned int      time,
+                    gpointer          user_data)
+{
+  GtkLabel *label = user_data;
+  char **uris;
+
+  uris = gtk_selection_data_get_uris (selection_data);
+
+  if (uris)
+    {
+      gtk_label_set_label (label, uris[0]);
+      g_strfreev (uris);
+    }
+}
+
+int
+main (int argc, char *argv[])
+{
+  GtkWidget *window, *label, *eventbox, *box;
+  GtkTargetEntry targets[] = {
+    { "application/vnd.portal.files", 0, 0 },
+  };
+
+  gtk_init (&argc, &argv);
+
+  window = g_object_connect (g_object_new (gtk_window_get_type (),
+                                           "type", GTK_WINDOW_TOPLEVEL,
+                                           "title", "hello world",
+                                           "resizable", FALSE,
+                                           "border_width", 10,
+                                           NULL),
+                             "signal::destroy", gtk_main_quit, NULL,
+                             NULL);
+
+  box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+  gtk_widget_show (box);
+  gtk_container_add (GTK_CONTAINER (window), box);
+
+  eventbox = gtk_event_box_new ();
+  gtk_container_add (GTK_CONTAINER (box), eventbox);
+  gtk_widget_show (eventbox);
+  gtk_event_box_set_above_child (GTK_EVENT_BOX (eventbox), TRUE);
+
+  label = gtk_label_new ("drag me");
+  gtk_container_add (GTK_CONTAINER (eventbox), label);
+
+  gtk_drag_source_set (eventbox, GDK_BUTTON1_MASK, targets, G_N_ELEMENTS (targets), GDK_ACTION_COPY);
+  g_signal_connect (eventbox, "drag-begin", G_CALLBACK (drag_begin_cb), NULL);
+  g_signal_connect (eventbox, "drag-data-get", G_CALLBACK (drag_data_get), NULL);
+  gtk_widget_show (label);
+
+  eventbox = gtk_event_box_new ();
+  gtk_container_add (GTK_CONTAINER (box), eventbox);
+  gtk_widget_show (eventbox);
+  gtk_event_box_set_above_child (GTK_EVENT_BOX (eventbox), TRUE);
+
+  label = gtk_label_new ("drop here");
+  gtk_widget_show (label);
+  gtk_container_add (GTK_CONTAINER (eventbox), label);
+  gtk_drag_dest_set (eventbox, GTK_DEST_DEFAULT_ALL, targets, G_N_ELEMENTS (targets), GDK_ACTION_COPY);
+
+  g_signal_connect (eventbox, "drag-data-received", G_CALLBACK (drag_data_received), label);
+
+  gtk_widget_show (window);
+
+  gtk_main ();
+
+  return 0;
+}
diff --git a/testsuite/gtk/defaultvalue.c b/testsuite/gtk/defaultvalue.c
index a82788b7e4..7ed024d312 100644
--- a/testsuite/gtk/defaultvalue.c
+++ b/testsuite/gtk/defaultvalue.c
@@ -458,9 +458,6 @@ main (int argc, char **argv)
   const GType *otypes;
   guint i;
   gchar *schema_dir;
-  GTestDBus *bus;
-  GMainLoop *loop;
-  gint result;
 
   /* These must be set before before gtk_test_init */
   g_setenv ("GIO_USE_VFS", "local", TRUE);
@@ -475,12 +472,6 @@ main (int argc, char **argv)
   if (g_getenv ("GTK_TEST_MESON") == NULL)
     g_setenv ("GSETTINGS_SCHEMA_DIR", schema_dir, TRUE);
 
-  /* Create one test bus for all tests, as we have a lot of very small
-   * and quick tests.
-   */
-  bus = g_test_dbus_new (G_TEST_DBUS_NONE);
-  g_test_dbus_up (bus);
-
   otypes = gtk_test_list_all_types (NULL);
   for (i = 0; otypes[i]; i++)
     {
@@ -497,19 +488,5 @@ main (int argc, char **argv)
       g_free (testname);
     }
 
-  result = g_test_run();
-
-  /* Work around the annoying issue that g_test_dbus_down is giving
-   * us an "Error while sending AddMatch" that comes out of an idle
-   */
-  loop = g_main_loop_new (NULL, FALSE);
-  g_timeout_add (1000, (GSourceFunc)g_main_loop_quit, loop);
-  g_main_loop_run (loop);
-  g_main_loop_unref (loop);
-
-  g_test_dbus_down (bus);
-  g_object_unref (bus);
-  g_free (schema_dir);
-
-  return result;
+  return g_test_run();
 }
diff --git a/testsuite/gtk/objects-finalize.c b/testsuite/gtk/objects-finalize.c
index 24540e313f..95b565c591 100644
--- a/testsuite/gtk/objects-finalize.c
+++ b/testsuite/gtk/objects-finalize.c
@@ -79,8 +79,7 @@ main (int argc, char **argv)
   const GType *all_types;
   guint n_types = 0, i;
   gchar *schema_dir;
-  GTestDBus *bus;
-  gint result;
+  int result;
 
   /* These must be set before before gtk_test_init */
   g_setenv ("GIO_USE_VFS", "local", TRUE);
@@ -95,12 +94,6 @@ main (int argc, char **argv)
   if (g_getenv ("GTK_TEST_MESON") == NULL)
     g_setenv ("GSETTINGS_SCHEMA_DIR", schema_dir, TRUE);
 
-  /* Create one test bus for all tests, as we have a lot of very small
-   * and quick tests.
-   */
-  bus = g_test_dbus_new (G_TEST_DBUS_NONE);
-  g_test_dbus_up (bus);
-
   all_types = gtk_test_list_all_types (&n_types);
 
   for (i = 0; i < n_types; i++)
@@ -133,8 +126,6 @@ main (int argc, char **argv)
 
   result = g_test_run();
 
-  g_test_dbus_down (bus);
-  g_object_unref (bus);
   g_free (schema_dir);
 
   return result;

Reply to: