Merge branch 'export-handles-properly' into 'main'

Add testfilelauncher

See merge request GNOME/gtk!5968
This commit is contained in:
Matthias Clasen 2023-05-15 10:12:29 +00:00
commit d517b25ea3
15 changed files with 242 additions and 52 deletions

View File

@ -135,7 +135,8 @@ gdk_toplevel_default_export_handle_finish (GdkToplevel *toplevel,
}
static void
gdk_toplevel_default_unexport_handle (GdkToplevel *toplevel)
gdk_toplevel_default_unexport_handle (GdkToplevel *toplevel,
const char *handle)
{
}
@ -791,7 +792,7 @@ gdk_toplevel_export_handle (GdkToplevel *toplevel,
* @result: the `GAsyncResult`
* @error: return location for an error
*
* Finishes the [method@Gdk.Toplevel.export_handle] cal and
* Finishes the [method@Gdk.Toplevel.export_handle] call and
* returns the resulting handle.
*
* Returns: (nullable) (transfer full): the exported handle,
@ -810,6 +811,7 @@ gdk_toplevel_export_handle_finish (GdkToplevel *toplevel,
/*< private >
* gdk_toplevel_unexport_handle:
* @toplevel: a `GdkToplevel`
* @handle: the handle to unexport
*
* Destroys the handle that was obtained with [method@Gdk.Toplevel.export_handle].
*
@ -819,7 +821,8 @@ gdk_toplevel_export_handle_finish (GdkToplevel *toplevel,
* Since: 4.10
*/
void
gdk_toplevel_unexport_handle (GdkToplevel *toplevel)
gdk_toplevel_unexport_handle (GdkToplevel *toplevel,
const char *handle)
{
GDK_TOPLEVEL_GET_IFACE (toplevel)->unexport_handle (toplevel);
GDK_TOPLEVEL_GET_IFACE (toplevel)->unexport_handle (toplevel, handle);
}

View File

@ -49,7 +49,8 @@ struct _GdkToplevelInterface
GAsyncResult *result,
GError **error);
void (* unexport_handle) (GdkToplevel *toplevel);
void (* unexport_handle) (GdkToplevel *toplevel,
const char *handle);
};
typedef enum
@ -82,7 +83,8 @@ char *gdk_toplevel_export_handle_finish (GdkToplevel *toplevel,
GAsyncResult *result,
GError **error);
void gdk_toplevel_unexport_handle (GdkToplevel *toplevel);
void gdk_toplevel_unexport_handle (GdkToplevel *toplevel,
const char *handle);
G_END_DECLS

View File

@ -1148,6 +1148,9 @@ gdk_wayland_surface_destroy (GdkSurface *surface,
gdk_wayland_surface_hide_surface (surface);
if (GDK_IS_TOPLEVEL (surface))
gdk_wayland_toplevel_destroy (GDK_TOPLEVEL (surface));
gdk_wayland_surface_destroy_wl_surface (GDK_WAYLAND_SURFACE (surface));
frame_clock = gdk_surface_get_frame_clock (surface);

View File

@ -39,3 +39,4 @@ void gdk_wayland_toplevel_announce_ssd (GdkToplevel *toplevel);
gboolean gdk_wayland_toplevel_inhibit_idle (GdkToplevel *toplevel);
void gdk_wayland_toplevel_uninhibit_idle (GdkToplevel *toplevel);
void gdk_wayland_toplevel_destroy (GdkToplevel *toplevel);

View File

@ -54,11 +54,16 @@ static void gdk_wayland_toplevel_sync_parent (GdkWaylandToplevel *to
static void gdk_wayland_toplevel_sync_parent_of_imported (GdkWaylandToplevel *toplevel);
static void gdk_wayland_surface_create_xdg_toplevel (GdkWaylandToplevel *toplevel);
static void gdk_wayland_toplevel_sync_title (GdkWaylandToplevel *toplevel);
static gboolean gdk_wayland_toplevel_is_exported (GdkWaylandToplevel *toplevel);
static void unset_transient_for_exported (GdkWaylandToplevel *toplevel);
/* {{{ GdkWaylandToplevel definition */
typedef struct {
struct zxdg_exported_v1 *xdg_exported;
struct zxdg_exported_v2 *xdg_exported_v2;
char *handle;
} GdkWaylandExported;
/**
* GdkWaylandToplevel:
*
@ -83,8 +88,7 @@ struct _GdkWaylandToplevel
GdkWaylandToplevel *transient_for;
struct org_kde_kwin_server_decoration *server_decoration;
struct zxdg_exported_v1 *xdg_exported;
struct zxdg_exported_v2 *xdg_exported_v2;
GList *exported;
struct {
int width;
@ -102,12 +106,6 @@ struct _GdkWaylandToplevel
gboolean size_is_fixed;
} next_layout;
struct {
GdkWaylandToplevelExported callback;
gpointer user_data;
GDestroyNotify destroy_func;
} exported;
struct {
gboolean was_set;
@ -1280,9 +1278,6 @@ gdk_wayland_toplevel_finalize (GObject *object)
display_wayland->toplevels = g_list_remove (display_wayland->toplevels, self);
if (gdk_wayland_toplevel_is_exported (self))
gdk_wayland_toplevel_unexport_handle (GDK_TOPLEVEL (self));
g_free (self->application.application_id);
g_free (self->application.app_menu_path);
g_free (self->application.menubar_path);
@ -1747,8 +1742,12 @@ xdg_exported_handle_v1 (void *data,
struct zxdg_exported_v1 *zxdg_exported_v1,
const char *handle)
{
g_task_return_pointer (G_TASK (data), g_strdup (handle), g_free);
g_object_unref (data);
GTask *task = G_TASK (data);
GdkWaylandExported *exported = (GdkWaylandExported *)g_task_get_task_data (task);
exported->handle = g_strdup (handle);
g_task_return_pointer (task, g_strdup (handle), g_free);
g_object_unref (task);
}
static const struct zxdg_exported_v1_listener xdg_exported_listener_v1 = {
@ -1760,8 +1759,12 @@ xdg_exported_handle_v2 (void *data,
struct zxdg_exported_v2 *zxdg_exported_v2,
const char *handle)
{
g_task_return_pointer (G_TASK (data), g_strdup (handle), g_free);
g_object_unref (data);
GTask *task = G_TASK (data);
GdkWaylandExported *exported = (GdkWaylandExported *)g_task_get_task_data (task);
exported->handle = g_strdup (handle);
g_task_return_pointer (task, g_strdup (handle), g_free);
g_object_unref (task);
}
static const struct zxdg_exported_v2_listener xdg_exported_listener_v2 = {
@ -1784,19 +1787,27 @@ gdk_wayland_toplevel_real_export_handle (GdkToplevel *toplevel,
if (display_wayland->xdg_exporter_v2)
{
wayland_toplevel->xdg_exported_v2 =
GdkWaylandExported *exported = g_new0 (GdkWaylandExported, 1);
exported->xdg_exported_v2 =
zxdg_exporter_v2_export_toplevel (display_wayland->xdg_exporter_v2,
gdk_wayland_surface_get_wl_surface (surface));
zxdg_exported_v2_add_listener (wayland_toplevel->xdg_exported_v2,
zxdg_exported_v2_add_listener (exported->xdg_exported_v2,
&xdg_exported_listener_v2, task);
wayland_toplevel->exported = g_list_prepend (wayland_toplevel->exported, exported);
g_task_set_task_data (task, exported, NULL);
}
else if (display_wayland->xdg_exporter)
{
wayland_toplevel->xdg_exported =
GdkWaylandExported *exported = g_new0 (GdkWaylandExported, 1);
exported->xdg_exported =
zxdg_exporter_v1_export (display_wayland->xdg_exporter,
gdk_wayland_surface_get_wl_surface (surface));
zxdg_exported_v1_add_listener (wayland_toplevel->xdg_exported,
zxdg_exported_v1_add_listener (exported->xdg_exported,
&xdg_exported_listener_v1, task);
wayland_toplevel->exported = g_list_prepend (wayland_toplevel->exported, exported);
g_task_set_task_data (task, exported, NULL);
}
else
{
@ -1815,15 +1826,36 @@ gdk_wayland_toplevel_real_export_handle_finish (GdkToplevel *toplevel,
}
static void
gdk_wayland_toplevel_real_unexport_handle (GdkToplevel *toplevel)
destroy_exported (GdkWaylandExported *exported)
{
g_clear_pointer (&exported->handle, g_free);
g_clear_pointer (&exported->xdg_exported_v2, zxdg_exported_v2_destroy);
g_clear_pointer (&exported->xdg_exported, zxdg_exported_v1_destroy);
g_free (exported);
}
static void
gdk_wayland_toplevel_real_unexport_handle (GdkToplevel *toplevel,
const char *handle)
{
GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel);
g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel));
g_return_if_fail (wayland_toplevel->xdg_exported_v2 || wayland_toplevel->xdg_exported);
g_return_if_fail (handle != NULL);
g_clear_pointer (&wayland_toplevel->xdg_exported_v2, zxdg_exported_v2_destroy);
g_clear_pointer (&wayland_toplevel->xdg_exported, zxdg_exported_v1_destroy);
for (GList *l = wayland_toplevel->exported; l; l = l->next)
{
GdkWaylandExported *exported = l->data;
if (exported->handle && strcmp (exported->handle, handle) == 0)
{
wayland_toplevel->exported = g_list_delete_link (wayland_toplevel->exported, l);
destroy_exported (exported);
return;
}
}
g_warn_if_reached ();
}
static gboolean
@ -2255,6 +2287,32 @@ gdk_wayland_toplevel_set_dbus_properties (GdkToplevel *toplevel,
maybe_set_gtk_surface_dbus_properties (wayland_toplevel);
}
void
gdk_wayland_toplevel_destroy (GdkToplevel *toplevel)
{
GdkWaylandToplevel *self = GDK_WAYLAND_TOPLEVEL (toplevel);
while (self->exported)
{
GdkWaylandExported *exported = self->exported->data;
self->exported = g_list_delete_link (self->exported, self->exported);
if (exported->handle == NULL)
{
GTask *task;
if (exported->xdg_exported_v2)
task = G_TASK (wl_proxy_get_user_data ((struct wl_proxy *) exported->xdg_exported_v2));
else
task = G_TASK (wl_proxy_get_user_data ((struct wl_proxy *) exported->xdg_exported));
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Surface was destroyed");
g_object_unref (task);
}
destroy_exported (exported);
}
}
/* }}} */
/* {{{ Toplevel API */
@ -2397,12 +2455,6 @@ gdk_wayland_toplevel_uninhibit_idle (GdkToplevel *toplevel)
* marking surfaces as transient for out-of-process surfaces.
*/
static gboolean
gdk_wayland_toplevel_is_exported (GdkWaylandToplevel *toplevel)
{
return toplevel->xdg_exported != NULL || toplevel->xdg_exported_v2 != NULL;
}
typedef struct {
GdkWaylandToplevelExported callback;
gpointer user_data;
@ -2450,6 +2502,12 @@ export_handle_done (GObject *source,
* from another surface as transient for this one, see
* [method@GdkWayland.WaylandToplevel.set_transient_for_exported].
*
* Before 4.12, this API could not safely be used multiple times,
* since there was no reference counting for handles. Starting with
* 4.12, every call to this function obtains a new handle, and every
* call to [method@GdkWayland.WaylandToplevel.drop_exported_handle] drops
* just the handle that it is given.
*
* Note that this API depends on an unstable Wayland protocol,
* and thus may require changes in the future.
*
@ -2486,15 +2544,53 @@ gdk_wayland_toplevel_export_handle (GdkToplevel *toplevel,
* It is an error to call this function on a surface that
* does not have a handle.
*
* Since 4.12, this function does nothing. Use
* [method@GdkWayland.WaylandToplevel.drop_exported_handle] instead to drop a
* handle that was obtained with [method@GdkWayland.WaylandToplevel.export_handle].
*
* Note that this API depends on an unstable Wayland protocol,
* and thus may require changes in the future.
*
* Deprecated: 4.12: Use [method@GdkWayland.WaylandToplevel.drop_exported_handle]
* instead, this function does nothing
*/
void
gdk_wayland_toplevel_unexport_handle (GdkToplevel *toplevel)
{
g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel));
GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel);
gdk_toplevel_unexport_handle (toplevel);
if (wayland_toplevel->exported != NULL &&
wayland_toplevel->exported->next == NULL)
{
GdkWaylandExported *exported = wayland_toplevel->exported->data;
if (exported->handle)
{
gdk_toplevel_unexport_handle (toplevel, exported->handle);
return;
}
}
g_warning ("Use gdk_wayland_toplevel_drop_exported_handle()");
}
/**
* gdk_wayland_toplevel_drop_exported_handle:
* @toplevel: (type GdkWaylandToplevel): the `GdkToplevel` that was exported
* @handle: the handle to drop
*
* Destroy a handle that was obtained with gdk_wayland_toplevel_export_handle().
*
* Note that this API depends on an unstable Wayland protocol,
* and thus may require changes in the future.
*
* Since: 4.12
*/
void
gdk_wayland_toplevel_drop_exported_handle (GdkToplevel *toplevel,
const char *handle)
{
gdk_toplevel_unexport_handle (toplevel, handle);
}
static void

View File

@ -52,9 +52,13 @@ gboolean gdk_wayland_toplevel_export_handle (GdkToplevel
gpointer user_data,
GDestroyNotify destroy_func);
GDK_AVAILABLE_IN_ALL
GDK_DEPRECATED_IN_4_12_FOR(gdk_wayland_toplevel_drop_exported_handle)
void gdk_wayland_toplevel_unexport_handle (GdkToplevel *toplevel);
GDK_AVAILABLE_IN_4_12
void gdk_wayland_toplevel_drop_exported_handle (GdkToplevel *toplevel,
const char *handle);
GDK_AVAILABLE_IN_ALL
gboolean gdk_wayland_toplevel_set_transient_for_exported (GdkToplevel *toplevel,
const char *parent_handle_str);

View File

@ -5318,7 +5318,8 @@ gdk_x11_toplevel_export_handle_finish (GdkToplevel *toplevel,
}
static void
gdk_x11_toplevel_unexport_handle (GdkToplevel *toplevel)
gdk_x11_toplevel_unexport_handle (GdkToplevel *toplevel,
const char *handle)
{
}

View File

@ -31,6 +31,7 @@ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
typedef struct {
GtkWindow *parent;
char *handle;
GAppLaunchContext *context;
char *uri;
GTask *task;
@ -39,9 +40,10 @@ typedef struct {
static void
gtk_show_uri_data_free (GtkShowUriData *data)
{
if (data->parent)
gtk_window_unexport_handle (data->parent);
if (data->parent && data->handle)
gtk_window_unexport_handle (data->parent, data->handle);
g_clear_object (&data->parent);
g_free (data->handle);
g_clear_object (&data->context);
g_free (data->uri);
g_clear_object (&data->task);
@ -72,7 +74,10 @@ window_handle_exported (GtkWindow *window,
GtkShowUriData *data = user_data;
if (handle)
{
g_app_launch_context_setenv (data->context, "PARENT_WINDOW_ID", handle);
data->handle = g_strdup (handle);
}
g_app_info_launch_default_for_uri_async (data->uri,
data->context,

View File

@ -52,6 +52,7 @@ typedef struct {
const char *method_name;
char *exported_handle;
GtkWindow *exported_window;
PortalErrorHandler error_handler;
} FilechooserPortalData;
@ -79,7 +80,11 @@ filechooser_portal_data_clear (FilechooserPortalData *data)
if (data->exported_window)
{
gtk_window_unexport_handle (data->exported_window);
if (data->exported_handle)
{
gtk_window_unexport_handle (data->exported_window, data->exported_handle);
g_clear_pointer (&data->exported_handle, g_free);
}
g_clear_object (&data->exported_window);
}
@ -460,6 +465,7 @@ window_handle_exported (GtkWindow *window,
gtk_grab_add (GTK_WIDGET (data->grab_widget));
}
data->exported_handle = g_strdup (handle_str);
show_portal_file_chooser (self, handle_str);
}

View File

@ -108,6 +108,7 @@ enum {
typedef struct {
GtkWindow *parent;
char *parent_handle;
GFile *file;
char *uri;
gboolean open_folder;
@ -128,8 +129,9 @@ open_uri_data_free (OpenUriData *data)
g_clear_object (&data->connection);
if (data->cancel_handler)
g_signal_handler_disconnect (data->cancellable, data->cancel_handler);
if (data->parent)
gtk_window_unexport_handle (data->parent);
if (data->parent && data->parent_handle)
gtk_window_unexport_handle (data->parent, data->parent_handle);
g_free (data->parent_handle);
g_clear_object (&data->parent);
g_clear_object (&data->file);
g_free (data->uri);
@ -426,6 +428,8 @@ window_handle_exported (GtkWindow *window,
GAppLaunchContext *context;
char *activation_token = NULL;
data->parent_handle = g_strdup (handle);
if (window)
display = gtk_widget_get_display (GTK_WIDGET (window));
else

View File

@ -48,6 +48,7 @@ typedef struct {
GtkPrintOperationResult result;
GtkPrintOperationPrintFunc print_cb;
GtkWindow *parent;
char *handle;
GMainLoop *loop;
guint32 token;
GDestroyNotify destroy;
@ -62,8 +63,9 @@ portal_data_free (gpointer data)
{
PortalData *portal = data;
if (portal->parent)
gtk_window_unexport_handle (portal->parent);
if (portal->parent && portal->handle)
gtk_window_unexport_handle (portal->parent, portal->handle);
g_free (portal->handle);
g_object_unref (portal->op);
g_object_unref (portal->proxy);
if (portal->loop)
@ -547,6 +549,8 @@ window_handle_exported (GtkWindow *window,
{
PortalData *portal = user_data;
portal->handle = g_strdup (handle_str);
g_dbus_proxy_call (portal->proxy,
"PreparePrint",
g_variant_new ("(ss@a{sv}@a{sv}@a{sv})",

View File

@ -6291,6 +6291,17 @@ prefix_handle (GdkDisplay *display,
return NULL;
}
static const char *
unprefix_handle (const char *handle)
{
if (g_str_has_prefix (handle, "wayland:"))
return handle + strlen ("wayland:");
else if (g_str_has_prefix (handle, "x11:"))
return handle + strlen ("x1!:");
else
return handle;
}
static void
export_handle_done (GObject *source,
GAsyncResult *result,
@ -6336,11 +6347,12 @@ gtk_window_export_handle (GtkWindow *window,
}
void
gtk_window_unexport_handle (GtkWindow *window)
gtk_window_unexport_handle (GtkWindow *window,
const char *handle)
{
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
gdk_toplevel_unexport_handle (GDK_TOPLEVEL (priv->surface));
gdk_toplevel_unexport_handle (GDK_TOPLEVEL (priv->surface), unprefix_handle (handle));
}
static GtkPointerFocus *

View File

@ -79,7 +79,8 @@ typedef void (*GtkWindowHandleExported) (GtkWindow *window,
gboolean gtk_window_export_handle (GtkWindow *window,
GtkWindowHandleExported callback,
gpointer user_data);
void gtk_window_unexport_handle (GtkWindow *window);
void gtk_window_unexport_handle (GtkWindow *window,
const char *handle);
GtkWidget * gtk_window_lookup_pointer_focus_widget (GtkWindow *window,
GdkDevice *device,

View File

@ -1,5 +1,6 @@
gtk_tests = [
# testname, optional extra sources
['testfilelauncher'],
['input'],
['testpopup'],
['testupload'],

47
tests/testfilelauncher.c Normal file
View File

@ -0,0 +1,47 @@
#include <gtk/gtk.h>
static void
launched_cb (GObject *source,
GAsyncResult *result,
gpointer data)
{
GtkFileLauncher *launcher = GTK_FILE_LAUNCHER (source);
GError *error = NULL;
if (!gtk_file_launcher_launch_finish (launcher, result, &error))
{
g_print ("Launching failed: %s\n", error->message);
g_error_free (error);
}
}
int
main (int argc, char *argv[])
{
GtkWidget *window;
GtkFileLauncher *launcher;
gtk_init ();
window = gtk_window_new ();
launcher = gtk_file_launcher_new (NULL);
gtk_window_present (GTK_WINDOW (window));
for (int i = 1; i < argc; i++)
{
GFile *file = g_file_new_for_commandline_arg (argv[i]);
g_print ("launching %s\n", argv[i]);
gtk_file_launcher_set_file (launcher, file);
gtk_file_launcher_launch (launcher, GTK_WINDOW (window), NULL, launched_cb, NULL);
g_object_unref (file);
}
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
g_main_context_iteration (NULL, FALSE);
return 0;
}