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