]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkproperty-x11.c
fdc32a41762c7c0f5b138166889a93ce1ba2fdeb
[~andy/gtk] / gdk / x11 / gdkproperty-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 "gdkproperty.h"
30 #include "gdkmain.h"
31 #include "gdkprivate.h"
32 #include "gdkinternals.h"
33 #include "gdkselection.h"
34 #include "gdkprivate-x11.h"
35 #include "gdkdisplay-x11.h"
36 #include "gdkscreen-x11.h"
37
38 #include <X11/Xlib.h>
39 #include <X11/Xatom.h>
40 #include <string.h>
41
42
43 /**
44  * SECTION:properties
45  * @Short_description: Functions to manipulate properties on windows
46  * @Title: Properties and Atoms
47  *
48  * Each window under X can have any number of associated
49  * <firstterm>properties</firstterm> attached to it.
50  * Properties are arbitrary chunks of data identified by
51  * <firstterm>atom</firstterm>s. (An <firstterm>atom</firstterm>
52  * is a numeric index into a string table on the X server. They are used
53  * to transfer strings efficiently between clients without
54  * having to transfer the entire string.) A property
55  * has an associated type, which is also identified
56  * using an atom.
57  *
58  * A property has an associated <firstterm>format</firstterm>,
59  * an integer describing how many bits are in each unit
60  * of data inside the property. It must be 8, 16, or 32.
61  * When data is transferred between the server and client,
62  * if they are of different endianesses it will be byteswapped
63  * as necessary according to the format of the property.
64  * Note that on the client side, properties of format 32
65  * will be stored with one unit per <emphasis>long</emphasis>,
66  * even if a long integer has more than 32 bits on the platform.
67  * (This decision was apparently made for Xlib to maintain
68  * compatibility with programs that assumed longs were 32
69  * bits, at the expense of programs that knew better.)
70  *
71  * The functions in this section are used to add, remove
72  * and change properties on windows, to convert atoms
73  * to and from strings and to manipulate some types of
74  * data commonly stored in X window properties.
75  */
76
77
78 static GPtrArray *virtual_atom_array;
79 static GHashTable *virtual_atom_hash;
80
81 static const gchar xatoms_string[] = 
82   /* These are all the standard predefined X atoms */
83   "\0"  /* leave a space for None, even though it is not a predefined atom */
84   "PRIMARY\0"
85   "SECONDARY\0"
86   "ARC\0"
87   "ATOM\0"
88   "BITMAP\0"
89   "CARDINAL\0"
90   "COLORMAP\0"
91   "CURSOR\0"
92   "CUT_BUFFER0\0"
93   "CUT_BUFFER1\0"
94   "CUT_BUFFER2\0"
95   "CUT_BUFFER3\0"
96   "CUT_BUFFER4\0"
97   "CUT_BUFFER5\0"
98   "CUT_BUFFER6\0"
99   "CUT_BUFFER7\0"
100   "DRAWABLE\0"
101   "FONT\0"
102   "INTEGER\0"
103   "PIXMAP\0"
104   "POINT\0"
105   "RECTANGLE\0"
106   "RESOURCE_MANAGER\0"
107   "RGB_COLOR_MAP\0"
108   "RGB_BEST_MAP\0"
109   "RGB_BLUE_MAP\0"
110   "RGB_DEFAULT_MAP\0"
111   "RGB_GRAY_MAP\0"
112   "RGB_GREEN_MAP\0"
113   "RGB_RED_MAP\0"
114   "STRING\0"
115   "VISUALID\0"
116   "WINDOW\0"
117   "WM_COMMAND\0"
118   "WM_HINTS\0"
119   "WM_CLIENT_MACHINE\0"
120   "WM_ICON_NAME\0"
121   "WM_ICON_SIZE\0"
122   "WM_NAME\0"
123   "WM_NORMAL_HINTS\0"
124   "WM_SIZE_HINTS\0"
125   "WM_ZOOM_HINTS\0"
126   "MIN_SPACE\0"
127   "NORM_SPACE\0"
128   "MAX_SPACE\0"
129   "END_SPACE\0"
130   "SUPERSCRIPT_X\0"
131   "SUPERSCRIPT_Y\0"
132   "SUBSCRIPT_X\0"
133   "SUBSCRIPT_Y\0"
134   "UNDERLINE_POSITION\0"
135   "UNDERLINE_THICKNESS\0"
136   "STRIKEOUT_ASCENT\0"
137   "STRIKEOUT_DESCENT\0"
138   "ITALIC_ANGLE\0"
139   "X_HEIGHT\0"
140   "QUAD_WIDTH\0"
141   "WEIGHT\0"
142   "POINT_SIZE\0"
143   "RESOLUTION\0"
144   "COPYRIGHT\0"
145   "NOTICE\0"
146   "FONT_NAME\0"
147   "FAMILY_NAME\0"
148   "FULL_NAME\0"
149   "CAP_HEIGHT\0"
150   "WM_CLASS\0"
151   "WM_TRANSIENT_FOR\0"
152   /* Below here, these are our additions. Increment N_CUSTOM_PREDEFINED
153    * if you add any.
154    */
155   "CLIPBOARD\0"                 /* = 69 */
156 ;
157
158 static const gint xatoms_offset[] = {
159     0,   1,   9,  19,  23,  28,  35,  44,  53,  60,  72,  84,
160    96, 108, 120, 132, 144, 156, 165, 170, 178, 185, 189, 201,
161   218, 232, 245, 258, 274, 287, 301, 313, 320, 329, 336, 347,
162   356, 374, 387, 400, 408, 424, 438, 452, 462, 473, 483, 493,
163   507, 521, 533, 545, 564, 584, 601, 619, 632, 641, 652, 659,
164   670, 681, 691, 698, 708, 720, 730, 741, 750, 767
165 };
166
167 #define N_CUSTOM_PREDEFINED 1
168
169 #define ATOM_TO_INDEX(atom) (GPOINTER_TO_UINT(atom))
170 #define INDEX_TO_ATOM(atom) ((GdkAtom)GUINT_TO_POINTER(atom))
171
172 static void
173 insert_atom_pair (GdkDisplay *display,
174                   GdkAtom     virtual_atom,
175                   Atom        xatom)
176 {
177   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);  
178   
179   if (!display_x11->atom_from_virtual)
180     {
181       display_x11->atom_from_virtual = g_hash_table_new (g_direct_hash, NULL);
182       display_x11->atom_to_virtual = g_hash_table_new (g_direct_hash, NULL);
183     }
184   
185   g_hash_table_insert (display_x11->atom_from_virtual, 
186                        GDK_ATOM_TO_POINTER (virtual_atom), 
187                        GUINT_TO_POINTER (xatom));
188   g_hash_table_insert (display_x11->atom_to_virtual,
189                        GUINT_TO_POINTER (xatom), 
190                        GDK_ATOM_TO_POINTER (virtual_atom));
191 }
192
193 static Atom
194 lookup_cached_xatom (GdkDisplay *display,
195                      GdkAtom     atom)
196 {
197   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
198
199   if (ATOM_TO_INDEX (atom) < G_N_ELEMENTS (xatoms_offset) - N_CUSTOM_PREDEFINED)
200     return ATOM_TO_INDEX (atom);
201   
202   if (display_x11->atom_from_virtual)
203     return GPOINTER_TO_UINT (g_hash_table_lookup (display_x11->atom_from_virtual,
204                                                   GDK_ATOM_TO_POINTER (atom)));
205
206   return None;
207 }
208
209 /**
210  * gdk_x11_atom_to_xatom_for_display:
211  * @display: A #GdkDisplay
212  * @atom: A #GdkAtom, or %GDK_NONE
213  *
214  * Converts from a #GdkAtom to the X atom for a #GdkDisplay
215  * with the same string value. The special value %GDK_NONE
216  * is converted to %None.
217  *
218  * Return value: the X atom corresponding to @atom, or %None
219  *
220  * Since: 2.2
221  **/
222 Atom
223 gdk_x11_atom_to_xatom_for_display (GdkDisplay *display,
224                                    GdkAtom     atom)
225 {
226   Atom xatom = None;
227
228   g_return_val_if_fail (GDK_IS_DISPLAY (display), None);
229
230   if (atom == GDK_NONE)
231     return None;
232
233   if (gdk_display_is_closed (display))
234     return None;
235
236   xatom = lookup_cached_xatom (display, atom);
237
238   if (!xatom)
239     {
240       char *name;
241
242       g_return_val_if_fail (ATOM_TO_INDEX (atom) < virtual_atom_array->len, None);
243
244       name = g_ptr_array_index (virtual_atom_array, ATOM_TO_INDEX (atom));
245
246       xatom = XInternAtom (GDK_DISPLAY_XDISPLAY (display), name, FALSE);
247       insert_atom_pair (display, atom, xatom);
248     }
249
250   return xatom;
251 }
252
253 void
254 _gdk_x11_precache_atoms (GdkDisplay          *display,
255                          const gchar * const *atom_names,
256                          gint                 n_atoms)
257 {
258   Atom *xatoms;
259   GdkAtom *atoms;
260   const gchar **xatom_names;
261   gint n_xatoms;
262   gint i;
263
264   xatoms = g_new (Atom, n_atoms);
265   xatom_names = g_new (const gchar *, n_atoms);
266   atoms = g_new (GdkAtom, n_atoms);
267
268   n_xatoms = 0;
269   for (i = 0; i < n_atoms; i++)
270     {
271       GdkAtom atom = gdk_atom_intern_static_string (atom_names[i]);
272       if (lookup_cached_xatom (display, atom) == None)
273         {
274           atoms[n_xatoms] = atom;
275           xatom_names[n_xatoms] = atom_names[i];
276           n_xatoms++;
277         }
278     }
279
280   if (n_xatoms)
281     {
282 #ifdef HAVE_XINTERNATOMS
283       XInternAtoms (GDK_DISPLAY_XDISPLAY (display),
284                     (char **)xatom_names, n_xatoms, False, xatoms);
285 #else
286       for (i = 0; i < n_xatoms; i++)
287         xatoms[i] = XInternAtom (GDK_DISPLAY_XDISPLAY (display),
288                                  xatom_names[i], False);
289 #endif
290     }
291
292   for (i = 0; i < n_xatoms; i++)
293     insert_atom_pair (display, atoms[i], xatoms[i]);
294
295   g_free (xatoms);
296   g_free (xatom_names);
297   g_free (atoms);
298 }
299
300 /**
301  * gdk_x11_atom_to_xatom:
302  * @atom: A #GdkAtom 
303  * 
304  * Converts from a #GdkAtom to the X atom for the default GDK display
305  * with the same string value.
306  * 
307  * Return value: the X atom corresponding to @atom.
308  **/
309 Atom
310 gdk_x11_atom_to_xatom (GdkAtom atom)
311 {
312   return gdk_x11_atom_to_xatom_for_display (gdk_display_get_default (), atom);
313 }
314
315 /**
316  * gdk_x11_xatom_to_atom_for_display:
317  * @display: A #GdkDisplay
318  * @xatom: an X atom 
319  * 
320  * Convert from an X atom for a #GdkDisplay to the corresponding
321  * #GdkAtom.
322  * 
323  * Return value: the corresponding #GdkAtom.
324  *
325  * Since: 2.2
326  **/
327 GdkAtom
328 gdk_x11_xatom_to_atom_for_display (GdkDisplay *display,
329                                    Atom        xatom)
330 {
331   GdkDisplayX11 *display_x11;
332   GdkAtom virtual_atom = GDK_NONE;
333   
334   g_return_val_if_fail (GDK_IS_DISPLAY (display), GDK_NONE);
335
336   if (xatom == None)
337     return GDK_NONE;
338
339   if (gdk_display_is_closed (display))
340     return GDK_NONE;
341
342   display_x11 = GDK_DISPLAY_X11 (display);
343   
344   if (xatom < G_N_ELEMENTS (xatoms_offset) - N_CUSTOM_PREDEFINED)
345     return INDEX_TO_ATOM (xatom);
346   
347   if (display_x11->atom_to_virtual)
348     virtual_atom = GDK_POINTER_TO_ATOM (g_hash_table_lookup (display_x11->atom_to_virtual,
349                                                              GUINT_TO_POINTER (xatom)));
350   
351   if (!virtual_atom)
352     {
353       /* If this atom doesn't exist, we'll die with an X error unless
354        * we take precautions
355        */
356       char *name;
357       gdk_x11_display_error_trap_push (display);
358       name = XGetAtomName (GDK_DISPLAY_XDISPLAY (display), xatom);
359       if (gdk_x11_display_error_trap_pop (display))
360         {
361           g_warning (G_STRLOC " invalid X atom: %ld", xatom);
362         }
363       else
364         {
365           virtual_atom = gdk_atom_intern (name, FALSE);
366           XFree (name);
367           
368           insert_atom_pair (display, virtual_atom, xatom);
369         }
370     }
371
372   return virtual_atom;
373 }
374
375 /**
376  * gdk_x11_xatom_to_atom:
377  * @xatom: an X atom for the default GDK display
378  * 
379  * Convert from an X atom for the default display to the corresponding
380  * #GdkAtom.
381  * 
382  * Return value: the corresponding G#dkAtom.
383  **/
384 GdkAtom
385 gdk_x11_xatom_to_atom (Atom xatom)
386 {
387   return gdk_x11_xatom_to_atom_for_display (gdk_display_get_default (), xatom);
388 }
389
390 static void
391 virtual_atom_check_init (void)
392 {
393   if (!virtual_atom_hash)
394     {
395       gint i;
396       
397       virtual_atom_hash = g_hash_table_new (g_str_hash, g_str_equal);
398       virtual_atom_array = g_ptr_array_new ();
399       
400       for (i = 0; i < G_N_ELEMENTS (xatoms_offset); i++)
401         {
402           g_ptr_array_add (virtual_atom_array, (gchar *)(xatoms_string + xatoms_offset[i]));
403           g_hash_table_insert (virtual_atom_hash, (gchar *)(xatoms_string + xatoms_offset[i]),
404                                GUINT_TO_POINTER (i));
405         }
406     }
407 }
408
409 static GdkAtom
410 intern_atom (const gchar *atom_name, 
411              gboolean     dup)
412 {
413   GdkAtom result;
414
415   virtual_atom_check_init ();
416   
417   result = GDK_POINTER_TO_ATOM (g_hash_table_lookup (virtual_atom_hash, atom_name));
418   if (!result)
419     {
420       result = INDEX_TO_ATOM (virtual_atom_array->len);
421       
422       g_ptr_array_add (virtual_atom_array, dup ? g_strdup (atom_name) : (gchar *)atom_name);
423       g_hash_table_insert (virtual_atom_hash, 
424                            g_ptr_array_index (virtual_atom_array,
425                                               ATOM_TO_INDEX (result)),
426                            GDK_ATOM_TO_POINTER (result));
427     }
428
429   return result;
430 }
431
432 /**
433  * gdk_atom_intern:
434  * @atom_name: a string.
435  * @only_if_exists: if %TRUE, GDK is allowed to not create a new atom, but
436  *   just return %GDK_NONE if the requested atom doesn't already
437  *   exists. Currently, the flag is ignored, since checking the
438  *   existance of an atom is as expensive as creating it.
439  *
440  * Finds or creates an atom corresponding to a given string.
441  *
442  * Returns: the atom corresponding to @atom_name.
443  */
444 GdkAtom
445 gdk_atom_intern (const gchar *atom_name, 
446                  gboolean     only_if_exists)
447 {
448   return intern_atom (atom_name, TRUE);
449 }
450
451 /**
452  * gdk_atom_intern_static_string:
453  * @atom_name: a static string
454  *
455  * Finds or creates an atom corresponding to a given string.
456  *
457  * Note that this function is identical to gdk_atom_intern() except
458  * that if a new #GdkAtom is created the string itself is used rather 
459  * than a copy. This saves memory, but can only be used if the string 
460  * will <emphasis>always</emphasis> exist. It can be used with statically
461  * allocated strings in the main program, but not with statically 
462  * allocated memory in dynamically loaded modules, if you expect to
463  * ever unload the module again (e.g. do not use this function in
464  * GTK+ theme engines).
465  *
466  * Returns: the atom corresponding to @atom_name
467  * 
468  * Since: 2.10
469  */
470 GdkAtom
471 gdk_atom_intern_static_string (const gchar *atom_name)
472 {
473   return intern_atom (atom_name, FALSE);
474 }
475
476 static G_CONST_RETURN char *
477 get_atom_name (GdkAtom atom)
478 {
479   virtual_atom_check_init ();
480
481   if (ATOM_TO_INDEX (atom) < virtual_atom_array->len)
482     return g_ptr_array_index (virtual_atom_array, ATOM_TO_INDEX (atom));
483   else
484     return NULL;
485 }
486
487 /**
488  * gdk_atom_name:
489  * @atom: a #GdkAtom.
490  *
491  * Determines the string corresponding to an atom.
492  *
493  * Returns: a newly-allocated string containing the string
494  *   corresponding to @atom. When you are done with the
495  *   return value, you should free it using g_free().
496  */
497 gchar *
498 gdk_atom_name (GdkAtom atom)
499 {
500   return g_strdup (get_atom_name (atom));
501 }
502
503 /**
504  * gdk_x11_get_xatom_by_name_for_display:
505  * @display: a #GdkDisplay
506  * @atom_name: a string
507  * 
508  * Returns the X atom for a #GdkDisplay corresponding to @atom_name.
509  * This function caches the result, so if called repeatedly it is much
510  * faster than XInternAtom(), which is a round trip to the server each time.
511  * 
512  * Return value: a X atom for a #GdkDisplay
513  *
514  * Since: 2.2
515  **/
516 Atom
517 gdk_x11_get_xatom_by_name_for_display (GdkDisplay  *display,
518                                        const gchar *atom_name)
519 {
520   g_return_val_if_fail (GDK_IS_DISPLAY (display), None);
521   return gdk_x11_atom_to_xatom_for_display (display,
522                                             gdk_atom_intern (atom_name, FALSE));
523 }
524
525 /**
526  * gdk_x11_get_xatom_by_name:
527  * @atom_name: a string
528  * 
529  * Returns the X atom for GDK's default display corresponding to @atom_name.
530  * This function caches the result, so if called repeatedly it is much
531  * faster than XInternAtom(), which is a round trip to the server each time.
532  * 
533  * Return value: a X atom for GDK's default display.
534  **/
535 Atom
536 gdk_x11_get_xatom_by_name (const gchar *atom_name)
537 {
538   return gdk_x11_get_xatom_by_name_for_display (gdk_display_get_default (),
539                                                 atom_name);
540 }
541
542 /**
543  * gdk_x11_get_xatom_name_for_display:
544  * @display: the #GdkDisplay where @xatom is defined
545  * @xatom: an X atom 
546  * 
547  * Returns the name of an X atom for its display. This
548  * function is meant mainly for debugging, so for convenience, unlike
549  * XAtomName() and gdk_atom_name(), the result doesn't need to
550  * be freed. 
551  *
552  * Return value: name of the X atom; this string is owned by GDK,
553  *   so it shouldn't be modifed or freed. 
554  *
555  * Since: 2.2
556  **/
557 G_CONST_RETURN gchar *
558 gdk_x11_get_xatom_name_for_display (GdkDisplay *display,
559                                     Atom        xatom)
560 {
561   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
562
563   return get_atom_name (gdk_x11_xatom_to_atom_for_display (display, xatom));
564 }
565
566 /**
567  * gdk_x11_get_xatom_name:
568  * @xatom: an X atom for GDK's default display
569  * 
570  * Returns the name of an X atom for GDK's default display. This
571  * function is meant mainly for debugging, so for convenience, unlike
572  * <function>XAtomName()</function> and gdk_atom_name(), the result 
573  * doesn't need to be freed. Also, this function will never return %NULL, 
574  * even if @xatom is invalid.
575  * 
576  * Return value: name of the X atom; this string is owned by GTK+,
577  *   so it shouldn't be modifed or freed. 
578  **/
579 G_CONST_RETURN gchar *
580 gdk_x11_get_xatom_name (Atom xatom)
581 {
582   return get_atom_name (gdk_x11_xatom_to_atom (xatom));
583 }
584
585 /**
586  * gdk_property_get:
587  * @window: a #GdkWindow.
588  * @property: the property to retrieve.
589  * @type: the desired property type, or %GDK_NONE, if any type of data
590  *   is acceptable. If this does not match the actual
591  *   type, then @actual_format and @actual_length will
592  *   be filled in, a warning will be printed to stderr
593  *   and no data will be returned.
594  * @offset: the offset into the property at which to begin
595  *   retrieving data, in 4 byte units.
596  * @length: the length of the data to retrieve in bytes.  Data is
597  *   considered to be retrieved in 4 byte chunks, so @length
598  *   will be rounded up to the next highest 4 byte boundary
599  *   (so be careful not to pass a value that might overflow
600  *   when rounded up).
601  * @pdelete: if %TRUE, delete the property after retrieving the
602  *   data.
603  * @actual_property_type: location to store the actual type of
604  *   the property.
605  * @actual_format: location to store the actual return format of the
606  *   data; either 8, 16 or 32 bits.
607  * @actual_length: location to store the length of the retrieved data, in
608  *   bytes.  Data returned in the 32 bit format is stored
609  *   in a long variable, so the actual number of 32 bit
610  *   elements should be be calculated via
611  *   @actual_length / sizeof(glong) to ensure portability to
612  *   64 bit systems.
613  * @data: location to store a pointer to the data. The retrieved
614  *   data should be freed with g_free() when you are finished
615  *   using it.
616  *
617  * Retrieves a portion of the contents of a property. If the
618  * property does not exist, then the function returns %FALSE,
619  * and %GDK_NONE will be stored in @actual_property_type.
620  *
621  * <note>
622  * <para>
623  * The XGetWindowProperty() function that gdk_property_get()
624  * uses has a very confusing and complicated set of semantics.
625  * Unfortunately, gdk_property_get() makes the situation
626  * worse instead of better (the semantics should be considered
627  * undefined), and also prints warnings to stderr in cases where it
628  * should return a useful error to the program. You are advised to use
629  * XGetWindowProperty() directly until a replacement function for
630  * gdk_property_get()
631  * is provided.
632  * </para>
633  * </note>
634  *
635  * Returns: %TRUE if data was successfully received and stored
636  *   in @data, otherwise %FALSE.
637  */
638 gboolean
639 gdk_property_get (GdkWindow   *window,
640                   GdkAtom      property,
641                   GdkAtom      type,
642                   gulong       offset,
643                   gulong       length,
644                   gint         pdelete,
645                   GdkAtom     *actual_property_type,
646                   gint        *actual_format_type,
647                   gint        *actual_length,
648                   guchar     **data)
649 {
650   GdkDisplay *display;
651   Atom ret_prop_type;
652   gint ret_format;
653   gulong ret_nitems;
654   gulong ret_bytes_after;
655   gulong get_length;
656   gulong ret_length;
657   guchar *ret_data;
658   Atom xproperty;
659   Atom xtype;
660   int res;
661
662   g_return_val_if_fail (!window || GDK_WINDOW_IS_X11 (window), FALSE);
663
664   if (!window)
665     {
666       GdkScreen *screen = gdk_screen_get_default ();
667       window = gdk_screen_get_root_window (screen);
668       
669       GDK_NOTE (MULTIHEAD, g_message ("gdk_property_get(): window is NULL\n"));
670     }
671   else if (!GDK_WINDOW_IS_X11 (window))
672     return FALSE;
673
674   if (GDK_WINDOW_DESTROYED (window))
675     return FALSE;
676
677   display = gdk_window_get_display (window);
678   xproperty = gdk_x11_atom_to_xatom_for_display (display, property);
679   if (type == GDK_NONE)
680     xtype = AnyPropertyType;
681   else
682     xtype = gdk_x11_atom_to_xatom_for_display (display, type);
683
684   ret_data = NULL;
685   
686   /* 
687    * Round up length to next 4 byte value.  Some code is in the (bad?)
688    * habit of passing G_MAXLONG as the length argument, causing an
689    * overflow to negative on the add.  In this case, we clamp the
690    * value to G_MAXLONG.
691    */
692   get_length = length + 3;
693   if (get_length > G_MAXLONG)
694     get_length = G_MAXLONG;
695
696   /* To fail, either the user passed 0 or G_MAXULONG */
697   get_length = get_length / 4;
698   if (get_length == 0)
699     {
700       g_warning ("gdk_propery-get(): invalid length 0");
701       return FALSE;
702     }
703
704   res = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
705                             GDK_WINDOW_XID (window), xproperty,
706                             offset, get_length, pdelete,
707                             xtype, &ret_prop_type, &ret_format,
708                             &ret_nitems, &ret_bytes_after,
709                             &ret_data);
710
711   if (res != Success || (ret_prop_type == None && ret_format == 0))
712     {
713       return FALSE;
714     }
715
716   if (actual_property_type)
717     *actual_property_type = gdk_x11_xatom_to_atom_for_display (display, ret_prop_type);
718   if (actual_format_type)
719     *actual_format_type = ret_format;
720
721   if ((xtype != AnyPropertyType) && (ret_prop_type != xtype))
722     {
723       XFree (ret_data);
724       g_warning ("Couldn't match property type %s to %s\n", 
725                  gdk_x11_get_xatom_name_for_display (display, ret_prop_type), 
726                  gdk_x11_get_xatom_name_for_display (display, xtype));
727       return FALSE;
728     }
729
730   /* FIXME: ignoring bytes_after could have very bad effects */
731
732   if (data)
733     {
734       if (ret_prop_type == XA_ATOM ||
735           ret_prop_type == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
736         {
737           /*
738            * data is an array of X atom, we need to convert it
739            * to an array of GDK Atoms
740            */
741           gint i;
742           GdkAtom *ret_atoms = g_new (GdkAtom, ret_nitems);
743           Atom *xatoms = (Atom *)ret_data;
744
745           *data = (guchar *)ret_atoms;
746
747           for (i = 0; i < ret_nitems; i++)
748             ret_atoms[i] = gdk_x11_xatom_to_atom_for_display (display, xatoms[i]);
749           
750           if (actual_length)
751             *actual_length = ret_nitems * sizeof (GdkAtom);
752         }
753       else
754         {
755           switch (ret_format)
756             {
757             case 8:
758               ret_length = ret_nitems;
759               break;
760             case 16:
761               ret_length = sizeof(short) * ret_nitems;
762               break;
763             case 32:
764               ret_length = sizeof(long) * ret_nitems;
765               break;
766             default:
767               g_warning ("unknown property return format: %d", ret_format);
768               XFree (ret_data);
769               return FALSE;
770             }
771           
772           *data = g_new (guchar, ret_length);
773           memcpy (*data, ret_data, ret_length);
774           if (actual_length)
775             *actual_length = ret_length;
776         }
777     }
778
779   XFree (ret_data);
780
781   return TRUE;
782 }
783
784 /**
785  * gdk_property_change:
786  * @window: a #GdkWindow.
787  * @property: the property to change.
788  * @type: the new type for the property. If @mode is
789  *   %GDK_PROP_MODE_PREPEND or %GDK_PROP_MODE_APPEND, then this
790  *   must match the existing type or an error will occur.
791  * @format: the new format for the property. If @mode is
792  *   %GDK_PROP_MODE_PREPEND or %GDK_PROP_MODE_APPEND, then this
793  *   must match the existing format or an error will occur.
794  * @mode: a value describing how the new data is to be combined
795  *   with the current data.
796  * @data: the data (a <literal>guchar *</literal>
797  *   <literal>gushort *</literal>, or <literal>gulong *</literal>,
798  *   depending on @format), cast to a <literal>guchar *</literal>.
799  * @nelements: the number of elements of size determined by the format,
800  *   contained in @data.
801  *
802  * Changes the contents of a property on a window.
803  */
804 void
805 gdk_property_change (GdkWindow    *window,
806                      GdkAtom       property,
807                      GdkAtom       type,
808                      gint          format,
809                      GdkPropMode   mode,
810                      const guchar *data,
811                      gint          nelements)
812 {
813   GdkDisplay *display;
814   Window xwindow;
815   Atom xproperty;
816   Atom xtype;
817
818   g_return_if_fail (!window || GDK_WINDOW_IS_X11 (window));
819
820   if (!window)
821     {
822       GdkScreen *screen;
823       
824       screen = gdk_screen_get_default ();
825       window = gdk_screen_get_root_window (screen);
826       
827       GDK_NOTE (MULTIHEAD, g_message ("gdk_property_change(): window is NULL\n"));
828     }
829   else if (!GDK_WINDOW_IS_X11 (window))
830     return;
831
832   if (GDK_WINDOW_DESTROYED (window))
833     return;
834
835   gdk_window_ensure_native (window);
836
837   display = gdk_window_get_display (window);
838   xproperty = gdk_x11_atom_to_xatom_for_display (display, property);
839   xtype = gdk_x11_atom_to_xatom_for_display (display, type);
840   xwindow = GDK_WINDOW_XID (window);
841
842   if (xtype == XA_ATOM ||
843       xtype == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
844     {
845       /*
846        * data is an array of GdkAtom, we need to convert it
847        * to an array of X Atoms
848        */
849       gint i;
850       GdkAtom *atoms = (GdkAtom*) data;
851       Atom *xatoms;
852
853       xatoms = g_new (Atom, nelements);
854       for (i = 0; i < nelements; i++)
855         xatoms[i] = gdk_x11_atom_to_xatom_for_display (display, atoms[i]);
856
857       XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow,
858                        xproperty, xtype,
859                        format, mode, (guchar *)xatoms, nelements);
860       g_free (xatoms);
861     }
862   else
863     XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow, xproperty, 
864                      xtype, format, mode, (guchar *)data, nelements);
865 }
866
867 /**
868  * gdk_property_delete:
869  * @window: a #GdkWindow.
870  * @property: the property to delete.
871  *
872  * Deletes a property from a window.
873  */
874 void
875 gdk_property_delete (GdkWindow *window,
876                      GdkAtom    property)
877 {
878   g_return_if_fail (!window || GDK_WINDOW_IS_X11 (window));
879
880   if (!window)
881     {
882       GdkScreen *screen = gdk_screen_get_default ();
883       window = gdk_screen_get_root_window (screen);
884       
885       GDK_NOTE (MULTIHEAD, 
886                 g_message ("gdk_property_delete(): window is NULL\n"));
887     }
888   else if (!GDK_WINDOW_IS_X11 (window))
889     return;
890
891   if (GDK_WINDOW_DESTROYED (window))
892     return;
893
894   XDeleteProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window),
895                    gdk_x11_atom_to_xatom_for_display (GDK_WINDOW_DISPLAY (window),
896                                                       property));
897 }