2024-09-24 14:12:15 +02:00
|
|
|
|
/*
|
|
|
|
|
* Copyright © 2024 GNOME Foundation 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/>.
|
|
|
|
|
*
|
|
|
|
|
* Authors: Julian Sparber <jsparber@gnome.org>
|
|
|
|
|
* Philip Withnall <philip@tecnocode.co.uk>
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* A stub implementation of xdg-desktop-portal */
|
|
|
|
|
|
2024-11-13 14:29:01 +01:00
|
|
|
|
#ifdef __FreeBSD__
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/user.h>
|
|
|
|
|
#endif /* __FreeBSD__ */
|
|
|
|
|
|
2024-09-24 14:12:15 +02:00
|
|
|
|
#include <glib.h>
|
|
|
|
|
#include <gio/gio.h>
|
|
|
|
|
#include <gio/gunixfdlist.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "fake-desktop-portal.h"
|
|
|
|
|
#include "fake-openuri-portal-generated.h"
|
|
|
|
|
#include "fake-request-portal-generated.h"
|
|
|
|
|
|
|
|
|
|
struct _GFakeDesktopPortalThread
|
|
|
|
|
{
|
|
|
|
|
GObject parent_instance;
|
|
|
|
|
|
|
|
|
|
char *address; /* (not nullable) */
|
|
|
|
|
GCancellable *cancellable; /* (not nullable) (owned) */
|
|
|
|
|
GThread *thread; /* (not nullable) (owned) */
|
|
|
|
|
GCond cond; /* (mutex mutex) */
|
|
|
|
|
GMutex mutex;
|
|
|
|
|
gboolean ready; /* (mutex mutex) */
|
|
|
|
|
|
|
|
|
|
char *request_activation_token; /* (mutex mutex) */
|
|
|
|
|
char *request_uri; /* (mutex mutex) */
|
|
|
|
|
} FakeDesktopPortalThread;
|
|
|
|
|
|
|
|
|
|
G_DEFINE_FINAL_TYPE (GFakeDesktopPortalThread, g_fake_desktop_portal_thread, G_TYPE_OBJECT)
|
|
|
|
|
|
|
|
|
|
static void g_fake_desktop_portal_thread_finalize (GObject *object);
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
g_fake_desktop_portal_thread_class_init (GFakeDesktopPortalThreadClass *klass)
|
|
|
|
|
{
|
|
|
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
|
|
|
|
|
gobject_class->finalize = g_fake_desktop_portal_thread_finalize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
g_fake_desktop_portal_thread_init (GFakeDesktopPortalThread *self)
|
|
|
|
|
{
|
|
|
|
|
self->cancellable = g_cancellable_new ();
|
|
|
|
|
g_cond_init (&self->cond);
|
|
|
|
|
g_mutex_init (&self->mutex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
g_fake_desktop_portal_thread_finalize (GObject *object)
|
|
|
|
|
{
|
|
|
|
|
GFakeDesktopPortalThread *self = G_FAKE_DESKTOP_PORTAL_THREAD (object);
|
|
|
|
|
|
|
|
|
|
g_assert (self->thread == NULL); /* should already have been joined */
|
|
|
|
|
|
|
|
|
|
g_mutex_clear (&self->mutex);
|
|
|
|
|
g_cond_clear (&self->cond);
|
|
|
|
|
g_clear_object (&self->cancellable);
|
|
|
|
|
g_clear_pointer (&self->address, g_free);
|
|
|
|
|
|
|
|
|
|
g_clear_pointer (&self->request_activation_token, g_free);
|
|
|
|
|
g_clear_pointer (&self->request_uri, g_free);
|
|
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (g_fake_desktop_portal_thread_parent_class)->finalize (object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
on_handle_close (FakeRequest *object,
|
|
|
|
|
GDBusMethodInvocation *invocation,
|
|
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
g_test_message ("Got close request");
|
|
|
|
|
fake_request_complete_close (object, invocation);
|
|
|
|
|
|
|
|
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char*
|
|
|
|
|
get_request_path (GDBusMethodInvocation *invocation,
|
|
|
|
|
const char *token)
|
|
|
|
|
{
|
|
|
|
|
char *request_obj_path;
|
|
|
|
|
char *sender;
|
|
|
|
|
|
|
|
|
|
sender = g_strdup (g_dbus_method_invocation_get_sender (invocation) + 1);
|
|
|
|
|
|
|
|
|
|
/* The object path needs to be the specific format.
|
|
|
|
|
* See: https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Request.html#org-freedesktop-portal-request */
|
|
|
|
|
for (size_t i = 0; sender[i]; i++)
|
|
|
|
|
if (sender[i] == '.')
|
|
|
|
|
sender[i] = '_';
|
|
|
|
|
|
|
|
|
|
request_obj_path = g_strdup_printf ("/org/freedesktop/portal/desktop/request/%s/%s", sender, token);
|
|
|
|
|
g_free (sender);
|
|
|
|
|
|
|
|
|
|
return request_obj_path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
handle_request (GFakeDesktopPortalThread *self,
|
|
|
|
|
FakeOpenURI *object,
|
|
|
|
|
GDBusMethodInvocation *invocation,
|
|
|
|
|
const gchar *arg_parent_window,
|
|
|
|
|
const gchar *arg_uri,
|
|
|
|
|
gboolean open_file,
|
|
|
|
|
GVariant *arg_options)
|
|
|
|
|
{
|
|
|
|
|
const char *activation_token = NULL;
|
|
|
|
|
GError *error = NULL;
|
|
|
|
|
FakeRequest *interface_request;
|
|
|
|
|
GVariantBuilder opt_builder;
|
|
|
|
|
char *request_obj_path;
|
|
|
|
|
const char *token = NULL;
|
|
|
|
|
|
|
|
|
|
if (arg_options)
|
|
|
|
|
{
|
|
|
|
|
g_variant_lookup (arg_options, "activation_token", "&s", &activation_token);
|
|
|
|
|
g_variant_lookup (arg_options, "handle_token", "&s", &token);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_set_str (&self->request_activation_token, activation_token);
|
|
|
|
|
g_set_str (&self->request_uri, arg_uri);
|
|
|
|
|
|
|
|
|
|
request_obj_path = get_request_path (invocation, token ? token : "t");
|
|
|
|
|
|
|
|
|
|
if (open_file)
|
|
|
|
|
{
|
|
|
|
|
g_test_message ("Got open file request for %s", arg_uri);
|
|
|
|
|
|
|
|
|
|
fake_open_uri_complete_open_file (object,
|
|
|
|
|
invocation,
|
|
|
|
|
NULL,
|
|
|
|
|
request_obj_path);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
g_test_message ("Got open URI request for %s", arg_uri);
|
|
|
|
|
|
|
|
|
|
fake_open_uri_complete_open_uri (object,
|
|
|
|
|
invocation,
|
|
|
|
|
request_obj_path);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface_request = fake_request_skeleton_new ();
|
|
|
|
|
g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
|
|
|
|
|
|
|
|
|
|
g_signal_connect (interface_request,
|
|
|
|
|
"handle-close",
|
|
|
|
|
G_CALLBACK (on_handle_close),
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (interface_request),
|
|
|
|
|
g_dbus_method_invocation_get_connection (invocation),
|
|
|
|
|
request_obj_path,
|
|
|
|
|
&error);
|
|
|
|
|
g_assert_no_error (error);
|
|
|
|
|
g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (interface_request),
|
|
|
|
|
G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
|
|
|
|
|
g_test_message ("Request skeleton exported at %s", request_obj_path);
|
|
|
|
|
|
|
|
|
|
/* We can't use `fake_request_emit_response()` because it doesn't set the sender */
|
|
|
|
|
g_dbus_connection_emit_signal (g_dbus_method_invocation_get_connection (invocation),
|
|
|
|
|
g_dbus_method_invocation_get_sender (invocation),
|
|
|
|
|
request_obj_path,
|
|
|
|
|
"org.freedesktop.portal.Request",
|
|
|
|
|
"Response",
|
|
|
|
|
g_variant_new ("(u@a{sv})",
|
|
|
|
|
0, /* Success */
|
|
|
|
|
g_variant_builder_end (&opt_builder)),
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
g_test_message ("Response emitted");
|
|
|
|
|
|
|
|
|
|
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (interface_request));
|
|
|
|
|
g_free (request_obj_path);
|
|
|
|
|
g_object_unref (interface_request);
|
|
|
|
|
|
|
|
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-13 14:21:44 +01:00
|
|
|
|
/* This is currently private as there’s only one user of it, but it could become
|
|
|
|
|
* a public API in future. */
|
|
|
|
|
static char *
|
|
|
|
|
_g_fd_query_path (int fd,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
#if defined(__FreeBSD__)
|
|
|
|
|
struct kinfo_file kf;
|
|
|
|
|
kf.kf_structsize = sizeof (kf);
|
|
|
|
|
if (fcntl (fd, F_KINFO, &kf) < 0)
|
|
|
|
|
{
|
|
|
|
|
int saved_errno = errno;
|
|
|
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
|
|
|
"Error querying file information for FD %d: %s",
|
|
|
|
|
fd, g_strerror (saved_errno));
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return g_strdup (kf.kf_path);
|
|
|
|
|
#elif defined(G_OS_UNIX)
|
|
|
|
|
char *path = NULL;
|
|
|
|
|
char *proc_path = g_strdup_printf ("/proc/self/fd/%d", fd);
|
|
|
|
|
path = g_file_read_link (proc_path, error);
|
|
|
|
|
g_free (proc_path);
|
|
|
|
|
return g_steal_pointer (&path);
|
|
|
|
|
#else
|
|
|
|
|
/* - A NetBSD implementation would probably use `fcntl()` with `F_GETPATH`:
|
|
|
|
|
* https://man.netbsd.org/fcntl.2
|
|
|
|
|
* - A Windows implementation would probably use `GetFinalPathNameByHandleW()`:
|
|
|
|
|
* https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlea
|
|
|
|
|
* - A Hurd implementation could open("/dev/fd/%u"):
|
|
|
|
|
* https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4396#note_2279923
|
|
|
|
|
*/
|
|
|
|
|
#error "_g_fd_query_path() not supported on this platform"
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-24 14:12:15 +02:00
|
|
|
|
static char *
|
|
|
|
|
handle_to_uri (GVariant *handle,
|
|
|
|
|
GUnixFDList *fd_list)
|
|
|
|
|
{
|
|
|
|
|
int fd = -1;
|
|
|
|
|
int fd_id;
|
|
|
|
|
char *path;
|
|
|
|
|
char *uri;
|
2024-11-13 14:21:44 +01:00
|
|
|
|
GError *local_error = NULL;
|
2024-09-24 14:12:15 +02:00
|
|
|
|
|
|
|
|
|
fd_id = g_variant_get_handle (handle);
|
|
|
|
|
fd = g_unix_fd_list_get (fd_list, fd_id, NULL);
|
|
|
|
|
|
|
|
|
|
if (fd == -1)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2024-11-13 14:21:44 +01:00
|
|
|
|
path = _g_fd_query_path (fd, &local_error);
|
|
|
|
|
g_assert_no_error (local_error);
|
2024-09-24 14:12:15 +02:00
|
|
|
|
|
|
|
|
|
uri = g_filename_to_uri (path, NULL, NULL);
|
|
|
|
|
g_free (path);
|
|
|
|
|
close (fd);
|
|
|
|
|
|
|
|
|
|
return uri;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
on_handle_open_file (FakeOpenURI *object,
|
|
|
|
|
GDBusMethodInvocation *invocation,
|
|
|
|
|
GUnixFDList *fd_list,
|
|
|
|
|
const gchar *arg_parent_window,
|
|
|
|
|
GVariant *arg_fd,
|
|
|
|
|
GVariant *arg_options,
|
|
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
GFakeDesktopPortalThread *self = G_FAKE_DESKTOP_PORTAL_THREAD (user_data);
|
|
|
|
|
char *uri = NULL;
|
|
|
|
|
|
|
|
|
|
uri = handle_to_uri (arg_fd, fd_list);
|
|
|
|
|
handle_request (self,
|
|
|
|
|
object,
|
|
|
|
|
invocation,
|
|
|
|
|
arg_parent_window,
|
|
|
|
|
uri,
|
|
|
|
|
TRUE,
|
|
|
|
|
arg_options);
|
|
|
|
|
g_free (uri);
|
|
|
|
|
|
|
|
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
on_handle_open_uri (FakeOpenURI *object,
|
|
|
|
|
GDBusMethodInvocation *invocation,
|
|
|
|
|
const gchar *arg_parent_window,
|
|
|
|
|
const gchar *arg_uri,
|
|
|
|
|
GVariant *arg_options,
|
|
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
GFakeDesktopPortalThread *self = G_FAKE_DESKTOP_PORTAL_THREAD (user_data);
|
|
|
|
|
|
|
|
|
|
handle_request (self,
|
|
|
|
|
object,
|
|
|
|
|
invocation,
|
|
|
|
|
arg_parent_window,
|
|
|
|
|
arg_uri,
|
|
|
|
|
TRUE,
|
|
|
|
|
arg_options);
|
|
|
|
|
|
|
|
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
on_name_acquired (GDBusConnection *connection,
|
|
|
|
|
const gchar *name,
|
|
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
GFakeDesktopPortalThread *self = G_FAKE_DESKTOP_PORTAL_THREAD (user_data);
|
|
|
|
|
|
|
|
|
|
g_test_message ("Acquired the name %s", name);
|
|
|
|
|
|
|
|
|
|
g_mutex_lock (&self->mutex);
|
|
|
|
|
self->ready = TRUE;
|
|
|
|
|
g_cond_signal (&self->cond);
|
|
|
|
|
g_mutex_unlock (&self->mutex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
on_name_lost (GDBusConnection *connection,
|
|
|
|
|
const gchar *name,
|
|
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
g_test_message ("Lost the name %s", name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
cancelled_cb (GCancellable *cancellable,
|
|
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
g_test_message ("fake-desktop-portal cancelled");
|
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gpointer
|
|
|
|
|
fake_desktop_portal_thread_cb (gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
GFakeDesktopPortalThread *self = G_FAKE_DESKTOP_PORTAL_THREAD (user_data);
|
|
|
|
|
GMainContext *context = NULL;
|
|
|
|
|
GDBusConnection *connection = NULL;
|
|
|
|
|
GSource *source = NULL;
|
|
|
|
|
guint id;
|
|
|
|
|
FakeOpenURI *interface_open_uri;
|
|
|
|
|
GError *local_error = NULL;
|
|
|
|
|
|
|
|
|
|
context = g_main_context_new ();
|
|
|
|
|
g_main_context_push_thread_default (context);
|
|
|
|
|
|
|
|
|
|
connection = g_dbus_connection_new_for_address_sync (self->address,
|
|
|
|
|
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
|
|
|
|
|
G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
|
|
|
|
|
NULL,
|
|
|
|
|
self->cancellable,
|
|
|
|
|
&local_error);
|
|
|
|
|
g_assert_no_error (local_error);
|
|
|
|
|
|
|
|
|
|
/* Listen for cancellation. The source will wake up the context iteration
|
|
|
|
|
* which can then re-check its exit condition below. */
|
|
|
|
|
source = g_cancellable_source_new (self->cancellable);
|
|
|
|
|
g_source_set_callback (source, G_SOURCE_FUNC (cancelled_cb), NULL, NULL);
|
|
|
|
|
g_source_attach (source, context);
|
|
|
|
|
g_source_unref (source);
|
|
|
|
|
|
|
|
|
|
/* Set up the interface */
|
|
|
|
|
g_test_message ("Acquired a message bus connection");
|
|
|
|
|
|
|
|
|
|
interface_open_uri = fake_open_uri_skeleton_new ();
|
|
|
|
|
|
|
|
|
|
g_signal_connect (interface_open_uri,
|
|
|
|
|
"handle-open-file",
|
|
|
|
|
G_CALLBACK (on_handle_open_file),
|
|
|
|
|
self);
|
|
|
|
|
g_signal_connect (interface_open_uri,
|
|
|
|
|
"handle-open-uri",
|
|
|
|
|
G_CALLBACK (on_handle_open_uri),
|
|
|
|
|
self);
|
|
|
|
|
|
|
|
|
|
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (interface_open_uri),
|
|
|
|
|
connection,
|
|
|
|
|
"/org/freedesktop/portal/desktop",
|
|
|
|
|
&local_error);
|
|
|
|
|
g_assert_no_error (local_error);
|
|
|
|
|
|
|
|
|
|
/* Own the portal name */
|
|
|
|
|
id = g_bus_own_name_on_connection (connection,
|
|
|
|
|
"org.freedesktop.portal.Desktop",
|
|
|
|
|
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
|
|
|
|
|
G_BUS_NAME_OWNER_FLAGS_REPLACE,
|
|
|
|
|
on_name_acquired,
|
|
|
|
|
on_name_lost,
|
|
|
|
|
self,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
while (!g_cancellable_is_cancelled (self->cancellable))
|
|
|
|
|
g_main_context_iteration (context, TRUE);
|
|
|
|
|
|
|
|
|
|
g_bus_unown_name (id);
|
|
|
|
|
g_clear_object (&connection);
|
|
|
|
|
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (interface_open_uri));
|
|
|
|
|
g_object_unref (interface_open_uri);
|
|
|
|
|
g_main_context_pop_thread_default (context);
|
|
|
|
|
g_clear_pointer (&context, g_main_context_unref);
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the activation token given to the most recent OpenURI request
|
|
|
|
|
*
|
|
|
|
|
* Returns: (transfer none) (nullable: an activation token
|
|
|
|
|
*/
|
|
|
|
|
const gchar *
|
|
|
|
|
g_fake_desktop_portal_thread_get_last_request_activation_token (GFakeDesktopPortalThread *self)
|
|
|
|
|
{
|
|
|
|
|
g_return_val_if_fail (G_IS_FAKE_DESKTOP_PORTAL_THREAD (self), NULL);
|
|
|
|
|
|
|
|
|
|
return self->request_activation_token;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the file or URI given to the most recent OpenURI request
|
|
|
|
|
*
|
|
|
|
|
* Returns: (transfer none) (nullable): an URI
|
|
|
|
|
*/
|
|
|
|
|
const gchar *
|
|
|
|
|
g_fake_desktop_portal_thread_get_last_request_uri (GFakeDesktopPortalThread *self)
|
|
|
|
|
{
|
|
|
|
|
g_return_val_if_fail (G_IS_FAKE_DESKTOP_PORTAL_THREAD (self), NULL);
|
|
|
|
|
|
|
|
|
|
return self->request_uri;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Create a new #GFakeDesktopPortalThread. The thread isn’t started until
|
|
|
|
|
* g_fake_desktop_portal_thread_run() is called on it.
|
|
|
|
|
*
|
|
|
|
|
* Returns: (transfer full): the new fake desktop portal wrapper
|
|
|
|
|
*/
|
|
|
|
|
GFakeDesktopPortalThread *
|
|
|
|
|
g_fake_desktop_portal_thread_new (const char *address)
|
|
|
|
|
{
|
|
|
|
|
GFakeDesktopPortalThread *self = g_object_new (G_TYPE_FAKE_DESKTOP_PORTAL_THREAD, NULL);
|
|
|
|
|
self->address = g_strdup (address);
|
|
|
|
|
return g_steal_pointer (&self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Start a worker thread which will run a fake
|
|
|
|
|
* `org.freedesktop.portal.Desktops` portal on the bus at @address. This is
|
|
|
|
|
* intended to be used with #GTestDBus to mock up a portal from within a unit
|
|
|
|
|
* test process, rather than relying on D-Bus activation of a mock portal
|
|
|
|
|
* subprocess.
|
|
|
|
|
*
|
|
|
|
|
* It blocks until the thread has owned its D-Bus name and is ready to handle
|
|
|
|
|
* requests.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
g_fake_desktop_portal_thread_run (GFakeDesktopPortalThread *self)
|
|
|
|
|
{
|
|
|
|
|
g_return_if_fail (G_IS_FAKE_DESKTOP_PORTAL_THREAD (self));
|
|
|
|
|
g_return_if_fail (self->thread == NULL);
|
|
|
|
|
|
|
|
|
|
self->thread = g_thread_new ("fake-desktop-portal", fake_desktop_portal_thread_cb, self);
|
|
|
|
|
|
|
|
|
|
/* Block until the thread is ready. */
|
|
|
|
|
g_mutex_lock (&self->mutex);
|
|
|
|
|
while (!self->ready)
|
|
|
|
|
g_cond_wait (&self->cond, &self->mutex);
|
|
|
|
|
g_mutex_unlock (&self->mutex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Stop and join a worker thread started with fake_desktop_portal_thread_run().
|
|
|
|
|
* Blocks until the thread has stopped and joined.
|
|
|
|
|
*
|
|
|
|
|
* Once this has been called, it’s safe to drop the final reference on @self. */
|
|
|
|
|
void
|
|
|
|
|
g_fake_desktop_portal_thread_stop (GFakeDesktopPortalThread *self)
|
|
|
|
|
{
|
|
|
|
|
g_return_if_fail (G_IS_FAKE_DESKTOP_PORTAL_THREAD (self));
|
|
|
|
|
g_return_if_fail (self->thread != NULL);
|
|
|
|
|
|
|
|
|
|
g_cancellable_cancel (self->cancellable);
|
|
|
|
|
g_thread_join (g_steal_pointer (&self->thread));
|
|
|
|
|
}
|
2024-12-10 12:47:43 +01:00
|
|
|
|
|
|
|
|
|
/* Whether fake-desktop-portal is supported on this platform. This basically
|
|
|
|
|
* means whether _g_fd_query_path() will work at runtime. */
|
|
|
|
|
gboolean
|
|
|
|
|
g_fake_desktop_portal_is_supported (void)
|
|
|
|
|
{
|
|
|
|
|
#ifdef __GNU__
|
|
|
|
|
return FALSE;
|
|
|
|
|
#else
|
|
|
|
|
return TRUE;
|
|
|
|
|
#endif
|
|
|
|
|
}
|