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