gnome-shell-3.6.3.1/src/st/st-clipboard.c

No issues found

  1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
  2 /*
  3  * st-clipboard.c: clipboard object
  4  *
  5  * Copyright 2009 Intel Corporation.
  6  *
  7  * This program is free software; you can redistribute it and/or modify it
  8  * under the terms and conditions of the GNU Lesser General Public License,
  9  * version 2.1, as published by the Free Software Foundation.
 10  *
 11  * This program is distributed in the hope it will be useful, but WITHOUT ANY
 12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 13  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
 14  * more details.
 15  *
 16  * You should have received a copy of the GNU Lesser General Public License
 17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
 18  */
 19 
 20 /**
 21  * SECTION:st-clipboard
 22  * @short_description: a simple representation of the X clipboard
 23  *
 24  * #StCliboard is a very simple object representation of the clipboard
 25  * available to applications. Text is always assumed to be UTF-8 and non-text
 26  * items are not handled.
 27  */
 28 
 29 
 30 #include "st-clipboard.h"
 31 #include <X11/Xlib.h>
 32 #include <X11/Xatom.h>
 33 #include <clutter/x11/clutter-x11.h>
 34 #include <string.h>
 35 
 36 G_DEFINE_TYPE (StClipboard, st_clipboard, G_TYPE_OBJECT)
 37 
 38 #define CLIPBOARD_PRIVATE(o) \
 39   (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_CLIPBOARD, StClipboardPrivate))
 40 
 41 struct _StClipboardPrivate
 42 {
 43   Window clipboard_window;
 44   gchar *clipboard_text;
 45 
 46   Atom  *supported_targets;
 47   gint   n_targets;
 48 };
 49 
 50 typedef struct _EventFilterData EventFilterData;
 51 struct _EventFilterData
 52 {
 53   StClipboard            *clipboard;
 54   StClipboardCallbackFunc callback;
 55   gpointer                user_data;
 56 };
 57 
 58 static Atom __atom_clip = None;
 59 static Atom __utf8_string = None;
 60 static Atom __atom_targets = None;
 61 
 62 static void
 63 st_clipboard_get_property (GObject    *object,
 64                            guint       property_id,
 65                            GValue     *value,
 66                            GParamSpec *pspec)
 67 {
 68   switch (property_id)
 69     {
 70     default:
 71       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 72     }
 73 }
 74 
 75 static void
 76 st_clipboard_set_property (GObject      *object,
 77                            guint         property_id,
 78                            const GValue *value,
 79                            GParamSpec   *pspec)
 80 {
 81   switch (property_id)
 82     {
 83     default:
 84       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 85     }
 86 }
 87 
 88 static void
 89 st_clipboard_dispose (GObject *object)
 90 {
 91   G_OBJECT_CLASS (st_clipboard_parent_class)->dispose (object);
 92 }
 93 
 94 static void
 95 st_clipboard_finalize (GObject *object)
 96 {
 97   StClipboardPrivate *priv = ((StClipboard *) object)->priv;
 98 
 99   g_free (priv->clipboard_text);
100   priv->clipboard_text = NULL;
101 
102   g_free (priv->supported_targets);
103   priv->supported_targets = NULL;
104   priv->n_targets = 0;
105 
106   G_OBJECT_CLASS (st_clipboard_parent_class)->finalize (object);
107 }
108 
109 static ClutterX11FilterReturn
110 st_clipboard_provider (XEvent       *xev,
111                        ClutterEvent *cev,
112                        StClipboard  *clipboard)
113 {
114   XSelectionEvent notify_event;
115   XSelectionRequestEvent *req_event;
116 
117   if (xev->type != SelectionRequest)
118     return CLUTTER_X11_FILTER_CONTINUE;
119 
120   req_event = &xev->xselectionrequest;
121 
122   clutter_x11_trap_x_errors ();
123 
124   if (req_event->target == __atom_targets)
125     {
126       XChangeProperty (req_event->display,
127                        req_event->requestor,
128                        req_event->property,
129                        XA_ATOM,
130                        32,
131                        PropModeReplace,
132                        (guchar*) clipboard->priv->supported_targets,
133                        clipboard->priv->n_targets);
134     }
135   else
136     {
137       XChangeProperty (req_event->display,
138                        req_event->requestor,
139                        req_event->property,
140                        req_event->target,
141                        8,
142                        PropModeReplace,
143                        (guchar*) clipboard->priv->clipboard_text,
144                        strlen (clipboard->priv->clipboard_text));
145     }
146 
147   notify_event.type = SelectionNotify;
148   notify_event.display = req_event->display;
149   notify_event.requestor = req_event->requestor;
150   notify_event.selection = req_event->selection;
151   notify_event.target = req_event->target;
152   notify_event.time = req_event->time;
153 
154   if (req_event->property == None)
155     notify_event.property = req_event->target;
156   else
157     notify_event.property = req_event->property;
158 
159   /* notify the requestor that they have a copy of the selection */
160   XSendEvent (req_event->display, req_event->requestor, False, 0,
161               (XEvent *) &notify_event);
162   /* Make it happen non async */
163   XSync (clutter_x11_get_default_display(), FALSE);
164 
165   clutter_x11_untrap_x_errors (); /* FIXME: Warn here on fail ? */
166 
167   return CLUTTER_X11_FILTER_REMOVE;
168 }
169 
170 
171 static void
172 st_clipboard_class_init (StClipboardClass *klass)
173 {
174   GObjectClass *object_class = G_OBJECT_CLASS (klass);
175 
176   g_type_class_add_private (klass, sizeof (StClipboardPrivate));
177 
178   object_class->get_property = st_clipboard_get_property;
179   object_class->set_property = st_clipboard_set_property;
180   object_class->dispose = st_clipboard_dispose;
181   object_class->finalize = st_clipboard_finalize;
182 }
183 
184 static void
185 st_clipboard_init (StClipboard *self)
186 {
187   Display *dpy;
188   StClipboardPrivate *priv;
189 
190   priv = self->priv = CLIPBOARD_PRIVATE (self);
191 
192   priv->clipboard_window =
193     XCreateSimpleWindow (clutter_x11_get_default_display (),
194                          clutter_x11_get_root_window (),
195                          -1, -1, 1, 1, 0, 0, 0);
196 
197   dpy = clutter_x11_get_default_display ();
198 
199   /* Only create once */
200   if (__atom_clip == None)
201     __atom_clip = XInternAtom (dpy, "CLIPBOARD", 0);
202 
203   if (__utf8_string == None)
204     __utf8_string = XInternAtom (dpy, "UTF8_STRING", 0);
205 
206   if (__atom_targets == None)
207     __atom_targets = XInternAtom (dpy, "TARGETS", 0);
208 
209   priv->n_targets = 2;
210   priv->supported_targets = g_new (Atom, priv->n_targets);
211 
212   priv->supported_targets[0] = __utf8_string;
213   priv->supported_targets[1] = __atom_targets;
214 
215   clutter_x11_add_filter ((ClutterX11FilterFunc) st_clipboard_provider,
216                           self);
217 }
218 
219 static ClutterX11FilterReturn
220 st_clipboard_x11_event_filter (XEvent          *xev,
221                                ClutterEvent    *cev,
222                                EventFilterData *filter_data)
223 {
224   Atom actual_type;
225   int actual_format, result;
226   unsigned long nitems, bytes_after;
227   unsigned char *data = NULL;
228 
229   if(xev->type != SelectionNotify)
230     return CLUTTER_X11_FILTER_CONTINUE;
231 
232   if (xev->xselection.property == None)
233     {
234       /* clipboard empty */
235       filter_data->callback (filter_data->clipboard,
236                              NULL,
237                              filter_data->user_data);
238 
239       clutter_x11_remove_filter ((ClutterX11FilterFunc) st_clipboard_x11_event_filter,
240                                  filter_data);
241       g_free (filter_data);
242       return CLUTTER_X11_FILTER_REMOVE;
243     }
244 
245   clutter_x11_trap_x_errors ();
246 
247   result = XGetWindowProperty (xev->xselection.display,
248                                xev->xselection.requestor,
249                                xev->xselection.property,
250                                0L, G_MAXINT,
251                                True,
252                                AnyPropertyType,
253                                &actual_type,
254                                &actual_format,
255                                &nitems,
256                                &bytes_after,
257                                &data);
258 
259   if (clutter_x11_untrap_x_errors () || result != Success)
260     {
261       /* FIXME: handle failure better */
262       g_warning ("Clipboard: prop retrival failed");
263     }
264 
265   filter_data->callback (filter_data->clipboard, (char*) data,
266                          filter_data->user_data);
267 
268   clutter_x11_remove_filter
269                           ((ClutterX11FilterFunc) st_clipboard_x11_event_filter,
270                           filter_data);
271 
272   g_free (filter_data);
273 
274   if (data)
275     XFree (data);
276 
277   return CLUTTER_X11_FILTER_REMOVE;
278 }
279 
280 /**
281  * st_clipboard_get_default:
282  *
283  * Get the global #StClipboard object that represents the clipboard.
284  *
285  * Returns: (transfer none): a #StClipboard owned by St and must not be
286  * unrefferenced or freed.
287  */
288 StClipboard*
289 st_clipboard_get_default (void)
290 {
291   static StClipboard *default_clipboard = NULL;
292 
293   if (!default_clipboard)
294     {
295       default_clipboard = g_object_new (ST_TYPE_CLIPBOARD, NULL);
296     }
297 
298   return default_clipboard;
299 }
300 
301 /**
302  * st_clipboard_get_text:
303  * @clipboard: A #StCliboard
304  * @callback: (scope async): function to be called when the text is retreived
305  * @user_data: data to be passed to the callback
306  *
307  * Request the data from the clipboard in text form. @callback is executed
308  * when the data is retreived.
309  *
310  */
311 void
312 st_clipboard_get_text (StClipboard            *clipboard,
313                        StClipboardCallbackFunc callback,
314                        gpointer                user_data)
315 {
316   EventFilterData *data;
317 
318   Display *dpy;
319 
320   g_return_if_fail (ST_IS_CLIPBOARD (clipboard));
321   g_return_if_fail (callback != NULL);
322 
323   data = g_new0 (EventFilterData, 1);
324   data->clipboard = clipboard;
325   data->callback = callback;
326   data->user_data = user_data;
327 
328   clutter_x11_add_filter ((ClutterX11FilterFunc) st_clipboard_x11_event_filter,
329                           data);
330 
331   dpy = clutter_x11_get_default_display ();
332 
333   clutter_x11_trap_x_errors (); /* safety on */
334 
335   XConvertSelection (dpy,
336                      __atom_clip,
337                      __utf8_string, __utf8_string,
338                      clipboard->priv->clipboard_window,
339                      CurrentTime);
340 
341   clutter_x11_untrap_x_errors ();
342 }
343 
344 /**
345  * st_clipboard_set_text:
346  * @clipboard: A #StClipboard
347  * @text: text to copy to the clipboard
348  *
349  * Sets text as the current contents of the clipboard.
350  *
351  */
352 void
353 st_clipboard_set_text (StClipboard *clipboard,
354                        const gchar *text)
355 {
356   StClipboardPrivate *priv;
357   Display *dpy;
358 
359   g_return_if_fail (ST_IS_CLIPBOARD (clipboard));
360   g_return_if_fail (text != NULL);
361 
362   priv = clipboard->priv;
363 
364   /* make a copy of the text */
365   g_free (priv->clipboard_text);
366   priv->clipboard_text = g_strdup (text);
367 
368   /* tell X we own the clipboard selection */
369   dpy = clutter_x11_get_default_display ();
370 
371   clutter_x11_trap_x_errors ();
372 
373   XSetSelectionOwner (dpy, __atom_clip, priv->clipboard_window, CurrentTime);
374   XSync (dpy, FALSE);
375 
376   clutter_x11_untrap_x_errors ();
377 }