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