--- /dev/null
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2017 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * 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.1 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 <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <glib/gstdio.h>
+
+#include "gopenuriportal.h"
+#include "xdp-dbus.h"
+#include "gtkwindowprivate.h"
+
+#ifdef G_OS_UNIX
+#include <gio/gunixfdlist.h>
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#else
+#define HAVE_O_CLOEXEC 1
+#endif
+
+
+static GXdpOpenURI *openuri;
+
+static gboolean
+init_openuri_portal (void)
+{
+ static gsize openuri_inited = 0;
+
+ if (g_once_init_enter (&openuri_inited))
+ {
+ GError *error = NULL;
+ GDBusConnection *connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+
+ if (connection != NULL)
+ {
+ openuri = gxdp_open_uri_proxy_new_sync (connection, 0,
+ "org.freedesktop.portal.Desktop",
+ "/org/freedesktop/portal/desktop",
+ NULL, &error);
+ if (openuri == NULL)
+ {
+ g_warning ("Cannot create document portal proxy: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_object_unref (connection);
+ }
+ else
+ {
+ g_warning ("Cannot connect to session bus when initializing document portal: %s",
+ error->message);
+ g_error_free (error);
+ }
+
+ g_once_init_leave (&openuri_inited, 1);
+ }
+
+ return openuri != NULL;
+}
+
+enum {
+ XDG_DESKTOP_PORTAL_SUCCESS = 0,
+ XDG_DESKTOP_PORTAL_CANCELLED = 1,
+ XDG_DESKTOP_PORTAL_FAILED = 2
+};
+
+static void
+response_received (GDBusConnection *connection,
+ const char *sender_name,
+ const char *object_path,
+ const char *interface_name,
+ const char *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ GTask *task = user_data;
+ guint32 response;
+ guint signal_id;
+
+ signal_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (task), "signal-id"));
+ g_dbus_connection_signal_unsubscribe (connection, signal_id);
+
+ g_variant_get (parameters, "(u@a{sv})", &response, NULL);
+
+ switch (response)
+ {
+ case XDG_DESKTOP_PORTAL_SUCCESS:
+ g_task_return_boolean (task, TRUE);
+ break;
+ case XDG_DESKTOP_PORTAL_CANCELLED:
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Launch cancelled");
+ break;
+ case XDG_DESKTOP_PORTAL_FAILED:
+ default:
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Launch failed");
+ break;
+ }
+
+ g_object_unref (task);
+}
+
+static void
+open_call_done (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GXdpOpenURI *portal = GXDP_OPEN_URI (source);
+ GDBusConnection *connection;
+ GTask *task = user_data;
+ GError *error = NULL;
+ gboolean open_file;
+ gboolean res;
+ char *path = NULL;
+ const char *handle;
+ guint signal_id;
+
+ connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (portal));
+ open_file = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (task), "open-file"));
+
+ if (open_file)
+ res = gxdp_open_uri_call_open_file_finish (portal, &path, NULL, result, &error);
+ else
+ res = gxdp_open_uri_call_open_uri_finish (portal, &path, result, &error);
+
+ if (!res)
+ {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ g_free (path);
+ return;
+ }
+
+ handle = (const char *)g_object_get_data (G_OBJECT (task), "handle");
+ if (g_strcmp0 (handle, path) != 0)
+ {
+ signal_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (task), "signal-id"));
+ g_dbus_connection_signal_unsubscribe (connection, signal_id);
+
+ signal_id = g_dbus_connection_signal_subscribe (connection,
+ "org.freedesktop.portal.Desktop",
+ "org.freedesktop.portal.Request",
+ "Response",
+ path,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
+ response_received,
+ task,
+ NULL);
+ g_object_set_data (G_OBJECT (task), "signal-id", GINT_TO_POINTER (signal_id));
+ }
+}
+
+static void
+open_uri (GFile *file,
+ const char *parent_window,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ GVariant *opts = NULL;
+ int i;
+ guint signal_id;
+
+ if (callback)
+ {
+ GDBusConnection *connection;
+ GVariantBuilder opt_builder;
+ char *token;
+ char *sender;
+ char *handle;
+
+ connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (openuri));
+
+ task = g_task_new (NULL, cancellable, callback, user_data);
+
+ token = g_strdup_printf ("gtk%d", g_random_int_range (0, G_MAXINT));
+ sender = g_strdup (g_dbus_connection_get_unique_name (connection) + 1);
+ for (i = 0; sender[i]; i++)
+ if (sender[i] == '.')
+ sender[i] = '_';
+
+ handle = g_strdup_printf ("/org/freedesktop/portal/desktop/request/%s/%s", sender, token);
+ g_object_set_data_full (G_OBJECT (task), "handle", handle, g_free);
+ g_free (sender);
+
+ signal_id = g_dbus_connection_signal_subscribe (connection,
+ "org.freedesktop.portal.Desktop",
+ "org.freedesktop.portal.Request",
+ "Response",
+ handle,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
+ response_received,
+ task,
+ NULL);
+ g_object_set_data (G_OBJECT (task), "signal-id", GINT_TO_POINTER (signal_id));
+
+ g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add (&opt_builder, "{sv}", "handle_token", g_variant_new_string (token));
+ g_free (token);
+
+ opts = g_variant_builder_end (&opt_builder);
+ }
+ else
+ task = NULL;
+
+ if (g_file_is_native (file))
+ {
+ const char *path = NULL;
+ GUnixFDList *fd_list = NULL;
+ int fd, fd_id, errsv;
+
+ if (task)
+ g_object_set_data (G_OBJECT (task), "open-file", GINT_TO_POINTER (TRUE));
+
+ path = g_file_peek_path (file);
+ fd = g_open (path, O_RDONLY | O_CLOEXEC);
+ errsv = errno;
+ if (fd == -1)
+ {
+ g_task_report_new_error (NULL, callback, user_data, NULL,
+ G_IO_ERROR, g_io_error_from_errno (errsv),
+ "OpenURI portal is not available");
+ return;
+ }
+
+#ifndef HAVE_O_CLOEXEC
+ fcntl (fd, F_SETFD, FD_CLOEXEC);
+#endif
+ fd_list = g_unix_fd_list_new_from_array (&fd, 1);
+ fd = -1;
+ fd_id = 0;
+
+ gxdp_open_uri_call_open_file (openuri,
+ parent_window ? parent_window : "",
+ g_variant_new ("h", fd_id),
+ opts,
+ fd_list,
+ cancellable,
+ task ? open_call_done : NULL,
+ task);
+ g_object_unref (fd_list);
+ }
+ else
+ {
+ char *uri = g_file_get_uri (file);
+
+ gxdp_open_uri_call_open_uri (openuri,
+ parent_window ? parent_window : "",
+ uri,
+ opts,
+ cancellable,
+ task ? open_call_done : NULL,
+ task);
+
+ g_free (uri);
+ }
+}
+
+typedef struct {
+ GtkWindow *parent;
+ GFile *file;
+ GTask *task;
+} OpenUriData;
+
+static void
+open_uri_data_free (OpenUriData *data)
+{
+ if (data->parent)
+ gtk_window_unexport_handle (data->parent);
+ g_clear_object (&data->parent);
+ g_clear_object (&data->file);
+ g_clear_object (&data->task);
+ g_free (data);
+}
+
+static void
+open_uri_done (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ OpenUriData *data = user_data;
+ GError *error = NULL;
+
+ if (!g_task_propagate_boolean (G_TASK (result), &error))
+ g_task_return_error (data->task, error);
+ else
+ g_task_return_boolean (data->task, TRUE);
+
+ open_uri_data_free (data);
+}
+
+static void
+window_handle_exported (GtkWindow *window,
+ const char *handle,
+ gpointer user_data)
+{
+ OpenUriData *data = user_data;
+
+ open_uri (data->file, handle, g_task_get_cancellable (data->task), open_uri_done, data);
+}
+
+void
+g_openuri_portal_open_async (GFile *file,
+ GtkWindow *parent,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ OpenUriData *data;
+
+ if (!init_openuri_portal ())
+ {
+ g_task_report_new_error (NULL, callback, user_data, NULL,
+ G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
+ "OpenURI portal is not available");
+ return;
+ }
+
+ data = g_new0 (OpenUriData, 1);
+ data->parent = parent ? g_object_ref (parent) : NULL;
+ data->file = g_object_ref (file);
+ data->task = g_task_new (parent, cancellable, callback, user_data);
+ g_task_set_source_tag (data->task, g_openuri_portal_open_async);
+
+ if (!parent || !gtk_window_export_handle (parent, window_handle_exported, data))
+ window_handle_exported (parent, NULL, data);
+}
+
+gboolean
+g_openuri_portal_open_finish (GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
--- /dev/null
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2017 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * 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.1 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 __G_OPEN_URI_PORTAL_H__
+
+#include "gtkwindow.h"
+#include <glib.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+void g_openuri_portal_open_async (GFile *file,
+ GtkWindow *window,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean g_openuri_portal_open_finish (GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif
#include "gtkshortcut.h"
#include "gtkstringlist.h"
+#ifndef G_OS_WIN32
+#include "gopenuriportal.h"
+#endif
+
#include <cairo-gobject.h>
#ifdef HAVE_UNISTD_H
g_slist_free_full (files, g_object_unref);
}
+#define FILE_MANAGER_DBUS_NAME "org.freedesktop.FileManager1"
+#define FILE_MANAGER_DBUS_IFACE "org.freedesktop.FileManager1"
+#define FILE_MANAGER_DBUS_PATH "/org/freedesktop/FileManager1"
+
/* Callback used when the "Open this folder" menu item is activated */
static void
open_folder_cb (GSimpleAction *action,
gpointer data)
{
GtkFileChooserWidget *impl = data;
- GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (impl)));
+ GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (impl));
+ GtkWindow *toplevel = GTK_IS_WINDOW (root) ? GTK_WINDOW (root) : NULL;
GSList *files;
+ GFile *file;
+ char *uri;
files = get_selected_files (impl);
+ if (!files)
+ return;
+
/* Sigh, just use the first one */
- if (files && GTK_IS_WINDOW (toplevel))
+ file = files->data;
+
+#ifdef G_OS_WIN32
+
+ uri = g_file_get_uri (file);
+ gtk_show_uri (toplevel, uri, GDK_CURRENT_TIME);
+ g_free (uri);
+
+#else
+
+ if (gdk_should_use_portal ())
{
- GFile *file = files->data;
- char *uri;
+ g_openuri_portal_open_async (file, toplevel, NULL, NULL, NULL);
+ }
+ else
+ {
+ GDBusConnection *bus;
+ GVariantBuilder *uris_builder;
+ GVariant *result;
+ GError *error = NULL;
uri = g_file_get_uri (file);
- gtk_show_uri (GTK_WINDOW (toplevel), uri, GDK_CURRENT_TIME);
+
+ bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+
+ uris_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
+ g_variant_builder_add (uris_builder, "s", uri);
+
+ result = g_dbus_connection_call_sync (bus,
+ FILE_MANAGER_DBUS_NAME,
+ FILE_MANAGER_DBUS_PATH,
+ FILE_MANAGER_DBUS_IFACE,
+ "ShowFolders",
+ g_variant_new ("(ass)", uris_builder, ""),
+ NULL, /* ignore returned type */
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (error)
+ {
+ if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER) ||
+ g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN))
+ g_debug ("No " FILE_MANAGER_DBUS_NAME " available");
+ else
+ g_warning ("Failed to call ShowFolders: %s", error->message);
+
+ g_error_free (error);
+ }
+
+ if (result)
+ g_variant_unref (result);
+ else
+ gtk_show_uri (toplevel, uri, GDK_CURRENT_TIME);
+
g_free (uri);
}
+#endif
+
g_slist_free_full (files, g_object_unref);
}
gtk_sources += ['gtkmountoperation-stub.c', ]
endif
+if not os_win32
+ gtk_sources += ['gopenuriportal.c', ]
+endif
+
gen_gtk_gresources_xml = find_program('gen-gtk-gresources-xml.py')
gtk_gresources_xml = configure_file(output: 'gtk.gresources.xml',
command: [
install: false,
)
+if os_win32
+ xdp_dbus_generated = []
+else
+ xdp_dbus_generated = gnome.gdbus_codegen('xdp-dbus',
+ sources : 'org.freedesktop.portal.OpenURI.xml',
+ interface_prefix : 'org.freedesktop.portal.',
+ namespace : 'GXdp',
+ )
+endif
+
gtkversion_cdata = configuration_data()
gtkversion_cdata.set('GTK_MAJOR_VERSION', gtk_major_version)
gtkversion_cdata.set('GTK_MINOR_VERSION', gtk_minor_version)
# Library
libgtk_static = static_library('gtk',
- sources: [typefuncs, gtk_sources, gtkmarshal_h, gtkprivatetypebuiltins_h],
+ sources: [typefuncs, gtk_sources, gtkmarshal_h, gtkprivatetypebuiltins_h, xdp_dbus_generated],
c_args: gtk_cargs + common_cflags,
include_directories: [confinc, gdkinc, gskinc, gtkinc],
dependencies: gtk_deps + [libgtk_css_dep, libgdk_dep, libgsk_dep],
--- /dev/null
+<?xml version="1.0"?>
+<!--
+ Copyright (C) 2016 Red Hat, Inc.
+
+ 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/>.
+
+ Author: Matthias Clasen <mclasen@redhat.com>
+-->
+
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+ <!--
+ org.freedesktop.portal.OpenURI:
+ @short_description: Portal for opening URIs
+
+ The OpenURI portal allows sandboxed applications to open
+ URIs (e.g. a http: link to the applications homepage)
+ under the control of the user.
+
+ This documentation describes version 3 of this interface.
+ -->
+ <interface name="org.freedesktop.portal.OpenURI">
+ <!--
+ OpenURI:
+ @parent_window: Identifier for the application window, see <link linkend="parent_window">Common Conventions</link>
+ @uri: The uri to open
+ @options: Vardict with optional further onformation
+ @handle: Object path for the #org.freedesktop.portal.Request object representing this call
+
+ Asks to open a uri.
+
+ Note that file:// uris are explicitly not supported by this method.
+ To request opening local files, use org.freedesktop.portal.OpenURI.OpenFile().
+
+ Supported keys in the @options vardict include:
+ <variablelist>
+ <varlistentry>
+ <term>handle_token s</term>
+ <listitem><para>
+ A string that will be used as the last element of the @handle. Must be a valid
+ object path element. See the #org.freedesktop.portal.Request documentation for
+ more information about the @handle.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>writable b</term>
+ <listitem><para>
+ Whether to allow the chosen application to write to the file.
+ </para><para>
+ This key only takes effect the uri points to a local file that
+ is exported in the document portal, and the chosen application
+ is sandboxed itself.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ask b</term>
+ <listitem><para>
+ Whether to ask the user to choose an app. If this is not passed, or false,
+ the portal may use a default or pick the last choice.
+ </para><para>
+ The ask option was introduced in version 3 of the interface.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ -->
+ <method name="OpenURI">
+ <arg type="s" name="parent_window" direction="in"/>
+ <arg type="s" name="uri" direction="in"/>
+ <arg type="a{sv}" name="options" direction="in"/>
+ <arg type="o" name="handle" direction="out"/>
+ </method>
+
+ <!--
+ OpenFile:
+ @parent_window: Identifier for the application window, see <link linkend="parent_window">Common Conventions</link>
+ @fd: File descriptor for the file to open
+ @options: Vardict with optional further onformation
+ @handle: Object path for the #org.freedesktop.portal.Request object representing this call
+
+ Asks to open a local file.
+
+ Supported keys in the @options vardict include:
+ <variablelist>
+ <varlistentry>
+ <term>handle_token s</term>
+ <listitem><para>
+ A string that will be used as the last element of the @handle. Must be a valid
+ object path element. See the #org.freedesktop.portal.Request documentation for
+ more information about the @handle.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>writable b</term>
+ <listitem><para>
+ Whether to allow the chosen application to write to the file.
+ </para><para>
+ This key only takes effect the uri points to a local file that
+ is exported in the document portal, and the chosen application
+ is sandboxed itself.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ask b</term>
+ <listitem><para>
+ Whether to ask the user to choose an app. If this is not passed, or false,
+ the portal may use a default or pick the last choice.
+ </para><para>
+ The ask option was introduced in version 3 of the interface.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ The OpenFile method was introduced in version 2 of the OpenURI portal API.
+ -->
+ <method name="OpenFile">
+ <annotation name="org.gtk.GDBus.C.UnixFD" value="true"/>
+ <arg type="s" name="parent_window" direction="in"/>
+ <arg type="h" name="fd" direction="in"/>
+ <arg type="a{sv}" name="options" direction="in"/>
+ <arg type="o" name="handle" direction="out"/>
+ </method>
+
+ <!--
+ OpenDirectory:
+ @parent_window: Identifier for the application window, see <link linkend="parent_window">Common Conventions</link>
+ @fd: File descriptor for a file
+ @options: Vardict with optional further onformation
+ @handle: Object path for the #org.freedesktop.portal.Request object representing this call
+
+ Asks to open the directory containing a local file in the file browser.
+
+ Supported keys in the @options vardict include:
+ <variablelist>
+ <varlistentry>
+ <term>handle_token s</term>
+ <listitem><para>
+ A string that will be used as the last element of the @handle. Must be a valid
+ object path element. See the #org.freedesktop.portal.Request documentation for
+ more information about the @handle.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ The OpenDirectory method was introduced in version 3 of the OpenURI portal API.
+ -->
+ <method name="OpenDirectory">
+ <annotation name="org.gtk.GDBus.C.UnixFD" value="true"/>
+ <arg type="s" name="parent_window" direction="in"/>
+ <arg type="h" name="fd" direction="in"/>
+ <arg type="a{sv}" name="options" direction="in"/>
+ <arg type="o" name="handle" direction="out"/>
+ </method>
+
+ <property name="version" type="u" access="read"/>
+ </interface>
+</node>