diff options
author | Benjamin Otte <otte@redhat.com> | 2017-11-19 18:06:13 +0100 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2017-12-03 05:43:24 +0100 |
commit | 4728dd0a9e82607d65837c60e4aef4f6dd989d31 (patch) | |
tree | eb9699ec415d1d5e43c2fddf59bd0d52b153f52d /gdk | |
parent | e94b9b9a62571e3fde676b4a4cba8fbaf2219609 (diff) | |
download | gtk+-4728dd0a9e82607d65837c60e4aef4f6dd989d31.tar.gz |
x11: Add an initial clipboard implementation
This does nothing but download the targets and debug-print them.
Diffstat (limited to 'gdk')
-rw-r--r-- | gdk/gdk.c | 4 | ||||
-rw-r--r-- | gdk/gdkinternals.h | 4 | ||||
-rw-r--r-- | gdk/x11/gdkclipboard-x11.c | 192 | ||||
-rw-r--r-- | gdk/x11/gdkclipboard-x11.h | 46 | ||||
-rw-r--r-- | gdk/x11/gdkdisplay-x11.c | 40 | ||||
-rw-r--r-- | gdk/x11/gdkdisplay-x11.h | 3 | ||||
-rw-r--r-- | gdk/x11/gdkselectioninputstream-x11.c | 443 | ||||
-rw-r--r-- | gdk/x11/gdkselectioninputstream-x11.h | 59 | ||||
-rw-r--r-- | gdk/x11/meson.build | 2 |
9 files changed, 779 insertions, 14 deletions
@@ -166,7 +166,9 @@ static const GDebugKey gdk_debug_keys[] = { { "frames", GDK_DEBUG_FRAMES }, { "settings", GDK_DEBUG_SETTINGS }, { "opengl", GDK_DEBUG_OPENGL }, - { "vulkan", GDK_DEBUG_VULKAN } + { "vulkan", GDK_DEBUG_VULKAN }, + { "selection", GDK_DEBUG_SELECTION }, + { "clipboard", GDK_DEBUG_CLIPBOARD } }; #endif diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index 15913d88ba..4dd2c9368f 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -70,7 +70,9 @@ typedef enum { GDK_DEBUG_FRAMES = 1 << 10, GDK_DEBUG_SETTINGS = 1 << 11, GDK_DEBUG_OPENGL = 1 << 12, - GDK_DEBUG_VULKAN = 1 << 13 + GDK_DEBUG_VULKAN = 1 << 13, + GDK_DEBUG_SELECTION = 1 << 14, + GDK_DEBUG_CLIPBOARD = 1 << 15 } GdkDebugFlag; typedef enum { diff --git a/gdk/x11/gdkclipboard-x11.c b/gdk/x11/gdkclipboard-x11.c new file mode 100644 index 0000000000..4a90d8afb0 --- /dev/null +++ b/gdk/x11/gdkclipboard-x11.c @@ -0,0 +1,192 @@ +/* GDK - The GIMP Drawing Kit + * 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 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 "gdkclipboardprivate.h" +#include "gdkclipboard-x11.h" +#include "gdkselectioninputstream-x11.h" +#include "gdkprivate-x11.h" + +#include <string.h> +#include <X11/Xatom.h> + +#ifdef HAVE_XFIXES +#include <X11/extensions/Xfixes.h> +#endif + +#define IDLE_ABORT_TIME 30 /* seconds */ + +typedef struct _GdkX11ClipboardClass GdkX11ClipboardClass; + +typedef struct _RetrievalInfo RetrievalInfo; + +struct _GdkX11Clipboard +{ + GdkClipboard parent; + + char *selection; + Atom xselection; + guint32 time; +}; + +struct _GdkX11ClipboardClass +{ + GdkClipboardClass parent_class; +}; + +G_DEFINE_TYPE (GdkX11Clipboard, gdk_x11_clipboard, GDK_TYPE_CLIPBOARD) + +static void +gdk_x11_clipboard_finalize (GObject *object) +{ + GdkX11Clipboard *cb = GDK_X11_CLIPBOARD (object); + + g_free (cb->selection); + + G_OBJECT_CLASS (gdk_x11_clipboard_parent_class)->finalize (object); +} + +static void +gdk_x11_clipboard_class_init (GdkX11ClipboardClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + //GdkClipboardClass *clipboard_class = GDK_CLIPBOARD_CLASS (class); + + object_class->finalize = gdk_x11_clipboard_finalize; +} + +static void +gdk_x11_clipboard_init (GdkX11Clipboard *clipboard) +{ +} + +#define SELECTION_MAX_SIZE(display) \ + MIN(262144, \ + XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) == 0 \ + ? XMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100 \ + : XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100) + +static void +gdk_x11_clipboard_request_targets_finish (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GInputStream *stream = G_INPUT_STREAM (source_object); + GdkX11Clipboard *cb = user_data; + GdkDisplay *display; + GdkContentFormats *formats; + GdkContentFormatsBuilder *builder; + GBytes *bytes; + GError *error = NULL; + const Atom *atoms; + guint i, n_atoms; + + bytes = g_input_stream_read_bytes_finish (stream, res, &error); + if (bytes == NULL || g_bytes_get_size (bytes) == 0) + { + if (bytes) + g_bytes_unref (bytes); + g_object_unref (stream); + g_object_unref (cb); + return; + } + + display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb)); + + atoms = g_bytes_get_data (bytes, NULL); + n_atoms = g_bytes_get_size (bytes) / sizeof (Atom); + builder = gdk_content_formats_builder_new (); + for (i = 0; i < n_atoms; i++) + { + gdk_content_formats_builder_add_mime_type (builder, gdk_x11_get_xatom_name_for_display (display , atoms[i])); + } + formats = gdk_content_formats_builder_free (builder); + GDK_NOTE(CLIPBOARD, char *s = gdk_content_formats_to_string (formats); g_printerr ("%s: got formats: %s\n", cb->selection, s); g_free (s)); + + g_input_stream_read_bytes_async (stream, + SELECTION_MAX_SIZE (display), + G_PRIORITY_DEFAULT, + NULL, + gdk_x11_clipboard_request_targets_finish, + cb); +} + +static void +gdk_x11_clipboard_request_targets (GdkX11Clipboard *cb) +{ + GInputStream *stream; + GdkDisplay *display; + + display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb)); + + stream = gdk_x11_selection_input_stream_new (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)), + cb->selection, + "TARGETS"); + + g_input_stream_read_bytes_async (stream, + SELECTION_MAX_SIZE (display), + G_PRIORITY_DEFAULT, + NULL, + gdk_x11_clipboard_request_targets_finish, + g_object_ref (cb)); +} + +GdkClipboard * +gdk_x11_clipboard_new (GdkDisplay *display, + const gchar *selection) +{ + GdkX11Clipboard *cb; + + cb = g_object_new (GDK_TYPE_X11_CLIPBOARD, + "display", display, + NULL); + + cb->selection = g_strdup (selection); + cb->xselection = gdk_x11_get_xatom_by_name_for_display (display, selection); + + gdk_display_request_selection_notification (display, gdk_atom_intern (selection, FALSE)); + gdk_x11_clipboard_request_targets (cb); + + return GDK_CLIPBOARD (cb); +} + +gboolean +gdk_x11_clipboard_handle_event (GdkX11Clipboard *cb, + XEvent *xevent) +{ + return FALSE; +} + +gboolean +gdk_x11_clipboard_handle_selection_notify (GdkX11Clipboard *cb, + XEvent *xevent) +{ +#ifdef HAVE_XFIXES + GdkDisplay *display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb)); + XFixesSelectionNotifyEvent *event = (XFixesSelectionNotifyEvent *)xevent; + + if (event->window != GDK_X11_DISPLAY (display)->leader_window || + event->selection != cb->xselection) + return FALSE; + + return TRUE; +#else + return FALSE; +#endif +} + diff --git a/gdk/x11/gdkclipboard-x11.h b/gdk/x11/gdkclipboard-x11.h new file mode 100644 index 0000000000..fa8485d3e8 --- /dev/null +++ b/gdk/x11/gdkclipboard-x11.h @@ -0,0 +1,46 @@ +/* GDK - The GIMP Drawing Kit + * 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 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 __GDK_CLIPBOARD_X11_H__ +#define __GDK_CLIPBOARD_X11_H__ + +#include "gdk/gdkclipboard.h" + +#include <X11/Xlib.h> + + +G_BEGIN_DECLS + +#define GDK_TYPE_X11_CLIPBOARD (gdk_x11_clipboard_get_type ()) +#define GDK_X11_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_X11_CLIPBOARD, GdkX11Clipboard)) +#define GDK_IS_X11_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_X11_CLIPBOARD)) + +typedef struct _GdkX11Clipboard GdkX11Clipboard; + +GType gdk_x11_clipboard_get_type (void) G_GNUC_CONST; + +GdkClipboard * gdk_x11_clipboard_new (GdkDisplay *display, + const gchar *selection); + +gboolean gdk_x11_clipboard_handle_event (GdkX11Clipboard *clipboard, + XEvent *xevent); +gboolean gdk_x11_clipboard_handle_selection_notify(GdkX11Clipboard *clipboard, + XEvent *xevent); + +G_END_DECLS + +#endif /* __GDK_CLIPBOARD_X11_H__ */ diff --git a/gdk/x11/gdkdisplay-x11.c b/gdk/x11/gdkdisplay-x11.c index a055056324..4c4da88776 100644 --- a/gdk/x11/gdkdisplay-x11.c +++ b/gdk/x11/gdkdisplay-x11.c @@ -24,6 +24,9 @@ #define VK_USE_PLATFORM_XLIB_KHR +#include "gdkdisplay-x11.h" +#include "gdkdisplayprivate.h" + #include "gdkasync.h" #include "gdkdisplay.h" #include "gdkeventsource.h" @@ -34,13 +37,12 @@ #include "gdkdeviceprivate.h" #include "gdkkeysprivate.h" #include "xsettings-client.h" -#include "gdkdisplay-x11.h" +#include "gdkclipboard-x11.h" #include "gdkprivate-x11.h" #include "gdkscreen-x11.h" #include "gdkglcontext-x11.h" #include "gdkvulkancontext-x11.h" #include "gdk-private.h" -#include "gdkdisplayprivate.h" #include <glib.h> #include <glib/gprintf.h> @@ -1140,16 +1142,24 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator, gdk_display_set_composited (display, composited); } - event->owner_change.type = GDK_OWNER_CHANGE; - event->owner_change.window = window; - event->owner_change.reason = selection_notify->subtype; - event->owner_change.selection = - gdk_x11_xatom_to_atom_for_display (display, - selection_notify->selection); - event->owner_change.time = selection_notify->timestamp; - event->owner_change.selection_time = selection_notify->selection_timestamp; - - return_val = TRUE; + if (gdk_x11_clipboard_handle_selection_notify (GDK_X11_CLIPBOARD (display->clipboard), xevent) || + gdk_x11_clipboard_handle_selection_notify (GDK_X11_CLIPBOARD (display->primary_clipboard), xevent)) + { + return_val = FALSE; + } + else + { + event->owner_change.type = GDK_OWNER_CHANGE; + event->owner_change.window = window; + event->owner_change.reason = selection_notify->subtype; + event->owner_change.selection = + gdk_x11_xatom_to_atom_for_display (display, + selection_notify->selection); + event->owner_change.time = selection_notify->timestamp; + event->owner_change.selection_time = selection_notify->selection_timestamp; + + return_val = TRUE; + } } else #endif @@ -1736,6 +1746,9 @@ gdk_x11_display_open (const gchar *display_name) } #endif + display->clipboard = gdk_x11_clipboard_new (display, "CLIPBOARD"); + display->primary_clipboard = gdk_x11_clipboard_new (display, "PRIMARY"); + /* * It is important that we first request the selection * notification, and then setup the initial state of @@ -2048,6 +2061,9 @@ gdk_x11_display_finalize (GObject *object) /* Empty the event queue */ _gdk_x11_display_free_translate_queue (GDK_DISPLAY (display_x11)); + /* Get rid of pending streams */ + g_slist_free_full (display_x11->input_streams, g_object_unref); + /* Atom Hashtable */ g_hash_table_destroy (display_x11->atom_from_virtual); g_hash_table_destroy (display_x11->atom_to_virtual); diff --git a/gdk/x11/gdkdisplay-x11.h b/gdk/x11/gdkdisplay-x11.h index 93434384da..11a32fbce6 100644 --- a/gdk/x11/gdkdisplay-x11.h +++ b/gdk/x11/gdkdisplay-x11.h @@ -106,6 +106,9 @@ struct _GdkX11Display /* translation queue */ GQueue *translate_queue; + /* streams reading selections */ + GSList *input_streams; + /* input GdkWindow list */ GList *input_windows; diff --git a/gdk/x11/gdkselectioninputstream-x11.c b/gdk/x11/gdkselectioninputstream-x11.c new file mode 100644 index 0000000000..9a4965c0d6 --- /dev/null +++ b/gdk/x11/gdkselectioninputstream-x11.c @@ -0,0 +1,443 @@ +/* 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> + */ + +#include "config.h" + +#include "gdkselectioninputstream-x11.h" + +#include "gdkdisplay-x11.h" +#include "gdkintl.h" +#include "gdkx11display.h" +#include "gdkx11property.h" +#include "gdkx11window.h" + +typedef struct GdkX11SelectionInputStreamPrivate GdkX11SelectionInputStreamPrivate; + +struct GdkX11SelectionInputStreamPrivate { + GdkDisplay *display; + GQueue chunks; + char *selection; + Atom xselection; + char *target; + Atom xtarget; + char *property; + Atom xproperty; + + GTask *pending_task; + guchar *pending_data; + gsize pending_size; + + guint complete : 1; +}; + +G_DEFINE_TYPE_WITH_PRIVATE (GdkX11SelectionInputStream, gdk_x11_selection_input_stream, G_TYPE_INPUT_STREAM); + +static GdkFilterReturn +gdk_x11_selection_input_stream_filter_event (GdkXEvent *xevent, + GdkEvent *gdkevent, + gpointer data); + +static gboolean +gdk_x11_selection_input_stream_has_data (GdkX11SelectionInputStream *stream) +{ + GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (stream); + + return !g_queue_is_empty (&priv->chunks) || priv->complete; +} + +static gssize +gdk_x11_selection_input_stream_fill_buffer (GdkX11SelectionInputStream *stream, + guchar *buffer, + gsize count) +{ + GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (stream); + gssize result = 0; + + while (!g_queue_is_empty (&priv->chunks) && count > 0) + { + GBytes *bytes = g_queue_pop_head (&priv->chunks); + gsize size = g_bytes_get_size (bytes); + + if (g_bytes_get_size (bytes) > 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_queue_push_head (&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; + } + + return result; +} + +static void +gdk_x11_selection_input_stream_flush (GdkX11SelectionInputStream *stream) +{ + GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (stream); + gssize written; + + if (!gdk_x11_selection_input_stream_has_data (stream)) + return; + + if (priv->pending_task == NULL) + return; + + written = gdk_x11_selection_input_stream_fill_buffer (stream, priv->pending_data, priv->pending_size); + g_task_return_int (priv->pending_task, written); + + priv->pending_task = NULL; + priv->pending_data = NULL; + priv->pending_size = 0; +} + +static void +gdk_x11_selection_input_stream_complete (GdkX11SelectionInputStream *stream) +{ + GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (stream); + + if (priv->complete) + return; + + priv->complete = TRUE; + + gdk_x11_selection_input_stream_flush (stream); + + GDK_X11_DISPLAY (priv->display)->input_streams = g_slist_remove (GDK_X11_DISPLAY (priv->display)->input_streams, stream); + gdk_window_remove_filter (GDK_X11_DISPLAY (priv->display)->leader_gdk_window, gdk_x11_selection_input_stream_filter_event, stream); + + g_object_unref (stream); +} + +static gssize +gdk_x11_selection_input_stream_read (GInputStream *stream, + void *buffer, + gsize count, + GCancellable *cancellable, + GError **error) +{ + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("X11 Selections cannot be read synchronously")); + + return -1; +} + +static gboolean +gdk_x11_selection_input_stream_close (GInputStream *stream, + GCancellable *cancellable, + GError **error) +{ + return TRUE; +} + +static void +gdk_x11_selection_input_stream_read_async (GInputStream *input_stream, + void *buffer, + gsize count, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GdkX11SelectionInputStream *stream = GDK_X11_SELECTION_INPUT_STREAM (input_stream); + GdkX11SelectionInputStreamPrivate *priv = gdk_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, gdk_x11_selection_input_stream_read_async); + g_task_set_priority (task, io_priority); + + if (gdk_x11_selection_input_stream_has_data (stream)) + { + gssize size; + + size = gdk_x11_selection_input_stream_fill_buffer (stream, buffer, count); + g_task_return_int (task, size); + } + else + { + priv->pending_data = buffer; + priv->pending_size = count; + priv->pending_task = task; + } +} + +static gssize +gdk_x11_selection_input_stream_read_finish (GInputStream *stream, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, stream), -1); + + return g_task_propagate_int (G_TASK (result), error); +} + +static void +gdk_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, gdk_x11_selection_input_stream_close_async); + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static gboolean +gdk_x11_selection_input_stream_close_finish (GInputStream *stream, + GAsyncResult *result, + GError **error) +{ + return TRUE; +} + +static void +gdk_x11_selection_input_stream_finalize (GObject *object) +{ + GdkX11SelectionInputStream *stream = GDK_X11_SELECTION_INPUT_STREAM (object); + GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (stream); + + gdk_x11_selection_input_stream_complete (stream); + + g_queue_foreach (&priv->chunks, (GFunc) g_bytes_unref, NULL); + g_queue_clear (&priv->chunks); + + g_free (priv->selection); + g_free (priv->target); + g_free (priv->property); + + G_OBJECT_CLASS (gdk_x11_selection_input_stream_parent_class)->finalize (object); +} + +static void +gdk_x11_selection_input_stream_class_init (GdkX11SelectionInputStreamClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GInputStreamClass *istream_class = G_INPUT_STREAM_CLASS (klass); + + object_class->finalize = gdk_x11_selection_input_stream_finalize; + + istream_class->read_fn = gdk_x11_selection_input_stream_read; + istream_class->close_fn = gdk_x11_selection_input_stream_close; + + istream_class->read_async = gdk_x11_selection_input_stream_read_async; + istream_class->read_finish = gdk_x11_selection_input_stream_read_finish; + istream_class->close_async = gdk_x11_selection_input_stream_close_async; + istream_class->close_finish = gdk_x11_selection_input_stream_close_finish; +} + +static void +gdk_x11_selection_input_stream_init (GdkX11SelectionInputStream *stream) +{ +} + +static void +XFree_without_return_value (gpointer data) +{ + XFree (data); +} + +static GBytes * +get_selection_property (Display *display, + Window owner, + Atom property, + Atom *ret_type, + gint *ret_format) +{ + gulong nitems; + gulong nbytes; + Atom prop_type; + gint prop_format; + guchar *data = NULL; + + if (XGetWindowProperty (display, owner, property, + 0, 0x1FFFFFFF, False, + AnyPropertyType, &prop_type, &prop_format, + &nitems, &nbytes, &data) != Success) + goto err; + + if (prop_type != None) + { + gsize 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; +} + + +static GdkFilterReturn +gdk_x11_selection_input_stream_filter_event (GdkXEvent *xev, + GdkEvent *gdkevent, + gpointer data) +{ + GdkX11SelectionInputStream *stream = GDK_X11_SELECTION_INPUT_STREAM (data); + GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (stream); + XEvent *xevent = xev; + Display *xdisplay; + Window xwindow; + +#if 0 + GList *tmp_list; + GtkRetrievalInfo *info = NULL; + GdkWindow *window; + GBytes *bytes; + gint length; + GdkAtom type; + GdkAtom selection; + GdkAtom property; + gint format; + guint32 time; +#endif + + xdisplay = gdk_x11_display_get_xdisplay (priv->display); + xwindow = GDK_X11_DISPLAY (priv->display)->leader_window; + + if (xevent->xany.window != xwindow) + return GDK_FILTER_CONTINUE; + + switch (xevent->type) + { + case SelectionNotify: + { + GBytes *bytes; + Atom type; + gint format; + + if (priv->xselection != xevent->xselection.selection || + priv->xtarget != xevent->xselection.target) + return GDK_FILTER_CONTINUE; + + GDK_NOTE(SELECTION, g_print ("%s:%s: got SelectionNotify\n", priv->selection, priv->target)); + + if (xevent->xselection.property != None) + bytes = get_selection_property (xdisplay, xwindow, xevent->xselection.property, &type, &format); + else + bytes = NULL; + + if (bytes == NULL) + { + gdk_x11_selection_input_stream_complete (stream); + } + else + { + g_queue_push_tail (&priv->chunks, bytes); + + if (type == gdk_x11_get_xatom_by_name_for_display (priv->display, "INCR")) + { + /* The remainder of the selection will come through PropertyNotify + events on xwindow */ + GDK_NOTE(SELECTION, g_print ("%s:%s: initiating INCR transfer\n", priv->selection, priv->target)); + gdk_x11_selection_input_stream_flush (stream); + } + else + { + gdk_x11_selection_input_stream_complete (stream); + } + + XDeleteProperty (xdisplay, xwindow, xevent->xselection.property); + } + } + return GDK_FILTER_REMOVE; + + default: + return GDK_FILTER_CONTINUE; + } +} + +GInputStream * +gdk_x11_selection_input_stream_new (GdkDisplay *display, + const char *selection, + const char *target) +{ + GdkX11SelectionInputStream *stream; + GdkX11SelectionInputStreamPrivate *priv; + + stream = g_object_new (GDK_TYPE_X11_SELECTION_INPUT_STREAM, NULL); + priv = gdk_x11_selection_input_stream_get_instance_private (stream); + + priv->display = display; + GDK_X11_DISPLAY (display)->input_streams = g_slist_prepend (GDK_X11_DISPLAY (display)->input_streams, stream); + priv->selection = g_strdup (selection); + priv->xselection = gdk_x11_get_xatom_by_name_for_display (display, priv->selection); + priv->target = g_strdup (target); + priv->xtarget = gdk_x11_get_xatom_by_name_for_display (display, priv->target); + priv->property = g_strdup_printf ("GDK_SELECTION_%p", stream); + priv->xproperty = gdk_x11_get_xatom_by_name_for_display (display, priv->property); + + gdk_window_add_filter (NULL, gdk_x11_selection_input_stream_filter_event, stream); + + XConvertSelection (GDK_DISPLAY_XDISPLAY (display), + priv->xselection, + priv->xtarget, + priv->xproperty, + GDK_X11_DISPLAY (display)->leader_window, + CurrentTime); + + return g_object_ref (stream); +} + diff --git a/gdk/x11/gdkselectioninputstream-x11.h b/gdk/x11/gdkselectioninputstream-x11.h new file mode 100644 index 0000000000..9947329ee3 --- /dev/null +++ b/gdk/x11/gdkselectioninputstream-x11.h @@ -0,0 +1,59 @@ +/* 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 __GDK_X11_SELECTION_INPUT_STREAM_H__ +#define __GDK_X11_SELECTION_INPUT_STREAM_H__ + +#include <gio/gio.h> +#include "gdktypes.h" + +G_BEGIN_DECLS + +#define GDK_TYPE_X11_SELECTION_INPUT_STREAM (gdk_x11_selection_input_stream_get_type ()) +#define GDK_X11_SELECTION_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_X11_SELECTION_INPUT_STREAM, GdkX11SelectionInputStream)) +#define GDK_X11_SELECTION_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDK_TYPE_X11_SELECTION_INPUT_STREAM, GdkX11SelectionInputStreamClass)) +#define GDK_IS_X11_SELECTION_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_X11_SELECTION_INPUT_STREAM)) +#define GDK_IS_X11_SELECTION_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDK_TYPE_X11_SELECTION_INPUT_STREAM)) +#define GDK_X11_SELECTION_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_X11_SELECTION_INPUT_STREAM, GdkX11SelectionInputStreamClass)) + +typedef struct GdkX11SelectionInputStream GdkX11SelectionInputStream; +typedef struct GdkX11SelectionInputStreamClass GdkX11SelectionInputStreamClass; + +struct GdkX11SelectionInputStream +{ + GInputStream parent_instance; +}; + +struct GdkX11SelectionInputStreamClass +{ + GInputStreamClass parent_class; +}; + + +GType gdk_x11_selection_input_stream_get_type (void) G_GNUC_CONST; + +GInputStream * gdk_x11_selection_input_stream_new (GdkDisplay *display, + const char *selection, + const char *target); + +G_END_DECLS + +#endif /* __GDK_X11_SELECTION_INPUT_STREAM_H__ */ diff --git a/gdk/x11/meson.build b/gdk/x11/meson.build index 526329f155..57ed429df4 100644 --- a/gdk/x11/meson.build +++ b/gdk/x11/meson.build @@ -2,6 +2,7 @@ gdk_x11_sources = files([ 'gdkapplaunchcontext-x11.c', 'gdkasync.c', + 'gdkclipboard-x11.c', 'gdkcursor-x11.c', 'gdkdevice-core-x11.c', 'gdkdevice-xi2.c', @@ -20,6 +21,7 @@ gdk_x11_sources = files([ 'gdkproperty-x11.c', 'gdkscreen-x11.c', 'gdkselection-x11.c', + 'gdkselectioninputstream-x11.c', 'gdkvisual-x11.c', 'gdkvulkancontext-x11.c', 'gdkwindow-x11.c', |