Commit f48b3cce authored by Carlos Garnacho's avatar Carlos Garnacho

wayland: Replace clipboard implementation

The wayland specific clipboard functions have been replaced by something
more similar to the hooking the win32 backend does, which allows for just
using the default GtkClipboard code in GTK+. As a consequence, the
wayland-specific GtkClipboard implementation is now gone.

https://bugzilla.gnome.org/show_bug.cgi?id=697855
parent 77447990
......@@ -35,10 +35,6 @@
#include <sys/time.h>
#include <sys/mman.h>
typedef struct _DataOffer DataOffer;
typedef struct _GdkWaylandSelectionOffer GdkWaylandSelectionOffer;
typedef struct _GdkWaylandTouchData GdkWaylandTouchData;
struct _GdkWaylandTouchData
......@@ -93,10 +89,6 @@ struct _GdkWaylandDeviceData
guint cursor_timeout_id;
guint cursor_image_index;
DataOffer *drag_offer;
DataOffer *selection_offer;
GdkWaylandSelectionOffer *selection_offer_out;
struct wl_surface *pointer_surface;
};
......@@ -512,61 +504,12 @@ _gdk_wayland_device_get_keymap (GdkDevice *device)
return GDK_WAYLAND_DEVICE (device)->device->keymap;
}
struct _DataOffer {
struct wl_data_offer *offer;
gint ref_count;
GPtrArray *types;
};
static void
data_offer_offer (void *data,
struct wl_data_offer *wl_data_offer,
const char *type)
{
DataOffer *offer = (DataOffer *)data;
g_debug (G_STRLOC ": %s wl_data_offer = %p type = %s",
G_STRFUNC, wl_data_offer, type);
g_ptr_array_add (offer->types, g_strdup (type));
}
static void
data_offer_unref (DataOffer *offer)
{
offer->ref_count--;
if (offer->ref_count == 0)
{
g_ptr_array_free (offer->types, TRUE);
g_free (offer);
}
}
static const struct wl_data_offer_listener data_offer_listener = {
data_offer_offer,
};
static void
data_device_data_offer (void *data,
struct wl_data_device *data_device,
struct wl_data_offer *_offer)
{
DataOffer *offer;
/* This structure is reference counted to handle the case where you get a
* leave but are in the middle of transferring data
*/
offer = g_new0 (DataOffer, 1);
offer->ref_count = 1;
offer->types = g_ptr_array_new_with_free_func (g_free);
offer->offer = _offer;
/* The DataOffer structure is then retrieved later since this sets the user
* data.
*/
wl_data_offer_add_listener (offer->offer,
&data_offer_listener,
offer);
gdk_wayland_selection_set_offer (_offer);
}
static void
......@@ -583,11 +526,6 @@ data_device_enter (void *data,
g_debug (G_STRLOC ": %s data_device = %p serial = %u, surface = %p, x = %d y = %d, offer = %p",
G_STRFUNC, data_device, serial, surface, x, y, offer);
/* Retrieve the DataOffer associated with with the wl_data_offer - this
* association is made when the listener is attached.
*/
g_assert (device->drag_offer == NULL);
device->drag_offer = wl_data_offer_get_user_data (offer);
}
static void
......@@ -599,8 +537,6 @@ data_device_leave (void *data,
g_debug (G_STRLOC ": %s data_device = %p",
G_STRFUNC, data_device);
data_offer_unref (device->drag_offer);
device->drag_offer = NULL;
}
static void
......@@ -627,33 +563,10 @@ data_device_selection (void *data,
struct wl_data_device *wl_data_device,
struct wl_data_offer *offer)
{
GdkWaylandDeviceData *device = (GdkWaylandDeviceData *)data;
g_debug (G_STRLOC ": %s wl_data_device = %p wl_data_offer = %p",
G_STRFUNC, wl_data_device, offer);
if (!offer)
{
if (device->selection_offer)
{
data_offer_unref (device->selection_offer);
device->selection_offer = NULL;
}
return;
}
if (device->selection_offer)
{
data_offer_unref (device->selection_offer);
device->selection_offer = NULL;
}
/* Retrieve the DataOffer associated with with the wl_data_offer -
* this association is made when the listener is attached.
*/
g_assert (device->selection_offer == NULL);
device->selection_offer = wl_data_offer_get_user_data (offer);
gdk_wayland_selection_set_offer (offer);
}
static const struct wl_data_device_listener data_device_listener = {
......@@ -1876,290 +1789,6 @@ _gdk_wayland_device_get_last_implicit_grab_serial (GdkWaylandDevice *device,
return serial;
}
static GdkAtom
mime_type_to_gdk_atom (char *mime_type)
{
if (strcmp (mime_type, "text/plain;charset=utf8"))
return gdk_atom_intern_static_string ("UTF8_STRING");
return GDK_NONE;
}
gint
gdk_wayland_device_get_selection_type_atoms (GdkDevice *gdk_device,
GdkAtom **atoms_out)
{
gint i;
GdkAtom *atoms;
GdkWaylandDeviceData *device;
g_return_val_if_fail (GDK_IS_WAYLAND_DEVICE (gdk_device), 0);
g_return_val_if_fail (atoms_out != NULL, 0);
device = GDK_WAYLAND_DEVICE (gdk_device)->device;
if (!device->selection_offer || device->selection_offer->types->len == 0)
{
*atoms_out = NULL;
return 0;
}
atoms = g_new0 (GdkAtom, device->selection_offer->types->len);
/* Convert list of targets to atoms */
for (i = 0; i < device->selection_offer->types->len; i++)
atoms[i] = mime_type_to_gdk_atom (device->selection_offer->types->pdata[i]);
*atoms_out = atoms;
return device->selection_offer->types->len;
}
typedef struct
{
GdkWaylandDeviceData *device;
DataOffer *offer;
GIOChannel *channel;
GdkDeviceWaylandRequestContentCallback cb;
gpointer userdata;
} RequestContentClosure;
static gboolean
_request_content_io_func (GIOChannel *channel,
GIOCondition condition,
gpointer userdata)
{
RequestContentClosure *closure = (RequestContentClosure *)userdata;
gchar *data = NULL;
gsize len = 0;
GError *error = NULL;
/* FIXME: We probably want to do something better than this
* to avoid blocking on the transfer of large pieces of data:
* call the callback multiple times I should think.
*/
if (g_io_channel_read_to_end (channel, &data, &len, &error) != G_IO_STATUS_NORMAL)
{
g_warning (G_STRLOC ": Error reading content from pipe: %s", error->message);
g_clear_error (&error);
}
/* Since we use _read_to_end we've got a guaranteed EOF and thus can go
* ahead and close the fd
*/
g_io_channel_shutdown (channel, TRUE, NULL);
closure->cb (closure->device->pointer, data, len, closure->userdata);
g_free (data);
data_offer_unref (closure->offer);
g_io_channel_unref (channel);
g_free (closure);
return FALSE;
}
gboolean
gdk_wayland_device_request_selection_content (GdkDevice *gdk_device,
const gchar *requested_mime_type,
GdkDeviceWaylandRequestContentCallback cb,
gpointer userdata)
{
int pipe_fd[2];
RequestContentClosure *closure;
GdkWaylandDeviceData *device;
GError *error = NULL;
g_return_val_if_fail (GDK_IS_WAYLAND_DEVICE (gdk_device), FALSE);
g_return_val_if_fail (requested_mime_type != NULL, FALSE);
g_return_val_if_fail (cb != NULL, FALSE);
device = GDK_WAYLAND_DEVICE (gdk_device)->device;
if (!device->selection_offer)
return FALSE;
/* TODO: Check mimetypes */
closure = g_new0 (RequestContentClosure, 1);
device->selection_offer->ref_count++;
pipe2 (pipe_fd, O_CLOEXEC);
wl_data_offer_receive (device->selection_offer->offer,
requested_mime_type,
pipe_fd[1]);
close (pipe_fd[1]);
closure->device = device;
closure->offer = device->selection_offer;
closure->channel = g_io_channel_unix_new (pipe_fd[0]);
closure->cb = cb;
closure->userdata = userdata;
if (!g_io_channel_set_encoding (closure->channel, NULL, &error))
{
g_warning (G_STRLOC ": Error setting encoding on channel: %s",
error->message);
g_clear_error (&error);
goto error;
}
g_io_add_watch (closure->channel,
G_IO_IN,
_request_content_io_func,
closure);
return TRUE;
error:
data_offer_unref (closure->offer);
g_io_channel_unref (closure->channel);
close (pipe_fd[1]);
g_free (closure);
return FALSE;
}
struct _GdkWaylandSelectionOffer {
GdkDeviceWaylandOfferContentCallback cb;
gpointer userdata;
struct wl_data_source *source;
GdkWaylandDeviceData *device;
};
static void
data_source_target (void *data,
struct wl_data_source *source,
const char *mime_type)
{
g_debug (G_STRLOC ": %s source = %p, mime_type = %s",
G_STRFUNC, source, mime_type);
}
static void
data_source_send (void *data,
struct wl_data_source *source,
const char *mime_type,
int32_t fd)
{
GdkWaylandSelectionOffer *offer = (GdkWaylandSelectionOffer *)data;
gchar *buf;
gssize len, bytes_written = 0;
g_debug (G_STRLOC ": %s source = %p, mime_type = %s fd = %d",
G_STRFUNC, source, mime_type, fd);
buf = offer->cb (offer->device->pointer, mime_type, &len, offer->userdata);
while (len > 0)
{
bytes_written += write (fd, buf + bytes_written, len);
if (bytes_written == -1)
goto error;
len -= bytes_written;
}
close (fd);
g_free (buf);
return;
error:
g_warning (G_STRLOC ": Error writing data to client: %s",
g_strerror (errno));
close (fd);
g_free (buf);
}
static void
data_source_cancelled (void *data,
struct wl_data_source *source)
{
g_debug (G_STRLOC ": %s source = %p",
G_STRFUNC, source);
}
static const struct wl_data_source_listener data_source_listener = {
data_source_target,
data_source_send,
data_source_cancelled
};
static guint32
_wl_time_now (void)
{
struct timeval tv;
gettimeofday (&tv, NULL);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
gboolean
gdk_wayland_device_offer_selection_content (GdkDevice *gdk_device,
const gchar **mime_types,
gint nr_mime_types,
GdkDeviceWaylandOfferContentCallback cb,
gpointer userdata)
{
GdkDisplay *display;
GdkWaylandDisplay *display_wayland;
GdkWaylandSelectionOffer *offer;
GdkWaylandDeviceData *device;
gint i;
g_return_val_if_fail (GDK_IS_WAYLAND_DEVICE (gdk_device), 0);
device = GDK_WAYLAND_DEVICE (gdk_device)->device;
display = device->display;
display_wayland = GDK_WAYLAND_DISPLAY (display);
offer = g_new0 (GdkWaylandSelectionOffer, 1);
offer->cb = cb;
offer->userdata = userdata;
offer->source =
wl_data_device_manager_create_data_source (display_wayland->data_device_manager);
offer->device = device;
for (i = 0; i < nr_mime_types; i++)
wl_data_source_offer (offer->source, mime_types[i]);
wl_data_source_add_listener (offer->source,
&data_source_listener,
offer);
wl_data_device_set_selection (device->data_device,
offer->source,
_gdk_wayland_display_get_serial (display_wayland));
device->selection_offer_out = offer;
return TRUE;
}
gboolean
gdk_wayland_device_clear_selection_content (GdkDevice *gdk_device)
{
GdkWaylandDeviceData *device;
g_return_val_if_fail (GDK_IS_WAYLAND_DEVICE (gdk_device), 0);
device = GDK_WAYLAND_DEVICE (gdk_device)->device;
if (!device->selection_offer_out)
return FALSE;
wl_data_device_set_selection (device->data_device,
NULL,
_wl_time_now ());
wl_data_source_destroy (device->selection_offer_out->source);
g_free (device->selection_offer_out);
device->selection_offer_out = NULL;
return TRUE;
}
void
gdk_wayland_device_unset_touch_grab (GdkDevice *gdk_device,
GdkEventSequence *sequence)
......
......@@ -886,3 +886,41 @@ _gdk_wayland_display_utf8_to_string_target (GdkDisplay *display,
{
return NULL;
}
void
gdk_wayland_selection_add_targets (GdkWindow *window,
GdkAtom selection,
guint ntargets,
GdkAtom *targets)
{
struct wl_data_source *data_source;
guint i;
g_return_if_fail (GDK_IS_WINDOW (window));
data_source = gdk_wayland_selection_get_data_source (window, selection);
if (!data_source)
return;
for (i = 0; i < ntargets; i++)
wl_data_source_offer (data_source, gdk_atom_name (targets[i]));
if (selection == atoms[ATOM_CLIPBOARD])
{
GdkDeviceManager *device_manager;
GdkDisplay *display;
GdkDevice *device;
display = gdk_display_get_default ();
device_manager = gdk_display_get_device_manager (display);
device = gdk_device_manager_get_client_pointer (device_manager);
gdk_wayland_device_set_selection (device, data_source);
}
}
void
gdk_wayland_selection_clear_targets (GdkAtom selection)
{
gdk_wayland_selection_unset_data_source (selection);
}
......@@ -34,37 +34,19 @@
G_BEGIN_DECLS
#if defined (GTK_COMPILATION) || defined (GDK_COMPILATION)
#define gdk_wayland_device_get_selection_type_atoms gdk_wayland_device_get_selection_type_atoms_libgtk_only
#define gdk_wayland_selection_add_targets gdk_wayland_selection_add_targets_libgtk_only
GDK_AVAILABLE_IN_ALL
int
gdk_wayland_device_get_selection_type_atoms (GdkDevice *device,
GdkAtom **atoms_out);
void
gdk_wayland_selection_add_targets (GdkWindow *window,
GdkAtom selection,
guint ntargets,
GdkAtom *targets);
typedef void (*GdkDeviceWaylandRequestContentCallback) (GdkDevice *device, const gchar *data, gsize len, gpointer userdata);
#define gdk_wayland_device_request_selection_content gdk_wayland_device_request_selection_content_libgtk_only
#define gdk_wayland_selection_clear_targets gdk_wayland_selection_clear_targets_libgtk_only
GDK_AVAILABLE_IN_ALL
gboolean
gdk_wayland_device_request_selection_content (GdkDevice *device,
const gchar *requested_mime_type,
GdkDeviceWaylandRequestContentCallback cb,
gpointer userdata);
typedef gchar *(*GdkDeviceWaylandOfferContentCallback) (GdkDevice *device, const gchar *mime_type, gssize *len, gpointer userdata);
void
gdk_wayland_selection_clear_targets (GdkAtom selection);
#define gdk_wayland_device_offer_selection_content gdk_wayland_device_offer_selection_content_libgtk_only
GDK_AVAILABLE_IN_ALL
gboolean
gdk_wayland_device_offer_selection_content (GdkDevice *gdk_device,
const gchar **mime_types,
gint nr_mime_types,
GdkDeviceWaylandOfferContentCallback cb,
gpointer userdata);
#define gdk_wayland_device_clear_selection_content gdk_wayland_device_clear_selection_content_libgtk_only
GDK_AVAILABLE_IN_ALL
gboolean
gdk_wayland_device_clear_selection_content (GdkDevice *gdk_device);
#endif
......
......@@ -526,7 +526,6 @@ gtk_private_h_sources = \
gtkcairoblurprivate.h \
gtkcellareaboxcontextprivate.h \
gtkclipboardprivate.h \
gtkclipboard-waylandprivate.h \
gtkcolorswatchprivate.h \
gtkcoloreditorprivate.h \
gtkcolorplaneprivate.h \
......@@ -1237,7 +1236,6 @@ else
# No wayland gtkdnd-wayland.c yet
gtk_clipboard_dnd_c_sources = \
gtkclipboard.c \
gtkclipboard-wayland.c \
gtkdnd.c
endif
......
/* GTK - The GIMP Toolkit
* Copyright (C) 2000 Red Hat, Inc.
* Copyright (C) 2004 Nokia Corporation
* Copyright (C) 2006-2008 Imendio AB
* Copyright (C) 2011-2012 Intel Corporation
*
* 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 "gtkclipboard-waylandprivate.h"
#ifdef GDK_WINDOWING_WAYLAND
#include <string.h>
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtkintl.h"
#include "gtkselectionprivate.h"
static void gtk_clipboard_wayland_owner_change (GtkClipboard *clipboard,
GdkEventOwnerChange *event);
static gboolean gtk_clipboard_wayland_set_contents (GtkClipboard *clipboard,
const GtkTargetEntry *targets,
guint n_targets,
GtkClipboardGetFunc get_func,
GtkClipboardClearFunc clear_func,
gpointer user_data,
gboolean have_owner);
static void gtk_clipboard_wayland_clear (GtkClipboard *clipboard);
static void gtk_clipboard_wayland_request_contents (GtkClipboard *clipboard,
GdkAtom target,
GtkClipboardReceivedFunc callback,
gpointer user_data);
static void gtk_clipboard_wayland_set_can_store (GtkClipboard *clipboard,
const GtkTargetEntry *targets,
gint n_targets);
static void gtk_clipboard_wayland_store (GtkClipboard *clipboard);
G_DEFINE_TYPE (GtkClipboardWayland, gtk_clipboard_wayland, GTK_TYPE_CLIPBOARD);
static void
gtk_clipboard_wayland_class_init (GtkClipboardWaylandClass *klass)
{
GtkClipboardClass *clipboard_class = GTK_CLIPBOARD_CLASS (klass);
clipboard_class->set_contents = gtk_clipboard_wayland_set_contents;
clipboard_class->clear = gtk_clipboard_wayland_clear;
clipboard_class->request_contents = gtk_clipboard_wayland_request_contents;
clipboard_class->set_can_store = gtk_clipboard_wayland_set_can_store;
clipboard_class->store = gtk_clipboard_wayland_store;
clipboard_class->owner_change = gtk_clipboard_wayland_owner_change;
}
static void
gtk_clipboard_wayland_init (GtkClipboardWayland *clipboard)
{
}
struct _SetContentClosure {
GtkClipboard *clipboard;
GtkClipboardGetFunc get_func;
GtkClipboardClearFunc clear_func;
guint info;
gboolean have_owner;
gpointer userdata;
GtkTargetPair *targets;
gint n_targets;
};
static gchar *
_offer_cb (GdkDevice *device,
const gchar *mime_type,
gssize *len,
gpointer userdata)
{
SetContentClosure *closure = (SetContentClosure *)userdata;
GtkSelectionData selection_data = { 0, };
guint info = 0;
gint i;
selection_data.target = gdk_atom_intern (mime_type, FALSE);
for (i = 0; i < closure->n_targets; i++)
{
if (closure->targets[i].target == selection_data.target)
{
info = closure->targets[i].info;
break;
}
}
closure->get_func (closure->clipboard,
&selection_data,
info,
closure->userdata);
*len = gtk_selection_data_get_length (&selection_data);
/* The caller of this callback will free this data - the GtkClipboardGetFunc
* uses gtk_selection_data_set which copies*/
return (gchar *)selection_data.data;
}
static void
clipboard_owner_destroyed (gpointer data,
GObject *owner)
{
GtkClipboardWayland *clipboard = GTK_CLIPBOARD_WAYLAND (data);
GtkClipboard *gtkclipboard = GTK_CLIPBOARD (data);
SetContentClosure *last_closure = clipboard->last_closure;
last_closure->userdata = NULL;
last_closure->get_func = NULL;
last_closure->clear_func = NULL;
last_closure->have_owner = FALSE;
gtk_clipboard_clear (gtkclipboard);
}
static gboolean
gtk_clipboard_wayland_set_contents (GtkClipboard *gtkclipboard,
const GtkTargetEntry *targets,
guint n_targets,
GtkClipboardGetFunc get_func,
GtkClipboardClearFunc clear_func,
gpointer user_data,
gboolean have_owner)
{
GtkClipboardWayland *clipboard = GTK_CLIPBOARD_WAYLAND (gtkclipboard);
GdkDeviceManager *device_manager;
GdkDevice *device;
gint i, j;
gchar **mimetypes;
SetContentClosure *closure, *last_closure;
if (gtkclipboard->selection != GDK_SELECTION_CLIPBOARD)
return FALSE;
last_closure = clipboard->last_closure;
if (!last_closure ||
(!last_closure->have_owner && have_owner) ||
(last_closure->userdata != user_data)) {
gtk_clipboard_clear (gtkclipboard);
closure = g_new0 (SetContentClosure, 1);
closure->clipboard = gtkclipboard;
closure->userdata = user_data;
closure->have_owner = have_owner;
if (have_owner)
g_object_weak_ref (G_OBJECT (user_data), clipboard_owner_destroyed, clipboard);
} else {
closure = last_closure;
g_free (closure->targets);
}
closure->get_func = get_func;
closure->clear_func = clear_func;
closure->targets = g_new0 (GtkTargetPair, n_targets);
device_manager = gdk_display_get_device_manager (gdk_display_get_default ());
device = gdk_device_manager_get_client_pointer (device_manager);
mimetypes = g_new (gchar *, n_targets);
for (i = 0, j = 0; i < n_targets; i++)
{
if (strcmp(targets[i].target, "COMPOUND_TEXT") == 0)
continue;
if (strcmp(targets[i].target, "UTF8_STRING") == 0)
continue;
if (strcmp(targets[i].target, "TEXT") == 0)
continue;
if (strcmp(targets[i].target, "STRING") == 0)
continue;
if (strcmp(targets[i].target, "GTK_TEXT_BUFFER_CONTENTS") == 0)
continue;
mimetypes[j] = targets[i].target;
closure->targets[j].target = gdk_atom_intern (targets[i].target, FALSE);
closure->targets[j].flags = targets[i].flags;
closure->targets[j].info = targets[i].info;
j++;