366 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			366 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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++;
 | |
|     }
 | |
| 
 | |
|   closure->n_targets = j;
 | |
| 
 | |
|   gdk_wayland_device_offer_selection_content (device,
 | |
|                                               (const gchar **)mimetypes,
 | |
|                                               j,
 | |
|                                               _offer_cb,
 | |
|                                               closure);
 | |
|   clipboard->last_closure = closure;
 | |
| 
 | |
|   g_free (mimetypes);
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_clipboard_wayland_clear (GtkClipboard *gtkclipboard)
 | |
| {
 | |
|   GtkClipboardWayland *clipboard = GTK_CLIPBOARD_WAYLAND (gtkclipboard);
 | |
|   GdkDeviceManager *device_manager;
 | |
|   GdkDevice *device;
 | |
| 
 | |
|   if (!clipboard->last_closure)
 | |
|     return;
 | |
| 
 | |
|   device_manager = gdk_display_get_device_manager (gdk_display_get_default ());
 | |
|   device = gdk_device_manager_get_client_pointer (device_manager);
 | |
| 
 | |
|   gdk_wayland_device_clear_selection_content (device);
 | |
| 
 | |
|   if (clipboard->last_closure->clear_func)
 | |
|     {
 | |
|       clipboard->last_closure->clear_func (gtkclipboard,
 | |
|                                            clipboard->last_closure->userdata);
 | |
|     }
 | |
| 
 | |
|   if (clipboard->last_closure->have_owner)
 | |
|     g_object_weak_unref (G_OBJECT (clipboard->last_closure->userdata),
 | |
|                          clipboard_owner_destroyed, clipboard);
 | |
|   g_free (clipboard->last_closure->targets);
 | |
|   g_free (clipboard->last_closure);
 | |
| 
 | |
|   clipboard->last_closure = NULL;
 | |
| }
 | |
| 
 | |
| typedef struct {
 | |
|     GtkClipboard *clipboard;
 | |
|     GCallback cb;
 | |
|     gpointer userdata;
 | |
|     GdkAtom target;
 | |
| } ClipboardRequestClosure;
 | |
| 
 | |
| static void
 | |
| _request_generic_cb (GdkDevice   *device,
 | |
|                      const gchar *data,
 | |
|                      gsize        len,
 | |
|                      gpointer     userdata)
 | |
| {
 | |
|   ClipboardRequestClosure *closure = (ClipboardRequestClosure *)userdata;
 | |
|   GtkClipboardReceivedFunc cb = (GtkClipboardReceivedFunc)closure->cb;
 | |
|   GtkSelectionData selection_data;
 | |
| 
 | |
|   selection_data.selection = GDK_SELECTION_CLIPBOARD;
 | |
|   selection_data.target = closure->target;
 | |
|   selection_data.type = closure->target;
 | |
|   selection_data.length = len;
 | |
|   selection_data.data = (guchar *)data;
 | |
| 
 | |
|   cb (closure->clipboard, &selection_data, closure->userdata);
 | |
| 
 | |
|   g_free (closure);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_clipboard_wayland_request_contents (GtkClipboard            *gtkclipboard,
 | |
|                                         GdkAtom                  target,
 | |
|                                         GtkClipboardReceivedFunc callback,
 | |
|                                         gpointer                 user_data)
 | |
| {
 | |
|   GdkDeviceManager *device_manager;
 | |
|   GdkDevice *device;
 | |
|   ClipboardRequestClosure *closure;
 | |
|   gchar *mime_type;
 | |
| 
 | |
|   device_manager = gdk_display_get_device_manager (gdk_display_get_default ());
 | |
|   device = gdk_device_manager_get_client_pointer (device_manager);
 | |
| 
 | |
|   if (target == gdk_atom_intern_static_string ("TARGETS"))
 | |
|     {
 | |
|       GtkSelectionData selection_data;
 | |
|       int n_atoms;
 | |
|       GdkAtom *atoms;
 | |
| 
 | |
|       selection_data.selection = GDK_NONE;
 | |
|       selection_data.format = 32;
 | |
|       selection_data.type = GDK_SELECTION_TYPE_ATOM;
 | |
| 
 | |
|       n_atoms = gdk_wayland_device_get_selection_type_atoms (device, &atoms);
 | |
|       selection_data.length = n_atoms;
 | |
|       selection_data.data = (guchar *) atoms;
 | |
| 
 | |
|       callback (gtkclipboard, &selection_data, user_data);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   /* When GTK+ requests text, it tries UTF8_STRING first and then
 | |
|    * falls back to COMPOUND_TEXT and then STRING.  We rewrite
 | |
|    * UTF8_STRING to text/plain;charset=utf-8, and if that doesn't
 | |
|    * work, just fail the fallback targets.  Maybe we could do this in
 | |
|    * a generic way that just compares the target against the targets
 | |
|    * advertised by the data offer. */
 | |
|   if (target == gdk_atom_intern_static_string ("UTF8_STRING"))
 | |
|     target = gdk_atom_intern_static_string ("text/plain;charset=utf-8");
 | |
|   else if (target == gdk_atom_intern_static_string ("COMPOUND_TEXT") ||
 | |
| 	   target == GDK_TARGET_STRING)
 | |
|     {
 | |
|       GtkSelectionData selection_data;
 | |
| 
 | |
|       selection_data.selection = GDK_NONE;
 | |
|       selection_data.target = GDK_NONE;
 | |
|       selection_data.type = GDK_NONE;
 | |
|       selection_data.length = 0;
 | |
|       selection_data.data = NULL;
 | |
| 
 | |
|       callback (gtkclipboard, &selection_data, user_data);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   closure = g_new0 (ClipboardRequestClosure, 1);
 | |
|   closure->clipboard = gtkclipboard;
 | |
|   closure->cb = (GCallback)callback;
 | |
|   closure->userdata = user_data;
 | |
|   closure->target = target;
 | |
| 
 | |
|   /* TODO: Do we need to check that target is valid ? */
 | |
|   mime_type = gdk_atom_name (target);
 | |
| 
 | |
|   gdk_wayland_device_request_selection_content (device,
 | |
|                                                 mime_type,
 | |
|                                                 _request_generic_cb,
 | |
|                                                 closure);
 | |
| 
 | |
|   g_free (mime_type);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_clipboard_wayland_owner_change (GtkClipboard        *clipboard,
 | |
|                                     GdkEventOwnerChange *event)
 | |
| {
 | |
| 
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_clipboard_wayland_set_can_store (GtkClipboard         *clipboard,
 | |
|                                      const GtkTargetEntry *targets,
 | |
|                                      gint                  n_targets)
 | |
| {
 | |
|   /* FIXME: Implement */
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_clipboard_wayland_store (GtkClipboard *clipboard)
 | |
| {
 | |
|   /* FIXME: Implement */
 | |
| }
 | |
| 
 | |
| #endif /* GDK_WINDOWING_WAYLAND */
 | 
