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