Control: tags -1 confirmed

On 2023-03-07 01:12:58 +0000, Simon McVittie wrote:
> Package: release.debian.org
> Severity: normal
> User: release.debian....@packages.debian.org
> Usertags: unblock
> X-Debbugs-Cc: gtk+...@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.

Agreed. Please go ahead.

Are there any other stable releases planned for the 3.24.x series during
the freeze?

Cheers

> 
> 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 <s...@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 <s...@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;


-- 
Sebastian Ramacher

Reply via email to