forked from AuroraMiddleware/gtk
Merge branch 'file-transfer' into 'master'
clipboard: file transfer portal support See merge request GNOME/gtk!1244
This commit is contained in:
commit
bbb56d8216
341
gdk/filetransferportal.c
Normal file
341
gdk/filetransferportal.c
Normal file
@ -0,0 +1,341 @@
|
||||
/*
|
||||
* 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>
|
||||
#include <gio/gunixfdlist.h>
|
||||
|
||||
#include "filetransferportalprivate.h"
|
||||
|
||||
static GDBusProxy *file_transfer_proxy = NULL;
|
||||
|
||||
static GDBusProxy *
|
||||
ensure_file_transfer_portal (void)
|
||||
{
|
||||
if (file_transfer_proxy == NULL)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
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, &error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
g_debug ("Failed to get proxy: %s", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
}
|
||||
|
||||
if (file_transfer_proxy)
|
||||
{
|
||||
char *owner = g_dbus_proxy_get_name_owner (file_transfer_proxy);
|
||||
|
||||
if (owner)
|
||||
{
|
||||
g_free (owner);
|
||||
return file_transfer_proxy;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gboolean
|
||||
file_transfer_portal_available (void)
|
||||
{
|
||||
gboolean available;
|
||||
|
||||
ensure_file_transfer_portal ();
|
||||
|
||||
available = file_transfer_proxy != NULL;
|
||||
|
||||
g_clear_object (&file_transfer_proxy);
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GTask *task;
|
||||
const char **files;
|
||||
int len;
|
||||
int start;
|
||||
} AddFileData;
|
||||
|
||||
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);
|
||||
g_object_unref (afd->task);
|
||||
g_free (afd);
|
||||
return;
|
||||
}
|
||||
|
||||
g_variant_unref (ret);
|
||||
|
||||
if (afd->start >= afd->len)
|
||||
{
|
||||
g_task_return_boolean (afd->task, TRUE);
|
||||
g_object_unref (afd->task);
|
||||
g_free (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;
|
||||
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]);
|
||||
g_object_unref (afd->task);
|
||||
g_free (afd);
|
||||
g_object_unref (fd_list);
|
||||
return;
|
||||
}
|
||||
fd_in = g_unix_fd_list_append (fd_list, fd, &error);
|
||||
close (fd);
|
||||
|
||||
if (fd_in == -1)
|
||||
{
|
||||
g_task_return_error (afd->task, error);
|
||||
g_object_unref (afd->task);
|
||||
g_free (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_dbus_proxy_call_with_unix_fd_list (proxy,
|
||||
"AddFiles",
|
||||
g_variant_new ("(sah)", key, &fds),
|
||||
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);
|
||||
g_object_unref (afd->task);
|
||||
g_free (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;
|
||||
GDBusProxy *proxy;
|
||||
AddFileData *afd;
|
||||
GVariantBuilder options;
|
||||
|
||||
task = g_task_new (NULL, NULL, callback, data);
|
||||
|
||||
proxy = ensure_file_transfer_portal ();
|
||||
|
||||
if (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 = files;
|
||||
afd->len = g_strv_length ((char **)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 (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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
GDBusProxy *proxy;
|
||||
GTask *task;
|
||||
GVariantBuilder options;
|
||||
|
||||
task = g_task_new (NULL, NULL, callback, data);
|
||||
|
||||
proxy = ensure_file_transfer_portal ();
|
||||
|
||||
if (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 (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;
|
||||
}
|
39
gdk/filetransferportalprivate.h
Normal file
39
gdk/filetransferportalprivate.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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__
|
||||
|
||||
|
||||
gboolean file_transfer_portal_available (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);
|
||||
|
||||
|
||||
#endif
|
@ -1054,6 +1054,7 @@ gdk_clipboard_write_serialize_done (GObject *content,
|
||||
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_clipboard_write_async (GdkClipboard *clipboard,
|
||||
const char *mime_type,
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "gdkcontentdeserializer.h"
|
||||
|
||||
#include "gdkcontentformats.h"
|
||||
#include "filetransferportalprivate.h"
|
||||
#include "gdktexture.h"
|
||||
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
@ -690,6 +691,93 @@ string_deserializer (GdkContentDeserializer *deserializer)
|
||||
g_object_unref (filter);
|
||||
}
|
||||
|
||||
static void
|
||||
portal_finish (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer deserializer)
|
||||
{
|
||||
char **files = NULL;
|
||||
GError *error = NULL;
|
||||
GValue *value;
|
||||
|
||||
if (!file_transfer_portal_retrieve_files_finish (result, &files, &error))
|
||||
{
|
||||
gdk_content_deserializer_return_error (deserializer, error);
|
||||
return;
|
||||
}
|
||||
|
||||
value = gdk_content_deserializer_get_value (deserializer);
|
||||
if (G_VALUE_HOLDS (value, G_TYPE_FILE))
|
||||
{
|
||||
if (files[0] != NULL)
|
||||
g_value_take_object (value, g_file_new_for_path (files[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
GSList *l = NULL;
|
||||
gsize i;
|
||||
|
||||
for (i = 0; files[i] != NULL; i++)
|
||||
l = g_slist_prepend (l, g_file_new_for_path (files[i]));
|
||||
g_value_take_boxed (value, g_slist_reverse (l));
|
||||
}
|
||||
g_strfreev (files);
|
||||
|
||||
gdk_content_deserializer_return_success (deserializer);
|
||||
}
|
||||
|
||||
static void
|
||||
portal_file_deserializer_finish (GObject *source,
|
||||
GAsyncResult *result,
|
||||
gpointer deserializer)
|
||||
{
|
||||
GOutputStream *stream = G_OUTPUT_STREAM (source);
|
||||
GError *error = NULL;
|
||||
gssize written;
|
||||
char *key;
|
||||
|
||||
written = g_output_stream_splice_finish (stream, result, &error);
|
||||
if (written < 0)
|
||||
{
|
||||
gdk_content_deserializer_return_error (deserializer, error);
|
||||
return;
|
||||
}
|
||||
|
||||
/* write terminating NULL */
|
||||
if (!g_output_stream_write (stream, "", 1, NULL, &error))
|
||||
{
|
||||
gdk_content_deserializer_return_error (deserializer, error);
|
||||
return;
|
||||
}
|
||||
|
||||
key = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (stream));
|
||||
if (key == NULL)
|
||||
{
|
||||
deserialize_not_found (deserializer);
|
||||
return;
|
||||
}
|
||||
|
||||
file_transfer_portal_retrieve_files (key, portal_finish, deserializer);
|
||||
gdk_content_deserializer_set_task_data (deserializer, key, g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
portal_file_deserializer (GdkContentDeserializer *deserializer)
|
||||
{
|
||||
GOutputStream *output;
|
||||
|
||||
output = g_memory_output_stream_new_resizable ();
|
||||
|
||||
g_output_stream_splice_async (output,
|
||||
gdk_content_deserializer_get_input_stream (deserializer),
|
||||
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
|
||||
gdk_content_deserializer_get_priority (deserializer),
|
||||
gdk_content_deserializer_get_cancellable (deserializer),
|
||||
portal_file_deserializer_finish,
|
||||
deserializer);
|
||||
g_object_unref (output);
|
||||
}
|
||||
|
||||
static void
|
||||
file_uri_deserializer_finish (GObject *source,
|
||||
GAsyncResult *result,
|
||||
@ -764,6 +852,7 @@ init (void)
|
||||
static gboolean initialized = FALSE;
|
||||
GSList *formats, *f;
|
||||
const char *charset;
|
||||
gboolean has_portal;
|
||||
|
||||
if (initialized)
|
||||
return;
|
||||
@ -816,11 +905,24 @@ init (void)
|
||||
|
||||
g_slist_free (formats);
|
||||
|
||||
has_portal = file_transfer_portal_available ();
|
||||
if (has_portal)
|
||||
gdk_content_register_deserializer ("application/vnd.portal.files",
|
||||
GDK_TYPE_FILE_LIST,
|
||||
portal_file_deserializer,
|
||||
NULL,
|
||||
NULL);
|
||||
gdk_content_register_deserializer ("text/uri-list",
|
||||
GDK_TYPE_FILE_LIST,
|
||||
file_uri_deserializer,
|
||||
NULL,
|
||||
NULL);
|
||||
if (has_portal)
|
||||
gdk_content_register_deserializer ("application/vnd.portal.files",
|
||||
G_TYPE_FILE,
|
||||
portal_file_deserializer,
|
||||
NULL,
|
||||
NULL);
|
||||
gdk_content_register_deserializer ("text/uri-list",
|
||||
G_TYPE_FILE,
|
||||
file_uri_deserializer,
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include "gdkcontentformats.h"
|
||||
#include "gdkpixbuf.h"
|
||||
#include "filetransferportalprivate.h"
|
||||
#include "gdktextureprivate.h"
|
||||
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
@ -701,6 +702,63 @@ file_serializer_finish (GObject *source,
|
||||
gdk_content_serializer_return_success (serializer);
|
||||
}
|
||||
|
||||
static void
|
||||
portal_ready (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer serializer)
|
||||
{
|
||||
GError *error = NULL;
|
||||
char *key;
|
||||
|
||||
if (!file_transfer_portal_register_files_finish (result, &key, &error))
|
||||
{
|
||||
gdk_content_serializer_return_error (serializer, error);
|
||||
return;
|
||||
}
|
||||
|
||||
g_output_stream_write_all_async (gdk_content_serializer_get_output_stream (serializer),
|
||||
key,
|
||||
strlen (key) + 1,
|
||||
gdk_content_serializer_get_priority (serializer),
|
||||
gdk_content_serializer_get_cancellable (serializer),
|
||||
file_serializer_finish,
|
||||
serializer);
|
||||
gdk_content_serializer_set_task_data (serializer, key, g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
portal_file_serializer (GdkContentSerializer *serializer)
|
||||
{
|
||||
GFile *file;
|
||||
const GValue *value;
|
||||
GPtrArray *files;
|
||||
|
||||
files = g_ptr_array_new_with_free_func (g_free);
|
||||
|
||||
value = gdk_content_serializer_get_value (serializer);
|
||||
|
||||
if (G_VALUE_HOLDS (value, G_TYPE_FILE))
|
||||
{
|
||||
file = g_value_get_object (gdk_content_serializer_get_value (serializer));
|
||||
if (file)
|
||||
g_ptr_array_add (files, g_file_get_path (file));
|
||||
g_ptr_array_add (files, NULL);
|
||||
}
|
||||
else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
for (l = g_value_get_boxed (value); l; l = l->next)
|
||||
g_ptr_array_add (files, g_file_get_path (l->data));
|
||||
|
||||
g_ptr_array_add (files, NULL);
|
||||
}
|
||||
|
||||
/* this call doesn't copy the strings, so keep the array around until the registration is done */
|
||||
file_transfer_portal_register_files ((const char **)files->pdata, TRUE, portal_ready, serializer);
|
||||
gdk_content_serializer_set_task_data (serializer, files, (GDestroyNotify)g_ptr_array_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
file_uri_serializer (GdkContentSerializer *serializer)
|
||||
{
|
||||
@ -808,6 +866,7 @@ init (void)
|
||||
static gboolean initialized = FALSE;
|
||||
GSList *formats, *f;
|
||||
const char *charset;
|
||||
gboolean has_portal;
|
||||
|
||||
if (initialized)
|
||||
return;
|
||||
@ -863,6 +922,14 @@ init (void)
|
||||
|
||||
g_slist_free (formats);
|
||||
|
||||
has_portal = file_transfer_portal_available ();
|
||||
|
||||
if (has_portal)
|
||||
gdk_content_register_serializer (G_TYPE_FILE,
|
||||
"application/vnd.portal.files",
|
||||
portal_file_serializer,
|
||||
NULL,
|
||||
NULL);
|
||||
gdk_content_register_serializer (G_TYPE_FILE,
|
||||
"text/uri-list",
|
||||
file_uri_serializer,
|
||||
@ -873,6 +940,12 @@ init (void)
|
||||
file_text_serializer,
|
||||
NULL,
|
||||
NULL);
|
||||
if (has_portal)
|
||||
gdk_content_register_serializer (GDK_TYPE_FILE_LIST,
|
||||
"application/vnd.portal.files",
|
||||
portal_file_serializer,
|
||||
NULL,
|
||||
NULL);
|
||||
gdk_content_register_serializer (GDK_TYPE_FILE_LIST,
|
||||
"text/uri-list",
|
||||
file_uri_serializer,
|
||||
|
@ -19,6 +19,7 @@ gdk_public_sources = files([
|
||||
'gdkdrawcontext.c',
|
||||
'gdkdrop.c',
|
||||
'gdkevents.c',
|
||||
'filetransferportal.c',
|
||||
'gdkframeclock.c',
|
||||
'gdkframeclockidle.c',
|
||||
'gdkframetimings.c',
|
||||
|
@ -20,15 +20,59 @@
|
||||
|
||||
|
||||
void
|
||||
hello (void)
|
||||
copy (void)
|
||||
{
|
||||
g_print ("hello world\n");
|
||||
GdkClipboard *clipboard = gdk_display_get_clipboard (gdk_display_get_default ());
|
||||
GFile *file = g_file_new_for_path ("/home/mclasen/faw-sig");
|
||||
|
||||
gdk_clipboard_set (clipboard, G_TYPE_FILE, file);
|
||||
|
||||
g_object_unref (file);
|
||||
}
|
||||
|
||||
static void
|
||||
value_received (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer data)
|
||||
{
|
||||
const GValue *value;
|
||||
GError *error = NULL;
|
||||
GSList *l;
|
||||
|
||||
value = gdk_clipboard_read_value_finish (GDK_CLIPBOARD (object), result, &error);
|
||||
if (value == NULL)
|
||||
{
|
||||
g_print ("Failed to read: %s\n", error->message);
|
||||
g_error_free (error);
|
||||
return;
|
||||
}
|
||||
|
||||
for (l = g_value_get_boxed (value); l; l = l->next)
|
||||
g_print ("%s\n", g_file_get_path (l->data));
|
||||
}
|
||||
|
||||
void
|
||||
paste (void)
|
||||
{
|
||||
GdkClipboard *clipboard = gdk_display_get_clipboard (gdk_display_get_default ());
|
||||
|
||||
gdk_clipboard_read_value_async (clipboard, GDK_TYPE_FILE_LIST, 0, NULL, value_received, NULL);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
clipboard_changed (GdkClipboard *clipboard)
|
||||
{
|
||||
GdkContentFormats *formats = gdk_clipboard_get_formats (clipboard);
|
||||
g_autofree char *s = gdk_content_formats_to_string (formats);
|
||||
g_print ("clipboard contents now: %s, local: %d\n", s, gdk_clipboard_is_local (clipboard));
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *window, *button;
|
||||
GtkWidget *window, *button, *box;
|
||||
GdkClipboard *clipboard;
|
||||
|
||||
gtk_init ();
|
||||
|
||||
@ -44,11 +88,22 @@ main (int argc, char *argv[])
|
||||
gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
|
||||
g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
|
||||
|
||||
button = gtk_button_new ();
|
||||
gtk_button_set_label (GTK_BUTTON (button), "hello world");
|
||||
g_signal_connect (button, "clicked", G_CALLBACK (hello), NULL);
|
||||
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (window), button);
|
||||
button = gtk_button_new ();
|
||||
gtk_button_set_label (GTK_BUTTON (button), "copy");
|
||||
g_signal_connect (button, "clicked", G_CALLBACK (copy), NULL);
|
||||
gtk_container_add (GTK_CONTAINER (box), button);
|
||||
|
||||
button = gtk_button_new ();
|
||||
gtk_button_set_label (GTK_BUTTON (button), "paste");
|
||||
g_signal_connect (button, "clicked", G_CALLBACK (paste), NULL);
|
||||
gtk_container_add (GTK_CONTAINER (box), button);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (window), box);
|
||||
|
||||
clipboard = gdk_display_get_clipboard (gdk_display_get_default ());
|
||||
g_signal_connect (clipboard, "changed", G_CALLBACK (clipboard_changed), NULL);
|
||||
|
||||
gtk_widget_show (window);
|
||||
|
||||
|
@ -103,23 +103,30 @@ visible_child_changed_cb (GtkWidget *stack,
|
||||
}
|
||||
}
|
||||
|
||||
static GList *
|
||||
static GSList *
|
||||
get_file_list (const char *dir)
|
||||
{
|
||||
GFileEnumerator *enumerator;
|
||||
GFile *file;
|
||||
GList *list = NULL;
|
||||
GFileInfo *info;
|
||||
GSList *list = NULL;
|
||||
|
||||
file = g_file_new_for_path (dir);
|
||||
enumerator = g_file_enumerate_children (file, "standard::name", 0, NULL, NULL);
|
||||
enumerator = g_file_enumerate_children (file, "standard::name,standard::type", 0, NULL, NULL);
|
||||
g_object_unref (file);
|
||||
if (enumerator == NULL)
|
||||
return NULL;
|
||||
|
||||
while (g_file_enumerator_iterate (enumerator, NULL, &file, NULL, NULL) && file != NULL)
|
||||
list = g_list_prepend (list, g_object_ref (file));
|
||||
while (g_file_enumerator_iterate (enumerator, &info, &file, NULL, NULL) && file != NULL)
|
||||
{
|
||||
/* the portal can't handle directories */
|
||||
if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR)
|
||||
continue;
|
||||
|
||||
return g_list_reverse (list);
|
||||
list = g_slist_prepend (list, g_object_ref (file));
|
||||
}
|
||||
|
||||
return g_slist_reverse (list);
|
||||
}
|
||||
|
||||
static void
|
||||
|
Loading…
Reference in New Issue
Block a user