]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkproperty-x11.c
Cast to avoid const warnings on data structure seeding.
[~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 #include <X11/Xlib.h>
29 #include <X11/Xatom.h>
30 #include <string.h>
31
32 #include "gdkalias.h"
33 #include "gdk.h"          /* For gdk_error_trap_push/pop() */
34 #include "gdkx.h"
35 #include "gdkproperty.h"
36 #include "gdkprivate.h"
37 #include "gdkinternals.h"
38 #include "gdkdisplay-x11.h"
39 #include "gdkscreen-x11.h"
40 #include "gdkselection.h"       /* only from predefined atom */
41
42 static GPtrArray *virtual_atom_array;
43 static GHashTable *virtual_atom_hash;
44
45 static const gchar *const XAtomsStrings[] = {
46   /* These are all the standard predefined X atoms */
47   "NONE",
48   "PRIMARY",
49   "SECONDARY",
50   "ARC",
51   "ATOM",
52   "BITMAP",
53   "CARDINAL",
54   "COLORMAP",
55   "CURSOR",
56   "CUT_BUFFER0",
57   "CUT_BUFFER1",
58   "CUT_BUFFER2",
59   "CUT_BUFFER3",
60   "CUT_BUFFER4",
61   "CUT_BUFFER5",
62   "CUT_BUFFER6",
63   "CUT_BUFFER7",
64   "DRAWABLE",
65   "FONT",
66   "INTEGER",
67   "PIXMAP",
68   "POINT",
69   "RECTANGLE",
70   "RESOURCE_MANAGER",
71   "RGB_COLOR_MAP",
72   "RGB_BEST_MAP",
73   "RGB_BLUE_MAP",
74   "RGB_DEFAULT_MAP",
75   "RGB_GRAY_MAP",
76   "RGB_GREEN_MAP",
77   "RGB_RED_MAP",
78   "STRING",
79   "VISUALID",
80   "WINDOW",
81   "WM_COMMAND",
82   "WM_HINTS",
83   "WM_CLIENT_MACHINE",
84   "WM_ICON_NAME",
85   "WM_ICON_SIZE",
86   "WM_NAME",
87   "WM_NORMAL_HINTS",
88   "WM_SIZE_HINTS",
89   "WM_ZOOM_HINTS",
90   "MIN_SPACE",
91   "NORM_SPACE",
92   "MAX_SPACE",
93   "END_SPACE",
94   "SUPERSCRIPT_X",
95   "SUPERSCRIPT_Y",
96   "SUBSCRIPT_X",
97   "SUBSCRIPT_Y",
98   "UNDERLINE_POSITION",
99   "UNDERLINE_THICKNESS",
100   "STRIKEOUT_ASCENT",
101   "STRIKEOUT_DESCENT",
102   "ITALIC_ANGLE",
103   "X_HEIGHT",
104   "QUAD_WIDTH",
105   "WEIGHT",
106   "POINT_SIZE",
107   "RESOLUTION",
108   "COPYRIGHT",
109   "NOTICE",
110   "FONT_NAME",
111   "FAMILY_NAME",
112   "FULL_NAME",
113   "CAP_HEIGHT",
114   "WM_CLASS",
115   "WM_TRANSIENT_FOR",
116   /* Below here, these are our additions. Increment N_CUSTOM_PREDEFINED
117    * if you add any.
118    */
119   "CLIPBOARD"                   /* = 69 */
120 };
121
122 #define N_CUSTOM_PREDEFINED 1
123
124 #define ATOM_TO_INDEX(atom) (GPOINTER_TO_UINT(atom))
125 #define INDEX_TO_ATOM(atom) ((GdkAtom)GUINT_TO_POINTER(atom))
126
127 static void
128 insert_atom_pair (GdkDisplay *display,
129                   GdkAtom     virtual_atom,
130                   Atom        xatom)
131 {
132   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);  
133   
134   if (!display_x11->atom_from_virtual)
135     {
136       display_x11->atom_from_virtual = g_hash_table_new (g_direct_hash, NULL);
137       display_x11->atom_to_virtual = g_hash_table_new (g_direct_hash, NULL);
138     }
139   
140   g_hash_table_insert (display_x11->atom_from_virtual, 
141                        GDK_ATOM_TO_POINTER (virtual_atom), 
142                        GUINT_TO_POINTER (xatom));
143   g_hash_table_insert (display_x11->atom_to_virtual,
144                        GUINT_TO_POINTER (xatom), 
145                        GDK_ATOM_TO_POINTER (virtual_atom));
146 }
147
148 static Atom
149 lookup_cached_xatom (GdkDisplay *display,
150                      GdkAtom     atom)
151 {
152   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
153
154   if (ATOM_TO_INDEX (atom) < G_N_ELEMENTS (XAtomsStrings) - N_CUSTOM_PREDEFINED)
155     return ATOM_TO_INDEX (atom);
156   
157   if (display_x11->atom_from_virtual)
158     return GPOINTER_TO_UINT (g_hash_table_lookup (display_x11->atom_from_virtual,
159                                                   GDK_ATOM_TO_POINTER (atom)));
160
161   return None;
162 }
163
164 /**
165  * gdk_x11_atom_to_xatom_for_display:
166  * @display: A #GdkDisplay
167  * @atom: A #GdkAtom 
168  * 
169  * Converts from a #GdkAtom to the X atom for a #GdkDisplay
170  * with the same string value.
171  * 
172  * Return value: the X atom corresponding to @atom.
173  *
174  * Since: 2.2
175  **/
176 Atom
177 gdk_x11_atom_to_xatom_for_display (GdkDisplay *display, 
178                                    GdkAtom atom)
179 {
180   Atom xatom = None;
181   
182   g_return_val_if_fail (GDK_IS_DISPLAY (display), None);
183
184   if (display->closed)
185     return None;
186   
187   xatom = lookup_cached_xatom (display, atom);
188   
189   if (!xatom)
190     {
191       char *name;
192       
193       g_return_val_if_fail (ATOM_TO_INDEX (atom) < virtual_atom_array->len, None);
194
195       name = g_ptr_array_index (virtual_atom_array, ATOM_TO_INDEX (atom));
196       
197       xatom = XInternAtom (GDK_DISPLAY_XDISPLAY (display), name, FALSE);
198       insert_atom_pair (display, atom, xatom);
199     }
200
201   return xatom;
202 }
203
204 void
205 _gdk_x11_precache_atoms (GdkDisplay          *display,
206                          const gchar * const *atom_names,
207                          gint                 n_atoms)
208 {
209   Atom *xatoms;
210   GdkAtom *atoms;
211   const gchar **xatom_names;
212   gint n_xatoms;
213   gint i;
214
215   xatoms = g_new (Atom, n_atoms);
216   xatom_names = g_new (const gchar *, n_atoms);
217   atoms = g_new (GdkAtom, n_atoms);
218
219   n_xatoms = 0;
220   for (i = 0; i < n_atoms; i++)
221     {
222       GdkAtom atom = gdk_atom_intern (atom_names[i], FALSE);
223       if (lookup_cached_xatom (display, atom) == None)
224         {
225           atoms[n_xatoms] = atom;
226           xatom_names[n_xatoms] = atom_names[i];
227           n_xatoms++;
228         }
229     }
230
231   if (n_xatoms)
232     {
233 #ifdef HAVE_XINTERNATOMS
234       XInternAtoms (GDK_DISPLAY_XDISPLAY (display),
235                     (char **)xatom_names, n_xatoms, False, xatoms);
236 #else
237       for (i = 0; i < n_xatoms; i++)
238         xatoms[i] = XInternAtom (GDK_DISPLAY_XDISPLAY (display),
239                                  xatom_names[i], False);
240 #endif
241     }
242
243   for (i = 0; i < n_xatoms; i++)
244     insert_atom_pair (display, atoms[i], xatoms[i]);
245
246   g_free (xatoms);
247   g_free (xatom_names);
248   g_free (atoms);
249 }
250
251 /**
252  * gdk_x11_atom_to_xatom:
253  * @atom: A #GdkAtom 
254  * 
255  * Converts from a #GdkAtom to the X atom for the default GDK display
256  * with the same string value.
257  * 
258  * Return value: the X atom corresponding to @atom.
259  **/
260 Atom
261 gdk_x11_atom_to_xatom (GdkAtom atom)
262 {
263   return gdk_x11_atom_to_xatom_for_display (gdk_display_get_default (), atom);
264 }
265
266 /**
267  * gdk_x11_xatom_to_atom_for_display:
268  * @display: A #GdkDisplay
269  * @xatom: an X atom 
270  * 
271  * Convert from an X atom for a #GdkDisplay to the corresponding
272  * #GdkAtom.
273  * 
274  * Return value: the corresponding #GdkAtom.
275  *
276  * Since: 2.2
277  **/
278 GdkAtom
279 gdk_x11_xatom_to_atom_for_display (GdkDisplay *display,
280                                    Atom        xatom)
281 {
282   GdkDisplayX11 *display_x11;
283   GdkAtom virtual_atom = GDK_NONE;
284   
285   g_return_val_if_fail (GDK_IS_DISPLAY (display), GDK_NONE);
286
287   if (display->closed)
288     return GDK_NONE;
289
290   display_x11 = GDK_DISPLAY_X11 (display);
291   
292   if (xatom < G_N_ELEMENTS (XAtomsStrings) - N_CUSTOM_PREDEFINED)
293     return INDEX_TO_ATOM (xatom);
294   
295   if (display_x11->atom_to_virtual)
296     virtual_atom = GDK_POINTER_TO_ATOM (g_hash_table_lookup (display_x11->atom_to_virtual,
297                                                              GUINT_TO_POINTER (xatom)));
298   
299   if (!virtual_atom)
300     {
301       /* If this atom doesn't exist, we'll die with an X error unless
302        * we take precautions
303        */
304       char *name;
305       gdk_error_trap_push ();
306       name = XGetAtomName (GDK_DISPLAY_XDISPLAY (display), xatom);
307       if (gdk_error_trap_pop ())
308         {
309           g_warning (G_STRLOC " invalid X atom: %ld", xatom);
310         }
311       else
312         {
313           virtual_atom = gdk_atom_intern (name, FALSE);
314           XFree (name);
315           
316           insert_atom_pair (display, virtual_atom, xatom);
317         }
318     }
319
320   return virtual_atom;
321 }
322
323 /**
324  * gdk_x11_xatom_to_atom:
325  * @xatom: an X atom for the default GDK display
326  * 
327  * Convert from an X atom for the default display to the corresponding
328  * #GdkAtom.
329  * 
330  * Return value: the corresponding G#dkAtom.
331  **/
332 GdkAtom
333 gdk_x11_xatom_to_atom (Atom xatom)
334 {
335   return gdk_x11_xatom_to_atom_for_display (gdk_display_get_default (), xatom);
336 }
337
338 static void
339 virtual_atom_check_init (void)
340 {
341   if (!virtual_atom_hash)
342     {
343       gint i;
344       
345       virtual_atom_hash = g_hash_table_new (g_str_hash, g_str_equal);
346       virtual_atom_array = g_ptr_array_new ();
347       
348       for (i = 0; i < G_N_ELEMENTS (XAtomsStrings); i++)
349         {
350           g_ptr_array_add (virtual_atom_array, (gchar *) XAtomsStrings[i]);
351           g_hash_table_insert (virtual_atom_hash, (gchar *) XAtomsStrings[i],
352                                GUINT_TO_POINTER (i));
353         }
354     }
355 }
356
357 GdkAtom
358 gdk_atom_intern (const gchar *atom_name, 
359                  gboolean     only_if_exists)
360 {
361   GdkAtom result;
362
363   virtual_atom_check_init ();
364   
365   result = GDK_POINTER_TO_ATOM (g_hash_table_lookup (virtual_atom_hash, atom_name));
366   if (!result)
367     {
368       result = INDEX_TO_ATOM (virtual_atom_array->len);
369       
370       g_ptr_array_add (virtual_atom_array, g_strdup (atom_name));
371       g_hash_table_insert (virtual_atom_hash, 
372                            g_ptr_array_index (virtual_atom_array,
373                                               ATOM_TO_INDEX (result)),
374                            GDK_ATOM_TO_POINTER (result));
375     }
376
377   return result;
378 }
379
380 static G_CONST_RETURN char *
381 get_atom_name (GdkAtom atom)
382 {
383   virtual_atom_check_init ();
384
385   if (ATOM_TO_INDEX (atom) < virtual_atom_array->len)
386     return g_ptr_array_index (virtual_atom_array, ATOM_TO_INDEX (atom));
387   else
388     return NULL;
389 }
390
391 gchar *
392 gdk_atom_name (GdkAtom atom)
393 {
394   return g_strdup (get_atom_name (atom));
395 }
396
397 /**
398  * gdk_x11_get_xatom_by_name_for_display:
399  * @display: a #GdkDisplay
400  * @atom_name: a string
401  * 
402  * Returns the X atom for a #GdkDisplay corresponding to @atom_name.
403  * This function caches the result, so if called repeatedly it is much
404  * faster than XInternAtom(), which is a round trip to the server each time.
405  * 
406  * Return value: a X atom for a #GdkDisplay
407  *
408  * Since: 2.2
409  **/
410 Atom
411 gdk_x11_get_xatom_by_name_for_display (GdkDisplay  *display,
412                                        const gchar *atom_name)
413 {
414   g_return_val_if_fail (GDK_IS_DISPLAY (display), None);
415   return gdk_x11_atom_to_xatom_for_display (display,
416                                             gdk_atom_intern (atom_name, FALSE));
417 }
418
419 /**
420  * gdk_x11_get_xatom_by_name:
421  * @atom_name: a string
422  * 
423  * Returns the X atom for GDK's default display corresponding to @atom_name.
424  * This function caches the result, so if called repeatedly it is much
425  * faster than XInternAtom(), which is a round trip to the server each time.
426  * 
427  * Return value: a X atom for GDK's default display.
428  **/
429 Atom
430 gdk_x11_get_xatom_by_name (const gchar *atom_name)
431 {
432   return gdk_x11_get_xatom_by_name_for_display (gdk_display_get_default (),
433                                                 atom_name);
434 }
435
436 /**
437  * gdk_x11_get_xatom_name_for_display:
438  * @display: the #GdkDisplay where @xatom is defined
439  * @xatom: an X atom 
440  * 
441  * Returns the name of an X atom for its display. This
442  * function is meant mainly for debugging, so for convenience, unlike
443  * XAtomName() and gdk_atom_name(), the result doesn't need to
444  * be freed. 
445  *
446  * Return value: name of the X atom; this string is owned by GDK,
447  *   so it shouldn't be modifed or freed. 
448  *
449  * Since: 2.2
450  **/
451 G_CONST_RETURN gchar *
452 gdk_x11_get_xatom_name_for_display (GdkDisplay *display,
453                                     Atom        xatom)
454 {
455   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
456
457   return get_atom_name (gdk_x11_xatom_to_atom_for_display (display, xatom));
458 }
459
460 /**
461  * gdk_x11_get_xatom_name:
462  * @xatom: an X atom for GDK's default display
463  * 
464  * Returns the name of an X atom for GDK's default display. This
465  * function is meant mainly for debugging, so for convenience, unlike
466  * <function>XAtomName()</function> and gdk_atom_name(), the result 
467  * doesn't need to be freed. Also, this function will never return %NULL, 
468  * even if @xatom is invalid.
469  * 
470  * Return value: name of the X atom; this string is owned by GTK+,
471  *   so it shouldn't be modifed or freed. 
472  **/
473 G_CONST_RETURN gchar *
474 gdk_x11_get_xatom_name (Atom xatom)
475 {
476   return get_atom_name (gdk_x11_xatom_to_atom (xatom));
477 }
478
479 gboolean
480 gdk_property_get (GdkWindow   *window,
481                   GdkAtom      property,
482                   GdkAtom      type,
483                   gulong       offset,
484                   gulong       length,
485                   gint         pdelete,
486                   GdkAtom     *actual_property_type,
487                   gint        *actual_format_type,
488                   gint        *actual_length,
489                   guchar     **data)
490 {
491   GdkDisplay *display;
492   Atom ret_prop_type;
493   gint ret_format;
494   gulong ret_nitems;
495   gulong ret_bytes_after;
496   gulong ret_length;
497   guchar *ret_data;
498   Atom xproperty;
499   Atom xtype;
500   int res;
501
502   g_return_val_if_fail (!window || GDK_IS_WINDOW (window), FALSE);
503
504   if (!window)
505     {
506       GdkScreen *screen = gdk_screen_get_default ();
507       window = gdk_screen_get_root_window (screen);
508       
509       GDK_NOTE (MULTIHEAD, g_message ("gdk_property_get(): window is NULL\n"));
510     }
511
512   if (GDK_WINDOW_DESTROYED (window))
513     return FALSE;
514
515   display = gdk_drawable_get_display (window);
516   xproperty = gdk_x11_atom_to_xatom_for_display (display, property);
517   if (type == GDK_NONE)
518     xtype = AnyPropertyType;
519   else
520     xtype = gdk_x11_atom_to_xatom_for_display (display, type);
521
522   ret_data = NULL;
523   
524   res = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
525                             GDK_WINDOW_XWINDOW (window), xproperty,
526                             offset, (length + 3) / 4, pdelete,
527                             xtype, &ret_prop_type, &ret_format,
528                             &ret_nitems, &ret_bytes_after,
529                             &ret_data);
530
531   if (res != Success || (ret_prop_type == None && ret_format == 0))
532     {
533       return FALSE;
534     }
535
536   if (actual_property_type)
537     *actual_property_type = gdk_x11_xatom_to_atom_for_display (display, ret_prop_type);
538   if (actual_format_type)
539     *actual_format_type = ret_format;
540
541   if ((xtype != AnyPropertyType) && (ret_prop_type != xtype))
542     {
543       XFree (ret_data);
544       g_warning ("Couldn't match property type %s to %s\n", 
545                  gdk_x11_get_xatom_name_for_display (display, ret_prop_type), 
546                  gdk_x11_get_xatom_name_for_display (display, xtype));
547       return FALSE;
548     }
549
550   /* FIXME: ignoring bytes_after could have very bad effects */
551
552   if (data)
553     {
554       if (ret_prop_type == XA_ATOM ||
555           ret_prop_type == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
556         {
557           /*
558            * data is an array of X atom, we need to convert it
559            * to an array of GDK Atoms
560            */
561           gint i;
562           GdkAtom *ret_atoms = g_new (GdkAtom, ret_nitems);
563           Atom *xatoms = (Atom *)ret_data;
564
565           *data = (guchar *)ret_atoms;
566
567           for (i = 0; i < ret_nitems; i++)
568             ret_atoms[i] = gdk_x11_xatom_to_atom_for_display (display, xatoms[i]);
569           
570           if (actual_length)
571             *actual_length = ret_nitems * sizeof (GdkAtom);
572         }
573       else
574         {
575           switch (ret_format)
576             {
577             case 8:
578               ret_length = ret_nitems;
579               break;
580             case 16:
581               ret_length = sizeof(short) * ret_nitems;
582               break;
583             case 32:
584               ret_length = sizeof(long) * ret_nitems;
585               break;
586             default:
587               g_warning ("unknown property return format: %d", ret_format);
588               XFree (ret_data);
589               return FALSE;
590             }
591           
592           *data = g_new (guchar, ret_length);
593           memcpy (*data, ret_data, ret_length);
594           if (actual_length)
595             *actual_length = ret_length;
596         }
597     }
598
599   XFree (ret_data);
600
601   return TRUE;
602 }
603
604 void
605 gdk_property_change (GdkWindow    *window,
606                      GdkAtom       property,
607                      GdkAtom       type,
608                      gint          format,
609                      GdkPropMode   mode,
610                      const guchar *data,
611                      gint          nelements)
612 {
613   GdkDisplay *display;
614   Window xwindow;
615   Atom xproperty;
616   Atom xtype;
617
618   g_return_if_fail (!window || GDK_IS_WINDOW (window));
619
620   if (!window)
621     {
622       GdkScreen *screen;
623       
624       screen = gdk_screen_get_default ();
625       window = gdk_screen_get_root_window (screen);
626       
627       GDK_NOTE (MULTIHEAD, g_message ("gdk_property_change(): window is NULL\n"));
628     }
629
630
631   if (GDK_WINDOW_DESTROYED (window))
632     return;
633
634   display = gdk_drawable_get_display (window);
635   
636   xproperty = gdk_x11_atom_to_xatom_for_display (display, property);
637   xtype = gdk_x11_atom_to_xatom_for_display (display, type);
638   xwindow = GDK_WINDOW_XID (window);
639
640   if (xtype == XA_ATOM ||
641       xtype == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
642     {
643       /*
644        * data is an array of GdkAtom, we need to convert it
645        * to an array of X Atoms
646        */
647       gint i;
648       GdkAtom *atoms = (GdkAtom*) data;
649       Atom *xatoms;
650
651       xatoms = g_new (Atom, nelements);
652       for (i = 0; i < nelements; i++)
653         xatoms[i] = gdk_x11_atom_to_xatom_for_display (display, atoms[i]);
654
655       XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow,
656                        xproperty, xtype,
657                        format, mode, (guchar *)xatoms, nelements);
658       g_free (xatoms);
659     }
660   else
661     XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow, xproperty, 
662                      xtype, format, mode, (guchar *)data, nelements);
663 }
664
665 void
666 gdk_property_delete (GdkWindow *window,
667                      GdkAtom    property)
668 {
669   g_return_if_fail (!window || GDK_IS_WINDOW (window));
670
671   if (!window)
672     {
673       GdkScreen *screen = gdk_screen_get_default ();
674       window = gdk_screen_get_root_window (screen);
675       
676       GDK_NOTE (MULTIHEAD, 
677                 g_message ("gdk_property_delete(): window is NULL\n"));
678     }
679
680   if (GDK_WINDOW_DESTROYED (window))
681     return;
682
683   XDeleteProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XWINDOW (window),
684                    gdk_x11_atom_to_xatom_for_display (GDK_WINDOW_DISPLAY (window),
685                                                       property));
686 }