]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkselection-x11.c
[GI] Add missing (out) and (array) annotations
[~andy/gtk] / gdk / x11 / gdkselection-x11.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26
27 #include "config.h"
28
29 #include "gdkselection.h"
30 #include "gdkproperty.h"
31 #include "gdkprivate.h"
32 #include "gdkprivate-x11.h"
33 #include "gdkdisplay-x11.h"
34
35 #include <X11/Xlib.h>
36 #include <X11/Xatom.h>
37 #include <string.h>
38
39
40 typedef struct _OwnerInfo OwnerInfo;
41
42 struct _OwnerInfo
43 {
44   GdkAtom    selection;
45   GdkWindow *owner;
46   gulong     serial;
47 };
48
49 static GSList *owner_list;
50
51 /* When a window is destroyed we check if it is the owner
52  * of any selections. This is somewhat inefficient, but
53  * owner_list is typically short, and it is a low memory,
54  * low code solution
55  */
56 void
57 _gdk_x11_selection_window_destroyed (GdkWindow *window)
58 {
59   GSList *tmp_list = owner_list;
60   while (tmp_list)
61     {
62       OwnerInfo *info = tmp_list->data;
63       tmp_list = tmp_list->next;
64
65       if (info->owner == window)
66         {
67           owner_list = g_slist_remove (owner_list, info);
68           g_free (info);
69         }
70     }
71 }
72
73 /* We only pass through those SelectionClear events that actually
74  * reflect changes to the selection owner that we didn't make ourself.
75  */
76 gboolean
77 _gdk_x11_selection_filter_clear_event (XSelectionClearEvent *event)
78 {
79   GSList *tmp_list = owner_list;
80   GdkDisplay *display = gdk_x11_lookup_xdisplay (event->display);
81
82   while (tmp_list)
83     {
84       OwnerInfo *info = tmp_list->data;
85
86       if (gdk_window_get_display (info->owner) == display &&
87           info->selection == gdk_x11_xatom_to_atom_for_display (display, event->selection))
88         {
89           if ((GDK_WINDOW_XID (info->owner) == event->window &&
90                event->serial >= info->serial))
91             {
92               owner_list = g_slist_remove (owner_list, info);
93               g_free (info);
94               return TRUE;
95             }
96           else
97             return FALSE;
98         }
99       tmp_list = tmp_list->next;
100     }
101
102   return FALSE;
103 }
104
105 gboolean
106 _gdk_x11_display_set_selection_owner (GdkDisplay *display,
107                                       GdkWindow  *owner,
108                                       GdkAtom     selection,
109                                       guint32     time,
110                                       gboolean    send_event)
111 {
112   Display *xdisplay;
113   Window xwindow;
114   Atom xselection;
115   GSList *tmp_list;
116   OwnerInfo *info;
117
118   if (gdk_display_is_closed (display))
119     return FALSE;
120
121   if (owner)
122     {
123       if (GDK_WINDOW_DESTROYED (owner) || !GDK_WINDOW_IS_X11 (owner))
124         return FALSE;
125
126       gdk_window_ensure_native (owner);
127       xdisplay = GDK_WINDOW_XDISPLAY (owner);
128       xwindow = GDK_WINDOW_XID (owner);
129     }
130   else
131     {
132       xdisplay = GDK_DISPLAY_XDISPLAY (display);
133       xwindow = None;
134     }
135
136   xselection = gdk_x11_atom_to_xatom_for_display (display, selection);
137
138   tmp_list = owner_list;
139   while (tmp_list)
140     {
141       info = tmp_list->data;
142       if (info->selection == selection)
143         {
144           owner_list = g_slist_remove (owner_list, info);
145           g_free (info);
146           break;
147         }
148       tmp_list = tmp_list->next;
149     }
150
151   if (owner)
152     {
153       info = g_new (OwnerInfo, 1);
154       info->owner = owner;
155       info->serial = NextRequest (GDK_WINDOW_XDISPLAY (owner));
156       info->selection = selection;
157
158       owner_list = g_slist_prepend (owner_list, info);
159     }
160
161   XSetSelectionOwner (xdisplay, xselection, xwindow, time);
162
163   return (XGetSelectionOwner (xdisplay, xselection) == xwindow);
164 }
165
166 GdkWindow *
167 _gdk_x11_display_get_selection_owner (GdkDisplay *display,
168                                       GdkAtom     selection)
169 {
170   Window xwindow;
171
172   if (gdk_display_is_closed (display))
173     return NULL;
174
175   xwindow = XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
176                                 gdk_x11_atom_to_xatom_for_display (display,
177                                                                    selection));
178   if (xwindow == None)
179     return NULL;
180
181   return gdk_x11_window_lookup_for_display (display, xwindow);
182 }
183
184 void
185 _gdk_x11_display_convert_selection (GdkDisplay *display,
186                                     GdkWindow  *requestor,
187                                     GdkAtom     selection,
188                                     GdkAtom     target,
189                                     guint32     time)
190 {
191   g_return_if_fail (selection != GDK_NONE);
192
193   if (GDK_WINDOW_DESTROYED (requestor) || !GDK_WINDOW_IS_X11 (requestor))
194     return;
195
196   gdk_window_ensure_native (requestor);
197
198   XConvertSelection (GDK_WINDOW_XDISPLAY (requestor),
199                      gdk_x11_atom_to_xatom_for_display (display, selection),
200                      gdk_x11_atom_to_xatom_for_display (display, target),
201                      gdk_x11_get_xatom_by_name_for_display (display, "GDK_SELECTION"),
202                      GDK_WINDOW_XID (requestor), time);
203 }
204
205 gint
206 _gdk_x11_display_get_selection_property (GdkDisplay  *display,
207                                          GdkWindow   *requestor,
208                                          guchar     **data,
209                                          GdkAtom     *ret_type,
210                                          gint        *ret_format)
211 {
212   gulong nitems;
213   gulong nbytes;
214   gulong length = 0;
215   Atom prop_type;
216   gint prop_format;
217   guchar *t = NULL;
218
219   if (GDK_WINDOW_DESTROYED (requestor) || !GDK_WINDOW_IS_X11 (requestor))
220     goto err;
221
222   t = NULL;
223
224   /* We can't delete the selection here, because it might be the INCR
225      protocol, in which case the client has to make sure they'll be
226      notified of PropertyChange events _before_ the property is deleted.
227      Otherwise there's no guarantee we'll win the race ... */
228   if (XGetWindowProperty (GDK_WINDOW_XDISPLAY (requestor),
229                           GDK_WINDOW_XID (requestor),
230                           gdk_x11_get_xatom_by_name_for_display (display, "GDK_SELECTION"),
231                           0, 0x1FFFFFFF /* MAXINT32 / 4 */, False,
232                           AnyPropertyType, &prop_type, &prop_format,
233                           &nitems, &nbytes, &t) != Success)
234     goto err;
235
236   if (prop_type != None)
237     {
238       if (ret_type)
239         *ret_type = gdk_x11_xatom_to_atom_for_display (display, prop_type);
240       if (ret_format)
241         *ret_format = prop_format;
242
243       if (prop_type == XA_ATOM ||
244           prop_type == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
245         {
246           Atom* atoms = (Atom*) t;
247           GdkAtom* atoms_dest;
248           gint num_atom, i;
249
250           if (prop_format != 32)
251             goto err;
252
253           num_atom = nitems;
254           length = sizeof (GdkAtom) * num_atom + 1;
255
256           if (data)
257             {
258               *data = g_malloc (length);
259               (*data)[length - 1] = '\0';
260               atoms_dest = (GdkAtom *)(*data);
261
262               for (i=0; i < num_atom; i++)
263                 atoms_dest[i] = gdk_x11_xatom_to_atom_for_display (display, atoms[i]);
264             }
265         }
266       else
267         {
268           switch (prop_format)
269             {
270             case 8:
271               length = nitems;
272               break;
273             case 16:
274               length = sizeof(short) * nitems;
275               break;
276             case 32:
277               length = sizeof(long) * nitems;
278               break;
279             default:
280               g_assert_not_reached ();
281               break;
282             }
283
284           /* Add on an extra byte to handle null termination.  X guarantees
285              that t will be 1 longer than nitems and null terminated */
286           length += 1;
287
288           if (data)
289             *data = g_memdup (t, length);
290         }
291
292       if (t)
293         XFree (t);
294
295       return length - 1;
296     }
297
298  err:
299   if (ret_type)
300     *ret_type = GDK_NONE;
301   if (ret_format)
302     *ret_format = 0;
303   if (data)
304     *data = NULL;
305
306   return 0;
307 }
308
309 void
310 _gdk_x11_display_send_selection_notify (GdkDisplay       *display,
311                                         GdkNativeWindow  requestor,
312                                         GdkAtom          selection,
313                                         GdkAtom          target,
314                                         GdkAtom          property,
315                                         guint32          time)
316 {
317   XSelectionEvent xevent;
318
319   xevent.type = SelectionNotify;
320   xevent.serial = 0;
321   xevent.send_event = True;
322   xevent.requestor = requestor;
323   xevent.selection = gdk_x11_atom_to_xatom_for_display (display, selection);
324   xevent.target = gdk_x11_atom_to_xatom_for_display (display, target);
325   if (property == GDK_NONE)
326     xevent.property = None;
327   else
328     xevent.property = gdk_x11_atom_to_xatom_for_display (display, property);
329   xevent.time = time;
330
331   _gdk_x11_display_send_xevent (display, requestor, False, NoEventMask, (XEvent*) & xevent);
332 }
333
334 /**
335  * gdk_x11_display_text_property_to_text_list:
336  * @display: The #GdkDisplay where the encoding is defined
337  * @encoding: an atom representing the encoding. The most
338  *    common values for this are STRING, or COMPOUND_TEXT.
339  *    This is value used as the type for the property
340  * @format: the format of the property
341  * @text: The text data
342  * @length: The number of items to transform
343  * @list: location to store an  array of strings in
344  *    the encoding of the current locale. This array should be
345  *    freed using gdk_free_text_list().
346  *
347  * Convert a text string from the encoding as it is stored
348  * in a property into an array of strings in the encoding of
349  * the current locale. (The elements of the array represent the
350  * nul-separated elements of the original text string.)
351  *
352  * Returns: the number of strings stored in list, or 0,
353  *     if the conversion failed
354  *
355  * Since: 2.24
356  */
357 gint
358 gdk_x11_display_text_property_to_text_list (GdkDisplay   *display,
359                                             GdkAtom       encoding,
360                                             gint          format,
361                                             const guchar *text,
362                                             gint          length,
363                                             gchar      ***list)
364 {
365   XTextProperty property;
366   gint count = 0;
367   gint res;
368   gchar **local_list;
369   g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
370
371   if (list)
372     *list = NULL;
373
374   if (gdk_display_is_closed (display))
375     return 0;
376
377   property.value = (guchar *)text;
378   property.encoding = gdk_x11_atom_to_xatom_for_display (display, encoding);
379   property.format = format;
380   property.nitems = length;
381   res = XmbTextPropertyToTextList (GDK_DISPLAY_XDISPLAY (display), &property,
382                                    &local_list, &count);
383   if (res == XNoMemory || res == XLocaleNotSupported || res == XConverterNotFound)
384     return 0;
385   else
386     {
387       if (list)
388         *list = local_list;
389       else
390         XFreeStringList (local_list);
391
392       return count;
393     }
394 }
395
396 /**
397  * gdk_x11_free_text_list:
398  * @list: the value stored in the @list parameter by
399  *   a call to gdk_x11_display_text_property_to_text_list().
400  *
401  * Frees the array of strings created by
402  * gdk_x11_display_text_property_to_text_list().
403  *
404  * Since: 2.24
405  */
406 void
407 gdk_x11_free_text_list (gchar **list)
408 {
409   g_return_if_fail (list != NULL);
410
411   XFreeStringList (list);
412 }
413
414 static gint
415 make_list (const gchar  *text,
416            gint          length,
417            gboolean      latin1,
418            gchar      ***list)
419 {
420   GSList *strings = NULL;
421   gint n_strings = 0;
422   gint i;
423   const gchar *p = text;
424   const gchar *q;
425   GSList *tmp_list;
426   GError *error = NULL;
427
428   while (p < text + length)
429     {
430       gchar *str;
431
432       q = p;
433       while (*q && q < text + length)
434         q++;
435
436       if (latin1)
437         {
438           str = g_convert (p, q - p,
439                            "UTF-8", "ISO-8859-1",
440                            NULL, NULL, &error);
441
442           if (!str)
443             {
444               g_warning ("Error converting selection from STRING: %s",
445                          error->message);
446               g_error_free (error);
447             }
448         }
449       else
450         {
451           str = g_strndup (p, q - p);
452           if (!g_utf8_validate (str, -1, NULL))
453             {
454               g_warning ("Error converting selection from UTF8_STRING");
455               g_free (str);
456               str = NULL;
457             }
458         }
459
460       if (str)
461         {
462           strings = g_slist_prepend (strings, str);
463           n_strings++;
464         }
465
466       p = q + 1;
467     }
468
469   if (list)
470     {
471       *list = g_new (gchar *, n_strings + 1);
472       (*list)[n_strings] = NULL;
473     }
474
475   i = n_strings;
476   tmp_list = strings;
477   while (tmp_list)
478     {
479       if (list)
480         (*list)[--i] = tmp_list->data;
481       else
482         g_free (tmp_list->data);
483
484       tmp_list = tmp_list->next;
485     }
486
487   g_slist_free (strings);
488
489   return n_strings;
490 }
491
492 gint
493 _gdk_x11_display_text_property_to_utf8_list (GdkDisplay    *display,
494                                              GdkAtom        encoding,
495                                              gint           format,
496                                              const guchar  *text,
497                                              gint           length,
498                                              gchar       ***list)
499 {
500   if (encoding == GDK_TARGET_STRING)
501     {
502       return make_list ((gchar *)text, length, TRUE, list);
503     }
504   else if (encoding == gdk_atom_intern_static_string ("UTF8_STRING"))
505     {
506       return make_list ((gchar *)text, length, FALSE, list);
507     }
508   else
509     {
510       gchar **local_list;
511       gint local_count;
512       gint i;
513       const gchar *charset = NULL;
514       gboolean need_conversion = !g_get_charset (&charset);
515       gint count = 0;
516       GError *error = NULL;
517
518       /* Probably COMPOUND text, we fall back to Xlib routines
519        */
520       local_count = gdk_x11_display_text_property_to_text_list (display,
521                                                                 encoding,
522                                                                 format,
523                                                                 text,
524                                                                 length,
525                                                                 &local_list);
526       if (list)
527         *list = g_new (gchar *, local_count + 1);
528
529       for (i=0; i<local_count; i++)
530         {
531           /* list contains stuff in our default encoding
532            */
533           if (need_conversion)
534             {
535               gchar *utf = g_convert (local_list[i], -1,
536                                       "UTF-8", charset,
537                                       NULL, NULL, &error);
538               if (utf)
539                 {
540                   if (list)
541                     (*list)[count++] = utf;
542                   else
543                     g_free (utf);
544                 }
545               else
546                 {
547                   g_warning ("Error converting to UTF-8 from '%s': %s",
548                              charset, error->message);
549                   g_error_free (error);
550                   error = NULL;
551                 }
552             }
553           else
554             {
555               if (list)
556                 {
557                   if (g_utf8_validate (local_list[i], -1, NULL))
558                     (*list)[count++] = g_strdup (local_list[i]);
559                   else
560                     g_warning ("Error converting selection");
561                 }
562             }
563         }
564
565       if (local_count)
566         gdk_x11_free_text_list (local_list);
567
568       if (list)
569         (*list)[count] = NULL;
570
571       return count;
572     }
573 }
574
575 /**
576  * gdk_x11_display_string_to_compound_text:
577  * @display: the #GdkDisplay where the encoding is defined
578  * @str: a nul-terminated string
579  * @encoding: (out) (transfer none): location to store the encoding atom
580  *     (to be used as the type for the property)
581  * @format: (out): location to store the format of the property
582  * @ctext: (out) (array length=length): location to store newly
583  *     allocated data for the property
584  * @length: the length of @ctext, in bytes
585  *
586  * Convert a string from the encoding of the current
587  * locale into a form suitable for storing in a window property.
588  *
589  * Returns: 0 upon success, non-zero upon failure
590  *
591  * Since: 2.24
592  */
593 gint
594 gdk_x11_display_string_to_compound_text (GdkDisplay  *display,
595                                          const gchar *str,
596                                          GdkAtom     *encoding,
597                                          gint        *format,
598                                          guchar     **ctext,
599                                          gint        *length)
600 {
601   gint res;
602   XTextProperty property;
603
604   g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
605
606   if (gdk_display_is_closed (display))
607     res = XLocaleNotSupported;
608   else
609     res = XmbTextListToTextProperty (GDK_DISPLAY_XDISPLAY (display),
610                                      (char **)&str, 1, XCompoundTextStyle,
611                                      &property);
612   if (res != Success)
613     {
614       property.encoding = None;
615       property.format = None;
616       property.value = NULL;
617       property.nitems = 0;
618     }
619
620   if (encoding)
621     *encoding = gdk_x11_xatom_to_atom_for_display (display, property.encoding);
622   if (format)
623     *format = property.format;
624   if (ctext)
625     *ctext = property.value;
626   if (length)
627     *length = property.nitems;
628
629   return res;
630 }
631
632 /* The specifications for COMPOUND_TEXT and STRING specify that C0 and
633  * C1 are not allowed except for \n and \t, however the X conversions
634  * routines for COMPOUND_TEXT only enforce this in one direction,
635  * causing cut-and-paste of \r and \r\n separated text to fail.
636  * This routine strips out all non-allowed C0 and C1 characters
637  * from the input string and also canonicalizes \r, and \r\n to \n
638  */
639 static gchar *
640 sanitize_utf8 (const gchar *src,
641                gboolean return_latin1)
642 {
643   gint len = strlen (src);
644   GString *result = g_string_sized_new (len);
645   const gchar *p = src;
646
647   while (*p)
648     {
649       if (*p == '\r')
650         {
651           p++;
652           if (*p == '\n')
653             p++;
654
655           g_string_append_c (result, '\n');
656         }
657       else
658         {
659           gunichar ch = g_utf8_get_char (p);
660
661           if (!((ch < 0x20 && ch != '\t' && ch != '\n') || (ch >= 0x7f && ch < 0xa0)))
662             {
663               if (return_latin1)
664                 {
665                   if (ch <= 0xff)
666                     g_string_append_c (result, ch);
667                   else
668                     g_string_append_printf (result,
669                                             ch < 0x10000 ? "\\u%04x" : "\\U%08x",
670                                             ch);
671                 }
672               else
673                 {
674                   char buf[7];
675                   gint buflen;
676
677                   buflen = g_unichar_to_utf8 (ch, buf);
678                   g_string_append_len (result, buf, buflen);
679                 }
680             }
681
682           p = g_utf8_next_char (p);
683         }
684     }
685
686   return g_string_free (result, FALSE);
687 }
688
689 gchar *
690 _gdk_x11_display_utf8_to_string_target (GdkDisplay  *display,
691                                         const gchar *str)
692 {
693   return sanitize_utf8 (str, TRUE);
694 }
695
696 /**
697  * gdk_x11_display_utf8_to_compound_text:
698  * @display: a #GdkDisplay
699  * @str: a UTF-8 string
700  * @encoding: (out): location to store resulting encoding
701  * @format: (out): location to store format of the result
702  * @ctext: (out) (array length=length): location to store the data of the result
703  * @length: location to store the length of the data
704  *     stored in @ctext
705  *
706  * Converts from UTF-8 to compound text.
707  *
708  * Return value: %TRUE if the conversion succeeded,
709  *     otherwise %FALSE
710  *
711  * Since: 2.24
712  */
713 gboolean
714 gdk_x11_display_utf8_to_compound_text (GdkDisplay  *display,
715                                        const gchar *str,
716                                        GdkAtom     *encoding,
717                                        gint        *format,
718                                        guchar     **ctext,
719                                        gint        *length)
720 {
721   gboolean need_conversion;
722   const gchar *charset;
723   gchar *locale_str, *tmp_str;
724   GError *error = NULL;
725   gboolean result;
726
727   g_return_val_if_fail (str != NULL, FALSE);
728   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
729
730   need_conversion = !g_get_charset (&charset);
731
732   tmp_str = sanitize_utf8 (str, FALSE);
733
734   if (need_conversion)
735     {
736       locale_str = g_convert (tmp_str, -1,
737                               charset, "UTF-8",
738                               NULL, NULL, &error);
739       g_free (tmp_str);
740
741       if (!locale_str)
742         {
743           if (!(error->domain = G_CONVERT_ERROR &&
744                 error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
745             {
746               g_warning ("Error converting from UTF-8 to '%s': %s",
747                          charset, error->message);
748             }
749           g_error_free (error);
750
751           if (encoding)
752             *encoding = None;
753           if (format)
754             *format = None;
755           if (ctext)
756             *ctext = NULL;
757           if (length)
758             *length = 0;
759
760           return FALSE;
761         }
762     }
763   else
764     locale_str = tmp_str;
765
766   result = gdk_x11_display_string_to_compound_text (display, locale_str,
767                                                     encoding, format,
768                                                     ctext, length);
769   result = (result == Success? TRUE : FALSE);
770
771   g_free (locale_str);
772
773   return result;
774 }
775
776 /**
777  * gdk_x11_free_compound_text:
778  * @ctext: The pointer stored in @ctext from a call to
779  *   gdk_x11_display_string_to_compound_text().
780  *
781  * Frees the data returned from gdk_x11_display_string_to_compound_text().
782  *
783  * Since: 2.24
784  */
785 void
786 gdk_x11_free_compound_text (guchar *ctext)
787 {
788   if (ctext)
789     XFree (ctext);
790 }