]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkproperty-x11.c
Document 2.2 API additions.
[~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  * Since: 2.2
156  **/
157 Atom
158 gdk_x11_atom_to_xatom_for_display (GdkDisplay *display, 
159                                    GdkAtom atom)
160 {
161   GdkDisplayX11 *display_x11;
162   Atom xatom = None;
163   
164   g_return_val_if_fail (GDK_IS_DISPLAY (display), None);
165
166   if (display->closed)
167     return None;
168   
169   display_x11 = GDK_DISPLAY_X11 (display); 
170   
171   if (ATOM_TO_INDEX (atom) < G_N_ELEMENTS (XAtomsStrings) - N_CUSTOM_PREDEFINED)
172     return ATOM_TO_INDEX (atom);
173   
174   if (display_x11->atom_from_virtual)
175     xatom = GPOINTER_TO_UINT (g_hash_table_lookup (display_x11->atom_from_virtual,
176                                                    GDK_ATOM_TO_POINTER (atom)));
177   if (!xatom)
178     {
179       char *name;
180       
181       g_return_val_if_fail (ATOM_TO_INDEX (atom) < virtual_atom_array->len, None);
182
183       name = g_ptr_array_index (virtual_atom_array, ATOM_TO_INDEX (atom));
184       
185       xatom = XInternAtom (GDK_DISPLAY_XDISPLAY (display), name, FALSE);
186       insert_atom_pair (display, atom, xatom);
187     }
188
189   return xatom;
190 }
191
192 /**
193  * gdk_x11_atom_to_xatom:
194  * @atom: A #GdkAtom 
195  * 
196  * Converts from a #GdkAtom to the X atom for the default GDK display
197  * with the same string value.
198  * 
199  * Return value: the X atom corresponding to @atom.
200  **/
201 Atom
202 gdk_x11_atom_to_xatom (GdkAtom atom)
203 {
204   return gdk_x11_atom_to_xatom_for_display (gdk_display_get_default (), atom);
205 }
206
207 /**
208  * gdk_x11_xatom_to_atom_for_display:
209  * @display: A #GdkDisplay
210  * @xatom: an X atom 
211  * 
212  * Convert from an X atom for a #GdkDisplay to the corresponding
213  * #GdkAtom.
214  * 
215  * Return value: the corresponding #GdkAtom.
216  *
217  * Since: 2.2
218  **/
219 GdkAtom
220 gdk_x11_xatom_to_atom_for_display (GdkDisplay *display,
221                                    Atom        xatom)
222 {
223   GdkDisplayX11 *display_x11;
224   GdkAtom virtual_atom = GDK_NONE;
225   
226   g_return_val_if_fail (GDK_IS_DISPLAY (display), GDK_NONE);
227
228   if (display->closed)
229     return GDK_NONE;
230
231   display_x11 = GDK_DISPLAY_X11 (display);
232   
233   if (xatom < G_N_ELEMENTS (XAtomsStrings) - N_CUSTOM_PREDEFINED)
234     return INDEX_TO_ATOM (xatom);
235   
236   if (display_x11->atom_to_virtual)
237     virtual_atom = GDK_POINTER_TO_ATOM (g_hash_table_lookup (display_x11->atom_to_virtual,
238                                                              GUINT_TO_POINTER (xatom)));
239   
240   if (!virtual_atom)
241     {
242       /* If this atom doesn't exist, we'll die with an X error unless
243        * we take precautions
244        */
245       char *name;
246       gdk_error_trap_push ();
247       name = XGetAtomName (GDK_DISPLAY_XDISPLAY (display), xatom);
248       if (gdk_error_trap_pop ())
249         {
250           g_warning (G_STRLOC " invalid X atom: %ld", xatom);
251         }
252       else
253         {
254           virtual_atom = gdk_atom_intern (name, FALSE);
255           XFree (name);
256           
257           insert_atom_pair (display, virtual_atom, xatom);
258         }
259     }
260
261   return virtual_atom;
262 }
263
264 /**
265  * gdk_x11_xatom_to_atom:
266  * @xatom: an X atom for the default GDK display
267  * 
268  * Convert from an X atom for the default display to the corresponding
269  * #GdkAtom.
270  * 
271  * Return value: the corresponding G#dkAtom.
272  **/
273 GdkAtom
274 gdk_x11_xatom_to_atom (Atom xatom)
275 {
276   return gdk_x11_xatom_to_atom_for_display (gdk_display_get_default (), xatom);
277 }
278
279 static void
280 virtual_atom_check_init (void)
281 {
282   if (!virtual_atom_hash)
283     {
284       gint i;
285       
286       virtual_atom_hash = g_hash_table_new (g_str_hash, g_str_equal);
287       virtual_atom_array = g_ptr_array_new ();
288       
289       for (i = 0; i < G_N_ELEMENTS (XAtomsStrings); i++)
290         {
291           g_ptr_array_add (virtual_atom_array, XAtomsStrings[i]);
292           g_hash_table_insert (virtual_atom_hash, XAtomsStrings[i],
293                                GUINT_TO_POINTER (i));
294         }
295     }
296 }
297
298 GdkAtom
299 gdk_atom_intern (const gchar *atom_name, 
300                  gboolean     only_if_exists)
301 {
302   GdkAtom result;
303
304   virtual_atom_check_init ();
305   
306   result = GDK_POINTER_TO_ATOM (g_hash_table_lookup (virtual_atom_hash, atom_name));
307   if (!result)
308     {
309       result = INDEX_TO_ATOM (virtual_atom_array->len);
310       
311       g_ptr_array_add (virtual_atom_array, g_strdup (atom_name));
312       g_hash_table_insert (virtual_atom_hash, 
313                            g_ptr_array_index (virtual_atom_array,
314                                               ATOM_TO_INDEX (result)),
315                            GDK_ATOM_TO_POINTER (result));
316     }
317
318   return result;
319 }
320
321 static G_CONST_RETURN char *
322 get_atom_name (GdkAtom atom)
323 {
324   virtual_atom_check_init ();
325
326   if (ATOM_TO_INDEX (atom) < virtual_atom_array->len)
327     return g_ptr_array_index (virtual_atom_array, ATOM_TO_INDEX (atom));
328   else
329     return NULL;
330 }
331
332 gchar *
333 gdk_atom_name (GdkAtom atom)
334 {
335   return g_strdup (get_atom_name (atom));
336 }
337
338 /**
339  * gdk_x11_get_xatom_by_name_for_display:
340  * @display: a #GdkDisplay
341  * @atom_name: a string
342  * 
343  * Returns the X atom for a #GdkDisplay corresponding to @atom_name.
344  * This function caches the result, so if called repeatedly it is much
345  * faster than XInternAtom(), which is a round trip to the server each time.
346  * 
347  * Return value: a X atom for a #GdkDisplay
348  *
349  * Since: 2.2
350  **/
351 Atom
352 gdk_x11_get_xatom_by_name_for_display (GdkDisplay  *display,
353                                        const gchar *atom_name)
354 {
355   g_return_val_if_fail (GDK_IS_DISPLAY (display), None);
356   return gdk_x11_atom_to_xatom_for_display (display,
357                                             gdk_atom_intern (atom_name, FALSE));
358 }
359
360 /**
361  * gdk_x11_get_xatom_by_name:
362  * @atom_name: a string
363  * 
364  * Returns the X atom for GDK's default display corresponding to @atom_name.
365  * This function caches the result, so if called repeatedly it is much
366  * faster than <function>XInternAtom()</function>, which is a round trip to 
367  * the server each time.
368  * 
369  * Return value: a X atom for GDK's default display.
370  **/
371 Atom
372 gdk_x11_get_xatom_by_name (const gchar *atom_name)
373 {
374   return gdk_x11_get_xatom_by_name_for_display (gdk_display_get_default (),
375                                                 atom_name);
376 }
377
378 /**
379  * gdk_x11_get_xatom_name_for_display:
380  * @display: the #GdkDisplay where @xatom is defined
381  * @xatom: an X atom 
382  * 
383  * Returns the name of an X atom for its display. This
384  * function is meant mainly for debugging, so for convenience, unlike
385  * XAtomName() and gdk_atom_name(), the result doesn't need to
386  * be freed. 
387  *
388  * Return value: name of the X atom; this string is owned by GDK,
389  *   so it shouldn't be modifed or freed. 
390  *
391  * Since: 2.2
392  **/
393 G_CONST_RETURN gchar *
394 gdk_x11_get_xatom_name_for_display (GdkDisplay *display,
395                                     Atom        xatom)
396 {
397   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
398
399   return get_atom_name (gdk_x11_xatom_to_atom_for_display (display, xatom));
400 }
401
402 /**
403  * gdk_x11_get_xatom_name:
404  * @xatom: an X atom for GDK's default display
405  * 
406  * Returns the name of an X atom for GDK's default display. This
407  * function is meant mainly for debugging, so for convenience, unlike
408  * <function>XAtomName()</function> and gdk_atom_name(), the result 
409  * doesn't need to be freed. Also, this function will never return %NULL, 
410  * even if @xatom is invalid.
411  * 
412  * Return value: name of the X atom; this string is owned by GTK+,
413  *   so it shouldn't be modifed or freed. 
414  **/
415 G_CONST_RETURN gchar *
416 gdk_x11_get_xatom_name (Atom xatom)
417 {
418   return get_atom_name (gdk_x11_xatom_to_atom (xatom));
419 }
420
421 gboolean
422 gdk_property_get (GdkWindow   *window,
423                   GdkAtom      property,
424                   GdkAtom      type,
425                   gulong       offset,
426                   gulong       length,
427                   gint         pdelete,
428                   GdkAtom     *actual_property_type,
429                   gint        *actual_format_type,
430                   gint        *actual_length,
431                   guchar     **data)
432 {
433   GdkDisplay *display;
434   Atom ret_prop_type;
435   gint ret_format;
436   gulong ret_nitems;
437   gulong ret_bytes_after;
438   gulong ret_length;
439   guchar *ret_data;
440   Atom xproperty;
441   Atom xtype;
442   int res;
443
444   g_return_val_if_fail (!window || GDK_IS_WINDOW (window), FALSE);
445
446   if (!window)
447     {
448       GdkScreen *screen = gdk_screen_get_default ();
449       window = gdk_screen_get_root_window (screen);
450       
451       GDK_NOTE (MULTIHEAD, g_message ("gdk_property_get(): window is NULL\n"));
452     }
453
454   if (GDK_WINDOW_DESTROYED (window))
455     return FALSE;
456
457   display = gdk_drawable_get_display (window);
458   xproperty = gdk_x11_atom_to_xatom_for_display (display, property);
459   xtype = gdk_x11_atom_to_xatom_for_display (display, type);
460
461   ret_data = NULL;
462   
463   res = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
464                             GDK_WINDOW_XWINDOW (window), xproperty,
465                             offset, (length + 3) / 4, pdelete,
466                             xtype, &ret_prop_type, &ret_format,
467                             &ret_nitems, &ret_bytes_after,
468                             &ret_data);
469
470   if (res != Success || (ret_prop_type == None && ret_format == 0))
471     {
472       return FALSE;
473     }
474
475   if (actual_property_type)
476     *actual_property_type = gdk_x11_xatom_to_atom_for_display (display, ret_prop_type);
477   if (actual_format_type)
478     *actual_format_type = ret_format;
479
480   if ((xtype != AnyPropertyType) && (ret_prop_type != xtype))
481     {
482       XFree (ret_data);
483       g_warning ("Couldn't match property type %s to %s\n", 
484                  gdk_x11_get_xatom_name_for_display (display, ret_prop_type), 
485                  gdk_x11_get_xatom_name_for_display (display, xtype));
486       return FALSE;
487     }
488
489   /* FIXME: ignoring bytes_after could have very bad effects */
490
491   if (data)
492     {
493       if (ret_prop_type == XA_ATOM ||
494           ret_prop_type == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
495         {
496           /*
497            * data is an array of X atom, we need to convert it
498            * to an array of GDK Atoms
499            */
500           gint i;
501           GdkAtom *ret_atoms = g_new (GdkAtom, ret_nitems);
502           Atom *xatoms = (Atom *)ret_data;
503
504           *data = (guchar *)ret_atoms;
505
506           for (i = 0; i < ret_nitems; i++)
507             ret_atoms[i] = gdk_x11_xatom_to_atom_for_display (display, xatoms[i]);
508           
509           if (actual_length)
510             *actual_length = ret_nitems * sizeof (GdkAtom);
511         }
512       else
513         {
514           switch (ret_format)
515             {
516             case 8:
517               ret_length = ret_nitems;
518               break;
519             case 16:
520               ret_length = sizeof(short) * ret_nitems;
521               break;
522             case 32:
523               ret_length = sizeof(long) * ret_nitems;
524               break;
525             default:
526               g_warning ("unknown property return format: %d", ret_format);
527               XFree (ret_data);
528               return FALSE;
529             }
530           
531           *data = g_new (guchar, ret_length);
532           memcpy (*data, ret_data, ret_length);
533           if (actual_length)
534             *actual_length = ret_length;
535         }
536     }
537
538   XFree (ret_data);
539
540   return TRUE;
541 }
542
543 void
544 gdk_property_change (GdkWindow    *window,
545                      GdkAtom       property,
546                      GdkAtom       type,
547                      gint          format,
548                      GdkPropMode   mode,
549                      const guchar *data,
550                      gint          nelements)
551 {
552   GdkDisplay *display;
553   Window xwindow;
554   Atom xproperty;
555   Atom xtype;
556
557   g_return_if_fail (!window || GDK_IS_WINDOW (window));
558
559   if (!window)
560     {
561       GdkScreen *screen;
562       
563       screen = gdk_screen_get_default ();
564       window = gdk_screen_get_root_window (screen);
565       
566       GDK_NOTE (MULTIHEAD, g_message ("gdk_property_delete(): window is NULL\n"));
567     }
568
569
570   if (GDK_WINDOW_DESTROYED (window))
571     return;
572
573   display = gdk_drawable_get_display (window);
574   
575   xproperty = gdk_x11_atom_to_xatom_for_display (display, property);
576   xtype = gdk_x11_atom_to_xatom_for_display (display, type);
577   xwindow = GDK_WINDOW_XID (window);
578
579   if (xtype == XA_ATOM ||
580       xtype == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
581     {
582       /*
583        * data is an array of GdkAtom, we need to convert it
584        * to an array of X Atoms
585        */
586       gint i;
587       GdkAtom *atoms = (GdkAtom*) data;
588       Atom *xatoms;
589
590       xatoms = g_new (Atom, nelements);
591       for (i = 0; i < nelements; i++)
592         xatoms[i] = gdk_x11_atom_to_xatom_for_display (display, atoms[i]);
593
594       XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow,
595                        xproperty, xtype,
596                        format, mode, (guchar *)xatoms, nelements);
597       g_free (xatoms);
598     }
599   else
600     XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow, xproperty, 
601                      xtype, format, mode, (guchar *)data, nelements);
602 }
603
604 void
605 gdk_property_delete (GdkWindow *window,
606                      GdkAtom    property)
607 {
608   g_return_if_fail (!window || GDK_IS_WINDOW (window));
609
610   if (!window)
611     {
612       GdkScreen *screen = gdk_screen_get_default ();
613       window = gdk_screen_get_root_window (screen);
614       
615       GDK_NOTE (MULTIHEAD, 
616                 g_message ("gdk_property_delete(): window is NULL\n"));
617     }
618
619   if (GDK_WINDOW_DESTROYED (window))
620     return;
621
622   XDeleteProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XWINDOW (window),
623                    gdk_x11_atom_to_xatom_for_display (GDK_WINDOW_DISPLAY (window),
624                                                       property));
625 }