Commit 57121ffe authored by Carlos Garnacho's avatar Carlos Garnacho

wayland: write wl_data_offer data asynchronously

Currently writing wl_data_offer data into the fd is 1) synchronous, which
is noticeable when transferring large amounts of data, and 2) buggy, write()
error checking is done on the accumulator, breaking both the written data
accounting and error checking itself.

Fix both by making writes asynchonous through GOutputStream, the operation
is spun off and either finished, or cancelled if new data is stored in the
selection while the transfer is active.
parent 69fd4348
......@@ -20,6 +20,7 @@
#include <fcntl.h>
#include <gio/gunixinputstream.h>
#include <gio/gunixoutputstream.h>
#include <glib-unix.h>
#include "gdkwayland.h"
......@@ -33,6 +34,7 @@
typedef struct _SelectionBuffer SelectionBuffer;
typedef struct _StoredSelection StoredSelection;
typedef struct _AsyncWriteData AsyncWriteData;
struct _SelectionBuffer
{
......@@ -48,6 +50,7 @@ struct _SelectionBuffer
struct _StoredSelection
{
GdkWindow *source;
GCancellable *cancellable;
guchar *data;
gsize data_len;
GdkAtom type;
......@@ -60,6 +63,12 @@ struct _DataSourceData
GdkAtom selection;
};
struct _AsyncWriteData {
GOutputStream *stream;
GdkWaylandSelection *selection;
gsize index;
};
enum {
ATOM_CLIPBOARD,
ATOM_DND
......@@ -87,6 +96,7 @@ struct _GdkWaylandSelection
};
static void selection_buffer_read (SelectionBuffer *buffer);
static void async_write_data_write (AsyncWriteData *write_data);
static void
selection_buffer_notify (SelectionBuffer *buffer)
......@@ -272,6 +282,12 @@ gdk_wayland_selection_free (GdkWaylandSelection *selection)
g_free (selection->stored_selection.data);
if (selection->stored_selection.cancellable)
{
g_cancellable_cancel (selection->stored_selection.cancellable);
g_object_unref (selection->stored_selection.cancellable);
}
if (selection->stored_selection.fd > 0)
close (selection->stored_selection.fd);
......@@ -366,34 +382,91 @@ gdk_wayland_selection_emit_request (GdkWindow *window,
gdk_event_free (event);
}
static gboolean
gdk_wayland_selection_check_write (GdkWaylandSelection *selection)
static AsyncWriteData *
async_write_data_new (GdkWaylandSelection *selection)
{
gssize len, bytes_written = 0;
gchar *buf;
AsyncWriteData *write_data;
if (selection->stored_selection.fd < 0 ||
selection->stored_selection.data_len == 0)
return FALSE;
write_data = g_slice_new0 (AsyncWriteData);
write_data->selection = selection;
write_data->stream =
g_unix_output_stream_new (selection->stored_selection.fd, TRUE);
return write_data;
}
len = selection->stored_selection.data_len;
buf = (gchar *) selection->stored_selection.data;
static void
async_write_data_free (AsyncWriteData *write_data)
{
g_object_unref (write_data->stream);
g_slice_free (AsyncWriteData, write_data);
}
while (len > 0)
static void
async_write_data_cb (GObject *object,
GAsyncResult *res,
gpointer user_data)
{
AsyncWriteData *write_data = user_data;
GError *error = NULL;
gsize bytes_written;
bytes_written = g_output_stream_write_finish (G_OUTPUT_STREAM (object),
res, &error);
if (error)
{
bytes_written += write (selection->stored_selection.fd,
buf + bytes_written, len);
g_warning ("Error writing selection data: %s", error->message);
g_error_free (error);
if (bytes_written < 0)
break;
async_write_data_free (write_data);
return;
}
write_data->index += bytes_written;
len -= bytes_written;
if (write_data->index <
write_data->selection->stored_selection.data_len)
{
/* Write the next chunk */
async_write_data_write (write_data);
}
else
async_write_data_free (write_data);
}
close (selection->stored_selection.fd);
static void
async_write_data_write (AsyncWriteData *write_data)
{
GdkWaylandSelection *selection = write_data->selection;
gsize buf_len;
guchar *buf;
buf = selection->stored_selection.data;
buf_len = selection->stored_selection.data_len;
g_output_stream_write_async (write_data->stream,
&buf[write_data->index],
buf_len - write_data->index,
G_PRIORITY_DEFAULT,
selection->stored_selection.cancellable,
async_write_data_cb,
write_data);
}
static gboolean
gdk_wayland_selection_check_write (GdkWaylandSelection *selection)
{
AsyncWriteData *write_data;
if (selection->stored_selection.fd < 0 ||
selection->stored_selection.data_len == 0)
return FALSE;
write_data = async_write_data_new (selection);
async_write_data_write (write_data);
selection->stored_selection.fd = -1;
return bytes_written != 0;
return TRUE;
}
void
......@@ -435,10 +508,17 @@ gdk_wayland_selection_store (GdkWindow *window,
g_free (selection->stored_selection.data);
}
if (selection->stored_selection.cancellable)
{
g_cancellable_cancel (selection->stored_selection.cancellable);
g_object_unref (selection->stored_selection.cancellable);
}
selection->stored_selection.source = window;
selection->stored_selection.data_len = array->len;
selection->stored_selection.data = (guchar *) g_array_free (array, FALSE);
selection->stored_selection.type = type;
selection->stored_selection.cancellable = g_cancellable_new ();
gdk_wayland_selection_check_write (selection);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment