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