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