]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkproperty-x11.c
Don't warn when G_MAXLONG is passed as length.
[~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 "gdk.h"          /* For gdk_error_trap_push/pop() */
33 #include "gdkx.h"
34 #include "gdkproperty.h"
35 #include "gdkprivate.h"
36 #include "gdkinternals.h"
37 #include "gdkdisplay-x11.h"
38 #include "gdkscreen-x11.h"
39 #include "gdkselection.h"       /* only from predefined atom */
40 #include "gdkalias.h"
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 get_length;
497   gulong ret_length;
498   guchar *ret_data;
499   Atom xproperty;
500   Atom xtype;
501   int res;
502
503   g_return_val_if_fail (!window || GDK_IS_WINDOW (window), FALSE);
504
505   if (!window)
506     {
507       GdkScreen *screen = gdk_screen_get_default ();
508       window = gdk_screen_get_root_window (screen);
509       
510       GDK_NOTE (MULTIHEAD, g_message ("gdk_property_get(): window is NULL\n"));
511     }
512
513   if (GDK_WINDOW_DESTROYED (window))
514     return FALSE;
515
516   display = gdk_drawable_get_display (window);
517   xproperty = gdk_x11_atom_to_xatom_for_display (display, property);
518   if (type == GDK_NONE)
519     xtype = AnyPropertyType;
520   else
521     xtype = gdk_x11_atom_to_xatom_for_display (display, type);
522
523   ret_data = NULL;
524   
525   /* 
526    * Round up length to next 4 byte value.  Some code is in the (bad?)
527    * habit of passing G_MAXLONG as the length argument, causing an
528    * overflow to negative on the add.  In this case, we clamp the
529    * value to G_MAXLONG.
530    */
531   get_length = length + 3;
532   if (get_length > G_MAXLONG)
533     get_length = G_MAXLONG;
534
535   /* To fail, either the user passed 0 or G_MAXULONG */
536   get_length = get_length / 4;
537   if (get_length == 0)
538     {
539       g_warning ("gdk_propery-get(): invalid length 0");
540       return FALSE;
541     }
542
543   res = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
544                             GDK_WINDOW_XWINDOW (window), xproperty,
545                             offset, get_length, pdelete,
546                             xtype, &ret_prop_type, &ret_format,
547                             &ret_nitems, &ret_bytes_after,
548                             &ret_data);
549
550   if (res != Success || (ret_prop_type == None && ret_format == 0))
551     {
552       return FALSE;
553     }
554
555   if (actual_property_type)
556     *actual_property_type = gdk_x11_xatom_to_atom_for_display (display, ret_prop_type);
557   if (actual_format_type)
558     *actual_format_type = ret_format;
559
560   if ((xtype != AnyPropertyType) && (ret_prop_type != xtype))
561     {
562       XFree (ret_data);
563       g_warning ("Couldn't match property type %s to %s\n", 
564                  gdk_x11_get_xatom_name_for_display (display, ret_prop_type), 
565                  gdk_x11_get_xatom_name_for_display (display, xtype));
566       return FALSE;
567     }
568
569   /* FIXME: ignoring bytes_after could have very bad effects */
570
571   if (data)
572     {
573       if (ret_prop_type == XA_ATOM ||
574           ret_prop_type == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
575         {
576           /*
577            * data is an array of X atom, we need to convert it
578            * to an array of GDK Atoms
579            */
580           gint i;
581           GdkAtom *ret_atoms = g_new (GdkAtom, ret_nitems);
582           Atom *xatoms = (Atom *)ret_data;
583
584           *data = (guchar *)ret_atoms;
585
586           for (i = 0; i < ret_nitems; i++)
587             ret_atoms[i] = gdk_x11_xatom_to_atom_for_display (display, xatoms[i]);
588           
589           if (actual_length)
590             *actual_length = ret_nitems * sizeof (GdkAtom);
591         }
592       else
593         {
594           switch (ret_format)
595             {
596             case 8:
597               ret_length = ret_nitems;
598               break;
599             case 16:
600               ret_length = sizeof(short) * ret_nitems;
601               break;
602             case 32:
603               ret_length = sizeof(long) * ret_nitems;
604               break;
605             default:
606               g_warning ("unknown property return format: %d", ret_format);
607               XFree (ret_data);
608               return FALSE;
609             }
610           
611           *data = g_new (guchar, ret_length);
612           memcpy (*data, ret_data, ret_length);
613           if (actual_length)
614             *actual_length = ret_length;
615         }
616     }
617
618   XFree (ret_data);
619
620   return TRUE;
621 }
622
623 void
624 gdk_property_change (GdkWindow    *window,
625                      GdkAtom       property,
626                      GdkAtom       type,
627                      gint          format,
628                      GdkPropMode   mode,
629                      const guchar *data,
630                      gint          nelements)
631 {
632   GdkDisplay *display;
633   Window xwindow;
634   Atom xproperty;
635   Atom xtype;
636
637   g_return_if_fail (!window || GDK_IS_WINDOW (window));
638
639   if (!window)
640     {
641       GdkScreen *screen;
642       
643       screen = gdk_screen_get_default ();
644       window = gdk_screen_get_root_window (screen);
645       
646       GDK_NOTE (MULTIHEAD, g_message ("gdk_property_change(): window is NULL\n"));
647     }
648
649
650   if (GDK_WINDOW_DESTROYED (window))
651     return;
652
653   display = gdk_drawable_get_display (window);
654   
655   xproperty = gdk_x11_atom_to_xatom_for_display (display, property);
656   xtype = gdk_x11_atom_to_xatom_for_display (display, type);
657   xwindow = GDK_WINDOW_XID (window);
658
659   if (xtype == XA_ATOM ||
660       xtype == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
661     {
662       /*
663        * data is an array of GdkAtom, we need to convert it
664        * to an array of X Atoms
665        */
666       gint i;
667       GdkAtom *atoms = (GdkAtom*) data;
668       Atom *xatoms;
669
670       xatoms = g_new (Atom, nelements);
671       for (i = 0; i < nelements; i++)
672         xatoms[i] = gdk_x11_atom_to_xatom_for_display (display, atoms[i]);
673
674       XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow,
675                        xproperty, xtype,
676                        format, mode, (guchar *)xatoms, nelements);
677       g_free (xatoms);
678     }
679   else
680     XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow, xproperty, 
681                      xtype, format, mode, (guchar *)data, nelements);
682 }
683
684 void
685 gdk_property_delete (GdkWindow *window,
686                      GdkAtom    property)
687 {
688   g_return_if_fail (!window || GDK_IS_WINDOW (window));
689
690   if (!window)
691     {
692       GdkScreen *screen = gdk_screen_get_default ();
693       window = gdk_screen_get_root_window (screen);
694       
695       GDK_NOTE (MULTIHEAD, 
696                 g_message ("gdk_property_delete(): window is NULL\n"));
697     }
698
699   if (GDK_WINDOW_DESTROYED (window))
700     return;
701
702   XDeleteProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XWINDOW (window),
703                    gdk_x11_atom_to_xatom_for_display (GDK_WINDOW_DISPLAY (window),
704                                                       property));
705 }
706
707 #define __GDK_PROPERTY_X11_C__
708 #include "gdkaliasdef.c"