x11: Add X11 selection input/output streams
These are rip off of GTK+ ones, with some adaptions to integrate them in mutter event dispatching code and make them easier to use in future commits. https://gitlab.gnome.org/GNOME/mutter/merge_requests/320
This commit is contained in:
parent
736cac43e9
commit
156980eff9
@ -379,6 +379,10 @@ mutter_sources = [
|
|||||||
'x11/meta-x11-display.c',
|
'x11/meta-x11-display.c',
|
||||||
'x11/meta-x11-display-private.h',
|
'x11/meta-x11-display-private.h',
|
||||||
'x11/meta-x11-errors.c',
|
'x11/meta-x11-errors.c',
|
||||||
|
'x11/meta-x11-selection-input-stream.c',
|
||||||
|
'x11/meta-x11-selection-input-stream-private.h',
|
||||||
|
'x11/meta-x11-selection-output-stream.c',
|
||||||
|
'x11/meta-x11-selection-output-stream-private.h',
|
||||||
'x11/mutter-Xatomtype.h',
|
'x11/mutter-Xatomtype.h',
|
||||||
'x11/session.c',
|
'x11/session.c',
|
||||||
'x11/session.h',
|
'x11/session.h',
|
||||||
|
@ -38,8 +38,10 @@
|
|||||||
#include "core/workspace-private.h"
|
#include "core/workspace-private.h"
|
||||||
#include "meta/meta-backend.h"
|
#include "meta/meta-backend.h"
|
||||||
#include "meta/meta-x11-errors.h"
|
#include "meta/meta-x11-errors.h"
|
||||||
#include "x11/meta-x11-display-private.h"
|
|
||||||
#include "x11/meta-startup-notification-x11.h"
|
#include "x11/meta-startup-notification-x11.h"
|
||||||
|
#include "x11/meta-x11-display-private.h"
|
||||||
|
#include "x11/meta-x11-selection-input-stream-private.h"
|
||||||
|
#include "x11/meta-x11-selection-output-stream-private.h"
|
||||||
#include "x11/window-x11.h"
|
#include "x11/window-x11.h"
|
||||||
#include "x11/xprops.h"
|
#include "x11/xprops.h"
|
||||||
|
|
||||||
@ -1717,6 +1719,22 @@ window_has_xwindow (MetaWindow *window,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
process_selection_event (MetaX11Display *x11_display,
|
||||||
|
XEvent *event)
|
||||||
|
{
|
||||||
|
gboolean handled = FALSE;
|
||||||
|
GList *l;
|
||||||
|
|
||||||
|
for (l = x11_display->selection.input_streams; l && !handled; l = l->next)
|
||||||
|
handled |= meta_x11_selection_input_stream_xevent (l->data, event);
|
||||||
|
|
||||||
|
for (l = x11_display->selection.output_streams; l && !handled; l = l->next)
|
||||||
|
handled |= meta_x11_selection_output_stream_xevent (l->data, event);
|
||||||
|
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* meta_display_handle_xevent:
|
* meta_display_handle_xevent:
|
||||||
* @display: The MetaDisplay that events are coming from
|
* @display: The MetaDisplay that events are coming from
|
||||||
@ -1760,6 +1778,12 @@ meta_x11_display_handle_xevent (MetaX11Display *x11_display,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (process_selection_event (x11_display, event))
|
||||||
|
{
|
||||||
|
bypass_gtk = bypass_compositor = TRUE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
display->current_time = event_get_time (x11_display, event);
|
display->current_time = event_get_time (x11_display, event);
|
||||||
|
|
||||||
if (META_IS_BACKEND_X11 (backend))
|
if (META_IS_BACKEND_X11 (backend))
|
||||||
|
@ -123,6 +123,11 @@ struct _MetaX11Display
|
|||||||
|
|
||||||
MetaUI *ui;
|
MetaUI *ui;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
GList *input_streams;
|
||||||
|
GList *output_streams;
|
||||||
|
} selection;
|
||||||
|
|
||||||
guint keys_grabbed : 1;
|
guint keys_grabbed : 1;
|
||||||
|
|
||||||
int composite_event_base;
|
int composite_event_base;
|
||||||
|
54
src/x11/meta-x11-selection-input-stream-private.h
Normal file
54
src/x11/meta-x11-selection-input-stream-private.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/* GIO - GLib Input, Output and Streaming Library
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 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.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/>.
|
||||||
|
*
|
||||||
|
* Author: Benjamin Otte <otte@gnome.org>
|
||||||
|
* Christian Kellner <gicmo@gnome.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef META_X11_SELECTION_INPUT_STREAM_H
|
||||||
|
#define META_X11_SELECTION_INPUT_STREAM_H
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#include "x11/meta-x11-display-private.h"
|
||||||
|
|
||||||
|
#define META_TYPE_X11_SELECTION_INPUT_STREAM (meta_x11_selection_input_stream_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (MetaX11SelectionInputStream,
|
||||||
|
meta_x11_selection_input_stream,
|
||||||
|
META, X11_SELECTION_INPUT_STREAM,
|
||||||
|
GInputStream)
|
||||||
|
|
||||||
|
void meta_x11_selection_input_stream_new_async (MetaX11Display *x11_display,
|
||||||
|
Window window,
|
||||||
|
const char *selection,
|
||||||
|
const char *target,
|
||||||
|
guint32 timestamp,
|
||||||
|
int io_priority,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
GInputStream * meta_x11_selection_input_stream_new_finish (GAsyncResult *result,
|
||||||
|
const char **type,
|
||||||
|
int *format,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
gboolean meta_x11_selection_input_stream_xevent (MetaX11SelectionInputStream *stream,
|
||||||
|
const XEvent *xevent);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* META_X11_SELECTION_INPUT_STREAM_H */
|
557
src/x11/meta-x11-selection-input-stream.c
Normal file
557
src/x11/meta-x11-selection-input-stream.c
Normal file
@ -0,0 +1,557 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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.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/>.
|
||||||
|
*
|
||||||
|
* Author: Benjamin Otte <otte@gnome.org>
|
||||||
|
* Christian Kellner <gicmo@gnome.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "meta-x11-selection-input-stream-private.h"
|
||||||
|
|
||||||
|
#include <gdk/gdkx.h>
|
||||||
|
|
||||||
|
#include "meta/meta-x11-errors.h"
|
||||||
|
#include "x11/meta-x11-display-private.h"
|
||||||
|
|
||||||
|
typedef struct MetaX11SelectionInputStreamPrivate MetaX11SelectionInputStreamPrivate;
|
||||||
|
|
||||||
|
struct _MetaX11SelectionInputStream
|
||||||
|
{
|
||||||
|
GInputStream parent_instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MetaX11SelectionInputStreamPrivate
|
||||||
|
{
|
||||||
|
MetaX11Display *x11_display;
|
||||||
|
Window window;
|
||||||
|
GAsyncQueue *chunks;
|
||||||
|
char *selection;
|
||||||
|
Atom xselection;
|
||||||
|
char *target;
|
||||||
|
Atom xtarget;
|
||||||
|
char *property;
|
||||||
|
Atom xproperty;
|
||||||
|
const char *type;
|
||||||
|
Atom xtype;
|
||||||
|
int format;
|
||||||
|
|
||||||
|
GTask *pending_task;
|
||||||
|
uint8_t *pending_data;
|
||||||
|
size_t pending_size;
|
||||||
|
|
||||||
|
guint complete : 1;
|
||||||
|
guint incr : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_PRIVATE (MetaX11SelectionInputStream,
|
||||||
|
meta_x11_selection_input_stream,
|
||||||
|
G_TYPE_INPUT_STREAM)
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_x11_selection_input_stream_has_data (MetaX11SelectionInputStream *stream)
|
||||||
|
{
|
||||||
|
MetaX11SelectionInputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_input_stream_get_instance_private (stream);
|
||||||
|
|
||||||
|
return g_async_queue_length (priv->chunks) > 0 || priv->complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NB: blocks when no data is in buffer */
|
||||||
|
static size_t
|
||||||
|
meta_x11_selection_input_stream_fill_buffer (MetaX11SelectionInputStream *stream,
|
||||||
|
uint8_t *buffer,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
MetaX11SelectionInputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_input_stream_get_instance_private (stream);
|
||||||
|
GBytes *bytes;
|
||||||
|
size_t result = 0;
|
||||||
|
|
||||||
|
g_async_queue_lock (priv->chunks);
|
||||||
|
|
||||||
|
for (bytes = g_async_queue_pop_unlocked (priv->chunks);
|
||||||
|
bytes != NULL && count > 0;
|
||||||
|
bytes = g_async_queue_try_pop_unlocked (priv->chunks))
|
||||||
|
{
|
||||||
|
size_t size = g_bytes_get_size (bytes);
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
{
|
||||||
|
/* EOF marker, put it back */
|
||||||
|
g_async_queue_push_front_unlocked (priv->chunks, bytes);
|
||||||
|
bytes = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (size > count)
|
||||||
|
{
|
||||||
|
GBytes *subbytes;
|
||||||
|
if (buffer)
|
||||||
|
memcpy (buffer, g_bytes_get_data (bytes, NULL), count);
|
||||||
|
subbytes = g_bytes_new_from_bytes (bytes, count, size - count);
|
||||||
|
g_async_queue_push_front_unlocked (priv->chunks, subbytes);
|
||||||
|
size = count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (buffer)
|
||||||
|
memcpy (buffer, g_bytes_get_data (bytes, NULL), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_bytes_unref (bytes);
|
||||||
|
result += size;
|
||||||
|
if (buffer)
|
||||||
|
buffer += size;
|
||||||
|
count -= size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes)
|
||||||
|
g_async_queue_push_front_unlocked (priv->chunks, bytes);
|
||||||
|
|
||||||
|
g_async_queue_unlock (priv->chunks);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_selection_input_stream_flush (MetaX11SelectionInputStream *stream)
|
||||||
|
{
|
||||||
|
MetaX11SelectionInputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_input_stream_get_instance_private (stream);
|
||||||
|
gssize written;
|
||||||
|
|
||||||
|
if (!meta_x11_selection_input_stream_has_data (stream))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (priv->pending_task == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
written = meta_x11_selection_input_stream_fill_buffer (stream,
|
||||||
|
priv->pending_data,
|
||||||
|
priv->pending_size);
|
||||||
|
g_task_return_int (priv->pending_task, written);
|
||||||
|
|
||||||
|
g_clear_object (&priv->pending_task);
|
||||||
|
priv->pending_data = NULL;
|
||||||
|
priv->pending_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_selection_input_stream_complete (MetaX11SelectionInputStream *stream)
|
||||||
|
{
|
||||||
|
MetaX11SelectionInputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_input_stream_get_instance_private (stream);
|
||||||
|
|
||||||
|
if (priv->complete)
|
||||||
|
return;
|
||||||
|
|
||||||
|
priv->complete = TRUE;
|
||||||
|
|
||||||
|
g_async_queue_push (priv->chunks, g_bytes_new (NULL, 0));
|
||||||
|
meta_x11_selection_input_stream_flush (stream);
|
||||||
|
|
||||||
|
priv->x11_display->selection.input_streams =
|
||||||
|
g_list_remove (priv->x11_display->selection.input_streams, stream);
|
||||||
|
|
||||||
|
g_object_unref (stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gssize
|
||||||
|
meta_x11_selection_input_stream_read (GInputStream *input_stream,
|
||||||
|
void *buffer,
|
||||||
|
size_t count,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
MetaX11SelectionInputStream *stream =
|
||||||
|
META_X11_SELECTION_INPUT_STREAM (input_stream);
|
||||||
|
|
||||||
|
return meta_x11_selection_input_stream_fill_buffer (stream, buffer, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_x11_selection_input_stream_close (GInputStream *stream,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_selection_input_stream_read_async (GInputStream *input_stream,
|
||||||
|
void *buffer,
|
||||||
|
size_t count,
|
||||||
|
int io_priority,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
MetaX11SelectionInputStream *stream =
|
||||||
|
META_X11_SELECTION_INPUT_STREAM (input_stream);
|
||||||
|
MetaX11SelectionInputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_input_stream_get_instance_private (stream);
|
||||||
|
GTask *task;
|
||||||
|
|
||||||
|
task = g_task_new (stream, cancellable, callback, user_data);
|
||||||
|
g_task_set_source_tag (task, meta_x11_selection_input_stream_read_async);
|
||||||
|
g_task_set_priority (task, io_priority);
|
||||||
|
|
||||||
|
if (meta_x11_selection_input_stream_has_data (stream))
|
||||||
|
{
|
||||||
|
gssize size;
|
||||||
|
|
||||||
|
size = meta_x11_selection_input_stream_fill_buffer (stream, buffer, count);
|
||||||
|
g_task_return_int (task, size);
|
||||||
|
g_object_unref (task);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
priv->pending_data = buffer;
|
||||||
|
priv->pending_size = count;
|
||||||
|
priv->pending_task = task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gssize
|
||||||
|
meta_x11_selection_input_stream_read_finish (GInputStream *stream,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (g_task_is_valid (result, stream), -1);
|
||||||
|
g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) ==
|
||||||
|
meta_x11_selection_input_stream_read_async, -1);
|
||||||
|
|
||||||
|
return g_task_propagate_int (G_TASK (result), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_selection_input_stream_close_async (GInputStream *stream,
|
||||||
|
int io_priority,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GTask *task;
|
||||||
|
|
||||||
|
task = g_task_new (stream, cancellable, callback, user_data);
|
||||||
|
g_task_set_source_tag (task, meta_x11_selection_input_stream_close_async);
|
||||||
|
g_task_return_boolean (task, TRUE);
|
||||||
|
g_object_unref (task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_x11_selection_input_stream_close_finish (GInputStream *stream,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_selection_input_stream_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
MetaX11SelectionInputStream *stream =
|
||||||
|
META_X11_SELECTION_INPUT_STREAM (object);
|
||||||
|
MetaX11SelectionInputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_input_stream_get_instance_private (stream);
|
||||||
|
|
||||||
|
g_async_queue_unref (priv->chunks);
|
||||||
|
|
||||||
|
g_free (priv->selection);
|
||||||
|
g_free (priv->target);
|
||||||
|
g_free (priv->property);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (meta_x11_selection_input_stream_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_selection_input_stream_class_init (MetaX11SelectionInputStreamClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
GInputStreamClass *istream_class = G_INPUT_STREAM_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->finalize = meta_x11_selection_input_stream_finalize;
|
||||||
|
|
||||||
|
istream_class->read_fn = meta_x11_selection_input_stream_read;
|
||||||
|
istream_class->close_fn = meta_x11_selection_input_stream_close;
|
||||||
|
|
||||||
|
istream_class->read_async = meta_x11_selection_input_stream_read_async;
|
||||||
|
istream_class->read_finish = meta_x11_selection_input_stream_read_finish;
|
||||||
|
istream_class->close_async = meta_x11_selection_input_stream_close_async;
|
||||||
|
istream_class->close_finish = meta_x11_selection_input_stream_close_finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_selection_input_stream_init (MetaX11SelectionInputStream *stream)
|
||||||
|
{
|
||||||
|
MetaX11SelectionInputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_input_stream_get_instance_private (stream);
|
||||||
|
|
||||||
|
priv->chunks = g_async_queue_new_full ((GDestroyNotify) g_bytes_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
XFree_without_return_value (gpointer data)
|
||||||
|
{
|
||||||
|
XFree (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GBytes *
|
||||||
|
get_selection_property (Display *xdisplay,
|
||||||
|
Window owner,
|
||||||
|
Atom property,
|
||||||
|
Atom *ret_type,
|
||||||
|
gint *ret_format)
|
||||||
|
{
|
||||||
|
gulong nitems;
|
||||||
|
gulong nbytes;
|
||||||
|
Atom prop_type;
|
||||||
|
gint prop_format;
|
||||||
|
uint8_t *data = NULL;
|
||||||
|
|
||||||
|
if (XGetWindowProperty (xdisplay, owner, property,
|
||||||
|
0, 0x1FFFFFFF, False,
|
||||||
|
AnyPropertyType, &prop_type, &prop_format,
|
||||||
|
&nitems, &nbytes, &data) != Success)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (prop_type != None)
|
||||||
|
{
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
switch (prop_format)
|
||||||
|
{
|
||||||
|
case 8:
|
||||||
|
length = nitems;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
length = sizeof (short) * nitems;
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
length = sizeof (long) * nitems;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_warning ("Unknown XGetWindowProperty() format %u", prop_format);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret_type = prop_type;
|
||||||
|
*ret_format = prop_format;
|
||||||
|
|
||||||
|
return g_bytes_new_with_free_func (data,
|
||||||
|
length,
|
||||||
|
XFree_without_return_value,
|
||||||
|
data);
|
||||||
|
}
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (data)
|
||||||
|
XFree (data);
|
||||||
|
|
||||||
|
*ret_type = None;
|
||||||
|
*ret_format = 0;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_x11_selection_input_stream_xevent (MetaX11SelectionInputStream *stream,
|
||||||
|
const XEvent *xevent)
|
||||||
|
{
|
||||||
|
MetaX11SelectionInputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_input_stream_get_instance_private (stream);
|
||||||
|
Display *xdisplay;
|
||||||
|
Window xwindow;
|
||||||
|
GBytes *bytes;
|
||||||
|
Atom type;
|
||||||
|
gint format;
|
||||||
|
|
||||||
|
xdisplay = priv->x11_display->xdisplay;
|
||||||
|
xwindow = priv->window;
|
||||||
|
|
||||||
|
if (xevent->xany.display != xdisplay ||
|
||||||
|
xevent->xany.window != xwindow)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
switch (xevent->type)
|
||||||
|
{
|
||||||
|
case PropertyNotify:
|
||||||
|
if (!priv->incr ||
|
||||||
|
xevent->xproperty.atom != priv->xproperty ||
|
||||||
|
xevent->xproperty.state != PropertyNewValue)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
bytes = get_selection_property (xdisplay, xwindow, xevent->xproperty.atom,
|
||||||
|
&type, &format);
|
||||||
|
|
||||||
|
if (bytes == NULL)
|
||||||
|
{
|
||||||
|
g_debug ("INCR request came out empty");
|
||||||
|
meta_x11_selection_input_stream_complete (stream);
|
||||||
|
}
|
||||||
|
else if (g_bytes_get_size (bytes) == 0 || type == None)
|
||||||
|
{
|
||||||
|
g_bytes_unref (bytes);
|
||||||
|
meta_x11_selection_input_stream_complete (stream);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_async_queue_push (priv->chunks, bytes);
|
||||||
|
meta_x11_selection_input_stream_flush (stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
XDeleteProperty (xdisplay, xwindow, xevent->xproperty.atom);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case SelectionNotify:
|
||||||
|
{
|
||||||
|
GTask *task;
|
||||||
|
|
||||||
|
/* selection is not for us */
|
||||||
|
if (priv->xselection != xevent->xselection.selection ||
|
||||||
|
priv->xtarget != xevent->xselection.target)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* We already received a selectionNotify before */
|
||||||
|
if (priv->pending_task == NULL ||
|
||||||
|
g_task_get_source_tag (priv->pending_task) != meta_x11_selection_input_stream_new_async)
|
||||||
|
{
|
||||||
|
g_debug ("Misbehaving client sent a reentrant SelectionNotify");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
task = g_steal_pointer (&priv->pending_task);
|
||||||
|
|
||||||
|
if (xevent->xselection.property == None)
|
||||||
|
{
|
||||||
|
g_task_return_new_error (task,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_FOUND,
|
||||||
|
_("Format %s not supported"), priv->target);
|
||||||
|
meta_x11_selection_input_stream_complete (stream);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bytes = get_selection_property (xdisplay, xwindow,
|
||||||
|
xevent->xselection.property,
|
||||||
|
&priv->xtype, &priv->format);
|
||||||
|
priv->type = gdk_x11_get_xatom_name (priv->xtype);
|
||||||
|
|
||||||
|
g_task_return_pointer (task, g_object_ref (stream), g_object_unref);
|
||||||
|
|
||||||
|
if (bytes == NULL)
|
||||||
|
{
|
||||||
|
meta_x11_selection_input_stream_complete (stream);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (priv->xtype == XInternAtom (priv->x11_display->xdisplay, "INCR", False))
|
||||||
|
{
|
||||||
|
/* The remainder of the selection will come through PropertyNotify
|
||||||
|
events on xwindow */
|
||||||
|
priv->incr = TRUE;
|
||||||
|
meta_x11_selection_input_stream_flush (stream);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_async_queue_push (priv->chunks, bytes);
|
||||||
|
|
||||||
|
meta_x11_selection_input_stream_complete (stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XDeleteProperty (xdisplay, xwindow, xevent->xselection.property);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref (task);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_x11_selection_input_stream_new_async (MetaX11Display *x11_display,
|
||||||
|
Window window,
|
||||||
|
const char *selection,
|
||||||
|
const char *target,
|
||||||
|
guint32 timestamp,
|
||||||
|
int io_priority,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
MetaX11SelectionInputStream *stream;
|
||||||
|
MetaX11SelectionInputStreamPrivate *priv;
|
||||||
|
|
||||||
|
stream = g_object_new (META_TYPE_X11_SELECTION_INPUT_STREAM, NULL);
|
||||||
|
priv = meta_x11_selection_input_stream_get_instance_private (stream);
|
||||||
|
|
||||||
|
priv->x11_display = x11_display;
|
||||||
|
x11_display->selection.input_streams =
|
||||||
|
g_list_prepend (x11_display->selection.input_streams, stream);
|
||||||
|
priv->selection = g_strdup (selection);
|
||||||
|
priv->xselection = XInternAtom (x11_display->xdisplay, priv->selection, False);
|
||||||
|
priv->target = g_strdup (target);
|
||||||
|
priv->xtarget = XInternAtom (x11_display->xdisplay, priv->target, False);
|
||||||
|
priv->property = g_strdup_printf ("META_SELECTION_%p", stream);
|
||||||
|
priv->xproperty = XInternAtom (x11_display->xdisplay, priv->property, False);
|
||||||
|
priv->window = window;
|
||||||
|
|
||||||
|
XConvertSelection (x11_display->xdisplay,
|
||||||
|
priv->xselection,
|
||||||
|
priv->xtarget,
|
||||||
|
priv->xproperty,
|
||||||
|
window,
|
||||||
|
timestamp);
|
||||||
|
|
||||||
|
priv->pending_task = g_task_new (NULL, cancellable, callback, user_data);
|
||||||
|
g_task_set_source_tag (priv->pending_task, meta_x11_selection_input_stream_new_async);
|
||||||
|
g_task_set_priority (priv->pending_task, io_priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
GInputStream *
|
||||||
|
meta_x11_selection_input_stream_new_finish (GAsyncResult *result,
|
||||||
|
const char **type,
|
||||||
|
int *format,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
MetaX11SelectionInputStream *stream;
|
||||||
|
MetaX11SelectionInputStreamPrivate *priv;
|
||||||
|
GTask *task;
|
||||||
|
|
||||||
|
g_return_val_if_fail (g_task_is_valid (result, NULL), NULL);
|
||||||
|
task = G_TASK (result);
|
||||||
|
g_return_val_if_fail (g_task_get_source_tag (task) ==
|
||||||
|
meta_x11_selection_input_stream_new_async, NULL);
|
||||||
|
|
||||||
|
stream = g_task_propagate_pointer (task, error);
|
||||||
|
if (!stream)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
priv = meta_x11_selection_input_stream_get_instance_private (stream);
|
||||||
|
|
||||||
|
if (type)
|
||||||
|
*type = priv->type;
|
||||||
|
if (format)
|
||||||
|
*format = priv->format;
|
||||||
|
|
||||||
|
return G_INPUT_STREAM (stream);
|
||||||
|
}
|
47
src/x11/meta-x11-selection-output-stream-private.h
Normal file
47
src/x11/meta-x11-selection-output-stream-private.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/* GIO - GLib Output, Output and Streaming Library
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 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.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/>.
|
||||||
|
*
|
||||||
|
* Author: Benjamin Otte <otte@gnome.org>
|
||||||
|
* Christian Kellner <gicmo@gnome.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef META_X11_SELECTION_OUTPUT_STREAM_H
|
||||||
|
#define META_X11_SELECTION_OUTPUT_STREAM_H
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#include "x11/meta-x11-display-private.h"
|
||||||
|
|
||||||
|
#define META_TYPE_X11_SELECTION_OUTPUT_STREAM (meta_x11_selection_output_stream_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (MetaX11SelectionOutputStream,
|
||||||
|
meta_x11_selection_output_stream,
|
||||||
|
META, X11_SELECTION_OUTPUT_STREAM,
|
||||||
|
GOutputStream)
|
||||||
|
|
||||||
|
GOutputStream * meta_x11_selection_output_stream_new (MetaX11Display *x11_display,
|
||||||
|
Window window,
|
||||||
|
const char *selection,
|
||||||
|
const char *target,
|
||||||
|
const char *property,
|
||||||
|
const char *type,
|
||||||
|
int format,
|
||||||
|
gulong timestamp);
|
||||||
|
|
||||||
|
gboolean meta_x11_selection_output_stream_xevent (MetaX11SelectionOutputStream *stream,
|
||||||
|
const XEvent *xevent);
|
||||||
|
|
||||||
|
#endif /* META_X11_SELECTION_OUTPUT_STREAM_H */
|
606
src/x11/meta-x11-selection-output-stream.c
Normal file
606
src/x11/meta-x11-selection-output-stream.c
Normal file
@ -0,0 +1,606 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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.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/>.
|
||||||
|
*
|
||||||
|
* Author: Benjamin Otte <otte@gnome.org>
|
||||||
|
* Christian Kellner <gicmo@gnome.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "meta-x11-selection-output-stream-private.h"
|
||||||
|
|
||||||
|
#include "meta/meta-x11-errors.h"
|
||||||
|
#include "x11/meta-x11-display-private.h"
|
||||||
|
|
||||||
|
typedef struct _MetaX11SelectionOutputStreamPrivate MetaX11SelectionOutputStreamPrivate;
|
||||||
|
|
||||||
|
struct _MetaX11SelectionOutputStream
|
||||||
|
{
|
||||||
|
GOutputStream parent_instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _MetaX11SelectionOutputStreamPrivate
|
||||||
|
{
|
||||||
|
MetaX11Display *x11_display;
|
||||||
|
Window xwindow;
|
||||||
|
char *selection;
|
||||||
|
Atom xselection;
|
||||||
|
char *target;
|
||||||
|
Atom xtarget;
|
||||||
|
char *property;
|
||||||
|
Atom xproperty;
|
||||||
|
const char *type;
|
||||||
|
Atom xtype;
|
||||||
|
int format;
|
||||||
|
gulong timestamp;
|
||||||
|
|
||||||
|
GMutex mutex;
|
||||||
|
GCond cond;
|
||||||
|
GByteArray *data;
|
||||||
|
guint flush_requested : 1;
|
||||||
|
|
||||||
|
GTask *pending_task;
|
||||||
|
|
||||||
|
guint incr : 1;
|
||||||
|
guint delete_pending : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_PRIVATE (MetaX11SelectionOutputStream,
|
||||||
|
meta_x11_selection_output_stream,
|
||||||
|
G_TYPE_OUTPUT_STREAM);
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_selection_output_stream_notify_selection (MetaX11SelectionOutputStream *stream)
|
||||||
|
{
|
||||||
|
MetaX11SelectionOutputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_output_stream_get_instance_private (stream);
|
||||||
|
XSelectionEvent event;
|
||||||
|
Display *xdisplay;
|
||||||
|
|
||||||
|
event = (XSelectionEvent) {
|
||||||
|
.type = SelectionNotify,
|
||||||
|
.time = priv->timestamp,
|
||||||
|
.requestor = priv->xwindow,
|
||||||
|
.selection = priv->xselection,
|
||||||
|
.target = priv->xtarget,
|
||||||
|
.property = priv->xproperty,
|
||||||
|
};
|
||||||
|
|
||||||
|
meta_x11_error_trap_push (priv->x11_display);
|
||||||
|
|
||||||
|
xdisplay = priv->x11_display->xdisplay;
|
||||||
|
|
||||||
|
XSendEvent (xdisplay,
|
||||||
|
priv->xwindow, False, NoEventMask,
|
||||||
|
(XEvent *) &event);
|
||||||
|
XSync (xdisplay, False);
|
||||||
|
|
||||||
|
meta_x11_error_trap_pop (priv->x11_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_x11_selection_output_stream_can_flush (MetaX11SelectionOutputStream *stream)
|
||||||
|
{
|
||||||
|
MetaX11SelectionOutputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_output_stream_get_instance_private (stream);
|
||||||
|
|
||||||
|
if (priv->delete_pending)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
get_max_request_size (MetaX11Display *display)
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
size = XExtendedMaxRequestSize (display->xdisplay);
|
||||||
|
if (size <= 0)
|
||||||
|
size = XMaxRequestSize (display->xdisplay);
|
||||||
|
|
||||||
|
return size - 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_x11_selection_output_stream_needs_flush_unlocked (MetaX11SelectionOutputStream *stream)
|
||||||
|
{
|
||||||
|
MetaX11SelectionOutputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_output_stream_get_instance_private (stream);
|
||||||
|
|
||||||
|
if (priv->data->len == 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (g_output_stream_is_closing (G_OUTPUT_STREAM (stream)))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
if (priv->flush_requested)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return priv->data->len >= get_max_request_size (priv->x11_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_x11_selection_output_stream_needs_flush (MetaX11SelectionOutputStream *stream)
|
||||||
|
{
|
||||||
|
MetaX11SelectionOutputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_output_stream_get_instance_private (stream);
|
||||||
|
|
||||||
|
gboolean result;
|
||||||
|
|
||||||
|
g_mutex_lock (&priv->mutex);
|
||||||
|
|
||||||
|
result = meta_x11_selection_output_stream_needs_flush_unlocked (stream);
|
||||||
|
|
||||||
|
g_mutex_unlock (&priv->mutex);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
get_element_size (int format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case 8:
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case 16:
|
||||||
|
return sizeof (short);
|
||||||
|
|
||||||
|
case 32:
|
||||||
|
return sizeof (long);
|
||||||
|
|
||||||
|
default:
|
||||||
|
g_warning ("Unknown format %u", format);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_selection_output_stream_perform_flush (MetaX11SelectionOutputStream *stream)
|
||||||
|
{
|
||||||
|
MetaX11SelectionOutputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_output_stream_get_instance_private (stream);
|
||||||
|
Display *xdisplay;
|
||||||
|
size_t element_size, n_elements;
|
||||||
|
|
||||||
|
g_assert (!priv->delete_pending);
|
||||||
|
|
||||||
|
xdisplay = priv->x11_display->xdisplay;
|
||||||
|
|
||||||
|
/* We operate on a foreign window, better guard against catastrophe */
|
||||||
|
meta_x11_error_trap_push (priv->x11_display);
|
||||||
|
|
||||||
|
g_mutex_lock (&priv->mutex);
|
||||||
|
|
||||||
|
element_size = get_element_size (priv->format);
|
||||||
|
n_elements = priv->data->len / element_size;
|
||||||
|
|
||||||
|
if (!g_output_stream_is_closing (G_OUTPUT_STREAM (stream)))
|
||||||
|
{
|
||||||
|
XWindowAttributes attrs;
|
||||||
|
|
||||||
|
priv->incr = TRUE;
|
||||||
|
XGetWindowAttributes (xdisplay,
|
||||||
|
priv->xwindow,
|
||||||
|
&attrs);
|
||||||
|
if (!(attrs.your_event_mask & PropertyChangeMask))
|
||||||
|
{
|
||||||
|
XSelectInput (xdisplay, priv->xwindow, attrs.your_event_mask | PropertyChangeMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
XChangeProperty (xdisplay,
|
||||||
|
priv->xwindow,
|
||||||
|
priv->xproperty,
|
||||||
|
XInternAtom (priv->x11_display->xdisplay, "INCR", True),
|
||||||
|
32,
|
||||||
|
PropModeReplace,
|
||||||
|
(guchar *) &(long) { n_elements },
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
XChangeProperty (xdisplay,
|
||||||
|
priv->xwindow,
|
||||||
|
priv->xproperty,
|
||||||
|
priv->xtype,
|
||||||
|
priv->format,
|
||||||
|
PropModeReplace,
|
||||||
|
priv->data->data,
|
||||||
|
n_elements);
|
||||||
|
g_byte_array_remove_range (priv->data, 0, n_elements * element_size);
|
||||||
|
if (priv->data->len < element_size)
|
||||||
|
priv->flush_requested = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_x11_selection_output_stream_notify_selection (stream);
|
||||||
|
|
||||||
|
priv->delete_pending = TRUE;
|
||||||
|
g_cond_broadcast (&priv->cond);
|
||||||
|
g_mutex_unlock (&priv->mutex);
|
||||||
|
|
||||||
|
/* XXX: handle failure here and report EPIPE for future operations on the stream? */
|
||||||
|
if (meta_x11_error_trap_pop_with_return (priv->x11_display))
|
||||||
|
g_warning ("Failed to flush selection output stream");
|
||||||
|
|
||||||
|
if (priv->pending_task)
|
||||||
|
{
|
||||||
|
size_t result;
|
||||||
|
|
||||||
|
result = GPOINTER_TO_SIZE (g_task_get_task_data (priv->pending_task));
|
||||||
|
g_task_return_int (priv->pending_task, result);
|
||||||
|
g_object_unref (priv->pending_task);
|
||||||
|
priv->pending_task = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_x11_selection_output_stream_invoke_flush (gpointer data)
|
||||||
|
{
|
||||||
|
MetaX11SelectionOutputStream *stream =
|
||||||
|
META_X11_SELECTION_OUTPUT_STREAM (data);
|
||||||
|
|
||||||
|
if (meta_x11_selection_output_stream_needs_flush (stream) &&
|
||||||
|
meta_x11_selection_output_stream_can_flush (stream))
|
||||||
|
meta_x11_selection_output_stream_perform_flush (stream);
|
||||||
|
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gssize
|
||||||
|
meta_x11_selection_output_stream_write (GOutputStream *output_stream,
|
||||||
|
const void *buffer,
|
||||||
|
size_t count,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
MetaX11SelectionOutputStream *stream =
|
||||||
|
META_X11_SELECTION_OUTPUT_STREAM (output_stream);
|
||||||
|
MetaX11SelectionOutputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_output_stream_get_instance_private (stream);
|
||||||
|
|
||||||
|
g_mutex_lock (&priv->mutex);
|
||||||
|
g_byte_array_append (priv->data, buffer, count);
|
||||||
|
g_mutex_unlock (&priv->mutex);
|
||||||
|
|
||||||
|
g_main_context_invoke (NULL, meta_x11_selection_output_stream_invoke_flush, stream);
|
||||||
|
|
||||||
|
g_mutex_lock (&priv->mutex);
|
||||||
|
if (meta_x11_selection_output_stream_needs_flush_unlocked (stream))
|
||||||
|
g_cond_wait (&priv->cond, &priv->mutex);
|
||||||
|
g_mutex_unlock (&priv->mutex);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_selection_output_stream_write_async (GOutputStream *output_stream,
|
||||||
|
const void *buffer,
|
||||||
|
size_t count,
|
||||||
|
int io_priority,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
MetaX11SelectionOutputStream *stream =
|
||||||
|
META_X11_SELECTION_OUTPUT_STREAM (output_stream);
|
||||||
|
MetaX11SelectionOutputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_output_stream_get_instance_private (stream);
|
||||||
|
GTask *task;
|
||||||
|
|
||||||
|
task = g_task_new (stream, cancellable, callback, user_data);
|
||||||
|
g_task_set_source_tag (task, meta_x11_selection_output_stream_write_async);
|
||||||
|
g_task_set_priority (task, io_priority);
|
||||||
|
|
||||||
|
g_mutex_lock (&priv->mutex);
|
||||||
|
g_byte_array_append (priv->data, buffer, count);
|
||||||
|
g_mutex_unlock (&priv->mutex);
|
||||||
|
|
||||||
|
if (!meta_x11_selection_output_stream_needs_flush (stream))
|
||||||
|
{
|
||||||
|
g_task_return_int (task, count);
|
||||||
|
g_object_unref (task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (!meta_x11_selection_output_stream_can_flush (stream))
|
||||||
|
{
|
||||||
|
g_assert (priv->pending_task == NULL);
|
||||||
|
priv->pending_task = task;
|
||||||
|
g_task_set_task_data (task, GSIZE_TO_POINTER (count), NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
meta_x11_selection_output_stream_perform_flush (stream);
|
||||||
|
g_task_return_int (task, count);
|
||||||
|
g_object_unref (task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gssize
|
||||||
|
meta_x11_selection_output_stream_write_finish (GOutputStream *stream,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (g_task_is_valid (result, stream), -1);
|
||||||
|
g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) ==
|
||||||
|
meta_x11_selection_output_stream_write_async, -1);
|
||||||
|
|
||||||
|
return g_task_propagate_int (G_TASK (result), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_x11_selection_output_request_flush (MetaX11SelectionOutputStream *stream)
|
||||||
|
{
|
||||||
|
MetaX11SelectionOutputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_output_stream_get_instance_private (stream);
|
||||||
|
gboolean needs_flush;
|
||||||
|
|
||||||
|
g_mutex_lock (&priv->mutex);
|
||||||
|
|
||||||
|
if (priv->data->len >= get_element_size (priv->format))
|
||||||
|
priv->flush_requested = TRUE;
|
||||||
|
|
||||||
|
needs_flush = meta_x11_selection_output_stream_needs_flush_unlocked (stream);
|
||||||
|
g_mutex_unlock (&priv->mutex);
|
||||||
|
|
||||||
|
return needs_flush;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_x11_selection_output_stream_flush (GOutputStream *output_stream,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
MetaX11SelectionOutputStream *stream =
|
||||||
|
META_X11_SELECTION_OUTPUT_STREAM (output_stream);
|
||||||
|
MetaX11SelectionOutputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_output_stream_get_instance_private (stream);
|
||||||
|
|
||||||
|
if (!meta_x11_selection_output_request_flush (stream))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
g_main_context_invoke (NULL, meta_x11_selection_output_stream_invoke_flush,
|
||||||
|
stream);
|
||||||
|
|
||||||
|
g_mutex_lock (&priv->mutex);
|
||||||
|
if (meta_x11_selection_output_stream_needs_flush_unlocked (stream))
|
||||||
|
g_cond_wait (&priv->cond, &priv->mutex);
|
||||||
|
g_mutex_unlock (&priv->mutex);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_selection_output_stream_flush_async (GOutputStream *output_stream,
|
||||||
|
int io_priority,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
MetaX11SelectionOutputStream *stream =
|
||||||
|
META_X11_SELECTION_OUTPUT_STREAM (output_stream);
|
||||||
|
MetaX11SelectionOutputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_output_stream_get_instance_private (stream);
|
||||||
|
GTask *task;
|
||||||
|
|
||||||
|
task = g_task_new (stream, cancellable, callback, user_data);
|
||||||
|
g_task_set_source_tag (task, meta_x11_selection_output_stream_flush_async);
|
||||||
|
g_task_set_priority (task, io_priority);
|
||||||
|
|
||||||
|
if (!meta_x11_selection_output_stream_can_flush (stream))
|
||||||
|
{
|
||||||
|
if (meta_x11_selection_output_request_flush (stream))
|
||||||
|
{
|
||||||
|
g_assert (priv->pending_task == NULL);
|
||||||
|
priv->pending_task = task;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_task_return_boolean (task, TRUE);
|
||||||
|
g_object_unref (task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_x11_selection_output_stream_perform_flush (stream);
|
||||||
|
g_task_return_boolean (task, TRUE);
|
||||||
|
g_object_unref (task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_x11_selection_output_stream_flush_finish (GOutputStream *stream,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
|
||||||
|
g_return_val_if_fail (g_async_result_is_tagged (result, meta_x11_selection_output_stream_flush_async), FALSE);
|
||||||
|
|
||||||
|
return g_task_propagate_boolean (G_TASK (result), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_x11_selection_output_stream_invoke_close (gpointer stream)
|
||||||
|
{
|
||||||
|
MetaX11SelectionOutputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_output_stream_get_instance_private (stream);
|
||||||
|
|
||||||
|
priv->x11_display->selection.output_streams =
|
||||||
|
g_list_remove (priv->x11_display->selection.output_streams, stream);
|
||||||
|
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_x11_selection_output_stream_close (GOutputStream *stream,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_main_context_invoke (NULL, meta_x11_selection_output_stream_invoke_close, stream);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_selection_output_stream_close_async (GOutputStream *stream,
|
||||||
|
int io_priority,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GTask *task;
|
||||||
|
|
||||||
|
task = g_task_new (stream, cancellable, callback, user_data);
|
||||||
|
g_task_set_source_tag (task, meta_x11_selection_output_stream_close_async);
|
||||||
|
g_task_set_priority (task, io_priority);
|
||||||
|
|
||||||
|
meta_x11_selection_output_stream_invoke_close (stream);
|
||||||
|
g_task_return_boolean (task, TRUE);
|
||||||
|
|
||||||
|
g_object_unref (task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_x11_selection_output_stream_close_finish (GOutputStream *stream,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
|
||||||
|
g_return_val_if_fail (g_async_result_is_tagged (result, meta_x11_selection_output_stream_close_async), FALSE);
|
||||||
|
|
||||||
|
return g_task_propagate_boolean (G_TASK (result), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_selection_output_stream_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
MetaX11SelectionOutputStream *stream =
|
||||||
|
META_X11_SELECTION_OUTPUT_STREAM (object);
|
||||||
|
MetaX11SelectionOutputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_output_stream_get_instance_private (stream);
|
||||||
|
|
||||||
|
g_byte_array_unref (priv->data);
|
||||||
|
g_cond_clear (&priv->cond);
|
||||||
|
g_mutex_clear (&priv->mutex);
|
||||||
|
|
||||||
|
g_free (priv->selection);
|
||||||
|
g_free (priv->target);
|
||||||
|
g_free (priv->property);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (meta_x11_selection_output_stream_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_selection_output_stream_class_init (MetaX11SelectionOutputStreamClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->finalize = meta_x11_selection_output_stream_finalize;
|
||||||
|
|
||||||
|
output_stream_class->write_fn = meta_x11_selection_output_stream_write;
|
||||||
|
output_stream_class->flush = meta_x11_selection_output_stream_flush;
|
||||||
|
output_stream_class->close_fn = meta_x11_selection_output_stream_close;
|
||||||
|
|
||||||
|
output_stream_class->write_async = meta_x11_selection_output_stream_write_async;
|
||||||
|
output_stream_class->write_finish = meta_x11_selection_output_stream_write_finish;
|
||||||
|
output_stream_class->flush_async = meta_x11_selection_output_stream_flush_async;
|
||||||
|
output_stream_class->flush_finish = meta_x11_selection_output_stream_flush_finish;
|
||||||
|
output_stream_class->close_async = meta_x11_selection_output_stream_close_async;
|
||||||
|
output_stream_class->close_finish = meta_x11_selection_output_stream_close_finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_selection_output_stream_init (MetaX11SelectionOutputStream *stream)
|
||||||
|
{
|
||||||
|
MetaX11SelectionOutputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_output_stream_get_instance_private (stream);
|
||||||
|
|
||||||
|
g_mutex_init (&priv->mutex);
|
||||||
|
g_cond_init (&priv->cond);
|
||||||
|
priv->data = g_byte_array_new ();
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_x11_selection_output_stream_xevent (MetaX11SelectionOutputStream *stream,
|
||||||
|
const XEvent *xevent)
|
||||||
|
{
|
||||||
|
MetaX11SelectionOutputStreamPrivate *priv =
|
||||||
|
meta_x11_selection_output_stream_get_instance_private (stream);
|
||||||
|
Display *xdisplay = priv->x11_display->xdisplay;
|
||||||
|
|
||||||
|
if (xevent->xany.display != xdisplay ||
|
||||||
|
xevent->xany.window != priv->xwindow)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
switch (xevent->type)
|
||||||
|
{
|
||||||
|
case PropertyNotify:
|
||||||
|
if (!priv->incr ||
|
||||||
|
xevent->xproperty.atom != priv->xproperty ||
|
||||||
|
xevent->xproperty.state != PropertyDelete)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
priv->delete_pending = FALSE;
|
||||||
|
if (meta_x11_selection_output_stream_needs_flush (stream) &&
|
||||||
|
meta_x11_selection_output_stream_can_flush (stream))
|
||||||
|
meta_x11_selection_output_stream_perform_flush (stream);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GOutputStream *
|
||||||
|
meta_x11_selection_output_stream_new (MetaX11Display *x11_display,
|
||||||
|
Window requestor,
|
||||||
|
const char *selection,
|
||||||
|
const char *target,
|
||||||
|
const char *property,
|
||||||
|
const char *type,
|
||||||
|
int format,
|
||||||
|
gulong timestamp)
|
||||||
|
{
|
||||||
|
MetaX11SelectionOutputStream *stream;
|
||||||
|
MetaX11SelectionOutputStreamPrivate *priv;
|
||||||
|
|
||||||
|
stream = g_object_new (META_TYPE_X11_SELECTION_OUTPUT_STREAM, NULL);
|
||||||
|
priv = meta_x11_selection_output_stream_get_instance_private (stream);
|
||||||
|
|
||||||
|
x11_display->selection.output_streams =
|
||||||
|
g_list_prepend (x11_display->selection.output_streams, stream);
|
||||||
|
|
||||||
|
priv->x11_display = x11_display;
|
||||||
|
priv->xwindow = requestor;
|
||||||
|
priv->selection = g_strdup (selection);
|
||||||
|
priv->xselection = XInternAtom (x11_display->xdisplay, priv->selection, False);
|
||||||
|
priv->target = g_strdup (target);
|
||||||
|
priv->xtarget = XInternAtom (x11_display->xdisplay, priv->target, False);
|
||||||
|
priv->property = g_strdup (property);
|
||||||
|
priv->xproperty = XInternAtom (x11_display->xdisplay, priv->property, False);
|
||||||
|
priv->type = g_strdup (type);
|
||||||
|
priv->xtype = XInternAtom (x11_display->xdisplay, priv->type, False);
|
||||||
|
priv->format = format;
|
||||||
|
priv->timestamp = timestamp;
|
||||||
|
|
||||||
|
return G_OUTPUT_STREAM (stream);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user