Tizen 2.0 Release
[framework/uifw/xorg/util/x11-utils.git] / xwininfo / xwininfo.c
1 /*
2  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 /*
24
25 Copyright 1987, 1998  The Open Group
26
27 Permission to use, copy, modify, distribute, and sell this software and its
28 documentation for any purpose is hereby granted without fee, provided that
29 the above copyright notice appear in all copies and that both that
30 copyright notice and this permission notice appear in supporting
31 documentation.
32
33 The above copyright notice and this permission notice shall be included
34 in all copies or substantial portions of the Software.
35
36 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
37 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
38 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
39 OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
40 HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
41 INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
42 FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
43 NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
44 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
45
46 Except as contained in this notice, the name of a copyright holder
47 shall not be used in advertising or otherwise to promote the sale, use
48 or other dealings in this Software without prior written authorization
49 of the copyright holder.
50
51 */
52
53
54 /*
55  * xwininfo.c   - MIT Project Athena, X Window system window
56  *                information utility.
57  *
58  *
59  *      This program will report all relevant information
60  *      about a specific window.
61  *
62  *  Author:     Mark Lillibridge, MIT Project Athena
63  *              16-Jun-87
64  */
65
66 #include "config.h"
67
68 #include <xcb/xcb.h>
69 #include <xcb/xproto.h>
70 #ifdef USE_XCB_ICCCM
71 # include <xcb/xcb_icccm.h>
72 #endif
73 #include <xcb/shape.h>
74
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <string.h>
78 #include <locale.h>
79 #include <langinfo.h>
80 #ifdef HAVE_ICONV
81 # include <iconv.h>
82 #endif
83 #include <ctype.h>
84 #include <errno.h>
85
86 #ifndef HAVE_STRNLEN
87 #include "strnlen.h"
88 #endif
89
90 /* Include routines to handle parsing defaults */
91 #include "dsimple.h"
92
93 typedef struct {
94     long code;
95     const char *name;
96 } binding;
97
98 #ifndef USE_XCB_ICCCM
99 /* Once xcb-icccm's API is stable, this should be replaced by
100    xcb_size_hints_t & xcb_size_hints_flags_t */
101 typedef struct {
102   /** User specified flags */
103   uint32_t flags;
104   /** User-specified position */
105   int32_t x, y;
106   /** User-specified size */
107   int32_t width, height;
108   /** Program-specified minimum size */
109   int32_t min_width, min_height;
110   /** Program-specified maximum size */
111   int32_t max_width, max_height;
112   /** Program-specified resize increments */
113   int32_t width_inc, height_inc;
114   /** Program-specified minimum aspect ratios */
115   int32_t min_aspect_num, min_aspect_den;
116   /** Program-specified maximum aspect ratios */
117   int32_t max_aspect_num, max_aspect_den;
118   /** Program-specified base size */
119   int32_t base_width, base_height;
120   /** Program-specified window gravity */
121   uint32_t win_gravity;
122 } wm_size_hints_t;
123
124 # define xcb_size_hints_t wm_size_hints_t
125
126 typedef struct {
127   /** Marks which fields in this structure are defined */
128   int32_t flags;
129   /** Does this application rely on the window manager to get keyboard
130       input? */
131   uint32_t input;
132   /** See below */
133   int32_t initial_state;
134   /** Pixmap to be used as icon */
135   xcb_pixmap_t icon_pixmap;
136   /** Window to be used as icon */
137   xcb_window_t icon_window;
138   /** Initial position of icon */
139   int32_t icon_x, icon_y;
140   /** Icon mask bitmap */
141   xcb_pixmap_t icon_mask;
142   /* Identifier of related window group */
143   xcb_window_t window_group;
144 } wm_hints_t;
145
146 #define xcb_icccm_wm_hints_t wm_hints_t
147
148 enum {
149   /* xcb_size_hints_flags_t */
150   XCB_ICCCM_SIZE_HINT_US_POSITION = 1 << 0,
151   XCB_ICCCM_SIZE_HINT_US_SIZE = 1 << 1,
152   XCB_ICCCM_SIZE_HINT_P_POSITION = 1 << 2,
153   XCB_ICCCM_SIZE_HINT_P_SIZE = 1 << 3,
154   XCB_ICCCM_SIZE_HINT_P_MIN_SIZE = 1 << 4,
155   XCB_ICCCM_SIZE_HINT_P_MAX_SIZE = 1 << 5,
156   XCB_ICCCM_SIZE_HINT_P_RESIZE_INC = 1 << 6,
157   XCB_ICCCM_SIZE_HINT_P_ASPECT = 1 << 7,
158   XCB_ICCCM_SIZE_HINT_BASE_SIZE = 1 << 8,
159   XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY = 1 << 9,
160   /* xcb_wm_state_t */
161   XCB_ICCCM_WM_STATE_WITHDRAWN = 0,
162   XCB_ICCCM_WM_STATE_NORMAL = 1,
163   XCB_ICCCM_WM_STATE_ICONIC = 3,
164   /* xcb_wm_t */
165   XCB_ICCCM_WM_HINT_INPUT = (1L << 0),
166   XCB_ICCCM_WM_HINT_STATE = (1L << 1),
167   XCB_ICCCM_WM_HINT_ICON_PIXMAP = (1L << 2),
168   XCB_ICCCM_WM_HINT_ICON_WINDOW = (1L << 3),
169   XCB_ICCCM_WM_HINT_ICON_POSITION = (1L << 4),
170   XCB_ICCCM_WM_HINT_ICON_MASK = (1L << 5),
171   XCB_ICCCM_WM_HINT_WINDOW_GROUP = (1L << 6),
172   XCB_ICCCM_WM_HINT_X_URGENCY = (1L << 8)
173 };
174
175 /* Once xcb-icccm's API is stable, these should be replaced by calls to it */
176 # define GET_TEXT_PROPERTY(Dpy, Win, Atom) \
177     xcb_get_property (Dpy, False, Win, Atom, XCB_GET_PROPERTY_TYPE_ANY, 0, BUFSIZ)
178 # define xcb_icccm_get_wm_name(Dpy, Win) \
179     GET_TEXT_PROPERTY(Dpy, Win, XCB_ATOM_WM_NAME)
180
181 # define xcb_icccm_get_wm_class(Dpy, Win) \
182     xcb_get_property (Dpy, False, Win, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, BUFSIZ)
183 # define xcb_icccm_get_wm_hints(Dpy, Win) \
184     xcb_get_property(Dpy, False, Win, XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 0, 9)
185
186 # define xcb_icccm_get_wm_size_hints(Dpy, Win, Atom) \
187     xcb_get_property (Dpy, False, Win, Atom, XCB_ATOM_WM_SIZE_HINTS, 0, 18)
188 # define xcb_icccm_get_wm_normal_hints(Dpy, Win) \
189     xcb_icccm_get_wm_size_hints(Dpy, Win, XCB_ATOM_WM_NORMAL_HINTS)
190 #endif
191
192 /* Possibly in xcb-emwh in the future? */
193 static xcb_atom_t atom_net_wm_name, atom_utf8_string;
194 static xcb_atom_t atom_net_wm_desktop, atom_net_wm_window_type,
195     atom_net_wm_state, atom_net_wm_pid, atom_net_frame_extents;
196 static xcb_get_property_cookie_t get_net_wm_name (xcb_connection_t *,
197                                                   xcb_window_t);
198
199 /* Information we keep track of for each window to allow prefetching/reusing */
200 struct wininfo {
201     xcb_window_t                        window;
202
203     /* cookies for requests we've sent */
204     xcb_get_geometry_cookie_t           geometry_cookie;
205     xcb_get_property_cookie_t           net_wm_name_cookie;
206     xcb_get_property_cookie_t           wm_name_cookie;
207     xcb_get_property_cookie_t           wm_class_cookie;
208     xcb_translate_coordinates_cookie_t  trans_coords_cookie;
209     xcb_query_tree_cookie_t             tree_cookie;
210     xcb_get_window_attributes_cookie_t  attr_cookie;
211     xcb_get_property_cookie_t           normal_hints_cookie;
212     xcb_get_property_cookie_t           hints_cookie;
213     xcb_get_property_cookie_t           wm_desktop_cookie;
214     xcb_get_property_cookie_t           wm_window_type_cookie;
215     xcb_get_property_cookie_t           wm_state_cookie;
216     xcb_get_property_cookie_t           wm_pid_cookie;
217     xcb_get_property_cookie_t           wm_client_machine_cookie;
218     xcb_get_property_cookie_t           frame_extents_cookie;
219     xcb_get_property_cookie_t           zoom_cookie;
220
221     /* cached results from previous requests */
222     xcb_get_geometry_reply_t *          geometry;
223     xcb_get_window_attributes_reply_t * win_attributes;
224     xcb_size_hints_t *                  normal_hints;
225 };
226
227 static void scale_init (xcb_screen_t *scrn);
228 static char *nscale (int, int, int, char *, size_t);
229 static char *xscale (int);
230 static char *yscale (int);
231 static char *bscale (int);
232 int main (int, char **);
233 static const char *LookupL (long, const binding *);
234 static const char *Lookup (int, const binding *);
235 static void Display_Window_Id (struct wininfo *, Bool);
236 static void Display_Stats_Info (struct wininfo *);
237 static void Display_Bits_Info (struct wininfo *);
238 static void Display_Event_Mask (long);
239 static void Display_Events_Info (struct wininfo *);
240 static void Display_Tree_Info (struct wininfo *, int);
241 static void display_tree_info_1 (struct wininfo *, int, int);
242 static void Display_Hints (xcb_size_hints_t *);
243 static void Display_Size_Hints (struct wininfo *);
244 static void Display_Window_Shape (xcb_window_t);
245 static void Display_WM_Info (struct wininfo *);
246 static void wininfo_wipe (struct wininfo *);
247
248 static const char *window_id_format = "0x%lx";
249
250 #ifdef HAVE_ICONV
251 static iconv_t iconv_from_utf8;
252 #endif
253 static const char *user_encoding;
254 static void print_utf8 (const char *, char *, size_t, const char *);
255 static void print_friendly_name (const char *, const char *, const char *);
256
257 static xcb_connection_t *dpy;
258 static xcb_screen_t *screen;
259 static xcb_generic_error_t *err;
260
261 #ifndef HAVE_STRLCAT
262 static size_t strlcat (char *dst, const char *src, size_t dstsize)
263 {
264     size_t sd = strlen (dst);
265     size_t ss = strlen (src);
266     size_t s = sd + ss;
267
268     if (s < dstsize) {
269         strcpy (dst + sd, src);
270     } else {
271         strncpy (dst + sd, src, dstsize-sd-1);
272         dst[dstsize] = '\0';
273     }
274     return s;
275 }
276 #endif
277
278 /*
279  * Report the syntax for calling xwininfo:
280  */
281 static void
282 usage (void)
283 {
284     fprintf (stderr,
285              "usage:  %s [-options ...]\n\n"
286              "where options include:\n"
287              "    -help                print this message\n"
288              "    -display host:dpy    X server to contact\n"
289              "    -root                use the root window\n"
290              "    -id windowid         use the window with the specified id\n"
291              "    -name windowname     use the window with the specified name\n"
292              "    -int                 print window id in decimal\n"
293              "    -children            print parent and child identifiers\n"
294              "    -tree                print children identifiers recursively\n"
295              "    -stats               print window geometry [DEFAULT]\n"
296              "    -bits                print window pixel information\n"
297              "    -events              print events selected for on window\n"
298              "    -size                print size hints\n"
299              "    -wm                  print window manager hints\n"
300              "    -shape               print shape extents\n"
301              "    -frame               don't ignore window manager frames\n"
302              "    -english             print sizes in english units\n"
303              "    -metric              print sizes in metric units\n"
304              "    -all                 -tree, -stats, -bits, -events, -wm, -size, -shape\n"
305              "\n",
306              program_name);
307     exit (1);
308 }
309
310 /*
311  * pixel to inch, metric converter.
312  * Hacked in by Mark W. Eichin <eichin@athena> [eichin:19880619.1509EST]
313  *
314  * Simply put: replace the old numbers with string print calls.
315  * Returning a local string is ok, since we only ever get called to
316  * print one x and one y, so as long as they don't collide, they're
317  * fine. This is not meant to be a general purpose routine.
318  *
319  */
320
321 static int xp = 0, xmm = 0;
322 static int yp = 0, ymm = 0;
323 static int bp = 0, bmm = 0;
324 static int english = 0, metric = 0;
325
326 static void
327 scale_init (xcb_screen_t *screen)
328 {
329     xp = screen->width_in_pixels;
330     yp = screen->height_in_pixels;
331     xmm = screen->width_in_millimeters;
332     ymm = screen->height_in_millimeters;
333     bp  = xp  + yp;
334     bmm = xmm + ymm;
335 }
336
337 #define MILE (5280*12)
338 #define YARD (3*12)
339 #define FOOT (12)
340
341 static char *
342 nscale (int n, int np, int nmm, char *nbuf, size_t nbufsize)
343 {
344     int s;
345     snprintf (nbuf, nbufsize, "%d", n);
346
347     if (metric||english) {
348         s = strlcat (nbuf, " (", nbufsize);
349
350         if (metric) {
351             snprintf (nbuf+s, nbufsize-s, "%.2f mm%s",
352                       ((double) n) * nmm/np , english ? "; " : "");
353         }
354         if (english) {
355             double inch_frac;
356             Bool printed_anything = False;
357             int mi, yar, ft, inr;
358
359             inch_frac = ((double) n)*(nmm/25.4)/np;
360             inr = (int)inch_frac;
361             inch_frac -= (double)inr;
362             if (inr >= MILE) {
363                 mi = inr/MILE;
364                 inr %= MILE;
365                 s = strlen (nbuf);
366                 snprintf (nbuf+s, nbufsize-s, "%d %s(?!?)",
367                           mi, (mi == 1) ? "mile" : "miles");
368                 printed_anything = True;
369             }
370             if (inr >= YARD) {
371                 yar = inr/YARD;
372                 inr %= YARD;
373                 if (printed_anything)
374                     strlcat (nbuf, ", ", nbufsize);
375                 s = strlen (nbuf);
376                 snprintf (nbuf+s, nbufsize-s, "%d %s",
377                          yar, (yar==1) ? "yard" : "yards");
378                 printed_anything = True;
379             }
380             if (inr >= FOOT) {
381                 ft = inr/FOOT;
382                 inr  %= FOOT;
383                 if (printed_anything)
384                     strlcat (nbuf, ", ", nbufsize);
385                 s = strlen (nbuf);
386                 snprintf (nbuf+s, nbufsize-s, "%d %s",
387                          ft, (ft==1) ? "foot" : "feet");
388                 printed_anything = True;
389             }
390             if (!printed_anything || inch_frac != 0.0 || inr != 0) {
391                 if (printed_anything)
392                     strlcat (nbuf, ", ", nbufsize);
393                 s = strlen (nbuf);
394                 snprintf (nbuf+s, nbufsize-s, "%.2f inches", inr+inch_frac);
395             }
396         }
397         strlcat (nbuf, ")", nbufsize);
398     }
399     return (nbuf);
400 }
401
402 static char xbuf[BUFSIZ];
403 static char *
404 xscale (int x)
405 {
406     return (nscale (x, xp, xmm, xbuf, sizeof(xbuf)));
407 }
408
409 static char ybuf[BUFSIZ];
410 static char *
411 yscale (int y)
412 {
413     return (nscale (y, yp, ymm, ybuf, sizeof(ybuf)));
414 }
415
416 static char bbuf[BUFSIZ];
417 static char *
418 bscale (int b)
419 {
420     return (nscale (b, bp, bmm, bbuf, sizeof(bbuf)));
421 }
422
423 /* end of pixel to inch, metric converter */
424
425 int
426 main (int argc, char **argv)
427 {
428     register int i;
429     int tree = 0, stats = 0, bits = 0, events = 0, wm = 0, size = 0, shape = 0;
430     int frame = 0, children = 0;
431     int use_root = 0;
432     xcb_window_t window = 0;
433     char *display_name = NULL;
434     const char *window_name = NULL;
435     struct wininfo wininfo;
436     struct wininfo *w = &wininfo;
437
438     program_name = argv[0];
439
440     if (!setlocale (LC_ALL, ""))
441         fprintf (stderr, "%s: can not set locale properly\n", program_name);
442     user_encoding = nl_langinfo (CODESET);
443     if (user_encoding == NULL)
444         user_encoding = "unknown encoding";
445
446     memset (w, 0, sizeof(struct wininfo));
447
448     /* Handle our command line arguments */
449     for (i = 1; i < argc; i++) {
450         if (!strcmp (argv[i], "-help"))
451             usage ();
452         if (!strcmp (argv[i], "-display") || !strcmp (argv[i], "-d")) {
453             if (++i >= argc)
454                 Fatal_Error("-display requires argument");
455             display_name = argv[i];
456             continue;
457         }
458         if (!strcmp (argv[i], "-root")) {
459             use_root = 1;
460             continue;
461         }
462         if (!strcmp (argv[i], "-id")) {
463             if (++i >= argc)
464                 Fatal_Error("-id requires argument");
465             window = strtoul(argv[i], NULL, 0);
466             continue;
467         }
468         if (!strcmp (argv[i], "-name")) {
469             if (++i >= argc)
470                 Fatal_Error("-name requires argument");
471             window_name = argv[i];
472             continue;
473         }
474         if (!strcmp (argv[i], "-int")) {
475             window_id_format = "%ld";
476             continue;
477         }
478         if (!strcmp (argv[i], "-children")) {
479             children = 1;
480             continue;
481         }
482         if (!strcmp (argv[i], "-tree")) {
483             tree = 1;
484             continue;
485         }
486         if (!strcmp (argv[i], "-stats")) {
487             stats = 1;
488             continue;
489         }
490         if (!strcmp (argv[i], "-bits")) {
491             bits = 1;
492             continue;
493         }
494         if (!strcmp (argv[i], "-events")) {
495             events = 1;
496             continue;
497         }
498         if (!strcmp (argv[i], "-wm")) {
499             wm = 1;
500             continue;
501         }
502         if (!strcmp (argv[i], "-frame")) {
503             frame = 1;
504             continue;
505         }
506         if (!strcmp (argv[i], "-size")) {
507             size = 1;
508             continue;
509         }
510         if (!strcmp (argv[i], "-shape")) {
511             shape = 1;
512             continue;
513         }
514         if (!strcmp (argv[i], "-english")) {
515             english = 1;
516             continue;
517         }
518         if (!strcmp (argv[i], "-metric")) {
519             metric = 1;
520             continue;
521         }
522         if (!strcmp (argv[i], "-all")) {
523             tree = stats = bits = events = wm = size = shape = 1;
524             continue;
525         }
526         usage ();
527     }
528
529     Setup_Display_And_Screen (display_name, &dpy, &screen);
530
531     /* preload atoms we may need later */
532     Intern_Atom (dpy, "_NET_WM_NAME");
533     Intern_Atom (dpy, "UTF8_STRING");
534     if (wm) {
535         Intern_Atom (dpy, "_NET_WM_DESKTOP");
536         Intern_Atom (dpy, "_NET_WM_WINDOW_TYPE");
537         Intern_Atom (dpy, "_NET_WM_STATE");
538         Intern_Atom (dpy, "_NET_WM_PID");
539         Intern_Atom (dpy, "_NET_FRAME_EXTENTS");
540     }
541     /* initialize scaling data */
542     scale_init(screen);
543
544     if (use_root)
545         window = screen->root;
546     else if (window_name) {
547         window = Window_With_Name (dpy, screen->root, window_name);
548         if (!window)
549             Fatal_Error ("No window with name \"%s\" exists!", window_name);
550     }
551
552     /* If no window selected on command line, let user pick one the hard way */
553     if (!window) {
554         printf ("\n"
555                 "xwininfo: Please select the window about which you\n"
556                 "          would like information by clicking the\n"
557                 "          mouse in that window.\n");
558         Intern_Atom (dpy, "_NET_VIRTUAL_ROOTS");
559         Intern_Atom (dpy, "WM_STATE");
560         window = Select_Window (dpy, screen, !frame);
561     }
562
563     /*
564      * Do the actual displaying as per parameters
565      */
566     if (!(children || tree || bits || events || wm || size))
567         stats = 1;
568
569     /*
570      * make sure that the window is valid
571      */
572     {
573         xcb_get_geometry_cookie_t gg_cookie =
574             xcb_get_geometry (dpy, window);
575
576         w->geometry = xcb_get_geometry_reply(dpy, gg_cookie, &err);
577
578         if (!w->geometry) {
579             char badid[20];
580
581             if (err)
582                 Print_X_Error (dpy, err);
583
584             snprintf (badid, sizeof(badid), window_id_format, window);
585             Fatal_Error ("No such window with id %s.", badid);
586         }
587     }
588
589     /* Send requests to prefetch data we'll need */
590     w->window = window;
591     w->net_wm_name_cookie = get_net_wm_name (dpy, window);
592     w->wm_name_cookie = xcb_icccm_get_wm_name (dpy, window);
593     if (children || tree)
594         w->tree_cookie = xcb_query_tree (dpy, window);
595     if (stats) {
596         w->trans_coords_cookie =
597             xcb_translate_coordinates (dpy, window, w->geometry->root,
598                                        -(w->geometry->border_width),
599                                        -(w->geometry->border_width));
600     }
601     if (stats || bits || events)
602         w->attr_cookie = xcb_get_window_attributes (dpy, window);
603     if (stats || size)
604         w->normal_hints_cookie = xcb_icccm_get_wm_normal_hints (dpy, window);
605     if (wm) {
606         w->hints_cookie = xcb_icccm_get_wm_hints(dpy, window);
607
608         atom_net_wm_desktop = Get_Atom (dpy, "_NET_WM_DESKTOP");
609         if (atom_net_wm_desktop) {
610             w->wm_desktop_cookie = xcb_get_property
611                 (dpy, False, window, atom_net_wm_desktop,
612                  XCB_ATOM_CARDINAL, 0, 4);
613         }
614
615         atom_net_wm_window_type = Get_Atom (dpy, "_NET_WM_WINDOW_TYPE");
616         if (atom_net_wm_window_type) {
617             w->wm_window_type_cookie = xcb_get_property
618                 (dpy, False, window, atom_net_wm_window_type,
619                  XCB_ATOM_ATOM, 0, BUFSIZ);
620         }
621
622         atom_net_wm_state = Get_Atom (dpy, "_NET_WM_STATE");
623         if (atom_net_wm_state) {
624             w->wm_state_cookie = xcb_get_property
625                 (dpy, False, window, atom_net_wm_state,
626                  XCB_ATOM_ATOM, 0, BUFSIZ);
627         }
628
629         atom_net_wm_pid = Get_Atom (dpy, "_NET_WM_PID");
630         if (atom_net_wm_pid) {
631             w->wm_pid_cookie = xcb_get_property
632                 (dpy, False, window, atom_net_wm_pid,
633                  XCB_ATOM_CARDINAL, 0, BUFSIZ);
634             w->wm_client_machine_cookie = xcb_get_property
635                 (dpy, False, window, XCB_ATOM_WM_CLIENT_MACHINE,
636                  XCB_GET_PROPERTY_TYPE_ANY, 0, BUFSIZ);
637         }
638
639         atom_net_frame_extents = Get_Atom (dpy, "_NET_FRAME_EXTENTS");
640         if (atom_net_frame_extents) {
641             w->frame_extents_cookie = xcb_get_property
642                 (dpy, False, window, atom_net_frame_extents,
643                  XCB_ATOM_CARDINAL, 0, 4 * 4);
644         }
645     }
646     if (size)
647         w->zoom_cookie = xcb_icccm_get_wm_size_hints (dpy, window,
648                                                       XCB_ATOM_WM_ZOOM_HINTS);
649     xcb_flush (dpy);
650
651     printf ("\nxwininfo: Window id: ");
652     Display_Window_Id (w, True);
653     if (children || tree)
654         Display_Tree_Info (w, tree);
655     if (stats)
656         Display_Stats_Info (w);
657     if (bits)
658         Display_Bits_Info (w);
659     if (events)
660         Display_Events_Info (w);
661     if (wm)
662         Display_WM_Info (w);
663     if (size)
664         Display_Size_Hints (w);
665     if (shape)
666         Display_Window_Shape (window);
667     printf ("\n");
668
669     wininfo_wipe (w);
670     xcb_disconnect (dpy);
671 #ifdef HAVE_ICONV
672     if (iconv_from_utf8 && (iconv_from_utf8 != (iconv_t) -1)) {
673         iconv_close (iconv_from_utf8);
674     }
675 #endif
676     exit (0);
677 }
678
679 /* Ensure win_attributes field is filled in */
680 static xcb_get_window_attributes_reply_t *
681 fetch_win_attributes (struct wininfo *w)
682 {
683     if (!w->win_attributes) {
684         w->win_attributes =
685             xcb_get_window_attributes_reply (dpy, w->attr_cookie, &err);
686
687         if (!w->win_attributes) {
688             Print_X_Error (dpy, err);
689             Fatal_Error ("Can't get window attributes.");
690         }
691     }
692     return w->win_attributes;
693 }
694
695 #ifndef USE_XCB_ICCCM
696 static Bool
697 wm_size_hints_reply (xcb_connection_t *dpy, xcb_get_property_cookie_t cookie,
698                      wm_size_hints_t *hints_return, xcb_generic_error_t **err)
699 {
700     xcb_get_property_reply_t *prop = xcb_get_property_reply (dpy, cookie, err);
701     int length;
702
703     if (!prop || (prop->type != XCB_ATOM_WM_SIZE_HINTS) ||
704         (prop->format != 32)) {
705         free (prop);
706         return False;
707     }
708
709     memset (hints_return, 0, sizeof(wm_size_hints_t));
710
711     length = xcb_get_property_value_length(prop);
712     if (length > sizeof(wm_size_hints_t))
713         length = sizeof(wm_size_hints_t);
714     memcpy (hints_return, xcb_get_property_value (prop), length);
715
716     free (prop);
717     return True;
718 }
719
720 #define xcb_icccm_get_wm_normal_hints_reply wm_size_hints_reply
721 #define xcb_icccm_get_wm_size_hints_reply wm_size_hints_reply
722 #endif
723
724
725
726 /* Ensure normal_hints field is filled in */
727 static xcb_size_hints_t *
728 fetch_normal_hints (struct wininfo *w, xcb_size_hints_t *hints_return)
729 {
730     xcb_size_hints_t hints;
731
732     if (!w->normal_hints) {
733         if (xcb_icccm_get_wm_normal_hints_reply (dpy, w->normal_hints_cookie,
734                                                  &hints, NULL)) {
735             w->normal_hints = malloc (sizeof(xcb_size_hints_t));
736             if (w->normal_hints)
737                 memcpy(w->normal_hints, &hints, sizeof(xcb_size_hints_t));
738         }
739     }
740     if (hints_return && w->normal_hints)
741         memcpy(hints_return, w->normal_hints, sizeof(xcb_size_hints_t));
742     return w->normal_hints;
743 }
744
745
746 /*
747  * Lookup: lookup a code in a table.
748  */
749 static char _lookup_buffer[100];
750
751 static const char *
752 LookupL (long code, const binding *table)
753 {
754     const char *name = NULL;
755
756     while (table->name) {
757         if (table->code == code) {
758             name = table->name;
759             break;
760         }
761         table++;
762     }
763
764     if (name == NULL) {
765         snprintf (_lookup_buffer, sizeof(_lookup_buffer),
766                   "unknown (code = %ld. = 0x%lx)", code, code);
767         name = _lookup_buffer;
768     }
769
770     return (name);
771 }
772
773 static const char *
774 Lookup (int code, const binding *table)
775 {
776     return LookupL ((long)code, table);
777 }
778
779 /*
780  * Routine to display a window id in dec/hex with name if window has one
781  *
782  * Requires wininfo members initialized: window, wm_name_cookie
783  */
784
785 static void
786 Display_Window_Id (struct wininfo *w, Bool newline_wanted)
787 {
788 #ifdef USE_XCB_ICCCM
789     xcb_icccm_get_text_property_reply_t wmn_reply;
790     uint8_t got_reply = False;
791 #endif
792     xcb_get_property_reply_t *prop;
793     const char *wm_name = NULL;
794     unsigned int wm_name_len = 0;
795     xcb_atom_t wm_name_encoding = XCB_NONE;
796
797     printf (window_id_format, w->window);      /* print id # in hex/dec */
798
799     if (!w->window) {
800         printf (" (none)");
801     } else {
802         if (w->window == screen->root) {
803             printf (" (the root window)");
804         }
805         /* Get window name if any */
806         prop = xcb_get_property_reply (dpy, w->net_wm_name_cookie, NULL);
807         if (prop && (prop->type != XCB_NONE)) {
808             wm_name = xcb_get_property_value (prop);
809             wm_name_len = xcb_get_property_value_length (prop);
810             wm_name_encoding = prop->type;
811         } else { /* No _NET_WM_NAME, check WM_NAME */
812 #ifdef USE_XCB_ICCCM
813             got_reply = xcb_icccm_get_wm_name_reply (dpy, w->wm_name_cookie,
814                                                      &wmn_reply, NULL);
815             if (got_reply) {
816                 wm_name = wmn_reply.name;
817                 wm_name_len = wmn_reply.name_len;
818                 wm_name_encoding = wmn_reply.encoding;
819             }
820 #else
821             prop = xcb_get_property_reply (dpy, w->wm_name_cookie, NULL);
822             if (prop && (prop->type != XCB_NONE)) {
823                 wm_name = xcb_get_property_value (prop);
824                 wm_name_len = xcb_get_property_value_length (prop);
825                 wm_name_encoding = prop->type;
826             }
827 #endif
828         }
829         if (wm_name_len == 0) {
830             printf (" (has no name)");
831         } else {
832             if (wm_name_encoding == XCB_ATOM_STRING) {
833                 printf (" \"%.*s\"", wm_name_len, wm_name);
834             } else if (wm_name_encoding == atom_utf8_string) {
835                 print_utf8 (" \"", (char *) wm_name, wm_name_len,  "\"");
836             } else {
837                 /* Encodings we don't support, including COMPOUND_TEXT */
838                 const char *enc_name = Get_Atom_Name (dpy, wm_name_encoding);
839                 if (enc_name) {
840                     printf (" (name in unsupported encoding %s)", enc_name);
841                 } else {
842                     printf (" (name in unsupported encoding ATOM 0x%x)",
843                             wm_name_encoding);
844                 }
845             }
846         }
847 #ifdef USE_XCB_ICCCM
848         if (got_reply)
849             xcb_icccm_get_text_property_reply_wipe (&wmn_reply);
850 #else
851         free (prop);
852 #endif
853     }
854
855     if (newline_wanted)
856         printf ("\n");
857
858     return;
859 }
860
861
862 /*
863  * Display Stats on window
864  */
865 static const binding _window_classes[] = {
866         { XCB_WINDOW_CLASS_INPUT_OUTPUT, "InputOutput" },
867         { XCB_WINDOW_CLASS_INPUT_ONLY, "InputOnly" },
868         { 0, NULL } };
869
870 static const binding _map_states[] = {
871         { XCB_MAP_STATE_UNMAPPED,       "IsUnMapped" },
872         { XCB_MAP_STATE_UNVIEWABLE,     "IsUnviewable" },
873         { XCB_MAP_STATE_VIEWABLE,       "IsViewable" },
874         { 0, NULL } };
875
876 static const binding _backing_store_states[] = {
877         { XCB_BACKING_STORE_NOT_USEFUL, "NotUseful" },
878         { XCB_BACKING_STORE_WHEN_MAPPED,"WhenMapped" },
879         { XCB_BACKING_STORE_ALWAYS,     "Always" },
880         { 0, NULL } };
881
882 static const binding _bit_gravity_states[] = {
883         { XCB_GRAVITY_BIT_FORGET,       "ForgetGravity" },
884         { XCB_GRAVITY_NORTH_WEST,       "NorthWestGravity" },
885         { XCB_GRAVITY_NORTH,            "NorthGravity" },
886         { XCB_GRAVITY_NORTH_EAST,       "NorthEastGravity" },
887         { XCB_GRAVITY_WEST,             "WestGravity" },
888         { XCB_GRAVITY_CENTER,           "CenterGravity" },
889         { XCB_GRAVITY_EAST,             "EastGravity" },
890         { XCB_GRAVITY_SOUTH_WEST,       "SouthWestGravity" },
891         { XCB_GRAVITY_SOUTH,            "SouthGravity" },
892         { XCB_GRAVITY_SOUTH_EAST,       "SouthEastGravity" },
893         { XCB_GRAVITY_STATIC,           "StaticGravity" },
894         { 0, NULL }};
895
896 static const binding _window_gravity_states[] = {
897         { XCB_GRAVITY_WIN_UNMAP,        "UnmapGravity" },
898         { XCB_GRAVITY_NORTH_WEST,       "NorthWestGravity" },
899         { XCB_GRAVITY_NORTH,            "NorthGravity" },
900         { XCB_GRAVITY_NORTH_EAST,       "NorthEastGravity" },
901         { XCB_GRAVITY_WEST,             "WestGravity" },
902         { XCB_GRAVITY_CENTER,           "CenterGravity" },
903         { XCB_GRAVITY_EAST,             "EastGravity" },
904         { XCB_GRAVITY_SOUTH_WEST,       "SouthWestGravity" },
905         { XCB_GRAVITY_SOUTH,            "SouthGravity" },
906         { XCB_GRAVITY_SOUTH_EAST,       "SouthEastGravity" },
907         { XCB_GRAVITY_STATIC,           "StaticGravity" },
908         { 0, NULL }};
909
910 static const binding _visual_classes[] = {
911         { XCB_VISUAL_CLASS_STATIC_GRAY, "StaticGray" },
912         { XCB_VISUAL_CLASS_GRAY_SCALE,  "GrayScale" },
913         { XCB_VISUAL_CLASS_STATIC_COLOR,"StaticColor" },
914         { XCB_VISUAL_CLASS_PSEUDO_COLOR,"PseudoColor" },
915         { XCB_VISUAL_CLASS_TRUE_COLOR,  "TrueColor" },
916         { XCB_VISUAL_CLASS_DIRECT_COLOR,"DirectColor" },
917         { 0, NULL }};
918
919 /*
920  * Requires wininfo members initialized:
921  *   window, geometry, attr_cookie, trans_coords_cookie, normal_hints_cookie
922  */
923 static void
924 Display_Stats_Info (struct wininfo *w)
925 {
926     xcb_translate_coordinates_reply_t *trans_coords;
927     xcb_get_window_attributes_reply_t *win_attributes;
928     xcb_size_hints_t hints;
929
930     int dw = screen->width_in_pixels, dh = screen->height_in_pixels;
931     int rx, ry, xright, ybelow;
932     int showright = 0, showbelow = 0;
933     xcb_window_t wmframe, parent;
934
935     trans_coords =
936         xcb_translate_coordinates_reply (dpy, w->trans_coords_cookie, NULL);
937     if (!trans_coords)
938         Fatal_Error ("Can't get translated coordinates.");
939
940     rx = (int16_t)trans_coords->dst_x;
941     ry = (int16_t)trans_coords->dst_y;
942     free (trans_coords);
943
944     xright = (dw - rx - w->geometry->border_width * 2 -
945               w->geometry->width);
946     ybelow = (dh - ry - w->geometry->border_width * 2 -
947               w->geometry->height);
948
949
950     printf ("\n");
951     printf ("  Absolute upper-left X:  %s\n", xscale (rx));
952     printf ("  Absolute upper-left Y:  %s\n", yscale (ry));
953     printf ("  Relative upper-left X:  %s\n", xscale (w->geometry->x));
954     printf ("  Relative upper-left Y:  %s\n", yscale (w->geometry->y));
955     printf ("  Width: %s\n", xscale (w->geometry->width));
956     printf ("  Height: %s\n", yscale (w->geometry->height));
957     printf ("  Depth: %d\n", w->geometry->depth);
958
959     win_attributes = fetch_win_attributes (w);
960
961     printf ("  Visual: 0x%lx\n", (unsigned long) win_attributes->visual);
962     if (screen)
963     {
964         xcb_depth_iterator_t depth_iter;
965         xcb_visualtype_t  *visual_type = NULL;
966
967         depth_iter = xcb_screen_allowed_depths_iterator (screen);
968         for (; depth_iter.rem; xcb_depth_next (&depth_iter)) {
969             xcb_visualtype_iterator_t visual_iter;
970
971             visual_iter = xcb_depth_visuals_iterator (depth_iter.data);
972             for (; visual_iter.rem; xcb_visualtype_next (&visual_iter)) {
973                 if (screen->root_visual == visual_iter.data->visual_id) {
974                     visual_type = visual_iter.data;
975                     break;
976                 }
977             }
978         }
979         if (visual_type)
980             printf ("  Visual Class: %s\n", Lookup (visual_type->_class,
981                                                     _visual_classes));
982     }
983
984     printf ("  Border width: %s\n", bscale (w->geometry->border_width));
985     printf ("  Class: %s\n",
986             Lookup (win_attributes->_class, _window_classes));
987     printf ("  Colormap: 0x%lx (%sinstalled)\n",
988             (unsigned long) win_attributes->colormap,
989             win_attributes->map_is_installed ? "" : "not ");
990     printf ("  Bit Gravity State: %s\n",
991             Lookup (win_attributes->bit_gravity, _bit_gravity_states));
992     printf ("  Window Gravity State: %s\n",
993             Lookup (win_attributes->win_gravity, _window_gravity_states));
994     printf ("  Backing Store State: %s\n",
995             Lookup (win_attributes->backing_store, _backing_store_states));
996     printf ("  Save Under State: %s\n",
997             win_attributes->save_under ? "yes" : "no");
998     printf ("  Map State: %s\n",
999             Lookup (win_attributes->map_state, _map_states));
1000     printf ("  Override Redirect State: %s\n",
1001             win_attributes->override_redirect ? "yes" : "no");
1002     printf ("  Corners:  +%d+%d  -%d+%d  -%d-%d  +%d-%d\n",
1003             rx, ry, xright, ry, xright, ybelow, rx, ybelow);
1004
1005     /*
1006      * compute geometry string that would recreate window
1007      */
1008     printf ("  -geometry ");
1009
1010     /* compute size in appropriate units */
1011     if (!fetch_normal_hints (w, &hints))
1012         hints.flags = 0;
1013
1014     if ((hints.flags & XCB_ICCCM_SIZE_HINT_P_RESIZE_INC)  &&
1015         (hints.width_inc != 0)  && (hints.height_inc != 0)) {
1016         if (hints.flags &
1017             (XCB_ICCCM_SIZE_HINT_BASE_SIZE|XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)) {
1018             if (hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) {
1019                 w->geometry->width -= hints.base_width;
1020                 w->geometry->height -= hints.base_height;
1021             } else {
1022                 /* ICCCM says MinSize is default for BaseSize */
1023                 w->geometry->width -= hints.min_width;
1024                 w->geometry->height -= hints.min_height;
1025             }
1026         }
1027         printf ("%dx%d", w->geometry->width/hints.width_inc,
1028                 w->geometry->height/hints.height_inc);
1029     } else
1030         printf ("%dx%d", w->geometry->width, w->geometry->height);
1031
1032     if (!(hints.flags & XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY))
1033         hints.win_gravity = XCB_GRAVITY_NORTH_WEST; /* per ICCCM */
1034     /* find our window manager frame, if any */
1035     for (wmframe = parent = w->window; parent != 0 ; wmframe = parent) {
1036         xcb_query_tree_cookie_t qt_cookie;
1037         xcb_query_tree_reply_t *tree;
1038
1039         qt_cookie = xcb_query_tree (dpy, wmframe);
1040         tree = xcb_query_tree_reply (dpy, qt_cookie, &err);
1041         if (!tree) {
1042             Print_X_Error (dpy, err);
1043             Fatal_Error ("Can't query window tree.");
1044         }
1045         parent = tree->parent;
1046         free (tree);
1047         if (parent == w->geometry->root || !parent)
1048             break;
1049     }
1050     if (wmframe != w->window) {
1051         /* WM reparented, so find edges of the frame */
1052         /* Only works for ICCCM-compliant WMs, and then only if the
1053            window has corner gravity.  We would need to know the original width
1054            of the window to correctly handle the other gravities. */
1055         xcb_get_geometry_cookie_t geom_cookie;
1056         xcb_get_geometry_reply_t *frame_geometry;
1057
1058         geom_cookie = xcb_get_geometry (dpy, wmframe);
1059         frame_geometry = xcb_get_geometry_reply (dpy, geom_cookie, &err);
1060
1061         if (!frame_geometry) {
1062             Print_X_Error (dpy, err);
1063             Fatal_Error ("Can't get frame geometry.");
1064         }
1065         switch (hints.win_gravity) {
1066             case XCB_GRAVITY_NORTH_WEST: case XCB_GRAVITY_SOUTH_WEST:
1067             case XCB_GRAVITY_NORTH_EAST: case XCB_GRAVITY_SOUTH_EAST:
1068             case XCB_GRAVITY_WEST:
1069                 rx = frame_geometry->x;
1070         }
1071         switch (hints.win_gravity) {
1072             case XCB_GRAVITY_NORTH_WEST: case XCB_GRAVITY_SOUTH_WEST:
1073             case XCB_GRAVITY_NORTH_EAST: case XCB_GRAVITY_SOUTH_EAST:
1074             case XCB_GRAVITY_EAST:
1075                 xright = dw - frame_geometry->x - frame_geometry->width -
1076                     (2 * frame_geometry->border_width);
1077         }
1078         switch (hints.win_gravity) {
1079             case XCB_GRAVITY_NORTH_WEST: case XCB_GRAVITY_SOUTH_WEST:
1080             case XCB_GRAVITY_NORTH_EAST: case XCB_GRAVITY_SOUTH_EAST:
1081             case XCB_GRAVITY_NORTH:
1082                 ry = frame_geometry->y;
1083         }
1084         switch (hints.win_gravity) {
1085             case XCB_GRAVITY_NORTH_WEST: case XCB_GRAVITY_SOUTH_WEST:
1086             case XCB_GRAVITY_NORTH_EAST: case XCB_GRAVITY_SOUTH_EAST:
1087             case XCB_GRAVITY_SOUTH:
1088                 ybelow = dh - frame_geometry->y - frame_geometry->height -
1089                     (2 * frame_geometry->border_width);
1090         }
1091         free (frame_geometry);
1092     }
1093     /* If edge gravity, offer a corner on that edge (because the application
1094        programmer cares about that edge), otherwise offer upper left unless
1095        some other corner is close to an edge of the screen.
1096        (For corner gravity, assume gravity was set by XWMGeometry.
1097        For CenterGravity, it doesn't matter.) */
1098     if (hints.win_gravity == XCB_GRAVITY_EAST  ||
1099         (abs (xright) <= 100  &&  abs (xright) < abs (rx)
1100          &&  hints.win_gravity != XCB_GRAVITY_WEST))
1101         showright = 1;
1102     if (hints.win_gravity == XCB_GRAVITY_SOUTH  ||
1103         (abs (ybelow) <= 100  &&  abs (ybelow) < abs (ry)
1104          &&  hints.win_gravity != XCB_GRAVITY_NORTH))
1105         showbelow = 1;
1106
1107     if (showright)
1108         printf ("-%d", xright);
1109     else
1110         printf ("+%d", rx);
1111     if (showbelow)
1112         printf ("-%d", ybelow);
1113     else
1114         printf ("+%d", ry);
1115     printf ("\n");
1116 }
1117
1118
1119 /*
1120  * Display bits info:
1121  */
1122 static const binding _gravities[] = {
1123     /* WARNING: the first two of these have the same value - see code */
1124         { XCB_GRAVITY_WIN_UNMAP,        "UnMapGravity" },
1125         { XCB_GRAVITY_BIT_FORGET,       "ForgetGravity" },
1126         { XCB_GRAVITY_NORTH_WEST,       "NorthWestGravity" },
1127         { XCB_GRAVITY_NORTH,            "NorthGravity" },
1128         { XCB_GRAVITY_NORTH_EAST,       "NorthEastGravity" },
1129         { XCB_GRAVITY_WEST,             "WestGravity" },
1130         { XCB_GRAVITY_CENTER,           "CenterGravity" },
1131         { XCB_GRAVITY_EAST,             "EastGravity" },
1132         { XCB_GRAVITY_SOUTH_WEST,       "SouthWestGravity" },
1133         { XCB_GRAVITY_SOUTH,            "SouthGravity" },
1134         { XCB_GRAVITY_SOUTH_EAST,       "SouthEastGravity" },
1135         { XCB_GRAVITY_STATIC,           "StaticGravity" },
1136         { 0, NULL } };
1137
1138 static const binding _backing_store_hint[] = {
1139         { XCB_BACKING_STORE_NOT_USEFUL, "NotUseful" },
1140         { XCB_BACKING_STORE_WHEN_MAPPED,"WhenMapped" },
1141         { XCB_BACKING_STORE_ALWAYS,     "Always" },
1142         { 0, NULL } };
1143
1144 static const binding _bool[] = {
1145         { 0, "No" },
1146         { 1, "Yes" },
1147         { 0, NULL } };
1148
1149 /*
1150  * Requires wininfo members initialized:
1151  *   window, attr_cookie (or win_attributes)
1152  */
1153 static void
1154 Display_Bits_Info (struct wininfo * w)
1155 {
1156     xcb_get_window_attributes_reply_t *win_attributes
1157         = fetch_win_attributes (w);
1158
1159     printf ("\n");
1160     printf ("  Bit gravity: %s\n",
1161             Lookup (win_attributes->bit_gravity, _gravities+1));
1162     printf ("  Window gravity: %s\n",
1163             Lookup (win_attributes->win_gravity, _gravities));
1164     printf ("  Backing-store hint: %s\n",
1165             Lookup (win_attributes->backing_store, _backing_store_hint));
1166     printf ("  Backing-planes to be preserved: 0x%lx\n",
1167             (unsigned long) win_attributes->backing_planes);
1168     printf ("  Backing pixel: %ld\n",
1169             (unsigned long) win_attributes->backing_pixel);
1170     printf ("  Save-unders: %s\n",
1171             Lookup (win_attributes->save_under, _bool));
1172 }
1173
1174
1175 /*
1176  * Routine to display all events in an event mask
1177  */
1178 static const binding _event_mask_names[] = {
1179         { XCB_EVENT_MASK_KEY_PRESS,             "KeyPress" },
1180         { XCB_EVENT_MASK_KEY_RELEASE,           "KeyRelease" },
1181         { XCB_EVENT_MASK_BUTTON_PRESS,          "ButtonPress" },
1182         { XCB_EVENT_MASK_BUTTON_RELEASE,        "ButtonRelease" },
1183         { XCB_EVENT_MASK_ENTER_WINDOW,          "EnterWindow" },
1184         { XCB_EVENT_MASK_LEAVE_WINDOW,          "LeaveWindow" },
1185         { XCB_EVENT_MASK_POINTER_MOTION,        "PointerMotion" },
1186         { XCB_EVENT_MASK_POINTER_MOTION_HINT,   "PointerMotionHint" },
1187         { XCB_EVENT_MASK_BUTTON_1_MOTION,       "Button1Motion" },
1188         { XCB_EVENT_MASK_BUTTON_2_MOTION,       "Button2Motion" },
1189         { XCB_EVENT_MASK_BUTTON_3_MOTION,       "Button3Motion" },
1190         { XCB_EVENT_MASK_BUTTON_4_MOTION,       "Button4Motion" },
1191         { XCB_EVENT_MASK_BUTTON_5_MOTION,       "Button5Motion" },
1192         { XCB_EVENT_MASK_BUTTON_MOTION,         "ButtonMotion" },
1193         { XCB_EVENT_MASK_KEYMAP_STATE,          "KeymapState" },
1194         { XCB_EVENT_MASK_EXPOSURE,              "Exposure" },
1195         { XCB_EVENT_MASK_VISIBILITY_CHANGE,     "VisibilityChange" },
1196         { XCB_EVENT_MASK_STRUCTURE_NOTIFY,      "StructureNotify" },
1197         { XCB_EVENT_MASK_RESIZE_REDIRECT,       "ResizeRedirect" },
1198         { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,   "SubstructureNotify" },
1199         { XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, "SubstructureRedirect" },
1200         { XCB_EVENT_MASK_FOCUS_CHANGE,          "FocusChange" },
1201         { XCB_EVENT_MASK_PROPERTY_CHANGE,       "PropertyChange" },
1202         { XCB_EVENT_MASK_COLOR_MAP_CHANGE,      "ColormapChange" },
1203         { XCB_EVENT_MASK_OWNER_GRAB_BUTTON,     "OwnerGrabButton" },
1204         { 0, NULL } };
1205
1206 static void
1207 Display_Event_Mask (long mask)
1208 {
1209     long bit, bit_mask;
1210
1211     for (bit=0, bit_mask=1; bit < sizeof(long)*8; bit++, bit_mask <<= 1)
1212         if (mask & bit_mask)
1213             printf ("      %s\n",
1214                     LookupL (bit_mask, _event_mask_names));
1215 }
1216
1217
1218 /*
1219  * Display info on events
1220  *
1221  * Requires wininfo members initialized:
1222  *   window, attr_cookie (or win_attributes)
1223  */
1224 static void
1225 Display_Events_Info (struct wininfo *w)
1226 {
1227     xcb_get_window_attributes_reply_t *win_attributes
1228         = fetch_win_attributes (w);
1229
1230     printf ("\n");
1231     printf ("  Someone wants these events:\n");
1232     Display_Event_Mask (win_attributes->all_event_masks);
1233
1234     printf ("  Do not propagate these events:\n");
1235     Display_Event_Mask (win_attributes->do_not_propagate_mask);
1236
1237     printf ("  Override redirection?: %s\n",
1238             Lookup (win_attributes->override_redirect, _bool));
1239 }
1240
1241
1242   /* left out visual stuff */
1243   /* left out colormap */
1244   /* left out map_installed */
1245
1246
1247 /*
1248  * Display root, parent, and (recursively) children information
1249  * recurse - true to show children information
1250  *
1251  * Requires wininfo members initialized: window, tree_cookie
1252  */
1253 static void
1254 Display_Tree_Info (struct wininfo *w, int recurse)
1255 {
1256     display_tree_info_1 (w, recurse, 0);
1257 }
1258
1259 /*
1260  * level - recursion level
1261  */
1262 static void
1263 display_tree_info_1 (struct wininfo *w, int recurse, int level)
1264 {
1265     int i, j;
1266     unsigned int num_children;
1267     xcb_query_tree_reply_t *tree;
1268
1269     tree = xcb_query_tree_reply (dpy, w->tree_cookie, &err);
1270     if (!tree) {
1271         Print_X_Error (dpy, err);
1272         Fatal_Error ("Can't query window tree.");
1273     }
1274
1275     if (level == 0) {
1276         struct wininfo rw, pw;
1277         rw.window = tree->root;
1278         rw.net_wm_name_cookie = get_net_wm_name (dpy, rw.window);
1279         rw.wm_name_cookie = xcb_icccm_get_wm_name (dpy, rw.window);
1280         pw.window = tree->parent;
1281         pw.net_wm_name_cookie = get_net_wm_name (dpy, pw.window);
1282         pw.wm_name_cookie = xcb_icccm_get_wm_name (dpy, pw.window);
1283         xcb_flush (dpy);
1284
1285         printf ("\n");
1286         printf ("  Root window id: ");
1287         Display_Window_Id (&rw, True);
1288         printf ("  Parent window id: ");
1289         Display_Window_Id (&pw, True);
1290     }
1291
1292     num_children = xcb_query_tree_children_length (tree);
1293
1294     if (level == 0  ||  num_children > 0) {
1295         printf ("     ");
1296         for (j = 0; j < level; j++) printf ("   ");
1297         printf ("%d child%s%s\n", num_children, num_children == 1 ? "" : "ren",
1298                 num_children ? ":" : ".");
1299     }
1300
1301     if (num_children > 0) {
1302         xcb_window_t *child_list = xcb_query_tree_children (tree);
1303         struct wininfo *children
1304             = calloc (num_children, sizeof(struct wininfo));
1305
1306         if (children == NULL)
1307             Fatal_Error ("Failed to allocate memory in display_tree_info");
1308
1309         for (i = (int)num_children - 1; i >= 0; i--) {
1310             struct wininfo *cw = &children[i];
1311
1312             cw->window = child_list[i];
1313             cw->net_wm_name_cookie = get_net_wm_name (dpy, child_list[i]);
1314             cw->wm_name_cookie = xcb_icccm_get_wm_name (dpy, child_list[i]);
1315             cw->wm_class_cookie = xcb_icccm_get_wm_class (dpy, child_list[i]);
1316             cw->geometry_cookie = xcb_get_geometry (dpy, child_list[i]);
1317             cw->trans_coords_cookie = xcb_translate_coordinates
1318                 (dpy, child_list[i], tree->root, 0, 0);
1319             if (recurse)
1320                 cw->tree_cookie = xcb_query_tree (dpy, child_list[i]);
1321         }
1322         xcb_flush (dpy);
1323
1324         for (i = (int)num_children - 1; i >= 0; i--) {
1325             struct wininfo *cw = &children[i];
1326             Bool got_wm_class = False;
1327             char *instance_name = NULL, *class_name = NULL;
1328             int instance_name_len, class_name_len;
1329 #ifdef USE_XCB_ICCCM
1330             xcb_icccm_get_wm_class_reply_t classhint;
1331 #else
1332             xcb_get_property_reply_t *classprop;
1333 #endif
1334             xcb_get_geometry_reply_t *geometry;
1335
1336             printf ("     ");
1337             for (j = 0; j < level; j++) printf ("   ");
1338             Display_Window_Id (cw, False);
1339             printf (": (");
1340
1341 #ifdef USE_XCB_ICCCM
1342             if (xcb_icccm_get_wm_class_reply (dpy, cw->wm_class_cookie,
1343                                         &classhint, NULL)) {
1344                 got_wm_class = True;
1345                 instance_name = classhint.instance_name;
1346                 class_name = classhint.class_name;
1347                 instance_name_len = strlen(instance_name);
1348                 class_name_len = strlen(class_name);
1349             }
1350 #else
1351             classprop = xcb_get_property_reply
1352                 (dpy, cw->wm_class_cookie, NULL);
1353             if (classprop) {
1354                 if (classprop->type == XCB_ATOM_STRING &&
1355                     classprop->format == 8) {
1356                     int proplen = xcb_get_property_value_length (classprop);
1357
1358                     instance_name = xcb_get_property_value (classprop);
1359                     instance_name_len = strnlen (instance_name, proplen);
1360                     if (instance_name_len < proplen) {
1361                         class_name = instance_name + instance_name_len + 1;
1362                         class_name_len = strnlen
1363                             (class_name, proplen - (instance_name_len + 1));
1364                     } else
1365                         class_name_len = 0;
1366                     got_wm_class = True;
1367                 }
1368                 else
1369                     free (classprop);
1370             }
1371 #endif
1372
1373             if (got_wm_class) {
1374                 if (instance_name)
1375                     printf ("\"%.*s\" ", instance_name_len, instance_name);
1376                 else
1377                     printf ("(none) ");
1378
1379                 if (class_name)
1380                     printf ("\"%.*s\") ",  class_name_len, class_name);
1381                 else
1382                     printf ("(none)) ");
1383
1384 #ifdef USE_XCB_ICCCM
1385                 xcb_icccm_get_wm_class_reply_wipe (&classhint);
1386 #else
1387                 free (classprop);
1388 #endif
1389             } else
1390                 printf (") ");
1391
1392             geometry = xcb_get_geometry_reply(dpy, cw->geometry_cookie, &err);
1393             if (geometry) {
1394                 xcb_translate_coordinates_reply_t *trans_coords;
1395
1396                 printf (" %ux%u+%d+%d", geometry->width, geometry->height,
1397                                         geometry->x, geometry->y);
1398
1399                 trans_coords = xcb_translate_coordinates_reply
1400                     (dpy, cw->trans_coords_cookie, &err);
1401
1402                 if (trans_coords) {
1403                     int16_t abs_x = (int16_t) trans_coords->dst_x;
1404                     int16_t abs_y = (int16_t) trans_coords->dst_y;
1405                     int border = geometry->border_width;
1406
1407                     printf ("  +%d+%d", abs_x - border, abs_y - border);
1408                     free (trans_coords);
1409                 } else if (err) {
1410                     Print_X_Error (dpy, err);
1411                 }
1412
1413                 free (geometry);
1414             } else if (err) {
1415                 Print_X_Error (dpy, err);
1416             }
1417             printf ("\n");
1418
1419             if (recurse)
1420                 display_tree_info_1 (cw, 1, level+1);
1421
1422             wininfo_wipe (cw);
1423         }
1424         free (children);
1425     }
1426
1427     free (tree); /* includes storage for child_list[] */
1428 }
1429
1430
1431 /*
1432  * Display a set of size hints
1433  */
1434 static void
1435 Display_Hints (xcb_size_hints_t *hints)
1436 {
1437     long flags;
1438
1439     flags = hints->flags;
1440
1441     if (flags & XCB_ICCCM_SIZE_HINT_US_POSITION)
1442         printf ("      User supplied location: %s, %s\n",
1443                 xscale (hints->x), yscale (hints->y));
1444
1445     if (flags & XCB_ICCCM_SIZE_HINT_P_POSITION)
1446         printf ("      Program supplied location: %s, %s\n",
1447                 xscale (hints->x), yscale (hints->y));
1448
1449     if (flags & XCB_ICCCM_SIZE_HINT_US_SIZE) {
1450         printf ("      User supplied size: %s by %s\n",
1451                 xscale (hints->width), yscale (hints->height));
1452     }
1453
1454     if (flags & XCB_ICCCM_SIZE_HINT_P_SIZE)
1455         printf ("      Program supplied size: %s by %s\n",
1456                 xscale (hints->width), yscale (hints->height));
1457
1458     if (flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)
1459         printf ("      Program supplied minimum size: %s by %s\n",
1460                 xscale (hints->min_width), yscale (hints->min_height));
1461
1462     if (flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)
1463         printf ("      Program supplied maximum size: %s by %s\n",
1464                 xscale (hints->max_width), yscale (hints->max_height));
1465
1466     if (flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) {
1467         printf ("      Program supplied base size: %s by %s\n",
1468                 xscale (hints->base_width), yscale (hints->base_height));
1469     }
1470
1471     if (flags & XCB_ICCCM_SIZE_HINT_P_RESIZE_INC) {
1472         printf ("      Program supplied x resize increment: %s\n",
1473                 xscale (hints->width_inc));
1474         printf ("      Program supplied y resize increment: %s\n",
1475                 yscale (hints->height_inc));
1476         if (hints->width_inc != 0 && hints->height_inc != 0) {
1477             if (flags & XCB_ICCCM_SIZE_HINT_US_SIZE)
1478                 printf ("      User supplied size in resize increments:  %s by %s\n",
1479                         (xscale (hints->width / hints->width_inc)),
1480                         (yscale (hints->height / hints->height_inc)));
1481             if (flags & XCB_ICCCM_SIZE_HINT_P_SIZE)
1482                 printf ("      Program supplied size in resize increments:  %s by %s\n",
1483                         (xscale (hints->width / hints->width_inc)),
1484                         (yscale (hints->height / hints->height_inc)));
1485             if (flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)
1486                 printf ("      Program supplied minimum size in resize increments: %s by %s\n",
1487                         xscale (hints->min_width / hints->width_inc), yscale (hints->min_height / hints->height_inc));
1488             if (flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE)
1489                 printf ("      Program supplied base size in resize increments:  %s by %s\n",
1490                         (xscale (hints->base_width / hints->width_inc)),
1491                         (yscale (hints->base_height / hints->height_inc)));
1492         }
1493     }
1494
1495     if (flags & XCB_ICCCM_SIZE_HINT_P_ASPECT) {
1496         printf ("      Program supplied min aspect ratio: %s/%s\n",
1497                 xscale (hints->min_aspect_num), yscale (hints->min_aspect_den));
1498         printf ("      Program supplied max aspect ratio: %s/%s\n",
1499                 xscale (hints->max_aspect_num), yscale (hints->max_aspect_den));
1500     }
1501
1502     if (flags & XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY) {
1503         printf ("      Program supplied window gravity: %s\n",
1504                 Lookup (hints->win_gravity, _gravities));
1505     }
1506 }
1507
1508
1509 /*
1510  * Display Size Hints info
1511  */
1512 static void
1513 Display_Size_Hints (struct wininfo *w)
1514 {
1515     xcb_size_hints_t hints;
1516
1517     printf ("\n");
1518     if (!fetch_normal_hints (w, &hints))
1519         printf ("  No normal window size hints defined\n");
1520     else {
1521         printf ("  Normal window size hints:\n");
1522         Display_Hints (&hints);
1523     }
1524
1525     if (!xcb_icccm_get_wm_size_hints_reply (dpy, w->zoom_cookie, &hints, NULL))
1526         printf ("  No zoom window size hints defined\n");
1527     else {
1528         printf ("  Zoom window size hints:\n");
1529         Display_Hints (&hints);
1530     }
1531 }
1532
1533
1534 static void
1535 Display_Window_Shape (xcb_window_t window)
1536 {
1537     const xcb_query_extension_reply_t *shape_query;
1538     xcb_shape_query_extents_cookie_t extents_cookie;
1539     xcb_shape_query_extents_reply_t *extents;
1540
1541     shape_query = xcb_get_extension_data (dpy, &xcb_shape_id);
1542     if (!shape_query->present)
1543         return;
1544
1545     printf ("\n");
1546
1547     extents_cookie = xcb_shape_query_extents (dpy, window);
1548     extents = xcb_shape_query_extents_reply (dpy, extents_cookie, &err);
1549
1550     if (!extents) {
1551         if (err)
1552             Print_X_Error (dpy, err);
1553         else
1554         {
1555             printf ("  No window shape defined\n");
1556             printf ("  No border shape defined\n");
1557         }
1558         return;
1559     }
1560
1561     if (!extents->bounding_shaped)
1562         printf ("  No window shape defined\n");
1563     else {
1564         printf ("  Window shape extents:  %sx%s",
1565                 xscale (extents->bounding_shape_extents_width),
1566                 yscale (extents->bounding_shape_extents_height));
1567         printf ("+%s+%s\n",
1568                 xscale (extents->bounding_shape_extents_x),
1569                 yscale (extents->bounding_shape_extents_y));
1570     }
1571     if (!extents->clip_shaped)
1572         printf ("  No border shape defined\n");
1573     else {
1574         printf ("  Border shape extents:  %sx%s",
1575                 xscale (extents->clip_shape_extents_width),
1576                 yscale (extents->clip_shape_extents_height));
1577         printf ("+%s+%s\n",
1578                 xscale (extents->clip_shape_extents_x),
1579                 yscale (extents->clip_shape_extents_y));
1580     }
1581
1582     free (extents);
1583 }
1584
1585 /*
1586  * Display Window Manager Info
1587  *
1588  * Requires wininfo members initialized:
1589  *   window, hints_cookie
1590  */
1591 static const binding _state_hints[] = {
1592         { XCB_ICCCM_WM_STATE_WITHDRAWN, "Withdrawn State" },
1593         { XCB_ICCCM_WM_STATE_NORMAL, "Normal State" },
1594         { XCB_ICCCM_WM_STATE_ICONIC, "Iconic State" },
1595 /* xwininfo previously also reported the ZoomState & InactiveState,
1596    but ICCCM declared those obsolete long ago */
1597         { 0, NULL } };
1598
1599 #ifndef USE_XCB_ICCCM
1600 static Bool
1601 wm_hints_reply (xcb_connection_t *dpy, xcb_get_property_cookie_t cookie,
1602                 wm_hints_t *hints_return, xcb_generic_error_t **err)
1603 {
1604     xcb_get_property_reply_t *prop = xcb_get_property_reply (dpy, cookie, err);
1605     int length;
1606
1607     if (!prop || (prop->type != XCB_ATOM_WM_HINTS) || (prop->format != 32)) {
1608         free (prop);
1609         return False;
1610     }
1611
1612     memset (hints_return, 0, sizeof(wm_hints_t));
1613
1614     length = xcb_get_property_value_length(prop);
1615     if (length > sizeof(wm_hints_t))
1616         length = sizeof(wm_hints_t);
1617     memcpy (hints_return, xcb_get_property_value (prop), length);
1618
1619     free (prop);
1620     return True;
1621 }
1622
1623 #define xcb_icccm_get_wm_hints_reply wm_hints_reply
1624 #endif
1625
1626 static void
1627 Display_WM_Info (struct wininfo *w)
1628 {
1629     xcb_icccm_wm_hints_t wmhints;
1630     long flags;
1631     xcb_get_property_reply_t *prop;
1632     int i;
1633
1634     printf ("\n");
1635     if (!xcb_icccm_get_wm_hints_reply(dpy, w->hints_cookie, &wmhints, &err))
1636     {
1637         printf ("  No window manager hints defined\n");
1638         if (err)
1639             Print_X_Error (dpy, err);
1640         flags = 0;
1641     } else
1642         flags = wmhints.flags;
1643
1644     printf ("  Window manager hints:\n");
1645
1646     if (flags & XCB_ICCCM_WM_HINT_INPUT)
1647         printf ("      Client accepts input or input focus: %s\n",
1648                 Lookup (wmhints.input, _bool));
1649
1650     if (flags & XCB_ICCCM_WM_HINT_ICON_WINDOW) {
1651         struct wininfo iw;
1652         iw.window = wmhints.icon_window;
1653         iw.net_wm_name_cookie = get_net_wm_name (dpy, iw.window);
1654         iw.wm_name_cookie = xcb_icccm_get_wm_name (dpy, iw.window);
1655
1656         printf ("      Icon window id: ");
1657         Display_Window_Id (&iw, True);
1658     }
1659
1660     if (flags & XCB_ICCCM_WM_HINT_ICON_POSITION)
1661         printf ("      Initial icon position: %s, %s\n",
1662                 xscale (wmhints.icon_x), yscale (wmhints.icon_y));
1663
1664     if (flags & XCB_ICCCM_WM_HINT_STATE)
1665         printf ("      Initial state is %s\n",
1666                 Lookup (wmhints.initial_state, _state_hints));
1667
1668     if (atom_net_wm_desktop) {
1669         prop = xcb_get_property_reply (dpy, w->wm_desktop_cookie, NULL);
1670         if (prop && (prop->type != XCB_NONE)) {
1671             uint32_t *desktop = xcb_get_property_value (prop);
1672             if (*desktop == 0xFFFFFFFF) {
1673                 printf ("      Displayed on all desktops\n");
1674             } else {
1675                 printf ("      Displayed on desktop %d\n", *desktop);
1676             }
1677         }
1678         free (prop);
1679     }
1680
1681     if (atom_net_wm_window_type) {
1682         prop = xcb_get_property_reply (dpy, w->wm_window_type_cookie,
1683                                        NULL);
1684         if (prop && (prop->type != XCB_NONE) && (prop->value_len > 0)) {
1685             xcb_atom_t *atoms = xcb_get_property_value (prop);
1686             int atom_count = prop->value_len;
1687
1688             if (atom_count > 0) {
1689                 printf ("      Window type:\n");
1690                 for (i = 0; i < atom_count; i++) {
1691                     const char *atom_name = Get_Atom_Name (dpy, atoms[i]);
1692
1693                     if (atom_name) {
1694                         print_friendly_name ("          %s\n", atom_name,
1695                                              "_NET_WM_WINDOW_TYPE_");
1696                     } else {
1697                         printf ("          (unresolvable ATOM 0x%x)\n",
1698                                 atoms[i]);
1699                     }
1700                 }
1701             }
1702         }
1703         free (prop);
1704     }
1705
1706     if (atom_net_wm_state) {
1707         prop = xcb_get_property_reply (dpy, w->wm_state_cookie, NULL);
1708         if (prop && (prop->type != XCB_NONE) && (prop->value_len > 0)) {
1709             xcb_atom_t *atoms = xcb_get_property_value (prop);
1710             int atom_count = prop->value_len;
1711
1712             if (atom_count > 0) {
1713                 printf ("      Window state:\n");
1714                 for (i = 0; i < atom_count; i++) {
1715                     const char *atom_name = Get_Atom_Name (dpy, atoms[i]);
1716
1717                     if (atom_name) {
1718                         print_friendly_name ("          %s\n", atom_name,
1719                                              "_NET_WM_STATE_");
1720                     } else {
1721                         printf ("          (unresolvable ATOM 0x%x)\n",
1722                                 atoms[i]);
1723                     }
1724                 }
1725             }
1726         }
1727         free (prop);
1728     }
1729
1730     if (atom_net_wm_pid) {
1731         printf ("      Process id: ");
1732         prop = xcb_get_property_reply (dpy, w->wm_pid_cookie, NULL);
1733         if (prop && (prop->type == XCB_ATOM_CARDINAL)) {
1734             uint32_t *pid = xcb_get_property_value (prop);
1735             printf ("%d", *pid);
1736         } else {
1737             printf ("(unknown)");
1738         }
1739         free (prop);
1740
1741         prop = xcb_get_property_reply (dpy, w->wm_client_machine_cookie, NULL);
1742         if (prop && (prop->type == XCB_ATOM_STRING)) {
1743             const char *hostname = xcb_get_property_value (prop);
1744             int hostname_len = xcb_get_property_value_length (prop);
1745             printf (" on host %.*s", hostname_len, hostname);
1746         }
1747         printf ("\n");
1748         free (prop);
1749     }
1750
1751     if (atom_net_frame_extents) {
1752         prop = xcb_get_property_reply (dpy, w->frame_extents_cookie, NULL);
1753         if (prop && (prop->type == XCB_ATOM_CARDINAL)
1754             && (prop->value_len == 4)) {
1755             uint32_t *extents = xcb_get_property_value (prop);
1756
1757             printf ("      Frame extents: %d, %d, %d, %d\n",
1758                     extents[0], extents[1], extents[2], extents[3]);
1759         }
1760         free (prop);
1761     }
1762 }
1763
1764 /* Frees all members of a wininfo struct, but not the struct itself */
1765 static void
1766 wininfo_wipe (struct wininfo *w)
1767 {
1768     free (w->geometry);
1769     free (w->win_attributes);
1770     free (w->normal_hints);
1771 }
1772
1773 /* Gets UTF-8 encoded EMWH property _NET_WM_NAME for a window */
1774 static xcb_get_property_cookie_t
1775 get_net_wm_name (xcb_connection_t *dpy, xcb_window_t win)
1776 {
1777     if (!atom_net_wm_name)
1778         atom_net_wm_name = Get_Atom (dpy, "_NET_WM_NAME");
1779
1780     if (!atom_utf8_string)
1781         atom_utf8_string = Get_Atom (dpy, "UTF8_STRING");
1782
1783     if (atom_net_wm_name && atom_utf8_string)
1784         return xcb_get_property (dpy, False, win, atom_net_wm_name,
1785                                  atom_utf8_string, 0, BUFSIZ);
1786     else {
1787         xcb_get_property_cookie_t dummy = { 0 };
1788         return dummy;
1789     }
1790 }
1791
1792 /* [Copied from code added by Yang Zhao to xprop/xprop.c]
1793  *
1794  * Validate a string as UTF-8 encoded according to RFC 3629
1795  *
1796  * Simply, a unicode code point (up to 21-bits long) is encoded as follows:
1797  *
1798  *    Char. number range  |        UTF-8 octet sequence
1799  *       (hexadecimal)    |              (binary)
1800  *    --------------------+---------------------------------------------
1801  *    0000 0000-0000 007F | 0xxxxxxx
1802  *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
1803  *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
1804  *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1805  *
1806  * Validation is done left-to-right, and an error condition, if any, refers to
1807  * only the left-most problem in the string.
1808  *
1809  * Return values:
1810  *   UTF8_VALID: Valid UTF-8 encoded string
1811  *   UTF8_OVERLONG: Using more bytes than needed for a code point
1812  *   UTF8_SHORT_TAIL: Not enough bytes in a multi-byte sequence
1813  *   UTF8_LONG_TAIL: Too many bytes in a multi-byte sequence
1814  *   UTF8_FORBIDDEN_VALUE: Forbidden prefix or code point outside 0x10FFFF
1815  */
1816 #define UTF8_VALID 0
1817 #define UTF8_FORBIDDEN_VALUE 1
1818 #define UTF8_OVERLONG 2
1819 #define UTF8_SHORT_TAIL 3
1820 #define UTF8_LONG_TAIL 4
1821 static int
1822 is_valid_utf8 (const char *string, int len)
1823 {
1824     unsigned long codepoint;
1825     int rem, i;
1826     unsigned char c;
1827
1828     rem = 0;
1829     for (i = 0; i < len; i++) {
1830         c = (unsigned char) string[i];
1831
1832         /* Order of type check:
1833          *   - Single byte code point
1834          *   - Non-starting byte of multi-byte sequence
1835          *   - Start of 2-byte sequence
1836          *   - Start of 3-byte sequence
1837          *   - Start of 4-byte sequence
1838          */
1839         if (!(c & 0x80)) {
1840             if (rem > 0) return UTF8_SHORT_TAIL;
1841             rem = 0;
1842             codepoint = c;
1843         } else if ((c & 0xC0) == 0x80) {
1844             if (rem == 0) return UTF8_LONG_TAIL;
1845             rem--;
1846             codepoint |= (c & 0x3F) << (rem * 6);
1847             if (codepoint == 0) return UTF8_OVERLONG;
1848         } else if ((c & 0xE0) == 0xC0) {
1849             if (rem > 0) return UTF8_SHORT_TAIL;
1850             rem = 1;
1851             codepoint = (c & 0x1F) << 6;
1852             if (codepoint == 0) return UTF8_OVERLONG;
1853         } else if ((c & 0xF0) == 0xE0) {
1854             if (rem > 0) return UTF8_SHORT_TAIL;
1855             rem = 2;
1856             codepoint = (c & 0x0F) << 12;
1857         } else if ((c & 0xF8) == 0xF0) {
1858             if (rem > 0) return UTF8_SHORT_TAIL;
1859             rem = 3;
1860             codepoint = (c & 0x07) << 18;
1861             if (codepoint > 0x10FFFF) return UTF8_FORBIDDEN_VALUE;
1862         } else
1863             return UTF8_FORBIDDEN_VALUE;
1864     }
1865
1866     return UTF8_VALID;
1867 }
1868
1869 /*
1870  * Converts a UTF-8 encoded string to the current locale encoding,
1871  * if possible, and prints it, with prefix before and suffix after.
1872  * Length of the string is specified in bytes, or -1 for going until '\0'
1873  */
1874 static void
1875 print_utf8 (const char *prefix, char *u8str, size_t length, const char *suffix)
1876 {
1877     size_t inlen = length;
1878
1879     if (inlen < 0) {
1880         inlen = strlen (u8str);
1881     }
1882
1883     if (is_valid_utf8 (u8str, inlen) != UTF8_VALID) {
1884         printf (" (invalid UTF8_STRING)");
1885         return;
1886     }
1887
1888     if (strcmp (user_encoding, "UTF-8") == 0) {
1889         /* Don't need to convert */
1890         printf ("%s", prefix);
1891         fwrite (u8str, 1, inlen, stdout);
1892         printf ("%s", suffix);
1893         return;
1894     }
1895
1896 #ifdef HAVE_ICONV
1897     if (!iconv_from_utf8) {
1898         iconv_from_utf8 = iconv_open (user_encoding, "UTF-8");
1899     }
1900
1901     if (iconv_from_utf8 != (iconv_t) -1) {
1902         Bool done = True;
1903         char *inp = u8str;
1904         char convbuf[BUFSIZ];
1905         int convres;
1906
1907         printf ("%s", prefix);
1908         do {
1909             char *outp = convbuf;
1910             size_t outlen = sizeof(convbuf);
1911
1912             convres = iconv (iconv_from_utf8, &inp, &inlen, &outp, &outlen);
1913
1914             if ((convres == -1) && (errno == E2BIG)) {
1915                 done = False;
1916                 convres = 0;
1917             }
1918
1919             if (convres == 0) {
1920                 fwrite (convbuf, 1, sizeof(convbuf) - outlen, stdout);
1921             } else {
1922                 printf (" (failure in conversion from UTF8_STRING to %s)",
1923                         user_encoding);
1924             }
1925         } while (!done);
1926         printf ("%s", suffix);
1927     } else {
1928         printf (" (can't load iconv conversion for UTF8_STRING to %s)",
1929                 user_encoding);
1930     }
1931 #else
1932     printf (" (can't convert UTF8_STRING to %s)", user_encoding);
1933 #endif
1934 }
1935
1936 /*
1937  * Takes a string such as an atom name, strips the prefix, converts
1938  * underscores to spaces, lowercases all but the first letter of each word,
1939  * and prints it.
1940  */
1941 static void
1942 print_friendly_name (const char *format, const char *string,
1943                      const char *prefix)
1944 {
1945     const char *name_start = string;
1946     char *lowered_name, *n;
1947     int prefix_len = strlen (prefix);
1948
1949     if (strncmp (name_start, prefix, prefix_len) == 0) {
1950         name_start += prefix_len;
1951     }
1952
1953     lowered_name = strdup (name_start);
1954     if (lowered_name) {
1955         Bool first = True;
1956
1957         for (n = lowered_name ; *n != 0 ; n++) {
1958             if (*n == '_') {
1959                 *n = ' ';
1960                 first = True;
1961             } else if (first) {
1962                 first = False;
1963             } else {
1964                 *n = tolower(*n);
1965             }
1966         }
1967         name_start = lowered_name;
1968     }
1969
1970     printf (format, name_start);
1971     free (lowered_name);
1972 }