1 /* GDK - The GIMP Drawing Kit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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.
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.
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/.
29 #include "gdkselection.h"
30 #include "gdkproperty.h"
31 #include "gdkprivate.h"
32 #include "gdkprivate-x11.h"
33 #include "gdkdisplay-x11.h"
36 #include <X11/Xatom.h>
40 typedef struct _OwnerInfo OwnerInfo;
49 static GSList *owner_list;
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,
57 _gdk_x11_selection_window_destroyed (GdkWindow *window)
59 GSList *tmp_list = owner_list;
62 OwnerInfo *info = tmp_list->data;
63 tmp_list = tmp_list->next;
65 if (info->owner == window)
67 owner_list = g_slist_remove (owner_list, info);
73 /* We only pass through those SelectionClear events that actually
74 * reflect changes to the selection owner that we didn't make ourself.
77 _gdk_x11_selection_filter_clear_event (XSelectionClearEvent *event)
79 GSList *tmp_list = owner_list;
80 GdkDisplay *display = gdk_x11_lookup_xdisplay (event->display);
84 OwnerInfo *info = tmp_list->data;
86 if (gdk_window_get_display (info->owner) == display &&
87 info->selection == gdk_x11_xatom_to_atom_for_display (display, event->selection))
89 if ((GDK_WINDOW_XID (info->owner) == event->window &&
90 event->serial >= info->serial))
92 owner_list = g_slist_remove (owner_list, info);
99 tmp_list = tmp_list->next;
106 _gdk_x11_display_set_selection_owner (GdkDisplay *display,
118 if (gdk_display_is_closed (display))
123 if (GDK_WINDOW_DESTROYED (owner) || !GDK_WINDOW_IS_X11 (owner))
126 gdk_window_ensure_native (owner);
127 xdisplay = GDK_WINDOW_XDISPLAY (owner);
128 xwindow = GDK_WINDOW_XID (owner);
132 xdisplay = GDK_DISPLAY_XDISPLAY (display);
136 xselection = gdk_x11_atom_to_xatom_for_display (display, selection);
138 tmp_list = owner_list;
141 info = tmp_list->data;
142 if (info->selection == selection)
144 owner_list = g_slist_remove (owner_list, info);
148 tmp_list = tmp_list->next;
153 info = g_new (OwnerInfo, 1);
155 info->serial = NextRequest (GDK_WINDOW_XDISPLAY (owner));
156 info->selection = selection;
158 owner_list = g_slist_prepend (owner_list, info);
161 XSetSelectionOwner (xdisplay, xselection, xwindow, time);
163 return (XGetSelectionOwner (xdisplay, xselection) == xwindow);
167 _gdk_x11_display_get_selection_owner (GdkDisplay *display,
172 if (gdk_display_is_closed (display))
175 xwindow = XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
176 gdk_x11_atom_to_xatom_for_display (display,
181 return gdk_x11_window_lookup_for_display (display, xwindow);
185 _gdk_x11_display_convert_selection (GdkDisplay *display,
186 GdkWindow *requestor,
191 g_return_if_fail (selection != GDK_NONE);
193 if (GDK_WINDOW_DESTROYED (requestor) || !GDK_WINDOW_IS_X11 (requestor))
196 gdk_window_ensure_native (requestor);
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);
206 _gdk_x11_display_get_selection_property (GdkDisplay *display,
207 GdkWindow *requestor,
219 if (GDK_WINDOW_DESTROYED (requestor) || !GDK_WINDOW_IS_X11 (requestor))
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)
236 if (prop_type != None)
239 *ret_type = gdk_x11_xatom_to_atom_for_display (display, prop_type);
241 *ret_format = prop_format;
243 if (prop_type == XA_ATOM ||
244 prop_type == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
246 Atom* atoms = (Atom*) t;
250 if (prop_format != 32)
254 length = sizeof (GdkAtom) * num_atom + 1;
258 *data = g_malloc (length);
259 (*data)[length - 1] = '\0';
260 atoms_dest = (GdkAtom *)(*data);
262 for (i=0; i < num_atom; i++)
263 atoms_dest[i] = gdk_x11_xatom_to_atom_for_display (display, atoms[i]);
274 length = sizeof(short) * nitems;
277 length = sizeof(long) * nitems;
280 g_assert_not_reached ();
284 /* Add on an extra byte to handle null termination. X guarantees
285 that t will be 1 longer than nitems and null terminated */
289 *data = g_memdup (t, length);
300 *ret_type = GDK_NONE;
310 _gdk_x11_display_send_selection_notify (GdkDisplay *display,
311 GdkNativeWindow requestor,
317 XSelectionEvent xevent;
319 xevent.type = SelectionNotify;
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;
328 xevent.property = gdk_x11_atom_to_xatom_for_display (display, property);
331 _gdk_x11_display_send_xevent (display, requestor, False, NoEventMask, (XEvent*) & xevent);
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().
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.)
352 * Returns: the number of strings stored in list, or 0,
353 * if the conversion failed
358 gdk_x11_display_text_property_to_text_list (GdkDisplay *display,
365 XTextProperty property;
369 g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
374 if (gdk_display_is_closed (display))
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)
390 XFreeStringList (local_list);
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().
401 * Frees the array of strings created by
402 * gdk_x11_display_text_property_to_text_list().
407 gdk_x11_free_text_list (gchar **list)
409 g_return_if_fail (list != NULL);
411 XFreeStringList (list);
415 make_list (const gchar *text,
420 GSList *strings = NULL;
423 const gchar *p = text;
426 GError *error = NULL;
428 while (p < text + length)
433 while (*q && q < text + length)
438 str = g_convert (p, q - p,
439 "UTF-8", "ISO-8859-1",
444 g_warning ("Error converting selection from STRING: %s",
446 g_error_free (error);
451 str = g_strndup (p, q - p);
452 if (!g_utf8_validate (str, -1, NULL))
454 g_warning ("Error converting selection from UTF8_STRING");
462 strings = g_slist_prepend (strings, str);
471 *list = g_new (gchar *, n_strings + 1);
472 (*list)[n_strings] = NULL;
480 (*list)[--i] = tmp_list->data;
482 g_free (tmp_list->data);
484 tmp_list = tmp_list->next;
487 g_slist_free (strings);
493 _gdk_x11_display_text_property_to_utf8_list (GdkDisplay *display,
500 if (encoding == GDK_TARGET_STRING)
502 return make_list ((gchar *)text, length, TRUE, list);
504 else if (encoding == gdk_atom_intern_static_string ("UTF8_STRING"))
506 return make_list ((gchar *)text, length, FALSE, list);
513 const gchar *charset = NULL;
514 gboolean need_conversion = !g_get_charset (&charset);
516 GError *error = NULL;
518 /* Probably COMPOUND text, we fall back to Xlib routines
520 local_count = gdk_x11_display_text_property_to_text_list (display,
527 *list = g_new (gchar *, local_count + 1);
529 for (i=0; i<local_count; i++)
531 /* list contains stuff in our default encoding
535 gchar *utf = g_convert (local_list[i], -1,
541 (*list)[count++] = utf;
547 g_warning ("Error converting to UTF-8 from '%s': %s",
548 charset, error->message);
549 g_error_free (error);
557 if (g_utf8_validate (local_list[i], -1, NULL))
558 (*list)[count++] = g_strdup (local_list[i]);
560 g_warning ("Error converting selection");
566 gdk_x11_free_text_list (local_list);
569 (*list)[count] = NULL;
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
586 * Convert a string from the encoding of the current
587 * locale into a form suitable for storing in a window property.
589 * Returns: 0 upon success, non-zero upon failure
594 gdk_x11_display_string_to_compound_text (GdkDisplay *display,
602 XTextProperty property;
604 g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
606 if (gdk_display_is_closed (display))
607 res = XLocaleNotSupported;
609 res = XmbTextListToTextProperty (GDK_DISPLAY_XDISPLAY (display),
610 (char **)&str, 1, XCompoundTextStyle,
614 property.encoding = None;
615 property.format = None;
616 property.value = NULL;
621 *encoding = gdk_x11_xatom_to_atom_for_display (display, property.encoding);
623 *format = property.format;
625 *ctext = property.value;
627 *length = property.nitems;
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
640 sanitize_utf8 (const gchar *src,
641 gboolean return_latin1)
643 gint len = strlen (src);
644 GString *result = g_string_sized_new (len);
645 const gchar *p = src;
655 g_string_append_c (result, '\n');
659 gunichar ch = g_utf8_get_char (p);
661 if (!((ch < 0x20 && ch != '\t' && ch != '\n') || (ch >= 0x7f && ch < 0xa0)))
666 g_string_append_c (result, ch);
668 g_string_append_printf (result,
669 ch < 0x10000 ? "\\u%04x" : "\\U%08x",
677 buflen = g_unichar_to_utf8 (ch, buf);
678 g_string_append_len (result, buf, buflen);
682 p = g_utf8_next_char (p);
686 return g_string_free (result, FALSE);
690 _gdk_x11_display_utf8_to_string_target (GdkDisplay *display,
693 return sanitize_utf8 (str, TRUE);
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
706 * Converts from UTF-8 to compound text.
708 * Return value: %TRUE if the conversion succeeded,
714 gdk_x11_display_utf8_to_compound_text (GdkDisplay *display,
721 gboolean need_conversion;
722 const gchar *charset;
723 gchar *locale_str, *tmp_str;
724 GError *error = NULL;
727 g_return_val_if_fail (str != NULL, FALSE);
728 g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
730 need_conversion = !g_get_charset (&charset);
732 tmp_str = sanitize_utf8 (str, FALSE);
736 locale_str = g_convert (tmp_str, -1,
743 if (!(error->domain = G_CONVERT_ERROR &&
744 error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
746 g_warning ("Error converting from UTF-8 to '%s': %s",
747 charset, error->message);
749 g_error_free (error);
764 locale_str = tmp_str;
766 result = gdk_x11_display_string_to_compound_text (display, locale_str,
769 result = (result == Success? TRUE : FALSE);
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().
781 * Frees the data returned from gdk_x11_display_string_to_compound_text().
786 gdk_x11_free_compound_text (guchar *ctext)