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