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