]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkproperty-x11.c
gdk: remove the GET_EFFECTIVE_KEYMAP() stuff from gdkkeys-x11.c
[~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   GdkX11Display *display_x11 = GDK_X11_DISPLAY (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   GdkX11Display *display_x11 = GDK_X11_DISPLAY (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: (transfer none): 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   GdkX11Display *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_X11_DISPLAY (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: (transfer none): 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 GdkAtom
410 _gdk_x11_display_manager_atom_intern (GdkDisplayManager *manager,
411                                       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 static const gchar *
434 get_atom_name (GdkAtom atom)
435 {
436   virtual_atom_check_init ();
437
438   if (ATOM_TO_INDEX (atom) < virtual_atom_array->len)
439     return g_ptr_array_index (virtual_atom_array, ATOM_TO_INDEX (atom));
440   else
441     return NULL;
442 }
443
444
445 gchar *
446 _gdk_x11_display_manager_get_atom_name (GdkDisplayManager *manager,
447                                         GdkAtom            atom)
448 {
449   return g_strdup (get_atom_name (atom));
450 }
451
452 /**
453  * gdk_x11_get_xatom_by_name_for_display:
454  * @display: a #GdkDisplay
455  * @atom_name: a string
456  * 
457  * Returns the X atom for a #GdkDisplay corresponding to @atom_name.
458  * This function caches the result, so if called repeatedly it is much
459  * faster than XInternAtom(), which is a round trip to the server each time.
460  * 
461  * Return value: a X atom for a #GdkDisplay
462  *
463  * Since: 2.2
464  **/
465 Atom
466 gdk_x11_get_xatom_by_name_for_display (GdkDisplay  *display,
467                                        const gchar *atom_name)
468 {
469   g_return_val_if_fail (GDK_IS_DISPLAY (display), None);
470   return gdk_x11_atom_to_xatom_for_display (display,
471                                             gdk_atom_intern (atom_name, FALSE));
472 }
473
474 /**
475  * gdk_x11_get_xatom_by_name:
476  * @atom_name: a string
477  * 
478  * Returns the X atom for GDK's default display corresponding to @atom_name.
479  * This function caches the result, so if called repeatedly it is much
480  * faster than XInternAtom(), which is a round trip to the server each time.
481  * 
482  * Return value: a X atom for GDK's default display.
483  **/
484 Atom
485 gdk_x11_get_xatom_by_name (const gchar *atom_name)
486 {
487   return gdk_x11_get_xatom_by_name_for_display (gdk_display_get_default (),
488                                                 atom_name);
489 }
490
491 /**
492  * gdk_x11_get_xatom_name_for_display:
493  * @display: the #GdkDisplay where @xatom is defined
494  * @xatom: an X atom 
495  * 
496  * Returns the name of an X atom for its display. This
497  * function is meant mainly for debugging, so for convenience, unlike
498  * XAtomName() and gdk_atom_name(), the result doesn't need to
499  * be freed. 
500  *
501  * Return value: name of the X atom; this string is owned by GDK,
502  *   so it shouldn't be modifed or freed. 
503  *
504  * Since: 2.2
505  **/
506 const gchar *
507 gdk_x11_get_xatom_name_for_display (GdkDisplay *display,
508                                     Atom        xatom)
509 {
510   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
511
512   return get_atom_name (gdk_x11_xatom_to_atom_for_display (display, xatom));
513 }
514
515 /**
516  * gdk_x11_get_xatom_name:
517  * @xatom: an X atom for GDK's default display
518  * 
519  * Returns the name of an X atom for GDK's default display. This
520  * function is meant mainly for debugging, so for convenience, unlike
521  * <function>XAtomName()</function> and gdk_atom_name(), the result 
522  * doesn't need to be freed. Also, this function will never return %NULL, 
523  * even if @xatom is invalid.
524  * 
525  * Return value: name of the X atom; this string is owned by GTK+,
526  *   so it shouldn't be modifed or freed. 
527  **/
528 const gchar *
529 gdk_x11_get_xatom_name (Atom xatom)
530 {
531   return get_atom_name (gdk_x11_xatom_to_atom (xatom));
532 }
533
534 gboolean
535 _gdk_x11_window_get_property (GdkWindow   *window,
536                               GdkAtom      property,
537                               GdkAtom      type,
538                               gulong       offset,
539                               gulong       length,
540                               gint         pdelete,
541                               GdkAtom     *actual_property_type,
542                               gint        *actual_format_type,
543                               gint        *actual_length,
544                               guchar     **data)
545 {
546   GdkDisplay *display;
547   Atom ret_prop_type;
548   gint ret_format;
549   gulong ret_nitems;
550   gulong ret_bytes_after;
551   gulong get_length;
552   gulong ret_length;
553   guchar *ret_data;
554   Atom xproperty;
555   Atom xtype;
556   int res;
557
558   g_return_val_if_fail (!window || GDK_WINDOW_IS_X11 (window), FALSE);
559
560   if (!window)
561     {
562       GdkScreen *screen = gdk_screen_get_default ();
563       window = gdk_screen_get_root_window (screen);
564       
565       GDK_NOTE (MULTIHEAD, g_message ("gdk_property_get(): window is NULL\n"));
566     }
567   else if (!GDK_WINDOW_IS_X11 (window))
568     return FALSE;
569
570   if (GDK_WINDOW_DESTROYED (window))
571     return FALSE;
572
573   display = gdk_window_get_display (window);
574   xproperty = gdk_x11_atom_to_xatom_for_display (display, property);
575   if (type == GDK_NONE)
576     xtype = AnyPropertyType;
577   else
578     xtype = gdk_x11_atom_to_xatom_for_display (display, type);
579
580   ret_data = NULL;
581   
582   /* 
583    * Round up length to next 4 byte value.  Some code is in the (bad?)
584    * habit of passing G_MAXLONG as the length argument, causing an
585    * overflow to negative on the add.  In this case, we clamp the
586    * value to G_MAXLONG.
587    */
588   get_length = length + 3;
589   if (get_length > G_MAXLONG)
590     get_length = G_MAXLONG;
591
592   /* To fail, either the user passed 0 or G_MAXULONG */
593   get_length = get_length / 4;
594   if (get_length == 0)
595     {
596       g_warning ("gdk_propery-get(): invalid length 0");
597       return FALSE;
598     }
599
600   res = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
601                             GDK_WINDOW_XID (window), xproperty,
602                             offset, get_length, pdelete,
603                             xtype, &ret_prop_type, &ret_format,
604                             &ret_nitems, &ret_bytes_after,
605                             &ret_data);
606
607   if (res != Success || (ret_prop_type == None && ret_format == 0))
608     {
609       return FALSE;
610     }
611
612   if (actual_property_type)
613     *actual_property_type = gdk_x11_xatom_to_atom_for_display (display, ret_prop_type);
614   if (actual_format_type)
615     *actual_format_type = ret_format;
616
617   if ((xtype != AnyPropertyType) && (ret_prop_type != xtype))
618     {
619       XFree (ret_data);
620       g_warning ("Couldn't match property type %s to %s\n", 
621                  gdk_x11_get_xatom_name_for_display (display, ret_prop_type), 
622                  gdk_x11_get_xatom_name_for_display (display, xtype));
623       return FALSE;
624     }
625
626   /* FIXME: ignoring bytes_after could have very bad effects */
627
628   if (data)
629     {
630       if (ret_prop_type == XA_ATOM ||
631           ret_prop_type == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
632         {
633           /*
634            * data is an array of X atom, we need to convert it
635            * to an array of GDK Atoms
636            */
637           gint i;
638           GdkAtom *ret_atoms = g_new (GdkAtom, ret_nitems);
639           Atom *xatoms = (Atom *)ret_data;
640
641           *data = (guchar *)ret_atoms;
642
643           for (i = 0; i < ret_nitems; i++)
644             ret_atoms[i] = gdk_x11_xatom_to_atom_for_display (display, xatoms[i]);
645           
646           if (actual_length)
647             *actual_length = ret_nitems * sizeof (GdkAtom);
648         }
649       else
650         {
651           switch (ret_format)
652             {
653             case 8:
654               ret_length = ret_nitems;
655               break;
656             case 16:
657               ret_length = sizeof(short) * ret_nitems;
658               break;
659             case 32:
660               ret_length = sizeof(long) * ret_nitems;
661               break;
662             default:
663               g_warning ("unknown property return format: %d", ret_format);
664               XFree (ret_data);
665               return FALSE;
666             }
667           
668           *data = g_new (guchar, ret_length);
669           memcpy (*data, ret_data, ret_length);
670           if (actual_length)
671             *actual_length = ret_length;
672         }
673     }
674
675   XFree (ret_data);
676
677   return TRUE;
678 }
679
680 void
681 _gdk_x11_window_change_property (GdkWindow    *window,
682                                  GdkAtom       property,
683                                  GdkAtom       type,
684                                  gint          format,
685                                  GdkPropMode   mode,
686                                  const guchar *data,
687                                  gint          nelements)
688 {
689   GdkDisplay *display;
690   Window xwindow;
691   Atom xproperty;
692   Atom xtype;
693
694   g_return_if_fail (!window || GDK_WINDOW_IS_X11 (window));
695
696   if (!window)
697     {
698       GdkScreen *screen;
699       
700       screen = gdk_screen_get_default ();
701       window = gdk_screen_get_root_window (screen);
702       
703       GDK_NOTE (MULTIHEAD, g_message ("gdk_property_change(): window is NULL\n"));
704     }
705   else if (!GDK_WINDOW_IS_X11 (window))
706     return;
707
708   if (GDK_WINDOW_DESTROYED (window))
709     return;
710
711   gdk_window_ensure_native (window);
712
713   display = gdk_window_get_display (window);
714   xproperty = gdk_x11_atom_to_xatom_for_display (display, property);
715   xtype = gdk_x11_atom_to_xatom_for_display (display, type);
716   xwindow = GDK_WINDOW_XID (window);
717
718   if (xtype == XA_ATOM ||
719       xtype == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
720     {
721       /*
722        * data is an array of GdkAtom, we need to convert it
723        * to an array of X Atoms
724        */
725       gint i;
726       GdkAtom *atoms = (GdkAtom*) data;
727       Atom *xatoms;
728
729       xatoms = g_new (Atom, nelements);
730       for (i = 0; i < nelements; i++)
731         xatoms[i] = gdk_x11_atom_to_xatom_for_display (display, atoms[i]);
732
733       XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow,
734                        xproperty, xtype,
735                        format, mode, (guchar *)xatoms, nelements);
736       g_free (xatoms);
737     }
738   else
739     XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow, xproperty, 
740                      xtype, format, mode, (guchar *)data, nelements);
741 }
742
743 void
744 _gdk_x11_window_delete_property (GdkWindow *window,
745                                  GdkAtom    property)
746 {
747   g_return_if_fail (!window || GDK_WINDOW_IS_X11 (window));
748
749   if (!window)
750     {
751       GdkScreen *screen = gdk_screen_get_default ();
752       window = gdk_screen_get_root_window (screen);
753       
754       GDK_NOTE (MULTIHEAD, 
755                 g_message ("gdk_property_delete(): window is NULL\n"));
756     }
757   else if (!GDK_WINDOW_IS_X11 (window))
758     return;
759
760   if (GDK_WINDOW_DESTROYED (window))
761     return;
762
763   XDeleteProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window),
764                    gdk_x11_atom_to_xatom_for_display (GDK_WINDOW_DISPLAY (window),
765                                                       property));
766 }