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];
279 XInternAtoms (GDK_DISPLAY_XDISPLAY (display),
280 (char **)xatom_names, n_xatoms, False, xatoms);
282 for (i = 0; i < n_xatoms; i++)
283 insert_atom_pair (display, atoms[i], xatoms[i]);
286 g_free (xatom_names);
291 * gdk_x11_atom_to_xatom:
294 * Converts from a #GdkAtom to the X atom for the default GDK display
295 * with the same string value.
297 * Return value: the X atom corresponding to @atom.
300 gdk_x11_atom_to_xatom (GdkAtom atom)
302 return gdk_x11_atom_to_xatom_for_display (gdk_display_get_default (), atom);
306 * gdk_x11_xatom_to_atom_for_display:
307 * @display: A #GdkDisplay
310 * Convert from an X atom for a #GdkDisplay to the corresponding
313 * Return value: (transfer none): the corresponding #GdkAtom.
318 gdk_x11_xatom_to_atom_for_display (GdkDisplay *display,
321 GdkX11Display *display_x11;
322 GdkAtom virtual_atom = GDK_NONE;
324 g_return_val_if_fail (GDK_IS_DISPLAY (display), GDK_NONE);
329 if (gdk_display_is_closed (display))
332 display_x11 = GDK_X11_DISPLAY (display);
334 if (xatom < G_N_ELEMENTS (xatoms_offset) - N_CUSTOM_PREDEFINED)
335 return INDEX_TO_ATOM (xatom);
337 if (display_x11->atom_to_virtual)
338 virtual_atom = GDK_POINTER_TO_ATOM (g_hash_table_lookup (display_x11->atom_to_virtual,
339 GUINT_TO_POINTER (xatom)));
343 /* If this atom doesn't exist, we'll die with an X error unless
344 * we take precautions
347 gdk_x11_display_error_trap_push (display);
348 name = XGetAtomName (GDK_DISPLAY_XDISPLAY (display), xatom);
349 if (gdk_x11_display_error_trap_pop (display))
351 g_warning (G_STRLOC " invalid X atom: %ld", xatom);
355 virtual_atom = gdk_atom_intern (name, FALSE);
358 insert_atom_pair (display, virtual_atom, xatom);
366 * gdk_x11_xatom_to_atom:
367 * @xatom: an X atom for the default GDK display
369 * Convert from an X atom for the default display to the corresponding
372 * Return value: (transfer none): the corresponding G#dkAtom.
375 gdk_x11_xatom_to_atom (Atom xatom)
377 return gdk_x11_xatom_to_atom_for_display (gdk_display_get_default (), xatom);
381 virtual_atom_check_init (void)
383 if (!virtual_atom_hash)
387 virtual_atom_hash = g_hash_table_new (g_str_hash, g_str_equal);
388 virtual_atom_array = g_ptr_array_new ();
390 for (i = 0; i < G_N_ELEMENTS (xatoms_offset); i++)
392 g_ptr_array_add (virtual_atom_array, (gchar *)(xatoms_string + xatoms_offset[i]));
393 g_hash_table_insert (virtual_atom_hash, (gchar *)(xatoms_string + xatoms_offset[i]),
394 GUINT_TO_POINTER (i));
400 _gdk_x11_display_manager_atom_intern (GdkDisplayManager *manager,
401 const gchar *atom_name,
406 virtual_atom_check_init ();
408 result = GDK_POINTER_TO_ATOM (g_hash_table_lookup (virtual_atom_hash, atom_name));
411 result = INDEX_TO_ATOM (virtual_atom_array->len);
413 g_ptr_array_add (virtual_atom_array, dup ? g_strdup (atom_name) : (gchar *)atom_name);
414 g_hash_table_insert (virtual_atom_hash,
415 g_ptr_array_index (virtual_atom_array,
416 ATOM_TO_INDEX (result)),
417 GDK_ATOM_TO_POINTER (result));
424 get_atom_name (GdkAtom atom)
426 virtual_atom_check_init ();
428 if (ATOM_TO_INDEX (atom) < virtual_atom_array->len)
429 return g_ptr_array_index (virtual_atom_array, ATOM_TO_INDEX (atom));
436 _gdk_x11_display_manager_get_atom_name (GdkDisplayManager *manager,
439 return g_strdup (get_atom_name (atom));
443 * gdk_x11_get_xatom_by_name_for_display:
444 * @display: a #GdkDisplay
445 * @atom_name: a string
447 * Returns the X atom for a #GdkDisplay corresponding to @atom_name.
448 * This function caches the result, so if called repeatedly it is much
449 * faster than XInternAtom(), which is a round trip to the server each time.
451 * Return value: a X atom for a #GdkDisplay
456 gdk_x11_get_xatom_by_name_for_display (GdkDisplay *display,
457 const gchar *atom_name)
459 g_return_val_if_fail (GDK_IS_DISPLAY (display), None);
460 return gdk_x11_atom_to_xatom_for_display (display,
461 gdk_atom_intern (atom_name, FALSE));
465 _gdk_x11_get_xatom_for_display_printf (GdkDisplay *display,
473 va_start (args, format);
474 atom_name = g_strdup_vprintf (format, args);
477 atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name);
485 * gdk_x11_get_xatom_by_name:
486 * @atom_name: a string
488 * Returns the X atom for GDK's default display corresponding to @atom_name.
489 * This function caches the result, so if called repeatedly it is much
490 * faster than XInternAtom(), which is a round trip to the server each time.
492 * Return value: a X atom for GDK's default display.
495 gdk_x11_get_xatom_by_name (const gchar *atom_name)
497 return gdk_x11_get_xatom_by_name_for_display (gdk_display_get_default (),
502 * gdk_x11_get_xatom_name_for_display:
503 * @display: the #GdkDisplay where @xatom is defined
506 * Returns the name of an X atom for its display. This
507 * function is meant mainly for debugging, so for convenience, unlike
508 * XAtomName() and gdk_atom_name(), the result doesn't need to
511 * Return value: name of the X atom; this string is owned by GDK,
512 * so it shouldn't be modifed or freed.
517 gdk_x11_get_xatom_name_for_display (GdkDisplay *display,
520 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
522 return get_atom_name (gdk_x11_xatom_to_atom_for_display (display, xatom));
526 * gdk_x11_get_xatom_name:
527 * @xatom: an X atom for GDK's default display
529 * Returns the name of an X atom for GDK's default display. This
530 * function is meant mainly for debugging, so for convenience, unlike
531 * <function>XAtomName()</function> and gdk_atom_name(), the result
532 * doesn't need to be freed. Also, this function will never return %NULL,
533 * even if @xatom is invalid.
535 * Return value: name of the X atom; this string is owned by GTK+,
536 * so it shouldn't be modifed or freed.
539 gdk_x11_get_xatom_name (Atom xatom)
541 return get_atom_name (gdk_x11_xatom_to_atom (xatom));
545 _gdk_x11_window_get_property (GdkWindow *window,
551 GdkAtom *actual_property_type,
552 gint *actual_format_type,
560 gulong ret_bytes_after;
568 g_return_val_if_fail (!window || GDK_WINDOW_IS_X11 (window), FALSE);
572 GdkScreen *screen = gdk_screen_get_default ();
573 window = gdk_screen_get_root_window (screen);
575 GDK_NOTE (MULTIHEAD, g_message ("gdk_property_get(): window is NULL\n"));
577 else if (!GDK_WINDOW_IS_X11 (window))
580 if (GDK_WINDOW_DESTROYED (window))
583 display = gdk_window_get_display (window);
584 xproperty = gdk_x11_atom_to_xatom_for_display (display, property);
585 if (type == GDK_NONE)
586 xtype = AnyPropertyType;
588 xtype = gdk_x11_atom_to_xatom_for_display (display, type);
593 * Round up length to next 4 byte value. Some code is in the (bad?)
594 * habit of passing G_MAXLONG as the length argument, causing an
595 * overflow to negative on the add. In this case, we clamp the
596 * value to G_MAXLONG.
598 get_length = length + 3;
599 if (get_length > G_MAXLONG)
600 get_length = G_MAXLONG;
602 /* To fail, either the user passed 0 or G_MAXULONG */
603 get_length = get_length / 4;
606 g_warning ("gdk_propery-get(): invalid length 0");
610 res = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
611 GDK_WINDOW_XID (window), xproperty,
612 offset, get_length, pdelete,
613 xtype, &ret_prop_type, &ret_format,
614 &ret_nitems, &ret_bytes_after,
617 if (res != Success || (ret_prop_type == None && ret_format == 0))
622 if (actual_property_type)
623 *actual_property_type = gdk_x11_xatom_to_atom_for_display (display, ret_prop_type);
624 if (actual_format_type)
625 *actual_format_type = ret_format;
627 if ((xtype != AnyPropertyType) && (ret_prop_type != xtype))
630 g_warning ("Couldn't match property type %s to %s\n",
631 gdk_x11_get_xatom_name_for_display (display, ret_prop_type),
632 gdk_x11_get_xatom_name_for_display (display, xtype));
636 /* FIXME: ignoring bytes_after could have very bad effects */
640 if (ret_prop_type == XA_ATOM ||
641 ret_prop_type == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
644 * data is an array of X atom, we need to convert it
645 * to an array of GDK Atoms
648 GdkAtom *ret_atoms = g_new (GdkAtom, ret_nitems);
649 Atom *xatoms = (Atom *)ret_data;
651 *data = (guchar *)ret_atoms;
653 for (i = 0; i < ret_nitems; i++)
654 ret_atoms[i] = gdk_x11_xatom_to_atom_for_display (display, xatoms[i]);
657 *actual_length = ret_nitems * sizeof (GdkAtom);
664 ret_length = ret_nitems;
667 ret_length = sizeof(short) * ret_nitems;
670 ret_length = sizeof(long) * ret_nitems;
673 g_warning ("unknown property return format: %d", ret_format);
678 *data = g_new (guchar, ret_length);
679 memcpy (*data, ret_data, ret_length);
681 *actual_length = ret_length;
691 _gdk_x11_window_change_property (GdkWindow *window,
704 g_return_if_fail (!window || GDK_WINDOW_IS_X11 (window));
710 screen = gdk_screen_get_default ();
711 window = gdk_screen_get_root_window (screen);
713 GDK_NOTE (MULTIHEAD, g_message ("gdk_property_change(): window is NULL\n"));
715 else if (!GDK_WINDOW_IS_X11 (window))
718 if (GDK_WINDOW_DESTROYED (window))
721 gdk_window_ensure_native (window);
723 display = gdk_window_get_display (window);
724 xproperty = gdk_x11_atom_to_xatom_for_display (display, property);
725 xtype = gdk_x11_atom_to_xatom_for_display (display, type);
726 xwindow = GDK_WINDOW_XID (window);
728 if (xtype == XA_ATOM ||
729 xtype == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
732 * data is an array of GdkAtom, we need to convert it
733 * to an array of X Atoms
736 GdkAtom *atoms = (GdkAtom*) data;
739 xatoms = g_new (Atom, nelements);
740 for (i = 0; i < nelements; i++)
741 xatoms[i] = gdk_x11_atom_to_xatom_for_display (display, atoms[i]);
743 XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow,
745 format, mode, (guchar *)xatoms, nelements);
749 XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow, xproperty,
750 xtype, format, mode, (guchar *)data, nelements);
754 _gdk_x11_window_delete_property (GdkWindow *window,
757 g_return_if_fail (!window || GDK_WINDOW_IS_X11 (window));
761 GdkScreen *screen = gdk_screen_get_default ();
762 window = gdk_screen_get_root_window (screen);
765 g_message ("gdk_property_delete(): window is NULL\n"));
767 else if (!GDK_WINDOW_IS_X11 (window))
770 if (GDK_WINDOW_DESTROYED (window))
773 XDeleteProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window),
774 gdk_x11_atom_to_xatom_for_display (GDK_WINDOW_DISPLAY (window),