]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkproperty-x11.c
stylecontext: Do invalidation on first resize container
[~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, see <http://www.gnu.org/licenses/>.
16  */
17
18 /*
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/. 
23  */
24
25 #include "config.h"
26
27 #include "gdkproperty.h"
28 #include "gdkmain.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"
35
36 #include <X11/Xlib.h>
37 #include <X11/Xatom.h>
38 #include <string.h>
39
40
41 /**
42  * SECTION:properties
43  * @Short_description: Functions to manipulate properties on windows
44  * @Title: Properties and Atoms
45  *
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
54  * using an atom.
55  *
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.)
68  *
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.
73  */
74
75
76 static GPtrArray *virtual_atom_array;
77 static GHashTable *virtual_atom_hash;
78
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 */
82   "PRIMARY\0"
83   "SECONDARY\0"
84   "ARC\0"
85   "ATOM\0"
86   "BITMAP\0"
87   "CARDINAL\0"
88   "COLORMAP\0"
89   "CURSOR\0"
90   "CUT_BUFFER0\0"
91   "CUT_BUFFER1\0"
92   "CUT_BUFFER2\0"
93   "CUT_BUFFER3\0"
94   "CUT_BUFFER4\0"
95   "CUT_BUFFER5\0"
96   "CUT_BUFFER6\0"
97   "CUT_BUFFER7\0"
98   "DRAWABLE\0"
99   "FONT\0"
100   "INTEGER\0"
101   "PIXMAP\0"
102   "POINT\0"
103   "RECTANGLE\0"
104   "RESOURCE_MANAGER\0"
105   "RGB_COLOR_MAP\0"
106   "RGB_BEST_MAP\0"
107   "RGB_BLUE_MAP\0"
108   "RGB_DEFAULT_MAP\0"
109   "RGB_GRAY_MAP\0"
110   "RGB_GREEN_MAP\0"
111   "RGB_RED_MAP\0"
112   "STRING\0"
113   "VISUALID\0"
114   "WINDOW\0"
115   "WM_COMMAND\0"
116   "WM_HINTS\0"
117   "WM_CLIENT_MACHINE\0"
118   "WM_ICON_NAME\0"
119   "WM_ICON_SIZE\0"
120   "WM_NAME\0"
121   "WM_NORMAL_HINTS\0"
122   "WM_SIZE_HINTS\0"
123   "WM_ZOOM_HINTS\0"
124   "MIN_SPACE\0"
125   "NORM_SPACE\0"
126   "MAX_SPACE\0"
127   "END_SPACE\0"
128   "SUPERSCRIPT_X\0"
129   "SUPERSCRIPT_Y\0"
130   "SUBSCRIPT_X\0"
131   "SUBSCRIPT_Y\0"
132   "UNDERLINE_POSITION\0"
133   "UNDERLINE_THICKNESS\0"
134   "STRIKEOUT_ASCENT\0"
135   "STRIKEOUT_DESCENT\0"
136   "ITALIC_ANGLE\0"
137   "X_HEIGHT\0"
138   "QUAD_WIDTH\0"
139   "WEIGHT\0"
140   "POINT_SIZE\0"
141   "RESOLUTION\0"
142   "COPYRIGHT\0"
143   "NOTICE\0"
144   "FONT_NAME\0"
145   "FAMILY_NAME\0"
146   "FULL_NAME\0"
147   "CAP_HEIGHT\0"
148   "WM_CLASS\0"
149   "WM_TRANSIENT_FOR\0"
150   /* Below here, these are our additions. Increment N_CUSTOM_PREDEFINED
151    * if you add any.
152    */
153   "CLIPBOARD\0"                 /* = 69 */
154 ;
155
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
163 };
164
165 #define N_CUSTOM_PREDEFINED 1
166
167 #define ATOM_TO_INDEX(atom) (GPOINTER_TO_UINT(atom))
168 #define INDEX_TO_ATOM(atom) ((GdkAtom)GUINT_TO_POINTER(atom))
169
170 static void
171 insert_atom_pair (GdkDisplay *display,
172                   GdkAtom     virtual_atom,
173                   Atom        xatom)
174 {
175   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);  
176   
177   if (!display_x11->atom_from_virtual)
178     {
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);
181     }
182   
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));
189 }
190
191 static Atom
192 lookup_cached_xatom (GdkDisplay *display,
193                      GdkAtom     atom)
194 {
195   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
196
197   if (ATOM_TO_INDEX (atom) < G_N_ELEMENTS (xatoms_offset) - N_CUSTOM_PREDEFINED)
198     return ATOM_TO_INDEX (atom);
199   
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)));
203
204   return None;
205 }
206
207 /**
208  * gdk_x11_atom_to_xatom_for_display:
209  * @display: A #GdkDisplay
210  * @atom: A #GdkAtom, or %GDK_NONE
211  *
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.
215  *
216  * Return value: the X atom corresponding to @atom, or %None
217  *
218  * Since: 2.2
219  **/
220 Atom
221 gdk_x11_atom_to_xatom_for_display (GdkDisplay *display,
222                                    GdkAtom     atom)
223 {
224   Atom xatom = None;
225
226   g_return_val_if_fail (GDK_IS_DISPLAY (display), None);
227
228   if (atom == GDK_NONE)
229     return None;
230
231   if (gdk_display_is_closed (display))
232     return None;
233
234   xatom = lookup_cached_xatom (display, atom);
235
236   if (!xatom)
237     {
238       char *name;
239
240       g_return_val_if_fail (ATOM_TO_INDEX (atom) < virtual_atom_array->len, None);
241
242       name = g_ptr_array_index (virtual_atom_array, ATOM_TO_INDEX (atom));
243
244       xatom = XInternAtom (GDK_DISPLAY_XDISPLAY (display), name, FALSE);
245       insert_atom_pair (display, atom, xatom);
246     }
247
248   return xatom;
249 }
250
251 void
252 _gdk_x11_precache_atoms (GdkDisplay          *display,
253                          const gchar * const *atom_names,
254                          gint                 n_atoms)
255 {
256   Atom *xatoms;
257   GdkAtom *atoms;
258   const gchar **xatom_names;
259   gint n_xatoms;
260   gint i;
261
262   xatoms = g_new (Atom, n_atoms);
263   xatom_names = g_new (const gchar *, n_atoms);
264   atoms = g_new (GdkAtom, n_atoms);
265
266   n_xatoms = 0;
267   for (i = 0; i < n_atoms; i++)
268     {
269       GdkAtom atom = gdk_atom_intern_static_string (atom_names[i]);
270       if (lookup_cached_xatom (display, atom) == None)
271         {
272           atoms[n_xatoms] = atom;
273           xatom_names[n_xatoms] = atom_names[i];
274           n_xatoms++;
275         }
276     }
277
278   if (n_xatoms)
279     XInternAtoms (GDK_DISPLAY_XDISPLAY (display),
280                   (char **)xatom_names, n_xatoms, False, xatoms);
281
282   for (i = 0; i < n_xatoms; i++)
283     insert_atom_pair (display, atoms[i], xatoms[i]);
284
285   g_free (xatoms);
286   g_free (xatom_names);
287   g_free (atoms);
288 }
289
290 /**
291  * gdk_x11_atom_to_xatom:
292  * @atom: A #GdkAtom 
293  * 
294  * Converts from a #GdkAtom to the X atom for the default GDK display
295  * with the same string value.
296  * 
297  * Return value: the X atom corresponding to @atom.
298  **/
299 Atom
300 gdk_x11_atom_to_xatom (GdkAtom atom)
301 {
302   return gdk_x11_atom_to_xatom_for_display (gdk_display_get_default (), atom);
303 }
304
305 /**
306  * gdk_x11_xatom_to_atom_for_display:
307  * @display: A #GdkDisplay
308  * @xatom: an X atom 
309  * 
310  * Convert from an X atom for a #GdkDisplay to the corresponding
311  * #GdkAtom.
312  * 
313  * Return value: (transfer none): the corresponding #GdkAtom.
314  *
315  * Since: 2.2
316  **/
317 GdkAtom
318 gdk_x11_xatom_to_atom_for_display (GdkDisplay *display,
319                                    Atom        xatom)
320 {
321   GdkX11Display *display_x11;
322   GdkAtom virtual_atom = GDK_NONE;
323   
324   g_return_val_if_fail (GDK_IS_DISPLAY (display), GDK_NONE);
325
326   if (xatom == None)
327     return GDK_NONE;
328
329   if (gdk_display_is_closed (display))
330     return GDK_NONE;
331
332   display_x11 = GDK_X11_DISPLAY (display);
333   
334   if (xatom < G_N_ELEMENTS (xatoms_offset) - N_CUSTOM_PREDEFINED)
335     return INDEX_TO_ATOM (xatom);
336   
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)));
340   
341   if (!virtual_atom)
342     {
343       /* If this atom doesn't exist, we'll die with an X error unless
344        * we take precautions
345        */
346       char *name;
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))
350         {
351           g_warning (G_STRLOC " invalid X atom: %ld", xatom);
352         }
353       else
354         {
355           virtual_atom = gdk_atom_intern (name, FALSE);
356           XFree (name);
357           
358           insert_atom_pair (display, virtual_atom, xatom);
359         }
360     }
361
362   return virtual_atom;
363 }
364
365 /**
366  * gdk_x11_xatom_to_atom:
367  * @xatom: an X atom for the default GDK display
368  * 
369  * Convert from an X atom for the default display to the corresponding
370  * #GdkAtom.
371  * 
372  * Return value: (transfer none): the corresponding G#dkAtom.
373  **/
374 GdkAtom
375 gdk_x11_xatom_to_atom (Atom xatom)
376 {
377   return gdk_x11_xatom_to_atom_for_display (gdk_display_get_default (), xatom);
378 }
379
380 static void
381 virtual_atom_check_init (void)
382 {
383   if (!virtual_atom_hash)
384     {
385       gint i;
386
387       virtual_atom_hash = g_hash_table_new (g_str_hash, g_str_equal);
388       virtual_atom_array = g_ptr_array_new ();
389
390       for (i = 0; i < G_N_ELEMENTS (xatoms_offset); i++)
391         {
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));
395         }
396     }
397 }
398
399 GdkAtom
400 _gdk_x11_display_manager_atom_intern (GdkDisplayManager *manager,
401                                       const gchar       *atom_name,
402                                       gboolean           dup)
403 {
404   GdkAtom result;
405
406   virtual_atom_check_init ();
407
408   result = GDK_POINTER_TO_ATOM (g_hash_table_lookup (virtual_atom_hash, atom_name));
409   if (!result)
410     {
411       result = INDEX_TO_ATOM (virtual_atom_array->len);
412
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));
418     }
419
420   return result;
421 }
422
423 static const gchar *
424 get_atom_name (GdkAtom atom)
425 {
426   virtual_atom_check_init ();
427
428   if (ATOM_TO_INDEX (atom) < virtual_atom_array->len)
429     return g_ptr_array_index (virtual_atom_array, ATOM_TO_INDEX (atom));
430   else
431     return NULL;
432 }
433
434
435 gchar *
436 _gdk_x11_display_manager_get_atom_name (GdkDisplayManager *manager,
437                                         GdkAtom            atom)
438 {
439   return g_strdup (get_atom_name (atom));
440 }
441
442 /**
443  * gdk_x11_get_xatom_by_name_for_display:
444  * @display: a #GdkDisplay
445  * @atom_name: a string
446  * 
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.
450  * 
451  * Return value: a X atom for a #GdkDisplay
452  *
453  * Since: 2.2
454  **/
455 Atom
456 gdk_x11_get_xatom_by_name_for_display (GdkDisplay  *display,
457                                        const gchar *atom_name)
458 {
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));
462 }
463
464 Atom
465 _gdk_x11_get_xatom_for_display_printf (GdkDisplay    *display,
466                                        const gchar   *format,
467                                        ...)
468 {
469   va_list args;
470   char *atom_name;
471   Atom atom;
472
473   va_start (args, format);
474   atom_name = g_strdup_vprintf (format, args);
475   va_end (args);
476
477   atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name);
478
479   g_free (atom_name);
480
481   return atom;
482 }
483
484 /**
485  * gdk_x11_get_xatom_by_name:
486  * @atom_name: a string
487  * 
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.
491  * 
492  * Return value: a X atom for GDK's default display.
493  **/
494 Atom
495 gdk_x11_get_xatom_by_name (const gchar *atom_name)
496 {
497   return gdk_x11_get_xatom_by_name_for_display (gdk_display_get_default (),
498                                                 atom_name);
499 }
500
501 /**
502  * gdk_x11_get_xatom_name_for_display:
503  * @display: the #GdkDisplay where @xatom is defined
504  * @xatom: an X atom 
505  * 
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
509  * be freed. 
510  *
511  * Return value: name of the X atom; this string is owned by GDK,
512  *   so it shouldn't be modifed or freed. 
513  *
514  * Since: 2.2
515  **/
516 const gchar *
517 gdk_x11_get_xatom_name_for_display (GdkDisplay *display,
518                                     Atom        xatom)
519 {
520   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
521
522   return get_atom_name (gdk_x11_xatom_to_atom_for_display (display, xatom));
523 }
524
525 /**
526  * gdk_x11_get_xatom_name:
527  * @xatom: an X atom for GDK's default display
528  * 
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.
534  * 
535  * Return value: name of the X atom; this string is owned by GTK+,
536  *   so it shouldn't be modifed or freed. 
537  **/
538 const gchar *
539 gdk_x11_get_xatom_name (Atom xatom)
540 {
541   return get_atom_name (gdk_x11_xatom_to_atom (xatom));
542 }
543
544 gboolean
545 _gdk_x11_window_get_property (GdkWindow   *window,
546                               GdkAtom      property,
547                               GdkAtom      type,
548                               gulong       offset,
549                               gulong       length,
550                               gint         pdelete,
551                               GdkAtom     *actual_property_type,
552                               gint        *actual_format_type,
553                               gint        *actual_length,
554                               guchar     **data)
555 {
556   GdkDisplay *display;
557   Atom ret_prop_type;
558   gint ret_format;
559   gulong ret_nitems;
560   gulong ret_bytes_after;
561   gulong get_length;
562   gulong ret_length;
563   guchar *ret_data;
564   Atom xproperty;
565   Atom xtype;
566   int res;
567
568   g_return_val_if_fail (!window || GDK_WINDOW_IS_X11 (window), FALSE);
569
570   if (!window)
571     {
572       GdkScreen *screen = gdk_screen_get_default ();
573       window = gdk_screen_get_root_window (screen);
574       
575       GDK_NOTE (MULTIHEAD, g_message ("gdk_property_get(): window is NULL\n"));
576     }
577   else if (!GDK_WINDOW_IS_X11 (window))
578     return FALSE;
579
580   if (GDK_WINDOW_DESTROYED (window))
581     return FALSE;
582
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;
587   else
588     xtype = gdk_x11_atom_to_xatom_for_display (display, type);
589
590   ret_data = NULL;
591   
592   /* 
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.
597    */
598   get_length = length + 3;
599   if (get_length > G_MAXLONG)
600     get_length = G_MAXLONG;
601
602   /* To fail, either the user passed 0 or G_MAXULONG */
603   get_length = get_length / 4;
604   if (get_length == 0)
605     {
606       g_warning ("gdk_propery-get(): invalid length 0");
607       return FALSE;
608     }
609
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,
615                             &ret_data);
616
617   if (res != Success || (ret_prop_type == None && ret_format == 0))
618     {
619       return FALSE;
620     }
621
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;
626
627   if ((xtype != AnyPropertyType) && (ret_prop_type != xtype))
628     {
629       XFree (ret_data);
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));
633       return FALSE;
634     }
635
636   /* FIXME: ignoring bytes_after could have very bad effects */
637
638   if (data)
639     {
640       if (ret_prop_type == XA_ATOM ||
641           ret_prop_type == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
642         {
643           /*
644            * data is an array of X atom, we need to convert it
645            * to an array of GDK Atoms
646            */
647           gint i;
648           GdkAtom *ret_atoms = g_new (GdkAtom, ret_nitems);
649           Atom *xatoms = (Atom *)ret_data;
650
651           *data = (guchar *)ret_atoms;
652
653           for (i = 0; i < ret_nitems; i++)
654             ret_atoms[i] = gdk_x11_xatom_to_atom_for_display (display, xatoms[i]);
655           
656           if (actual_length)
657             *actual_length = ret_nitems * sizeof (GdkAtom);
658         }
659       else
660         {
661           switch (ret_format)
662             {
663             case 8:
664               ret_length = ret_nitems;
665               break;
666             case 16:
667               ret_length = sizeof(short) * ret_nitems;
668               break;
669             case 32:
670               ret_length = sizeof(long) * ret_nitems;
671               break;
672             default:
673               g_warning ("unknown property return format: %d", ret_format);
674               XFree (ret_data);
675               return FALSE;
676             }
677           
678           *data = g_new (guchar, ret_length);
679           memcpy (*data, ret_data, ret_length);
680           if (actual_length)
681             *actual_length = ret_length;
682         }
683     }
684
685   XFree (ret_data);
686
687   return TRUE;
688 }
689
690 void
691 _gdk_x11_window_change_property (GdkWindow    *window,
692                                  GdkAtom       property,
693                                  GdkAtom       type,
694                                  gint          format,
695                                  GdkPropMode   mode,
696                                  const guchar *data,
697                                  gint          nelements)
698 {
699   GdkDisplay *display;
700   Window xwindow;
701   Atom xproperty;
702   Atom xtype;
703
704   g_return_if_fail (!window || GDK_WINDOW_IS_X11 (window));
705
706   if (!window)
707     {
708       GdkScreen *screen;
709       
710       screen = gdk_screen_get_default ();
711       window = gdk_screen_get_root_window (screen);
712       
713       GDK_NOTE (MULTIHEAD, g_message ("gdk_property_change(): window is NULL\n"));
714     }
715   else if (!GDK_WINDOW_IS_X11 (window))
716     return;
717
718   if (GDK_WINDOW_DESTROYED (window))
719     return;
720
721   gdk_window_ensure_native (window);
722
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);
727
728   if (xtype == XA_ATOM ||
729       xtype == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
730     {
731       /*
732        * data is an array of GdkAtom, we need to convert it
733        * to an array of X Atoms
734        */
735       gint i;
736       GdkAtom *atoms = (GdkAtom*) data;
737       Atom *xatoms;
738
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]);
742
743       XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow,
744                        xproperty, xtype,
745                        format, mode, (guchar *)xatoms, nelements);
746       g_free (xatoms);
747     }
748   else
749     XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow, xproperty, 
750                      xtype, format, mode, (guchar *)data, nelements);
751 }
752
753 void
754 _gdk_x11_window_delete_property (GdkWindow *window,
755                                  GdkAtom    property)
756 {
757   g_return_if_fail (!window || GDK_WINDOW_IS_X11 (window));
758
759   if (!window)
760     {
761       GdkScreen *screen = gdk_screen_get_default ();
762       window = gdk_screen_get_root_window (screen);
763       
764       GDK_NOTE (MULTIHEAD, 
765                 g_message ("gdk_property_delete(): window is NULL\n"));
766     }
767   else if (!GDK_WINDOW_IS_X11 (window))
768     return;
769
770   if (GDK_WINDOW_DESTROYED (window))
771     return;
772
773   XDeleteProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window),
774                    gdk_x11_atom_to_xatom_for_display (GDK_WINDOW_DISPLAY (window),
775                                                       property));
776 }