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, see <http://www.gnu.org/licenses/>.
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
27 #include "gdkproperty.h"
29 #include "gdkprivate.h"
30 #include "gdkinternals.h"
31 #include "gdkselection.h"
32 #include "gdkprivate-x11.h"
33 #include "gdkdisplay-x11.h"
34 #include "gdkscreen-x11.h"
37 #include <X11/Xatom.h>
43 * @Short_description: Functions to manipulate properties on windows
44 * @Title: Properties and Atoms
46 * Each window under X can have any number of associated
47 * <firstterm>properties</firstterm> attached to it.
48 * Properties are arbitrary chunks of data identified by
49 * <firstterm>atom</firstterm>s. (An <firstterm>atom</firstterm>
50 * is a numeric index into a string table on the X server. They are used
51 * to transfer strings efficiently between clients without
52 * having to transfer the entire string.) A property
53 * has an associated type, which is also identified
56 * A property has an associated <firstterm>format</firstterm>,
57 * an integer describing how many bits are in each unit
58 * of data inside the property. It must be 8, 16, or 32.
59 * When data is transferred between the server and client,
60 * if they are of different endianesses it will be byteswapped
61 * as necessary according to the format of the property.
62 * Note that on the client side, properties of format 32
63 * will be stored with one unit per <emphasis>long</emphasis>,
64 * even if a long integer has more than 32 bits on the platform.
65 * (This decision was apparently made for Xlib to maintain
66 * compatibility with programs that assumed longs were 32
67 * bits, at the expense of programs that knew better.)
69 * The functions in this section are used to add, remove
70 * and change properties on windows, to convert atoms
71 * to and from strings and to manipulate some types of
72 * data commonly stored in X window properties.
76 static GPtrArray *virtual_atom_array;
77 static GHashTable *virtual_atom_hash;
79 static const gchar xatoms_string[] =
80 /* These are all the standard predefined X atoms */
81 "\0" /* leave a space for None, even though it is not a predefined atom */
117 "WM_CLIENT_MACHINE\0"
132 "UNDERLINE_POSITION\0"
133 "UNDERLINE_THICKNESS\0"
135 "STRIKEOUT_DESCENT\0"
150 /* Below here, these are our additions. Increment N_CUSTOM_PREDEFINED
153 "CLIPBOARD\0" /* = 69 */
156 static const gint xatoms_offset[] = {
157 0, 1, 9, 19, 23, 28, 35, 44, 53, 60, 72, 84,
158 96, 108, 120, 132, 144, 156, 165, 170, 178, 185, 189, 201,
159 218, 232, 245, 258, 274, 287, 301, 313, 320, 329, 336, 347,
160 356, 374, 387, 400, 408, 424, 438, 452, 462, 473, 483, 493,
161 507, 521, 533, 545, 564, 584, 601, 619, 632, 641, 652, 659,
162 670, 681, 691, 698, 708, 720, 730, 741, 750, 767
165 #define N_CUSTOM_PREDEFINED 1
167 #define ATOM_TO_INDEX(atom) (GPOINTER_TO_UINT(atom))
168 #define INDEX_TO_ATOM(atom) ((GdkAtom)GUINT_TO_POINTER(atom))
171 insert_atom_pair (GdkDisplay *display,
172 GdkAtom virtual_atom,
175 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
177 if (!display_x11->atom_from_virtual)
179 display_x11->atom_from_virtual = g_hash_table_new (g_direct_hash, NULL);
180 display_x11->atom_to_virtual = g_hash_table_new (g_direct_hash, NULL);
183 g_hash_table_insert (display_x11->atom_from_virtual,
184 GDK_ATOM_TO_POINTER (virtual_atom),
185 GUINT_TO_POINTER (xatom));
186 g_hash_table_insert (display_x11->atom_to_virtual,
187 GUINT_TO_POINTER (xatom),
188 GDK_ATOM_TO_POINTER (virtual_atom));
192 lookup_cached_xatom (GdkDisplay *display,
195 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
197 if (ATOM_TO_INDEX (atom) < G_N_ELEMENTS (xatoms_offset) - N_CUSTOM_PREDEFINED)
198 return ATOM_TO_INDEX (atom);
200 if (display_x11->atom_from_virtual)
201 return GPOINTER_TO_UINT (g_hash_table_lookup (display_x11->atom_from_virtual,
202 GDK_ATOM_TO_POINTER (atom)));
208 * gdk_x11_atom_to_xatom_for_display:
209 * @display: A #GdkDisplay
210 * @atom: A #GdkAtom, or %GDK_NONE
212 * Converts from a #GdkAtom to the X atom for a #GdkDisplay
213 * with the same string value. The special value %GDK_NONE
214 * is converted to %None.
216 * Return value: the X atom corresponding to @atom, or %None
221 gdk_x11_atom_to_xatom_for_display (GdkDisplay *display,
226 g_return_val_if_fail (GDK_IS_DISPLAY (display), None);
228 if (atom == GDK_NONE)
231 if (gdk_display_is_closed (display))
234 xatom = lookup_cached_xatom (display, atom);
240 g_return_val_if_fail (ATOM_TO_INDEX (atom) < virtual_atom_array->len, None);
242 name = g_ptr_array_index (virtual_atom_array, ATOM_TO_INDEX (atom));
244 xatom = XInternAtom (GDK_DISPLAY_XDISPLAY (display), name, FALSE);
245 insert_atom_pair (display, atom, xatom);
252 _gdk_x11_precache_atoms (GdkDisplay *display,
253 const gchar * const *atom_names,
258 const gchar **xatom_names;
262 xatoms = g_new (Atom, n_atoms);
263 xatom_names = g_new (const gchar *, n_atoms);
264 atoms = g_new (GdkAtom, n_atoms);
267 for (i = 0; i < n_atoms; i++)
269 GdkAtom atom = gdk_atom_intern_static_string (atom_names[i]);
270 if (lookup_cached_xatom (display, atom) == None)
272 atoms[n_xatoms] = atom;
273 xatom_names[n_xatoms] = atom_names[i];
280 #ifdef HAVE_XINTERNATOMS
281 XInternAtoms (GDK_DISPLAY_XDISPLAY (display),
282 (char **)xatom_names, n_xatoms, False, xatoms);
284 for (i = 0; i < n_xatoms; i++)
285 xatoms[i] = XInternAtom (GDK_DISPLAY_XDISPLAY (display),
286 xatom_names[i], False);
290 for (i = 0; i < n_xatoms; i++)
291 insert_atom_pair (display, atoms[i], xatoms[i]);
294 g_free (xatom_names);
299 * gdk_x11_atom_to_xatom:
302 * Converts from a #GdkAtom to the X atom for the default GDK display
303 * with the same string value.
305 * Return value: the X atom corresponding to @atom.
308 gdk_x11_atom_to_xatom (GdkAtom atom)
310 return gdk_x11_atom_to_xatom_for_display (gdk_display_get_default (), atom);
314 * gdk_x11_xatom_to_atom_for_display:
315 * @display: A #GdkDisplay
318 * Convert from an X atom for a #GdkDisplay to the corresponding
321 * Return value: (transfer none): the corresponding #GdkAtom.
326 gdk_x11_xatom_to_atom_for_display (GdkDisplay *display,
329 GdkX11Display *display_x11;
330 GdkAtom virtual_atom = GDK_NONE;
332 g_return_val_if_fail (GDK_IS_DISPLAY (display), GDK_NONE);
337 if (gdk_display_is_closed (display))
340 display_x11 = GDK_X11_DISPLAY (display);
342 if (xatom < G_N_ELEMENTS (xatoms_offset) - N_CUSTOM_PREDEFINED)
343 return INDEX_TO_ATOM (xatom);
345 if (display_x11->atom_to_virtual)
346 virtual_atom = GDK_POINTER_TO_ATOM (g_hash_table_lookup (display_x11->atom_to_virtual,
347 GUINT_TO_POINTER (xatom)));
351 /* If this atom doesn't exist, we'll die with an X error unless
352 * we take precautions
355 gdk_x11_display_error_trap_push (display);
356 name = XGetAtomName (GDK_DISPLAY_XDISPLAY (display), xatom);
357 if (gdk_x11_display_error_trap_pop (display))
359 g_warning (G_STRLOC " invalid X atom: %ld", xatom);
363 virtual_atom = gdk_atom_intern (name, FALSE);
366 insert_atom_pair (display, virtual_atom, xatom);
374 * gdk_x11_xatom_to_atom:
375 * @xatom: an X atom for the default GDK display
377 * Convert from an X atom for the default display to the corresponding
380 * Return value: (transfer none): the corresponding G#dkAtom.
383 gdk_x11_xatom_to_atom (Atom xatom)
385 return gdk_x11_xatom_to_atom_for_display (gdk_display_get_default (), xatom);
389 virtual_atom_check_init (void)
391 if (!virtual_atom_hash)
395 virtual_atom_hash = g_hash_table_new (g_str_hash, g_str_equal);
396 virtual_atom_array = g_ptr_array_new ();
398 for (i = 0; i < G_N_ELEMENTS (xatoms_offset); i++)
400 g_ptr_array_add (virtual_atom_array, (gchar *)(xatoms_string + xatoms_offset[i]));
401 g_hash_table_insert (virtual_atom_hash, (gchar *)(xatoms_string + xatoms_offset[i]),
402 GUINT_TO_POINTER (i));
408 _gdk_x11_display_manager_atom_intern (GdkDisplayManager *manager,
409 const gchar *atom_name,
414 virtual_atom_check_init ();
416 result = GDK_POINTER_TO_ATOM (g_hash_table_lookup (virtual_atom_hash, atom_name));
419 result = INDEX_TO_ATOM (virtual_atom_array->len);
421 g_ptr_array_add (virtual_atom_array, dup ? g_strdup (atom_name) : (gchar *)atom_name);
422 g_hash_table_insert (virtual_atom_hash,
423 g_ptr_array_index (virtual_atom_array,
424 ATOM_TO_INDEX (result)),
425 GDK_ATOM_TO_POINTER (result));
432 get_atom_name (GdkAtom atom)
434 virtual_atom_check_init ();
436 if (ATOM_TO_INDEX (atom) < virtual_atom_array->len)
437 return g_ptr_array_index (virtual_atom_array, ATOM_TO_INDEX (atom));
444 _gdk_x11_display_manager_get_atom_name (GdkDisplayManager *manager,
447 return g_strdup (get_atom_name (atom));
451 * gdk_x11_get_xatom_by_name_for_display:
452 * @display: a #GdkDisplay
453 * @atom_name: a string
455 * Returns the X atom for a #GdkDisplay corresponding to @atom_name.
456 * This function caches the result, so if called repeatedly it is much
457 * faster than XInternAtom(), which is a round trip to the server each time.
459 * Return value: a X atom for a #GdkDisplay
464 gdk_x11_get_xatom_by_name_for_display (GdkDisplay *display,
465 const gchar *atom_name)
467 g_return_val_if_fail (GDK_IS_DISPLAY (display), None);
468 return gdk_x11_atom_to_xatom_for_display (display,
469 gdk_atom_intern (atom_name, FALSE));
473 * gdk_x11_get_xatom_by_name:
474 * @atom_name: a string
476 * Returns the X atom for GDK's default display corresponding to @atom_name.
477 * This function caches the result, so if called repeatedly it is much
478 * faster than XInternAtom(), which is a round trip to the server each time.
480 * Return value: a X atom for GDK's default display.
483 gdk_x11_get_xatom_by_name (const gchar *atom_name)
485 return gdk_x11_get_xatom_by_name_for_display (gdk_display_get_default (),
490 * gdk_x11_get_xatom_name_for_display:
491 * @display: the #GdkDisplay where @xatom is defined
494 * Returns the name of an X atom for its display. This
495 * function is meant mainly for debugging, so for convenience, unlike
496 * XAtomName() and gdk_atom_name(), the result doesn't need to
499 * Return value: name of the X atom; this string is owned by GDK,
500 * so it shouldn't be modifed or freed.
505 gdk_x11_get_xatom_name_for_display (GdkDisplay *display,
508 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
510 return get_atom_name (gdk_x11_xatom_to_atom_for_display (display, xatom));
514 * gdk_x11_get_xatom_name:
515 * @xatom: an X atom for GDK's default display
517 * Returns the name of an X atom for GDK's default display. This
518 * function is meant mainly for debugging, so for convenience, unlike
519 * <function>XAtomName()</function> and gdk_atom_name(), the result
520 * doesn't need to be freed. Also, this function will never return %NULL,
521 * even if @xatom is invalid.
523 * Return value: name of the X atom; this string is owned by GTK+,
524 * so it shouldn't be modifed or freed.
527 gdk_x11_get_xatom_name (Atom xatom)
529 return get_atom_name (gdk_x11_xatom_to_atom (xatom));
533 _gdk_x11_window_get_property (GdkWindow *window,
539 GdkAtom *actual_property_type,
540 gint *actual_format_type,
548 gulong ret_bytes_after;
556 g_return_val_if_fail (!window || GDK_WINDOW_IS_X11 (window), FALSE);
560 GdkScreen *screen = gdk_screen_get_default ();
561 window = gdk_screen_get_root_window (screen);
563 GDK_NOTE (MULTIHEAD, g_message ("gdk_property_get(): window is NULL\n"));
565 else if (!GDK_WINDOW_IS_X11 (window))
568 if (GDK_WINDOW_DESTROYED (window))
571 display = gdk_window_get_display (window);
572 xproperty = gdk_x11_atom_to_xatom_for_display (display, property);
573 if (type == GDK_NONE)
574 xtype = AnyPropertyType;
576 xtype = gdk_x11_atom_to_xatom_for_display (display, type);
581 * Round up length to next 4 byte value. Some code is in the (bad?)
582 * habit of passing G_MAXLONG as the length argument, causing an
583 * overflow to negative on the add. In this case, we clamp the
584 * value to G_MAXLONG.
586 get_length = length + 3;
587 if (get_length > G_MAXLONG)
588 get_length = G_MAXLONG;
590 /* To fail, either the user passed 0 or G_MAXULONG */
591 get_length = get_length / 4;
594 g_warning ("gdk_propery-get(): invalid length 0");
598 res = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
599 GDK_WINDOW_XID (window), xproperty,
600 offset, get_length, pdelete,
601 xtype, &ret_prop_type, &ret_format,
602 &ret_nitems, &ret_bytes_after,
605 if (res != Success || (ret_prop_type == None && ret_format == 0))
610 if (actual_property_type)
611 *actual_property_type = gdk_x11_xatom_to_atom_for_display (display, ret_prop_type);
612 if (actual_format_type)
613 *actual_format_type = ret_format;
615 if ((xtype != AnyPropertyType) && (ret_prop_type != xtype))
618 g_warning ("Couldn't match property type %s to %s\n",
619 gdk_x11_get_xatom_name_for_display (display, ret_prop_type),
620 gdk_x11_get_xatom_name_for_display (display, xtype));
624 /* FIXME: ignoring bytes_after could have very bad effects */
628 if (ret_prop_type == XA_ATOM ||
629 ret_prop_type == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
632 * data is an array of X atom, we need to convert it
633 * to an array of GDK Atoms
636 GdkAtom *ret_atoms = g_new (GdkAtom, ret_nitems);
637 Atom *xatoms = (Atom *)ret_data;
639 *data = (guchar *)ret_atoms;
641 for (i = 0; i < ret_nitems; i++)
642 ret_atoms[i] = gdk_x11_xatom_to_atom_for_display (display, xatoms[i]);
645 *actual_length = ret_nitems * sizeof (GdkAtom);
652 ret_length = ret_nitems;
655 ret_length = sizeof(short) * ret_nitems;
658 ret_length = sizeof(long) * ret_nitems;
661 g_warning ("unknown property return format: %d", ret_format);
666 *data = g_new (guchar, ret_length);
667 memcpy (*data, ret_data, ret_length);
669 *actual_length = ret_length;
679 _gdk_x11_window_change_property (GdkWindow *window,
692 g_return_if_fail (!window || GDK_WINDOW_IS_X11 (window));
698 screen = gdk_screen_get_default ();
699 window = gdk_screen_get_root_window (screen);
701 GDK_NOTE (MULTIHEAD, g_message ("gdk_property_change(): window is NULL\n"));
703 else if (!GDK_WINDOW_IS_X11 (window))
706 if (GDK_WINDOW_DESTROYED (window))
709 gdk_window_ensure_native (window);
711 display = gdk_window_get_display (window);
712 xproperty = gdk_x11_atom_to_xatom_for_display (display, property);
713 xtype = gdk_x11_atom_to_xatom_for_display (display, type);
714 xwindow = GDK_WINDOW_XID (window);
716 if (xtype == XA_ATOM ||
717 xtype == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
720 * data is an array of GdkAtom, we need to convert it
721 * to an array of X Atoms
724 GdkAtom *atoms = (GdkAtom*) data;
727 xatoms = g_new (Atom, nelements);
728 for (i = 0; i < nelements; i++)
729 xatoms[i] = gdk_x11_atom_to_xatom_for_display (display, atoms[i]);
731 XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow,
733 format, mode, (guchar *)xatoms, nelements);
737 XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow, xproperty,
738 xtype, format, mode, (guchar *)data, nelements);
742 _gdk_x11_window_delete_property (GdkWindow *window,
745 g_return_if_fail (!window || GDK_WINDOW_IS_X11 (window));
749 GdkScreen *screen = gdk_screen_get_default ();
750 window = gdk_screen_get_root_window (screen);
753 g_message ("gdk_property_delete(): window is NULL\n"));
755 else if (!GDK_WINDOW_IS_X11 (window))
758 if (GDK_WINDOW_DESTROYED (window))
761 XDeleteProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window),
762 gdk_x11_atom_to_xatom_for_display (GDK_WINDOW_DISPLAY (window),