1863f95b1Smrg/*
283283e49Smrg * Copyright (c) 1999, 2010, Oracle and/or its affiliates.
3863f95b1Smrg *
4863f95b1Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5863f95b1Smrg * copy of this software and associated documentation files (the "Software"),
6863f95b1Smrg * to deal in the Software without restriction, including without limitation
7863f95b1Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8863f95b1Smrg * and/or sell copies of the Software, and to permit persons to whom the
9863f95b1Smrg * Software is furnished to do so, subject to the following conditions:
10863f95b1Smrg *
11863f95b1Smrg * The above copyright notice and this permission notice (including the next
12863f95b1Smrg * paragraph) shall be included in all copies or substantial portions of the
13863f95b1Smrg * Software.
14863f95b1Smrg *
15863f95b1Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16863f95b1Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17863f95b1Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18863f95b1Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19863f95b1Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20863f95b1Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21863f95b1Smrg * DEALINGS IN THE SOFTWARE.
22863f95b1Smrg */
23ff7e0accSmrg/*
24ff7e0accSmrg
25ff7e0accSmrgCopyright 1987, 1998  The Open Group
26ff7e0accSmrg
27ff7e0accSmrgPermission to use, copy, modify, distribute, and sell this software and its
28ff7e0accSmrgdocumentation for any purpose is hereby granted without fee, provided that
29ff7e0accSmrgthe above copyright notice appear in all copies and that both that
30ff7e0accSmrgcopyright notice and this permission notice appear in supporting
31ff7e0accSmrgdocumentation.
32ff7e0accSmrg
33ff7e0accSmrgThe above copyright notice and this permission notice shall be included
34ff7e0accSmrgin all copies or substantial portions of the Software.
35ff7e0accSmrg
36ff7e0accSmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
37ff7e0accSmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
38ff7e0accSmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
39ff7e0accSmrgOF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
40ff7e0accSmrgHOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
41ff7e0accSmrgINDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
42ff7e0accSmrgFROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
43ff7e0accSmrgNEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
44ff7e0accSmrgWITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
45ff7e0accSmrg
46ff7e0accSmrgExcept as contained in this notice, the name of a copyright holder
47ff7e0accSmrgshall not be used in advertising or otherwise to promote the sale, use
48ff7e0accSmrgor other dealings in this Software without prior written authorization
49ff7e0accSmrgof the copyright holder.
50ff7e0accSmrg
51ff7e0accSmrg*/
52ff7e0accSmrg
53ff7e0accSmrg
54ff7e0accSmrg/*
55ff7e0accSmrg * xwininfo.c	- MIT Project Athena, X Window system window
56ff7e0accSmrg *		  information utility.
57ff7e0accSmrg *
58ff7e0accSmrg *
59ff7e0accSmrg *	This program will report all relevant information
60ff7e0accSmrg *	about a specific window.
61ff7e0accSmrg *
62ff7e0accSmrg *  Author:	Mark Lillibridge, MIT Project Athena
63ff7e0accSmrg *		16-Jun-87
64ff7e0accSmrg */
65ff7e0accSmrg
66ff7e0accSmrg#include "config.h"
67863f95b1Smrg
68863f95b1Smrg#include <xcb/xcb.h>
69863f95b1Smrg#include <xcb/xproto.h>
70863f95b1Smrg#ifdef USE_XCB_ICCCM
71863f95b1Smrg# include <xcb/xcb_icccm.h>
72ff7e0accSmrg#endif
73863f95b1Smrg#include <xcb/shape.h>
74863f95b1Smrg
75ff7e0accSmrg#include <stdio.h>
76ff7e0accSmrg#include <stdlib.h>
77863f95b1Smrg#include <string.h>
78863f95b1Smrg#include <locale.h>
79863f95b1Smrg#include <langinfo.h>
80863f95b1Smrg#ifdef HAVE_ICONV
81863f95b1Smrg# include <iconv.h>
82863f95b1Smrg#endif
83863f95b1Smrg#include <ctype.h>
84863f95b1Smrg#include <errno.h>
85863f95b1Smrg
86863f95b1Smrg#ifndef HAVE_STRNLEN
87863f95b1Smrg#include "strnlen.h"
88863f95b1Smrg#endif
89ff7e0accSmrg
90ff7e0accSmrg/* Include routines to handle parsing defaults */
91ff7e0accSmrg#include "dsimple.h"
92ff7e0accSmrg
93ff7e0accSmrgtypedef struct {
94863f95b1Smrg    long code;
95863f95b1Smrg    const char *name;
96ff7e0accSmrg} binding;
97ff7e0accSmrg
98863f95b1Smrg#ifndef USE_XCB_ICCCM
99863f95b1Smrg/* Once xcb-icccm's API is stable, this should be replaced by
100863f95b1Smrg   xcb_size_hints_t & xcb_size_hints_flags_t */
101863f95b1Smrgtypedef struct {
102863f95b1Smrg  /** User specified flags */
103863f95b1Smrg  uint32_t flags;
104863f95b1Smrg  /** User-specified position */
105863f95b1Smrg  int32_t x, y;
106863f95b1Smrg  /** User-specified size */
107863f95b1Smrg  int32_t width, height;
108863f95b1Smrg  /** Program-specified minimum size */
109863f95b1Smrg  int32_t min_width, min_height;
110863f95b1Smrg  /** Program-specified maximum size */
111863f95b1Smrg  int32_t max_width, max_height;
112863f95b1Smrg  /** Program-specified resize increments */
113863f95b1Smrg  int32_t width_inc, height_inc;
114863f95b1Smrg  /** Program-specified minimum aspect ratios */
115863f95b1Smrg  int32_t min_aspect_num, min_aspect_den;
116863f95b1Smrg  /** Program-specified maximum aspect ratios */
117863f95b1Smrg  int32_t max_aspect_num, max_aspect_den;
118863f95b1Smrg  /** Program-specified base size */
119863f95b1Smrg  int32_t base_width, base_height;
120863f95b1Smrg  /** Program-specified window gravity */
121863f95b1Smrg  uint32_t win_gravity;
122863f95b1Smrg} wm_size_hints_t;
123863f95b1Smrg
124863f95b1Smrg# define xcb_size_hints_t wm_size_hints_t
125863f95b1Smrg
126863f95b1Smrgtypedef struct {
127863f95b1Smrg  /** Marks which fields in this structure are defined */
128863f95b1Smrg  int32_t flags;
129863f95b1Smrg  /** Does this application rely on the window manager to get keyboard
130863f95b1Smrg      input? */
131863f95b1Smrg  uint32_t input;
132863f95b1Smrg  /** See below */
133863f95b1Smrg  int32_t initial_state;
134863f95b1Smrg  /** Pixmap to be used as icon */
135863f95b1Smrg  xcb_pixmap_t icon_pixmap;
136863f95b1Smrg  /** Window to be used as icon */
137863f95b1Smrg  xcb_window_t icon_window;
138863f95b1Smrg  /** Initial position of icon */
139863f95b1Smrg  int32_t icon_x, icon_y;
140863f95b1Smrg  /** Icon mask bitmap */
141863f95b1Smrg  xcb_pixmap_t icon_mask;
142863f95b1Smrg  /* Identifier of related window group */
143863f95b1Smrg  xcb_window_t window_group;
144863f95b1Smrg} wm_hints_t;
145863f95b1Smrg
146853aa076Smrg#define xcb_icccm_wm_hints_t wm_hints_t
147863f95b1Smrg
148863f95b1Smrgenum {
149863f95b1Smrg  /* xcb_size_hints_flags_t */
150853aa076Smrg  XCB_ICCCM_SIZE_HINT_US_POSITION = 1 << 0,
151853aa076Smrg  XCB_ICCCM_SIZE_HINT_US_SIZE = 1 << 1,
152853aa076Smrg  XCB_ICCCM_SIZE_HINT_P_POSITION = 1 << 2,
153853aa076Smrg  XCB_ICCCM_SIZE_HINT_P_SIZE = 1 << 3,
154853aa076Smrg  XCB_ICCCM_SIZE_HINT_P_MIN_SIZE = 1 << 4,
155853aa076Smrg  XCB_ICCCM_SIZE_HINT_P_MAX_SIZE = 1 << 5,
156853aa076Smrg  XCB_ICCCM_SIZE_HINT_P_RESIZE_INC = 1 << 6,
157853aa076Smrg  XCB_ICCCM_SIZE_HINT_P_ASPECT = 1 << 7,
158853aa076Smrg  XCB_ICCCM_SIZE_HINT_BASE_SIZE = 1 << 8,
159853aa076Smrg  XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY = 1 << 9,
160863f95b1Smrg  /* xcb_wm_state_t */
161853aa076Smrg  XCB_ICCCM_WM_STATE_WITHDRAWN = 0,
162853aa076Smrg  XCB_ICCCM_WM_STATE_NORMAL = 1,
163853aa076Smrg  XCB_ICCCM_WM_STATE_ICONIC = 3,
164863f95b1Smrg  /* xcb_wm_t */
165853aa076Smrg  XCB_ICCCM_WM_HINT_INPUT = (1L << 0),
166853aa076Smrg  XCB_ICCCM_WM_HINT_STATE = (1L << 1),
167853aa076Smrg  XCB_ICCCM_WM_HINT_ICON_PIXMAP = (1L << 2),
168853aa076Smrg  XCB_ICCCM_WM_HINT_ICON_WINDOW = (1L << 3),
169853aa076Smrg  XCB_ICCCM_WM_HINT_ICON_POSITION = (1L << 4),
170853aa076Smrg  XCB_ICCCM_WM_HINT_ICON_MASK = (1L << 5),
171853aa076Smrg  XCB_ICCCM_WM_HINT_WINDOW_GROUP = (1L << 6),
172853aa076Smrg  XCB_ICCCM_WM_HINT_X_URGENCY = (1L << 8)
173863f95b1Smrg};
174863f95b1Smrg
175863f95b1Smrg/* Once xcb-icccm's API is stable, these should be replaced by calls to it */
176863f95b1Smrg# define GET_TEXT_PROPERTY(Dpy, Win, Atom) \
177863f95b1Smrg    xcb_get_property (Dpy, False, Win, Atom, XCB_GET_PROPERTY_TYPE_ANY, 0, BUFSIZ)
178853aa076Smrg# define xcb_icccm_get_wm_name(Dpy, Win) \
179853aa076Smrg    GET_TEXT_PROPERTY(Dpy, Win, XCB_ATOM_WM_NAME)
180863f95b1Smrg
181853aa076Smrg# define xcb_icccm_get_wm_class(Dpy, Win) \
182863f95b1Smrg    xcb_get_property (Dpy, False, Win, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, BUFSIZ)
183853aa076Smrg# define xcb_icccm_get_wm_hints(Dpy, Win) \
184863f95b1Smrg    xcb_get_property(Dpy, False, Win, XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 0, 9)
185863f95b1Smrg
186853aa076Smrg# define xcb_icccm_get_wm_size_hints(Dpy, Win, Atom) \
187863f95b1Smrg    xcb_get_property (Dpy, False, Win, Atom, XCB_ATOM_WM_SIZE_HINTS, 0, 18)
188853aa076Smrg# define xcb_icccm_get_wm_normal_hints(Dpy, Win) \
189853aa076Smrg    xcb_icccm_get_wm_size_hints(Dpy, Win, XCB_ATOM_WM_NORMAL_HINTS)
190863f95b1Smrg#endif
191863f95b1Smrg
192863f95b1Smrg/* Possibly in xcb-emwh in the future? */
193863f95b1Smrgstatic xcb_atom_t atom_net_wm_name, atom_utf8_string;
194863f95b1Smrgstatic xcb_atom_t atom_net_wm_desktop, atom_net_wm_window_type,
195863f95b1Smrg    atom_net_wm_state, atom_net_wm_pid, atom_net_frame_extents;
196863f95b1Smrgstatic xcb_get_property_cookie_t get_net_wm_name (xcb_connection_t *,
197863f95b1Smrg						  xcb_window_t);
198863f95b1Smrg
199863f95b1Smrg/* Information we keep track of for each window to allow prefetching/reusing */
200863f95b1Smrgstruct wininfo {
201863f95b1Smrg    xcb_window_t			window;
202863f95b1Smrg
203863f95b1Smrg    /* cookies for requests we've sent */
204863f95b1Smrg    xcb_get_geometry_cookie_t		geometry_cookie;
205863f95b1Smrg    xcb_get_property_cookie_t		net_wm_name_cookie;
206863f95b1Smrg    xcb_get_property_cookie_t		wm_name_cookie;
207863f95b1Smrg    xcb_get_property_cookie_t		wm_class_cookie;
208863f95b1Smrg    xcb_translate_coordinates_cookie_t	trans_coords_cookie;
209863f95b1Smrg    xcb_query_tree_cookie_t		tree_cookie;
210863f95b1Smrg    xcb_get_window_attributes_cookie_t	attr_cookie;
211863f95b1Smrg    xcb_get_property_cookie_t		normal_hints_cookie;
212863f95b1Smrg    xcb_get_property_cookie_t		hints_cookie;
213863f95b1Smrg    xcb_get_property_cookie_t		wm_desktop_cookie;
214863f95b1Smrg    xcb_get_property_cookie_t		wm_window_type_cookie;
215863f95b1Smrg    xcb_get_property_cookie_t		wm_state_cookie;
216863f95b1Smrg    xcb_get_property_cookie_t		wm_pid_cookie;
217863f95b1Smrg    xcb_get_property_cookie_t		wm_client_machine_cookie;
218863f95b1Smrg    xcb_get_property_cookie_t		frame_extents_cookie;
219863f95b1Smrg    xcb_get_property_cookie_t		zoom_cookie;
220863f95b1Smrg
221863f95b1Smrg    /* cached results from previous requests */
222863f95b1Smrg    xcb_get_geometry_reply_t *		geometry;
223863f95b1Smrg    xcb_get_window_attributes_reply_t *	win_attributes;
224863f95b1Smrg    xcb_size_hints_t *			normal_hints;
225863f95b1Smrg};
226863f95b1Smrg
227863f95b1Smrgstatic void scale_init (xcb_screen_t *scrn);
228863f95b1Smrgstatic char *nscale (int, int, int, char *, size_t);
229863f95b1Smrgstatic char *xscale (int);
230863f95b1Smrgstatic char *yscale (int);
231863f95b1Smrgstatic char *bscale (int);
232863f95b1Smrgint main (int, char **);
233863f95b1Smrgstatic const char *LookupL (long, const binding *);
234863f95b1Smrgstatic const char *Lookup (int, const binding *);
235863f95b1Smrgstatic void Display_Window_Id (struct wininfo *, Bool);
236863f95b1Smrgstatic void Display_Stats_Info (struct wininfo *);
237863f95b1Smrgstatic void Display_Bits_Info (struct wininfo *);
238863f95b1Smrgstatic void Display_Event_Mask (long);
239863f95b1Smrgstatic void Display_Events_Info (struct wininfo *);
240863f95b1Smrgstatic void Display_Tree_Info (struct wininfo *, int);
241863f95b1Smrgstatic void display_tree_info_1 (struct wininfo *, int, int);
242863f95b1Smrgstatic void Display_Hints (xcb_size_hints_t *);
243863f95b1Smrgstatic void Display_Size_Hints (struct wininfo *);
244863f95b1Smrgstatic void Display_Window_Shape (xcb_window_t);
245863f95b1Smrgstatic void Display_WM_Info (struct wininfo *);
246863f95b1Smrgstatic void wininfo_wipe (struct wininfo *);
247863f95b1Smrg
248cf2cd791Smrgstatic Bool window_id_format_dec = False;
249863f95b1Smrg
250863f95b1Smrg#ifdef HAVE_ICONV
251863f95b1Smrgstatic iconv_t iconv_from_utf8;
252863f95b1Smrg#endif
253863f95b1Smrgstatic const char *user_encoding;
254857c29c5Skamilstatic void print_utf8 (const char *, char *, size_t, const char *);
255cf2cd791Smrgstatic char *get_friendly_name (const char *, const char *);
256863f95b1Smrg
257863f95b1Smrgstatic xcb_connection_t *dpy;
258863f95b1Smrgstatic xcb_screen_t *screen;
259863f95b1Smrgstatic xcb_generic_error_t *err;
260ff7e0accSmrg
261ff7e0accSmrg#ifndef HAVE_STRLCAT
262863f95b1Smrgstatic size_t strlcat (char *dst, const char *src, size_t dstsize)
263ff7e0accSmrg{
264863f95b1Smrg    size_t sd = strlen (dst);
265863f95b1Smrg    size_t ss = strlen (src);
266ff7e0accSmrg    size_t s = sd + ss;
267863f95b1Smrg
268ff7e0accSmrg    if (s < dstsize) {
269863f95b1Smrg	strcpy (dst + sd, src);
270ff7e0accSmrg    } else {
271863f95b1Smrg	strncpy (dst + sd, src, dstsize-sd-1);
272ff7e0accSmrg	dst[dstsize] = '\0';
273ff7e0accSmrg    }
274ff7e0accSmrg    return s;
275ff7e0accSmrg}
276ff7e0accSmrg#endif
277ff7e0accSmrg
278ff7e0accSmrg/*
279ff7e0accSmrg * Report the syntax for calling xwininfo:
280ff7e0accSmrg */
28196eca5d1Smrg_X_NORETURN _X_COLD
282853aa076Smrgstatic void
283863f95b1Smrgusage (void)
284ff7e0accSmrg{
285ff7e0accSmrg    fprintf (stderr,
286863f95b1Smrg	     "usage:  %s [-options ...]\n\n"
287863f95b1Smrg	     "where options include:\n"
288e8f4a63fSmrg	     "    -help                 print this message\n"
289e8f4a63fSmrg	     "    -version              print version message\n"
290e8f4a63fSmrg	     "    -d[isplay] <host:dpy> X server to contact\n"
291e8f4a63fSmrg	     "    -root                 use the root window\n"
292e8f4a63fSmrg	     "    -id <wdid>            use the window with the specified id\n"
293e8f4a63fSmrg	     "    -name <wdname>        use the window with the specified name\n"
294e8f4a63fSmrg	     "    -int                  print window id in decimal\n"
295e8f4a63fSmrg	     "    -children             print parent and child identifiers\n"
296e8f4a63fSmrg	     "    -tree                 print children identifiers recursively\n"
297e8f4a63fSmrg	     "    -stats                print window geometry [DEFAULT]\n"
298e8f4a63fSmrg	     "    -bits                 print window pixel information\n"
299e8f4a63fSmrg	     "    -events               print events selected for on window\n"
300e8f4a63fSmrg	     "    -size                 print size hints\n"
301e8f4a63fSmrg	     "    -wm                   print window manager hints\n"
302e8f4a63fSmrg	     "    -shape                print shape extents\n"
303e8f4a63fSmrg	     "    -frame                don't ignore window manager frames\n"
304e8f4a63fSmrg	     "    -english              print sizes in english units\n"
305e8f4a63fSmrg	     "    -metric               print sizes in metric units\n"
306e8f4a63fSmrg	     "    -all                  -tree, -stats, -bits, -events, -wm, -size, -shape\n"
307863f95b1Smrg	     "\n",
308863f95b1Smrg	     program_name);
309ff7e0accSmrg    exit (1);
310ff7e0accSmrg}
311ff7e0accSmrg
312ff7e0accSmrg/*
313ff7e0accSmrg * pixel to inch, metric converter.
314ff7e0accSmrg * Hacked in by Mark W. Eichin <eichin@athena> [eichin:19880619.1509EST]
315ff7e0accSmrg *
316ff7e0accSmrg * Simply put: replace the old numbers with string print calls.
317ff7e0accSmrg * Returning a local string is ok, since we only ever get called to
318ff7e0accSmrg * print one x and one y, so as long as they don't collide, they're
319ff7e0accSmrg * fine. This is not meant to be a general purpose routine.
320ff7e0accSmrg *
321ff7e0accSmrg */
322ff7e0accSmrg
323863f95b1Smrgstatic int xp = 0, xmm = 0;
324863f95b1Smrgstatic int yp = 0, ymm = 0;
325863f95b1Smrgstatic int bp = 0, bmm = 0;
326ff7e0accSmrgstatic int english = 0, metric = 0;
327ff7e0accSmrg
328ff7e0accSmrgstatic void
329cf2cd791Smrgscale_init (xcb_screen_t *scale_screen)
330ff7e0accSmrg{
331cf2cd791Smrg    xp = scale_screen->width_in_pixels;
332cf2cd791Smrg    yp = scale_screen->height_in_pixels;
333cf2cd791Smrg    xmm = scale_screen->width_in_millimeters;
334cf2cd791Smrg    ymm = scale_screen->height_in_millimeters;
335863f95b1Smrg    bp  = xp  + yp;
336863f95b1Smrg    bmm = xmm + ymm;
337ff7e0accSmrg}
338ff7e0accSmrg
339ff7e0accSmrg#define MILE (5280*12)
340ff7e0accSmrg#define YARD (3*12)
341ff7e0accSmrg#define FOOT (12)
342ff7e0accSmrg
343ff7e0accSmrgstatic char *
344863f95b1Smrgnscale (int n, int np, int nmm, char *nbuf, size_t nbufsize)
345ff7e0accSmrg{
346863f95b1Smrg    snprintf (nbuf, nbufsize, "%d", n);
347863f95b1Smrg
348ff7e0accSmrg    if (metric||english) {
34983283e49Smrg	int s = strlcat (nbuf, " (", nbufsize);
350ff7e0accSmrg
351ff7e0accSmrg	if (metric) {
352863f95b1Smrg	    snprintf (nbuf+s, nbufsize-s, "%.2f mm%s",
353863f95b1Smrg		      ((double) n) * nmm/np , english ? "; " : "");
354ff7e0accSmrg	}
355ff7e0accSmrg	if (english) {
356ff7e0accSmrg	    double inch_frac;
357ff7e0accSmrg	    Bool printed_anything = False;
35883283e49Smrg	    int inr;
359ff7e0accSmrg
360ff7e0accSmrg	    inch_frac = ((double) n)*(nmm/25.4)/np;
361ff7e0accSmrg	    inr = (int)inch_frac;
362ff7e0accSmrg	    inch_frac -= (double)inr;
363ff7e0accSmrg	    if (inr >= MILE) {
36483283e49Smrg		int mi = inr/MILE;
365ff7e0accSmrg		inr %= MILE;
366863f95b1Smrg		s = strlen (nbuf);
367863f95b1Smrg		snprintf (nbuf+s, nbufsize-s, "%d %s(?!?)",
368863f95b1Smrg			  mi, (mi == 1) ? "mile" : "miles");
369ff7e0accSmrg		printed_anything = True;
370ff7e0accSmrg	    }
371ff7e0accSmrg	    if (inr >= YARD) {
37283283e49Smrg		int yar = inr/YARD;
373ff7e0accSmrg		inr %= YARD;
374ff7e0accSmrg		if (printed_anything)
375863f95b1Smrg		    strlcat (nbuf, ", ", nbufsize);
376863f95b1Smrg		s = strlen (nbuf);
377863f95b1Smrg		snprintf (nbuf+s, nbufsize-s, "%d %s",
378863f95b1Smrg			 yar, (yar==1) ? "yard" : "yards");
379ff7e0accSmrg		printed_anything = True;
380ff7e0accSmrg	    }
381ff7e0accSmrg	    if (inr >= FOOT) {
38283283e49Smrg		int ft = inr/FOOT;
383ff7e0accSmrg		inr  %= FOOT;
384ff7e0accSmrg		if (printed_anything)
385863f95b1Smrg		    strlcat (nbuf, ", ", nbufsize);
386863f95b1Smrg		s = strlen (nbuf);
387863f95b1Smrg		snprintf (nbuf+s, nbufsize-s, "%d %s",
388863f95b1Smrg			 ft, (ft==1) ? "foot" : "feet");
389ff7e0accSmrg		printed_anything = True;
390ff7e0accSmrg	    }
391ff7e0accSmrg	    if (!printed_anything || inch_frac != 0.0 || inr != 0) {
392ff7e0accSmrg		if (printed_anything)
393863f95b1Smrg		    strlcat (nbuf, ", ", nbufsize);
394863f95b1Smrg		s = strlen (nbuf);
395863f95b1Smrg		snprintf (nbuf+s, nbufsize-s, "%.2f inches", inr+inch_frac);
396ff7e0accSmrg	    }
397ff7e0accSmrg	}
398ff7e0accSmrg	strlcat (nbuf, ")", nbufsize);
399ff7e0accSmrg    }
400863f95b1Smrg    return (nbuf);
401863f95b1Smrg}
402863f95b1Smrg
403ff7e0accSmrgstatic char xbuf[BUFSIZ];
404ff7e0accSmrgstatic char *
405863f95b1Smrgxscale (int x)
406ff7e0accSmrg{
407863f95b1Smrg    return (nscale (x, xp, xmm, xbuf, sizeof(xbuf)));
408ff7e0accSmrg}
409ff7e0accSmrg
410ff7e0accSmrgstatic char ybuf[BUFSIZ];
411ff7e0accSmrgstatic char *
412863f95b1Smrgyscale (int y)
413ff7e0accSmrg{
414863f95b1Smrg    return (nscale (y, yp, ymm, ybuf, sizeof(ybuf)));
415ff7e0accSmrg}
416ff7e0accSmrg
417ff7e0accSmrgstatic char bbuf[BUFSIZ];
418ff7e0accSmrgstatic char *
419863f95b1Smrgbscale (int b)
420ff7e0accSmrg{
421863f95b1Smrg    return (nscale (b, bp, bmm, bbuf, sizeof(bbuf)));
422ff7e0accSmrg}
423ff7e0accSmrg
424cf2cd791Smrgstatic const char *
425cf2cd791Smrgwindow_id_str (xcb_window_t id)
426cf2cd791Smrg{
427cf2cd791Smrg    static char str[20];
428cf2cd791Smrg
429cf2cd791Smrg    if (window_id_format_dec)
430cf2cd791Smrg	snprintf (str, sizeof(str), "%u", id);
431cf2cd791Smrg    else
432cf2cd791Smrg	snprintf (str, sizeof(str), "0x%x", id);
433cf2cd791Smrg
434cf2cd791Smrg    return str;
435cf2cd791Smrg}
436cf2cd791Smrg
437ff7e0accSmrg/* end of pixel to inch, metric converter */
438ff7e0accSmrg
439863f95b1Smrgint
440863f95b1Smrgmain (int argc, char **argv)
441ff7e0accSmrg{
442863f95b1Smrg    int tree = 0, stats = 0, bits = 0, events = 0, wm = 0, size = 0, shape = 0;
443863f95b1Smrg    int frame = 0, children = 0;
444863f95b1Smrg    int use_root = 0;
445863f95b1Smrg    xcb_window_t window = 0;
446863f95b1Smrg    char *display_name = NULL;
447863f95b1Smrg    const char *window_name = NULL;
448863f95b1Smrg    struct wininfo wininfo;
449863f95b1Smrg    struct wininfo *w = &wininfo;
450863f95b1Smrg
451863f95b1Smrg    program_name = argv[0];
452863f95b1Smrg
453863f95b1Smrg    if (!setlocale (LC_ALL, ""))
454863f95b1Smrg	fprintf (stderr, "%s: can not set locale properly\n", program_name);
455863f95b1Smrg    user_encoding = nl_langinfo (CODESET);
456863f95b1Smrg    if (user_encoding == NULL)
457863f95b1Smrg	user_encoding = "unknown encoding";
458863f95b1Smrg
459863f95b1Smrg    memset (w, 0, sizeof(struct wininfo));
460863f95b1Smrg
461863f95b1Smrg    /* Handle our command line arguments */
46283283e49Smrg    for (int i = 1; i < argc; i++) {
463863f95b1Smrg	if (!strcmp (argv[i], "-help"))
464863f95b1Smrg	    usage ();
465863f95b1Smrg	if (!strcmp (argv[i], "-display") || !strcmp (argv[i], "-d")) {
466863f95b1Smrg	    if (++i >= argc)
467863f95b1Smrg		Fatal_Error("-display requires argument");
468863f95b1Smrg	    display_name = argv[i];
469863f95b1Smrg	    continue;
470863f95b1Smrg	}
471863f95b1Smrg	if (!strcmp (argv[i], "-root")) {
472863f95b1Smrg	    use_root = 1;
473863f95b1Smrg	    continue;
474863f95b1Smrg	}
475863f95b1Smrg	if (!strcmp (argv[i], "-id")) {
476863f95b1Smrg	    if (++i >= argc)
477863f95b1Smrg		Fatal_Error("-id requires argument");
478863f95b1Smrg	    window = strtoul(argv[i], NULL, 0);
479863f95b1Smrg	    continue;
480863f95b1Smrg	}
481863f95b1Smrg	if (!strcmp (argv[i], "-name")) {
482863f95b1Smrg	    if (++i >= argc)
483863f95b1Smrg		Fatal_Error("-name requires argument");
484863f95b1Smrg	    window_name = argv[i];
485863f95b1Smrg	    continue;
486863f95b1Smrg	}
487863f95b1Smrg	if (!strcmp (argv[i], "-int")) {
488cf2cd791Smrg	    window_id_format_dec = True;
489863f95b1Smrg	    continue;
490863f95b1Smrg	}
491863f95b1Smrg	if (!strcmp (argv[i], "-children")) {
492863f95b1Smrg	    children = 1;
493863f95b1Smrg	    continue;
494863f95b1Smrg	}
495863f95b1Smrg	if (!strcmp (argv[i], "-tree")) {
496863f95b1Smrg	    tree = 1;
497863f95b1Smrg	    continue;
498863f95b1Smrg	}
499863f95b1Smrg	if (!strcmp (argv[i], "-stats")) {
500863f95b1Smrg	    stats = 1;
501863f95b1Smrg	    continue;
502863f95b1Smrg	}
503863f95b1Smrg	if (!strcmp (argv[i], "-bits")) {
504863f95b1Smrg	    bits = 1;
505863f95b1Smrg	    continue;
506863f95b1Smrg	}
507863f95b1Smrg	if (!strcmp (argv[i], "-events")) {
508863f95b1Smrg	    events = 1;
509863f95b1Smrg	    continue;
510863f95b1Smrg	}
511863f95b1Smrg	if (!strcmp (argv[i], "-wm")) {
512863f95b1Smrg	    wm = 1;
513863f95b1Smrg	    continue;
514863f95b1Smrg	}
515863f95b1Smrg	if (!strcmp (argv[i], "-frame")) {
516863f95b1Smrg	    frame = 1;
517863f95b1Smrg	    continue;
518863f95b1Smrg	}
519863f95b1Smrg	if (!strcmp (argv[i], "-size")) {
520863f95b1Smrg	    size = 1;
521863f95b1Smrg	    continue;
522863f95b1Smrg	}
523863f95b1Smrg	if (!strcmp (argv[i], "-shape")) {
524863f95b1Smrg	    shape = 1;
525863f95b1Smrg	    continue;
526863f95b1Smrg	}
527863f95b1Smrg	if (!strcmp (argv[i], "-english")) {
528863f95b1Smrg	    english = 1;
529863f95b1Smrg	    continue;
530863f95b1Smrg	}
531863f95b1Smrg	if (!strcmp (argv[i], "-metric")) {
532863f95b1Smrg	    metric = 1;
533863f95b1Smrg	    continue;
534863f95b1Smrg	}
535863f95b1Smrg	if (!strcmp (argv[i], "-all")) {
536863f95b1Smrg	    tree = stats = bits = events = wm = size = shape = 1;
537863f95b1Smrg	    continue;
538863f95b1Smrg	}
53996eca5d1Smrg	if (!strcmp(argv[i], "-version")) {
54096eca5d1Smrg	    puts(PACKAGE_STRING);
54196eca5d1Smrg	    exit(0);
54296eca5d1Smrg	}
54396eca5d1Smrg	fprintf (stderr, "%s: unrecognized argument %s\n\n",
54496eca5d1Smrg		 program_name, argv[i]);
545863f95b1Smrg	usage ();
546863f95b1Smrg    }
547ff7e0accSmrg
548863f95b1Smrg    Setup_Display_And_Screen (display_name, &dpy, &screen);
549863f95b1Smrg
550863f95b1Smrg    /* preload atoms we may need later */
551863f95b1Smrg    Intern_Atom (dpy, "_NET_WM_NAME");
552863f95b1Smrg    Intern_Atom (dpy, "UTF8_STRING");
553863f95b1Smrg    if (wm) {
554863f95b1Smrg	Intern_Atom (dpy, "_NET_WM_DESKTOP");
555863f95b1Smrg	Intern_Atom (dpy, "_NET_WM_WINDOW_TYPE");
556863f95b1Smrg	Intern_Atom (dpy, "_NET_WM_STATE");
557863f95b1Smrg	Intern_Atom (dpy, "_NET_WM_PID");
558863f95b1Smrg	Intern_Atom (dpy, "_NET_FRAME_EXTENTS");
559863f95b1Smrg    }
560863f95b1Smrg    /* initialize scaling data */
561863f95b1Smrg    scale_init(screen);
562863f95b1Smrg
563863f95b1Smrg    if (use_root)
564863f95b1Smrg	window = screen->root;
565863f95b1Smrg    else if (window_name) {
566863f95b1Smrg	window = Window_With_Name (dpy, screen->root, window_name);
567863f95b1Smrg	if (!window)
568863f95b1Smrg	    Fatal_Error ("No window with name \"%s\" exists!", window_name);
569863f95b1Smrg    }
570ff7e0accSmrg
571863f95b1Smrg    /* If no window selected on command line, let user pick one the hard way */
572863f95b1Smrg    if (!window) {
573863f95b1Smrg	printf ("\n"
574863f95b1Smrg		"xwininfo: Please select the window about which you\n"
575863f95b1Smrg		"          would like information by clicking the\n"
576863f95b1Smrg		"          mouse in that window.\n");
577863f95b1Smrg	Intern_Atom (dpy, "_NET_VIRTUAL_ROOTS");
578863f95b1Smrg	Intern_Atom (dpy, "WM_STATE");
579863f95b1Smrg	window = Select_Window (dpy, screen, !frame);
580863f95b1Smrg    }
581ff7e0accSmrg
582863f95b1Smrg    /*
583863f95b1Smrg     * Do the actual displaying as per parameters
584863f95b1Smrg     */
585863f95b1Smrg    if (!(children || tree || bits || events || wm || size))
586863f95b1Smrg	stats = 1;
587ff7e0accSmrg
588863f95b1Smrg    /*
589863f95b1Smrg     * make sure that the window is valid
590863f95b1Smrg     */
591863f95b1Smrg    {
592863f95b1Smrg	xcb_get_geometry_cookie_t gg_cookie =
593863f95b1Smrg	    xcb_get_geometry (dpy, window);
594ff7e0accSmrg
595863f95b1Smrg	w->geometry = xcb_get_geometry_reply(dpy, gg_cookie, &err);
596ff7e0accSmrg
597863f95b1Smrg	if (!w->geometry) {
598863f95b1Smrg	    if (err)
599863f95b1Smrg		Print_X_Error (dpy, err);
600863f95b1Smrg
601cf2cd791Smrg	    Fatal_Error ("No such window with id %s.", window_id_str (window));
602863f95b1Smrg	}
603ff7e0accSmrg    }
604863f95b1Smrg
605863f95b1Smrg    /* Send requests to prefetch data we'll need */
606863f95b1Smrg    w->window = window;
607863f95b1Smrg    w->net_wm_name_cookie = get_net_wm_name (dpy, window);
608853aa076Smrg    w->wm_name_cookie = xcb_icccm_get_wm_name (dpy, window);
609863f95b1Smrg    if (children || tree)
610863f95b1Smrg	w->tree_cookie = xcb_query_tree (dpy, window);
611863f95b1Smrg    if (stats) {
612863f95b1Smrg	w->trans_coords_cookie =
613863f95b1Smrg	    xcb_translate_coordinates (dpy, window, w->geometry->root,
614863f95b1Smrg				       -(w->geometry->border_width),
615863f95b1Smrg				       -(w->geometry->border_width));
616ff7e0accSmrg    }
617863f95b1Smrg    if (stats || bits || events)
618863f95b1Smrg	w->attr_cookie = xcb_get_window_attributes (dpy, window);
619863f95b1Smrg    if (stats || size)
620853aa076Smrg	w->normal_hints_cookie = xcb_icccm_get_wm_normal_hints (dpy, window);
621863f95b1Smrg    if (wm) {
622853aa076Smrg	w->hints_cookie = xcb_icccm_get_wm_hints(dpy, window);
623863f95b1Smrg
624863f95b1Smrg	atom_net_wm_desktop = Get_Atom (dpy, "_NET_WM_DESKTOP");
625863f95b1Smrg	if (atom_net_wm_desktop) {
626863f95b1Smrg	    w->wm_desktop_cookie = xcb_get_property
627863f95b1Smrg		(dpy, False, window, atom_net_wm_desktop,
628863f95b1Smrg		 XCB_ATOM_CARDINAL, 0, 4);
629863f95b1Smrg	}
630863f95b1Smrg
631863f95b1Smrg	atom_net_wm_window_type	= Get_Atom (dpy, "_NET_WM_WINDOW_TYPE");
632863f95b1Smrg	if (atom_net_wm_window_type) {
633863f95b1Smrg	    w->wm_window_type_cookie = xcb_get_property
634863f95b1Smrg		(dpy, False, window, atom_net_wm_window_type,
635863f95b1Smrg		 XCB_ATOM_ATOM, 0, BUFSIZ);
636863f95b1Smrg	}
637863f95b1Smrg
638863f95b1Smrg	atom_net_wm_state = Get_Atom (dpy, "_NET_WM_STATE");
639863f95b1Smrg	if (atom_net_wm_state) {
640863f95b1Smrg	    w->wm_state_cookie = xcb_get_property
641863f95b1Smrg		(dpy, False, window, atom_net_wm_state,
642863f95b1Smrg		 XCB_ATOM_ATOM, 0, BUFSIZ);
643863f95b1Smrg	}
644863f95b1Smrg
645863f95b1Smrg	atom_net_wm_pid	= Get_Atom (dpy, "_NET_WM_PID");
646863f95b1Smrg	if (atom_net_wm_pid) {
647863f95b1Smrg	    w->wm_pid_cookie = xcb_get_property
648863f95b1Smrg		(dpy, False, window, atom_net_wm_pid,
649863f95b1Smrg		 XCB_ATOM_CARDINAL, 0, BUFSIZ);
650863f95b1Smrg	    w->wm_client_machine_cookie = xcb_get_property
651863f95b1Smrg		(dpy, False, window, XCB_ATOM_WM_CLIENT_MACHINE,
652863f95b1Smrg		 XCB_GET_PROPERTY_TYPE_ANY, 0, BUFSIZ);
653863f95b1Smrg	}
654863f95b1Smrg
655863f95b1Smrg	atom_net_frame_extents = Get_Atom (dpy, "_NET_FRAME_EXTENTS");
656863f95b1Smrg	if (atom_net_frame_extents) {
657863f95b1Smrg	    w->frame_extents_cookie = xcb_get_property
658863f95b1Smrg		(dpy, False, window, atom_net_frame_extents,
659863f95b1Smrg		 XCB_ATOM_CARDINAL, 0, 4 * 4);
660863f95b1Smrg	}
661ff7e0accSmrg    }
662863f95b1Smrg    if (size)
663853aa076Smrg	w->zoom_cookie = xcb_icccm_get_wm_size_hints (dpy, window,
664853aa076Smrg						      XCB_ATOM_WM_ZOOM_HINTS);
665863f95b1Smrg    xcb_flush (dpy);
666863f95b1Smrg
667863f95b1Smrg    printf ("\nxwininfo: Window id: ");
668863f95b1Smrg    Display_Window_Id (w, True);
669863f95b1Smrg    if (children || tree)
670863f95b1Smrg	Display_Tree_Info (w, tree);
671863f95b1Smrg    if (stats)
672863f95b1Smrg	Display_Stats_Info (w);
673863f95b1Smrg    if (bits)
674863f95b1Smrg	Display_Bits_Info (w);
675863f95b1Smrg    if (events)
676863f95b1Smrg	Display_Events_Info (w);
677863f95b1Smrg    if (wm)
678863f95b1Smrg	Display_WM_Info (w);
679863f95b1Smrg    if (size)
680863f95b1Smrg	Display_Size_Hints (w);
681863f95b1Smrg    if (shape)
682863f95b1Smrg	Display_Window_Shape (window);
683863f95b1Smrg    printf ("\n");
684863f95b1Smrg
685863f95b1Smrg    wininfo_wipe (w);
686863f95b1Smrg    xcb_disconnect (dpy);
687863f95b1Smrg#ifdef HAVE_ICONV
688863f95b1Smrg    if (iconv_from_utf8 && (iconv_from_utf8 != (iconv_t) -1)) {
689863f95b1Smrg	iconv_close (iconv_from_utf8);
690ff7e0accSmrg    }
691863f95b1Smrg#endif
692863f95b1Smrg    exit (0);
693863f95b1Smrg}
694863f95b1Smrg
695863f95b1Smrg/* Ensure win_attributes field is filled in */
696863f95b1Smrgstatic xcb_get_window_attributes_reply_t *
697863f95b1Smrgfetch_win_attributes (struct wininfo *w)
698863f95b1Smrg{
699863f95b1Smrg    if (!w->win_attributes) {
700863f95b1Smrg	w->win_attributes =
701863f95b1Smrg	    xcb_get_window_attributes_reply (dpy, w->attr_cookie, &err);
702863f95b1Smrg
703863f95b1Smrg	if (!w->win_attributes) {
704863f95b1Smrg	    Print_X_Error (dpy, err);
705863f95b1Smrg	    Fatal_Error ("Can't get window attributes.");
706863f95b1Smrg	}
707ff7e0accSmrg    }
708863f95b1Smrg    return w->win_attributes;
709863f95b1Smrg}
710863f95b1Smrg
711863f95b1Smrg#ifndef USE_XCB_ICCCM
712863f95b1Smrgstatic Bool
713cf2cd791Smrgwm_size_hints_reply (xcb_connection_t *wshr_dpy, xcb_get_property_cookie_t cookie,
714cf2cd791Smrg		     wm_size_hints_t *hints_return, xcb_generic_error_t **wshr_err)
715863f95b1Smrg{
716cf2cd791Smrg    xcb_get_property_reply_t *prop = xcb_get_property_reply (wshr_dpy, cookie, wshr_err);
717e8f4a63fSmrg    size_t length;
718863f95b1Smrg
719863f95b1Smrg    if (!prop || (prop->type != XCB_ATOM_WM_SIZE_HINTS) ||
720863f95b1Smrg	(prop->format != 32)) {
721863f95b1Smrg	free (prop);
722863f95b1Smrg	return False;
723ff7e0accSmrg    }
724863f95b1Smrg
725863f95b1Smrg    memset (hints_return, 0, sizeof(wm_size_hints_t));
726863f95b1Smrg
727e8f4a63fSmrg    length = (size_t) xcb_get_property_value_length(prop);
728863f95b1Smrg    if (length > sizeof(wm_size_hints_t))
729863f95b1Smrg	length = sizeof(wm_size_hints_t);
730863f95b1Smrg    memcpy (hints_return, xcb_get_property_value (prop), length);
731863f95b1Smrg
732863f95b1Smrg    free (prop);
733863f95b1Smrg    return True;
734863f95b1Smrg}
735863f95b1Smrg
736853aa076Smrg#define xcb_icccm_get_wm_normal_hints_reply wm_size_hints_reply
737853aa076Smrg#define xcb_icccm_get_wm_size_hints_reply wm_size_hints_reply
738863f95b1Smrg#endif
739863f95b1Smrg
740863f95b1Smrg
741863f95b1Smrg
742863f95b1Smrg/* Ensure normal_hints field is filled in */
743863f95b1Smrgstatic xcb_size_hints_t *
744863f95b1Smrgfetch_normal_hints (struct wininfo *w, xcb_size_hints_t *hints_return)
745863f95b1Smrg{
746863f95b1Smrg    if (!w->normal_hints) {
74783283e49Smrg	xcb_size_hints_t hints;
74883283e49Smrg
749853aa076Smrg	if (xcb_icccm_get_wm_normal_hints_reply (dpy, w->normal_hints_cookie,
750853aa076Smrg						 &hints, NULL)) {
751863f95b1Smrg	    w->normal_hints = malloc (sizeof(xcb_size_hints_t));
752863f95b1Smrg	    if (w->normal_hints)
753863f95b1Smrg		memcpy(w->normal_hints, &hints, sizeof(xcb_size_hints_t));
754863f95b1Smrg	}
755ff7e0accSmrg    }
756863f95b1Smrg    if (hints_return && w->normal_hints)
757863f95b1Smrg	memcpy(hints_return, w->normal_hints, sizeof(xcb_size_hints_t));
758863f95b1Smrg    return w->normal_hints;
759ff7e0accSmrg}
760ff7e0accSmrg
761ff7e0accSmrg
762ff7e0accSmrg/*
763ff7e0accSmrg * Lookup: lookup a code in a table.
764ff7e0accSmrg */
765ff7e0accSmrg
766ff7e0accSmrgstatic const char *
767863f95b1SmrgLookupL (long code, const binding *table)
768ff7e0accSmrg{
769863f95b1Smrg    const char *name = NULL;
770ff7e0accSmrg
771863f95b1Smrg    while (table->name) {
772863f95b1Smrg	if (table->code == code) {
773863f95b1Smrg	    name = table->name;
774863f95b1Smrg	    break;
775ff7e0accSmrg	}
776863f95b1Smrg	table++;
777863f95b1Smrg    }
778ff7e0accSmrg
779863f95b1Smrg    if (name == NULL) {
78083283e49Smrg	static char _lookup_buffer[100];
78183283e49Smrg
782863f95b1Smrg	snprintf (_lookup_buffer, sizeof(_lookup_buffer),
783863f95b1Smrg		  "unknown (code = %ld. = 0x%lx)", code, code);
784863f95b1Smrg	name = _lookup_buffer;
785863f95b1Smrg    }
786863f95b1Smrg
787863f95b1Smrg    return (name);
788ff7e0accSmrg}
789ff7e0accSmrg
790ff7e0accSmrgstatic const char *
791863f95b1SmrgLookup (int code, const binding *table)
792ff7e0accSmrg{
793863f95b1Smrg    return LookupL ((long)code, table);
794ff7e0accSmrg}
795ff7e0accSmrg
796ff7e0accSmrg/*
797ff7e0accSmrg * Routine to display a window id in dec/hex with name if window has one
798863f95b1Smrg *
799863f95b1Smrg * Requires wininfo members initialized: window, wm_name_cookie
800ff7e0accSmrg */
801ff7e0accSmrg
802ff7e0accSmrgstatic void
803863f95b1SmrgDisplay_Window_Id (struct wininfo *w, Bool newline_wanted)
804ff7e0accSmrg{
805863f95b1Smrg#ifdef USE_XCB_ICCCM
806853aa076Smrg    xcb_icccm_get_text_property_reply_t wmn_reply;
807853aa076Smrg    uint8_t got_reply = False;
808ff7e0accSmrg#endif
809863f95b1Smrg    xcb_get_property_reply_t *prop;
810863f95b1Smrg    const char *wm_name = NULL;
811863f95b1Smrg    unsigned int wm_name_len = 0;
812863f95b1Smrg    xcb_atom_t wm_name_encoding = XCB_NONE;
813ff7e0accSmrg
814cf2cd791Smrg    printf ("%s", window_id_str (w->window));
815863f95b1Smrg
816863f95b1Smrg    if (!w->window) {
817863f95b1Smrg	printf (" (none)");
818ff7e0accSmrg    } else {
819863f95b1Smrg	if (w->window == screen->root) {
820863f95b1Smrg	    printf (" (the root window)");
821ff7e0accSmrg	}
822863f95b1Smrg	/* Get window name if any */
823863f95b1Smrg	prop = xcb_get_property_reply (dpy, w->net_wm_name_cookie, NULL);
824863f95b1Smrg	if (prop && (prop->type != XCB_NONE)) {
825863f95b1Smrg	    wm_name = xcb_get_property_value (prop);
826863f95b1Smrg	    wm_name_len = xcb_get_property_value_length (prop);
827863f95b1Smrg	    wm_name_encoding = prop->type;
828853aa076Smrg	} else { /* No _NET_WM_NAME, check WM_NAME */
829863f95b1Smrg#ifdef USE_XCB_ICCCM
830853aa076Smrg	    got_reply = xcb_icccm_get_wm_name_reply (dpy, w->wm_name_cookie,
831853aa076Smrg						     &wmn_reply, NULL);
832863f95b1Smrg	    if (got_reply) {
833863f95b1Smrg		wm_name = wmn_reply.name;
834863f95b1Smrg		wm_name_len = wmn_reply.name_len;
835863f95b1Smrg		wm_name_encoding = wmn_reply.encoding;
836863f95b1Smrg	    }
837ff7e0accSmrg#else
838863f95b1Smrg	    prop = xcb_get_property_reply (dpy, w->wm_name_cookie, NULL);
839863f95b1Smrg	    if (prop && (prop->type != XCB_NONE)) {
840863f95b1Smrg		wm_name = xcb_get_property_value (prop);
841863f95b1Smrg		wm_name_len = xcb_get_property_value_length (prop);
842863f95b1Smrg		wm_name_encoding = prop->type;
843863f95b1Smrg	    }
844863f95b1Smrg#endif
845ff7e0accSmrg	}
846853aa076Smrg	if (wm_name_len == 0) {
847863f95b1Smrg	    printf (" (has no name)");
848863f95b1Smrg        } else {
849863f95b1Smrg	    if (wm_name_encoding == XCB_ATOM_STRING) {
850863f95b1Smrg		printf (" \"%.*s\"", wm_name_len, wm_name);
851863f95b1Smrg	    } else if (wm_name_encoding == atom_utf8_string) {
852857c29c5Skamil		print_utf8 (" \"", (char *) wm_name, wm_name_len,  "\"");
853863f95b1Smrg	    } else {
854863f95b1Smrg		/* Encodings we don't support, including COMPOUND_TEXT */
855863f95b1Smrg		const char *enc_name = Get_Atom_Name (dpy, wm_name_encoding);
856863f95b1Smrg		if (enc_name) {
857863f95b1Smrg		    printf (" (name in unsupported encoding %s)", enc_name);
858863f95b1Smrg		} else {
859863f95b1Smrg		    printf (" (name in unsupported encoding ATOM 0x%x)",
860863f95b1Smrg			    wm_name_encoding);
861863f95b1Smrg		}
862863f95b1Smrg	    }
863863f95b1Smrg	}
864863f95b1Smrg#ifdef USE_XCB_ICCCM
865863f95b1Smrg	if (got_reply)
866853aa076Smrg	    xcb_icccm_get_text_property_reply_wipe (&wmn_reply);
867863f95b1Smrg#else
868863f95b1Smrg	free (prop);
869ff7e0accSmrg#endif
870ff7e0accSmrg    }
871ff7e0accSmrg
872ff7e0accSmrg    if (newline_wanted)
873863f95b1Smrg	printf ("\n");
874ff7e0accSmrg
875ff7e0accSmrg    return;
876ff7e0accSmrg}
877ff7e0accSmrg
878ff7e0accSmrg
879ff7e0accSmrg/*
880ff7e0accSmrg * Display Stats on window
881ff7e0accSmrg */
882ff7e0accSmrgstatic const binding _window_classes[] = {
883863f95b1Smrg	{ XCB_WINDOW_CLASS_INPUT_OUTPUT, "InputOutput" },
884863f95b1Smrg	{ XCB_WINDOW_CLASS_INPUT_ONLY, "InputOnly" },
88510998002Smrg        { 0, NULL } };
886ff7e0accSmrg
887ff7e0accSmrgstatic const binding _map_states[] = {
888863f95b1Smrg	{ XCB_MAP_STATE_UNMAPPED,	"IsUnMapped" },
889863f95b1Smrg	{ XCB_MAP_STATE_UNVIEWABLE,	"IsUnviewable" },
890863f95b1Smrg	{ XCB_MAP_STATE_VIEWABLE,	"IsViewable" },
89110998002Smrg	{ 0, NULL } };
892ff7e0accSmrg
893ff7e0accSmrgstatic const binding _backing_store_states[] = {
894863f95b1Smrg	{ XCB_BACKING_STORE_NOT_USEFUL, "NotUseful" },
895863f95b1Smrg	{ XCB_BACKING_STORE_WHEN_MAPPED,"WhenMapped" },
896863f95b1Smrg	{ XCB_BACKING_STORE_ALWAYS,	"Always" },
89710998002Smrg	{ 0, NULL } };
898ff7e0accSmrg
899ff7e0accSmrgstatic const binding _bit_gravity_states[] = {
900863f95b1Smrg	{ XCB_GRAVITY_BIT_FORGET,	"ForgetGravity" },
901863f95b1Smrg	{ XCB_GRAVITY_NORTH_WEST,	"NorthWestGravity" },
902863f95b1Smrg	{ XCB_GRAVITY_NORTH,		"NorthGravity" },
903863f95b1Smrg	{ XCB_GRAVITY_NORTH_EAST,	"NorthEastGravity" },
904863f95b1Smrg	{ XCB_GRAVITY_WEST,		"WestGravity" },
905863f95b1Smrg	{ XCB_GRAVITY_CENTER,		"CenterGravity" },
906863f95b1Smrg	{ XCB_GRAVITY_EAST,		"EastGravity" },
907863f95b1Smrg	{ XCB_GRAVITY_SOUTH_WEST,	"SouthWestGravity" },
908863f95b1Smrg	{ XCB_GRAVITY_SOUTH,		"SouthGravity" },
909863f95b1Smrg	{ XCB_GRAVITY_SOUTH_EAST,	"SouthEastGravity" },
910863f95b1Smrg	{ XCB_GRAVITY_STATIC,		"StaticGravity" },
91110998002Smrg	{ 0, NULL }};
912ff7e0accSmrg
913ff7e0accSmrgstatic const binding _window_gravity_states[] = {
914863f95b1Smrg	{ XCB_GRAVITY_WIN_UNMAP,	"UnmapGravity" },
915863f95b1Smrg	{ XCB_GRAVITY_NORTH_WEST,	"NorthWestGravity" },
916863f95b1Smrg	{ XCB_GRAVITY_NORTH,		"NorthGravity" },
917863f95b1Smrg	{ XCB_GRAVITY_NORTH_EAST,	"NorthEastGravity" },
918863f95b1Smrg	{ XCB_GRAVITY_WEST,		"WestGravity" },
919863f95b1Smrg	{ XCB_GRAVITY_CENTER,		"CenterGravity" },
920863f95b1Smrg	{ XCB_GRAVITY_EAST,		"EastGravity" },
921863f95b1Smrg	{ XCB_GRAVITY_SOUTH_WEST,	"SouthWestGravity" },
922863f95b1Smrg	{ XCB_GRAVITY_SOUTH,		"SouthGravity" },
923863f95b1Smrg	{ XCB_GRAVITY_SOUTH_EAST,	"SouthEastGravity" },
924863f95b1Smrg	{ XCB_GRAVITY_STATIC,		"StaticGravity" },
92510998002Smrg	{ 0, NULL }};
926ff7e0accSmrg
927ff7e0accSmrgstatic const binding _visual_classes[] = {
928863f95b1Smrg	{ XCB_VISUAL_CLASS_STATIC_GRAY,	"StaticGray" },
929863f95b1Smrg	{ XCB_VISUAL_CLASS_GRAY_SCALE,	"GrayScale" },
930863f95b1Smrg	{ XCB_VISUAL_CLASS_STATIC_COLOR,"StaticColor" },
931863f95b1Smrg	{ XCB_VISUAL_CLASS_PSEUDO_COLOR,"PseudoColor" },
932863f95b1Smrg	{ XCB_VISUAL_CLASS_TRUE_COLOR,	"TrueColor" },
933863f95b1Smrg	{ XCB_VISUAL_CLASS_DIRECT_COLOR,"DirectColor" },
93410998002Smrg	{ 0, NULL }};
935ff7e0accSmrg
936863f95b1Smrg/*
937863f95b1Smrg * Requires wininfo members initialized:
938863f95b1Smrg *   window, geometry, attr_cookie, trans_coords_cookie, normal_hints_cookie
939863f95b1Smrg */
940ff7e0accSmrgstatic void
941863f95b1SmrgDisplay_Stats_Info (struct wininfo *w)
942ff7e0accSmrg{
943863f95b1Smrg    xcb_translate_coordinates_reply_t *trans_coords;
944863f95b1Smrg    xcb_get_window_attributes_reply_t *win_attributes;
945863f95b1Smrg    xcb_size_hints_t hints;
946863f95b1Smrg
947863f95b1Smrg    int dw = screen->width_in_pixels, dh = screen->height_in_pixels;
948863f95b1Smrg    int rx, ry, xright, ybelow;
949863f95b1Smrg    int showright = 0, showbelow = 0;
950863f95b1Smrg    xcb_window_t wmframe, parent;
951863f95b1Smrg
952863f95b1Smrg    trans_coords =
953863f95b1Smrg	xcb_translate_coordinates_reply (dpy, w->trans_coords_cookie, NULL);
954863f95b1Smrg    if (!trans_coords)
955863f95b1Smrg	Fatal_Error ("Can't get translated coordinates.");
956863f95b1Smrg
957863f95b1Smrg    rx = (int16_t)trans_coords->dst_x;
958863f95b1Smrg    ry = (int16_t)trans_coords->dst_y;
959863f95b1Smrg    free (trans_coords);
960863f95b1Smrg
961863f95b1Smrg    xright = (dw - rx - w->geometry->border_width * 2 -
962863f95b1Smrg	      w->geometry->width);
963863f95b1Smrg    ybelow = (dh - ry - w->geometry->border_width * 2 -
964863f95b1Smrg	      w->geometry->height);
965863f95b1Smrg
966863f95b1Smrg
967863f95b1Smrg    printf ("\n");
968863f95b1Smrg    printf ("  Absolute upper-left X:  %s\n", xscale (rx));
969863f95b1Smrg    printf ("  Absolute upper-left Y:  %s\n", yscale (ry));
970863f95b1Smrg    printf ("  Relative upper-left X:  %s\n", xscale (w->geometry->x));
971863f95b1Smrg    printf ("  Relative upper-left Y:  %s\n", yscale (w->geometry->y));
972863f95b1Smrg    printf ("  Width: %s\n", xscale (w->geometry->width));
973863f95b1Smrg    printf ("  Height: %s\n", yscale (w->geometry->height));
974863f95b1Smrg    printf ("  Depth: %d\n", w->geometry->depth);
975863f95b1Smrg
976863f95b1Smrg    win_attributes = fetch_win_attributes (w);
977863f95b1Smrg
978863f95b1Smrg    printf ("  Visual: 0x%lx\n", (unsigned long) win_attributes->visual);
979863f95b1Smrg    if (screen)
980863f95b1Smrg    {
981863f95b1Smrg	xcb_depth_iterator_t depth_iter;
982863f95b1Smrg	xcb_visualtype_t  *visual_type = NULL;
983863f95b1Smrg
984863f95b1Smrg	depth_iter = xcb_screen_allowed_depths_iterator (screen);
985863f95b1Smrg	for (; depth_iter.rem; xcb_depth_next (&depth_iter)) {
986863f95b1Smrg	    xcb_visualtype_iterator_t visual_iter;
987863f95b1Smrg
988863f95b1Smrg	    visual_iter = xcb_depth_visuals_iterator (depth_iter.data);
989863f95b1Smrg	    for (; visual_iter.rem; xcb_visualtype_next (&visual_iter)) {
990cf2cd791Smrg		if (win_attributes->visual == visual_iter.data->visual_id) {
991863f95b1Smrg		    visual_type = visual_iter.data;
992863f95b1Smrg		    break;
993863f95b1Smrg		}
994863f95b1Smrg	    }
995863f95b1Smrg	}
996863f95b1Smrg	if (visual_type)
997863f95b1Smrg	    printf ("  Visual Class: %s\n", Lookup (visual_type->_class,
998863f95b1Smrg						    _visual_classes));
999863f95b1Smrg    }
1000863f95b1Smrg
1001863f95b1Smrg    printf ("  Border width: %s\n", bscale (w->geometry->border_width));
1002863f95b1Smrg    printf ("  Class: %s\n",
1003863f95b1Smrg	    Lookup (win_attributes->_class, _window_classes));
1004863f95b1Smrg    printf ("  Colormap: 0x%lx (%sinstalled)\n",
1005863f95b1Smrg	    (unsigned long) win_attributes->colormap,
1006863f95b1Smrg	    win_attributes->map_is_installed ? "" : "not ");
1007863f95b1Smrg    printf ("  Bit Gravity State: %s\n",
1008863f95b1Smrg	    Lookup (win_attributes->bit_gravity, _bit_gravity_states));
1009863f95b1Smrg    printf ("  Window Gravity State: %s\n",
1010863f95b1Smrg	    Lookup (win_attributes->win_gravity, _window_gravity_states));
1011863f95b1Smrg    printf ("  Backing Store State: %s\n",
1012863f95b1Smrg	    Lookup (win_attributes->backing_store, _backing_store_states));
1013863f95b1Smrg    printf ("  Save Under State: %s\n",
1014863f95b1Smrg	    win_attributes->save_under ? "yes" : "no");
1015863f95b1Smrg    printf ("  Map State: %s\n",
1016863f95b1Smrg	    Lookup (win_attributes->map_state, _map_states));
1017863f95b1Smrg    printf ("  Override Redirect State: %s\n",
1018863f95b1Smrg	    win_attributes->override_redirect ? "yes" : "no");
1019863f95b1Smrg    printf ("  Corners:  +%d+%d  -%d+%d  -%d-%d  +%d-%d\n",
1020863f95b1Smrg	    rx, ry, xright, ry, xright, ybelow, rx, ybelow);
1021863f95b1Smrg
1022863f95b1Smrg    /*
1023863f95b1Smrg     * compute geometry string that would recreate window
1024863f95b1Smrg     */
1025863f95b1Smrg    printf ("  -geometry ");
1026863f95b1Smrg
1027863f95b1Smrg    /* compute size in appropriate units */
1028863f95b1Smrg    if (!fetch_normal_hints (w, &hints))
1029863f95b1Smrg	hints.flags = 0;
1030863f95b1Smrg
1031853aa076Smrg    if ((hints.flags & XCB_ICCCM_SIZE_HINT_P_RESIZE_INC)  &&
1032863f95b1Smrg	(hints.width_inc != 0)  && (hints.height_inc != 0)) {
1033853aa076Smrg	if (hints.flags &
1034853aa076Smrg	    (XCB_ICCCM_SIZE_HINT_BASE_SIZE|XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)) {
1035853aa076Smrg	    if (hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) {
1036863f95b1Smrg		w->geometry->width -= hints.base_width;
1037863f95b1Smrg		w->geometry->height -= hints.base_height;
1038863f95b1Smrg	    } else {
1039863f95b1Smrg		/* ICCCM says MinSize is default for BaseSize */
1040863f95b1Smrg		w->geometry->width -= hints.min_width;
1041863f95b1Smrg		w->geometry->height -= hints.min_height;
1042863f95b1Smrg	    }
1043863f95b1Smrg	}
1044863f95b1Smrg	printf ("%dx%d", w->geometry->width/hints.width_inc,
1045863f95b1Smrg		w->geometry->height/hints.height_inc);
1046863f95b1Smrg    } else
1047863f95b1Smrg	printf ("%dx%d", w->geometry->width, w->geometry->height);
1048863f95b1Smrg
1049853aa076Smrg    if (!(hints.flags & XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY))
1050863f95b1Smrg	hints.win_gravity = XCB_GRAVITY_NORTH_WEST; /* per ICCCM */
1051863f95b1Smrg    /* find our window manager frame, if any */
1052863f95b1Smrg    for (wmframe = parent = w->window; parent != 0 ; wmframe = parent) {
1053863f95b1Smrg	xcb_query_tree_cookie_t qt_cookie;
1054863f95b1Smrg	xcb_query_tree_reply_t *tree;
1055863f95b1Smrg
1056863f95b1Smrg	qt_cookie = xcb_query_tree (dpy, wmframe);
1057863f95b1Smrg	tree = xcb_query_tree_reply (dpy, qt_cookie, &err);
1058863f95b1Smrg	if (!tree) {
1059863f95b1Smrg	    Print_X_Error (dpy, err);
1060863f95b1Smrg	    Fatal_Error ("Can't query window tree.");
1061863f95b1Smrg	}
1062863f95b1Smrg	parent = tree->parent;
1063863f95b1Smrg	free (tree);
1064863f95b1Smrg	if (parent == w->geometry->root || !parent)
1065863f95b1Smrg	    break;
1066863f95b1Smrg    }
1067863f95b1Smrg    if (wmframe != w->window) {
1068863f95b1Smrg	/* WM reparented, so find edges of the frame */
1069863f95b1Smrg	/* Only works for ICCCM-compliant WMs, and then only if the
1070863f95b1Smrg	   window has corner gravity.  We would need to know the original width
1071863f95b1Smrg	   of the window to correctly handle the other gravities. */
1072863f95b1Smrg	xcb_get_geometry_cookie_t geom_cookie;
1073863f95b1Smrg	xcb_get_geometry_reply_t *frame_geometry;
1074863f95b1Smrg
1075863f95b1Smrg	geom_cookie = xcb_get_geometry (dpy, wmframe);
1076863f95b1Smrg	frame_geometry = xcb_get_geometry_reply (dpy, geom_cookie, &err);
1077863f95b1Smrg
1078863f95b1Smrg	if (!frame_geometry) {
1079863f95b1Smrg	    Print_X_Error (dpy, err);
1080863f95b1Smrg	    Fatal_Error ("Can't get frame geometry.");
1081863f95b1Smrg	}
1082863f95b1Smrg	switch (hints.win_gravity) {
1083863f95b1Smrg	    case XCB_GRAVITY_NORTH_WEST: case XCB_GRAVITY_SOUTH_WEST:
1084863f95b1Smrg	    case XCB_GRAVITY_NORTH_EAST: case XCB_GRAVITY_SOUTH_EAST:
1085863f95b1Smrg	    case XCB_GRAVITY_WEST:
1086863f95b1Smrg		rx = frame_geometry->x;
1087863f95b1Smrg	}
1088863f95b1Smrg	switch (hints.win_gravity) {
1089863f95b1Smrg	    case XCB_GRAVITY_NORTH_WEST: case XCB_GRAVITY_SOUTH_WEST:
1090863f95b1Smrg	    case XCB_GRAVITY_NORTH_EAST: case XCB_GRAVITY_SOUTH_EAST:
1091863f95b1Smrg	    case XCB_GRAVITY_EAST:
1092863f95b1Smrg		xright = dw - frame_geometry->x - frame_geometry->width -
1093863f95b1Smrg		    (2 * frame_geometry->border_width);
1094863f95b1Smrg	}
1095863f95b1Smrg	switch (hints.win_gravity) {
1096863f95b1Smrg	    case XCB_GRAVITY_NORTH_WEST: case XCB_GRAVITY_SOUTH_WEST:
1097863f95b1Smrg	    case XCB_GRAVITY_NORTH_EAST: case XCB_GRAVITY_SOUTH_EAST:
1098863f95b1Smrg	    case XCB_GRAVITY_NORTH:
1099863f95b1Smrg		ry = frame_geometry->y;
1100863f95b1Smrg	}
1101863f95b1Smrg	switch (hints.win_gravity) {
1102863f95b1Smrg	    case XCB_GRAVITY_NORTH_WEST: case XCB_GRAVITY_SOUTH_WEST:
1103863f95b1Smrg	    case XCB_GRAVITY_NORTH_EAST: case XCB_GRAVITY_SOUTH_EAST:
1104863f95b1Smrg	    case XCB_GRAVITY_SOUTH:
1105863f95b1Smrg		ybelow = dh - frame_geometry->y - frame_geometry->height -
1106863f95b1Smrg		    (2 * frame_geometry->border_width);
1107863f95b1Smrg	}
1108863f95b1Smrg	free (frame_geometry);
1109863f95b1Smrg    }
1110863f95b1Smrg    /* If edge gravity, offer a corner on that edge (because the application
1111863f95b1Smrg       programmer cares about that edge), otherwise offer upper left unless
1112863f95b1Smrg       some other corner is close to an edge of the screen.
1113863f95b1Smrg       (For corner gravity, assume gravity was set by XWMGeometry.
1114863f95b1Smrg       For CenterGravity, it doesn't matter.) */
1115863f95b1Smrg    if (hints.win_gravity == XCB_GRAVITY_EAST  ||
1116863f95b1Smrg	(abs (xright) <= 100  &&  abs (xright) < abs (rx)
1117863f95b1Smrg	 &&  hints.win_gravity != XCB_GRAVITY_WEST))
1118863f95b1Smrg	showright = 1;
1119863f95b1Smrg    if (hints.win_gravity == XCB_GRAVITY_SOUTH  ||
1120863f95b1Smrg	(abs (ybelow) <= 100  &&  abs (ybelow) < abs (ry)
1121863f95b1Smrg	 &&  hints.win_gravity != XCB_GRAVITY_NORTH))
1122863f95b1Smrg	showbelow = 1;
1123863f95b1Smrg
1124863f95b1Smrg    if (showright)
1125863f95b1Smrg	printf ("-%d", xright);
1126863f95b1Smrg    else
1127863f95b1Smrg	printf ("+%d", rx);
1128863f95b1Smrg    if (showbelow)
1129863f95b1Smrg	printf ("-%d", ybelow);
1130863f95b1Smrg    else
1131863f95b1Smrg	printf ("+%d", ry);
1132863f95b1Smrg    printf ("\n");
1133ff7e0accSmrg}
1134ff7e0accSmrg
1135ff7e0accSmrg
1136ff7e0accSmrg/*
1137ff7e0accSmrg * Display bits info:
1138ff7e0accSmrg */
1139ff7e0accSmrgstatic const binding _gravities[] = {
1140863f95b1Smrg    /* WARNING: the first two of these have the same value - see code */
1141863f95b1Smrg	{ XCB_GRAVITY_WIN_UNMAP,	"UnMapGravity" },
1142863f95b1Smrg	{ XCB_GRAVITY_BIT_FORGET,	"ForgetGravity" },
1143863f95b1Smrg	{ XCB_GRAVITY_NORTH_WEST,	"NorthWestGravity" },
1144863f95b1Smrg	{ XCB_GRAVITY_NORTH,		"NorthGravity" },
1145863f95b1Smrg	{ XCB_GRAVITY_NORTH_EAST,	"NorthEastGravity" },
1146863f95b1Smrg	{ XCB_GRAVITY_WEST,		"WestGravity" },
1147863f95b1Smrg	{ XCB_GRAVITY_CENTER,		"CenterGravity" },
1148863f95b1Smrg	{ XCB_GRAVITY_EAST,		"EastGravity" },
1149863f95b1Smrg	{ XCB_GRAVITY_SOUTH_WEST,	"SouthWestGravity" },
1150863f95b1Smrg	{ XCB_GRAVITY_SOUTH,		"SouthGravity" },
1151863f95b1Smrg	{ XCB_GRAVITY_SOUTH_EAST,	"SouthEastGravity" },
1152863f95b1Smrg	{ XCB_GRAVITY_STATIC,		"StaticGravity" },
115310998002Smrg	{ 0, NULL } };
1154ff7e0accSmrg
1155ff7e0accSmrgstatic const binding _backing_store_hint[] = {
1156863f95b1Smrg	{ XCB_BACKING_STORE_NOT_USEFUL, "NotUseful" },
1157863f95b1Smrg	{ XCB_BACKING_STORE_WHEN_MAPPED,"WhenMapped" },
1158863f95b1Smrg	{ XCB_BACKING_STORE_ALWAYS,	"Always" },
115910998002Smrg	{ 0, NULL } };
1160ff7e0accSmrg
1161ff7e0accSmrgstatic const binding _bool[] = {
1162ff7e0accSmrg	{ 0, "No" },
1163ff7e0accSmrg	{ 1, "Yes" },
116410998002Smrg	{ 0, NULL } };
1165ff7e0accSmrg
1166863f95b1Smrg/*
1167863f95b1Smrg * Requires wininfo members initialized:
1168863f95b1Smrg *   window, attr_cookie (or win_attributes)
1169863f95b1Smrg */
1170ff7e0accSmrgstatic void
1171863f95b1SmrgDisplay_Bits_Info (struct wininfo * w)
1172ff7e0accSmrg{
1173863f95b1Smrg    xcb_get_window_attributes_reply_t *win_attributes
1174863f95b1Smrg	= fetch_win_attributes (w);
1175863f95b1Smrg
1176863f95b1Smrg    printf ("\n");
1177863f95b1Smrg    printf ("  Bit gravity: %s\n",
1178863f95b1Smrg	    Lookup (win_attributes->bit_gravity, _gravities+1));
1179863f95b1Smrg    printf ("  Window gravity: %s\n",
1180863f95b1Smrg	    Lookup (win_attributes->win_gravity, _gravities));
1181863f95b1Smrg    printf ("  Backing-store hint: %s\n",
1182863f95b1Smrg	    Lookup (win_attributes->backing_store, _backing_store_hint));
1183863f95b1Smrg    printf ("  Backing-planes to be preserved: 0x%lx\n",
1184863f95b1Smrg	    (unsigned long) win_attributes->backing_planes);
1185863f95b1Smrg    printf ("  Backing pixel: %ld\n",
1186863f95b1Smrg	    (unsigned long) win_attributes->backing_pixel);
1187863f95b1Smrg    printf ("  Save-unders: %s\n",
1188863f95b1Smrg	    Lookup (win_attributes->save_under, _bool));
1189ff7e0accSmrg}
1190ff7e0accSmrg
1191ff7e0accSmrg
1192ff7e0accSmrg/*
1193ff7e0accSmrg * Routine to display all events in an event mask
1194ff7e0accSmrg */
1195ff7e0accSmrgstatic const binding _event_mask_names[] = {
1196863f95b1Smrg	{ XCB_EVENT_MASK_KEY_PRESS,		"KeyPress" },
1197863f95b1Smrg	{ XCB_EVENT_MASK_KEY_RELEASE,		"KeyRelease" },
1198863f95b1Smrg	{ XCB_EVENT_MASK_BUTTON_PRESS,		"ButtonPress" },
1199863f95b1Smrg	{ XCB_EVENT_MASK_BUTTON_RELEASE,	"ButtonRelease" },
1200863f95b1Smrg	{ XCB_EVENT_MASK_ENTER_WINDOW,		"EnterWindow" },
1201863f95b1Smrg	{ XCB_EVENT_MASK_LEAVE_WINDOW,		"LeaveWindow" },
1202863f95b1Smrg	{ XCB_EVENT_MASK_POINTER_MOTION,	"PointerMotion" },
1203863f95b1Smrg	{ XCB_EVENT_MASK_POINTER_MOTION_HINT,	"PointerMotionHint" },
1204863f95b1Smrg	{ XCB_EVENT_MASK_BUTTON_1_MOTION,	"Button1Motion" },
1205863f95b1Smrg	{ XCB_EVENT_MASK_BUTTON_2_MOTION,	"Button2Motion" },
1206863f95b1Smrg	{ XCB_EVENT_MASK_BUTTON_3_MOTION,	"Button3Motion" },
1207863f95b1Smrg	{ XCB_EVENT_MASK_BUTTON_4_MOTION,	"Button4Motion" },
1208863f95b1Smrg	{ XCB_EVENT_MASK_BUTTON_5_MOTION,	"Button5Motion" },
1209863f95b1Smrg	{ XCB_EVENT_MASK_BUTTON_MOTION,		"ButtonMotion" },
1210863f95b1Smrg	{ XCB_EVENT_MASK_KEYMAP_STATE,		"KeymapState" },
1211863f95b1Smrg	{ XCB_EVENT_MASK_EXPOSURE,		"Exposure" },
1212863f95b1Smrg	{ XCB_EVENT_MASK_VISIBILITY_CHANGE,	"VisibilityChange" },
1213863f95b1Smrg	{ XCB_EVENT_MASK_STRUCTURE_NOTIFY,	"StructureNotify" },
1214863f95b1Smrg	{ XCB_EVENT_MASK_RESIZE_REDIRECT,	"ResizeRedirect" },
1215863f95b1Smrg	{ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,	"SubstructureNotify" },
1216863f95b1Smrg	{ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,	"SubstructureRedirect" },
1217863f95b1Smrg	{ XCB_EVENT_MASK_FOCUS_CHANGE,		"FocusChange" },
1218863f95b1Smrg	{ XCB_EVENT_MASK_PROPERTY_CHANGE,	"PropertyChange" },
1219863f95b1Smrg	{ XCB_EVENT_MASK_COLOR_MAP_CHANGE,	"ColormapChange" },
1220863f95b1Smrg	{ XCB_EVENT_MASK_OWNER_GRAB_BUTTON,	"OwnerGrabButton" },
122110998002Smrg	{ 0, NULL } };
1222ff7e0accSmrg
1223ff7e0accSmrgstatic void
1224863f95b1SmrgDisplay_Event_Mask (long mask)
1225ff7e0accSmrg{
1226e8f4a63fSmrg    unsigned long bit, bit_mask;
1227ff7e0accSmrg
1228863f95b1Smrg    for (bit=0, bit_mask=1; bit < sizeof(long)*8; bit++, bit_mask <<= 1)
1229863f95b1Smrg	if (mask & bit_mask)
1230863f95b1Smrg	    printf ("      %s\n",
1231863f95b1Smrg		    LookupL (bit_mask, _event_mask_names));
1232ff7e0accSmrg}
1233ff7e0accSmrg
1234ff7e0accSmrg
1235ff7e0accSmrg/*
1236ff7e0accSmrg * Display info on events
1237863f95b1Smrg *
1238863f95b1Smrg * Requires wininfo members initialized:
1239863f95b1Smrg *   window, attr_cookie (or win_attributes)
1240ff7e0accSmrg */
1241ff7e0accSmrgstatic void
1242863f95b1SmrgDisplay_Events_Info (struct wininfo *w)
1243ff7e0accSmrg{
1244863f95b1Smrg    xcb_get_window_attributes_reply_t *win_attributes
1245863f95b1Smrg	= fetch_win_attributes (w);
1246ff7e0accSmrg
1247863f95b1Smrg    printf ("\n");
1248863f95b1Smrg    printf ("  Someone wants these events:\n");
1249863f95b1Smrg    Display_Event_Mask (win_attributes->all_event_masks);
1250ff7e0accSmrg
1251863f95b1Smrg    printf ("  Do not propagate these events:\n");
1252863f95b1Smrg    Display_Event_Mask (win_attributes->do_not_propagate_mask);
1253ff7e0accSmrg
1254863f95b1Smrg    printf ("  Override redirection?: %s\n",
1255863f95b1Smrg	    Lookup (win_attributes->override_redirect, _bool));
1256ff7e0accSmrg}
1257ff7e0accSmrg
1258ff7e0accSmrg
1259ff7e0accSmrg  /* left out visual stuff */
1260ff7e0accSmrg  /* left out colormap */
1261ff7e0accSmrg  /* left out map_installed */
1262ff7e0accSmrg
1263ff7e0accSmrg
1264ff7e0accSmrg/*
1265ff7e0accSmrg * Display root, parent, and (recursively) children information
1266ff7e0accSmrg * recurse - true to show children information
1267863f95b1Smrg *
1268863f95b1Smrg * Requires wininfo members initialized: window, tree_cookie
1269ff7e0accSmrg */
1270ff7e0accSmrgstatic void
1271863f95b1SmrgDisplay_Tree_Info (struct wininfo *w, int recurse)
1272ff7e0accSmrg{
1273863f95b1Smrg    display_tree_info_1 (w, recurse, 0);
1274ff7e0accSmrg}
1275ff7e0accSmrg
1276ff7e0accSmrg/*
1277ff7e0accSmrg * level - recursion level
1278ff7e0accSmrg */
1279ff7e0accSmrgstatic void
1280863f95b1Smrgdisplay_tree_info_1 (struct wininfo *w, int recurse, int level)
1281ff7e0accSmrg{
1282863f95b1Smrg    unsigned int num_children;
1283863f95b1Smrg    xcb_query_tree_reply_t *tree;
1284863f95b1Smrg
1285863f95b1Smrg    tree = xcb_query_tree_reply (dpy, w->tree_cookie, &err);
1286863f95b1Smrg    if (!tree) {
1287863f95b1Smrg	Print_X_Error (dpy, err);
1288863f95b1Smrg	Fatal_Error ("Can't query window tree.");
1289863f95b1Smrg    }
1290ff7e0accSmrg
1291863f95b1Smrg    if (level == 0) {
1292863f95b1Smrg	struct wininfo rw, pw;
1293863f95b1Smrg	rw.window = tree->root;
1294863f95b1Smrg	rw.net_wm_name_cookie = get_net_wm_name (dpy, rw.window);
1295853aa076Smrg	rw.wm_name_cookie = xcb_icccm_get_wm_name (dpy, rw.window);
1296863f95b1Smrg	pw.window = tree->parent;
1297863f95b1Smrg	pw.net_wm_name_cookie = get_net_wm_name (dpy, pw.window);
1298853aa076Smrg	pw.wm_name_cookie = xcb_icccm_get_wm_name (dpy, pw.window);
1299863f95b1Smrg	xcb_flush (dpy);
1300863f95b1Smrg
1301863f95b1Smrg	printf ("\n");
1302863f95b1Smrg	printf ("  Root window id: ");
1303863f95b1Smrg	Display_Window_Id (&rw, True);
1304863f95b1Smrg	printf ("  Parent window id: ");
1305863f95b1Smrg	Display_Window_Id (&pw, True);
1306863f95b1Smrg    }
1307863f95b1Smrg
1308863f95b1Smrg    num_children = xcb_query_tree_children_length (tree);
1309ff7e0accSmrg
1310863f95b1Smrg    if (level == 0  ||  num_children > 0) {
1311863f95b1Smrg	printf ("     ");
131283283e49Smrg	for (int j = 0; j < level; j++) printf ("   ");
1313863f95b1Smrg	printf ("%d child%s%s\n", num_children, num_children == 1 ? "" : "ren",
1314863f95b1Smrg		num_children ? ":" : ".");
1315863f95b1Smrg    }
1316863f95b1Smrg
1317863f95b1Smrg    if (num_children > 0) {
1318863f95b1Smrg	xcb_window_t *child_list = xcb_query_tree_children (tree);
1319863f95b1Smrg	struct wininfo *children
1320863f95b1Smrg	    = calloc (num_children, sizeof(struct wininfo));
1321863f95b1Smrg
1322863f95b1Smrg	if (children == NULL)
1323863f95b1Smrg	    Fatal_Error ("Failed to allocate memory in display_tree_info");
1324863f95b1Smrg
132583283e49Smrg	for (int i = (int)num_children - 1; i >= 0; i--) {
1326863f95b1Smrg	    struct wininfo *cw = &children[i];
1327863f95b1Smrg
1328863f95b1Smrg	    cw->window = child_list[i];
1329863f95b1Smrg	    cw->net_wm_name_cookie = get_net_wm_name (dpy, child_list[i]);
1330853aa076Smrg	    cw->wm_name_cookie = xcb_icccm_get_wm_name (dpy, child_list[i]);
1331853aa076Smrg	    cw->wm_class_cookie = xcb_icccm_get_wm_class (dpy, child_list[i]);
1332863f95b1Smrg	    cw->geometry_cookie = xcb_get_geometry (dpy, child_list[i]);
1333863f95b1Smrg	    cw->trans_coords_cookie = xcb_translate_coordinates
1334863f95b1Smrg		(dpy, child_list[i], tree->root, 0, 0);
1335863f95b1Smrg	    if (recurse)
1336863f95b1Smrg		cw->tree_cookie = xcb_query_tree (dpy, child_list[i]);
1337ff7e0accSmrg	}
1338863f95b1Smrg	xcb_flush (dpy);
1339863f95b1Smrg
134083283e49Smrg	for (int i = (int)num_children - 1; i >= 0; i--) {
1341863f95b1Smrg	    struct wininfo *cw = &children[i];
1342863f95b1Smrg	    Bool got_wm_class = False;
1343863f95b1Smrg	    char *instance_name = NULL, *class_name = NULL;
1344863f95b1Smrg	    int instance_name_len, class_name_len;
1345863f95b1Smrg#ifdef USE_XCB_ICCCM
1346853aa076Smrg	    xcb_icccm_get_wm_class_reply_t classhint;
1347863f95b1Smrg#else
1348863f95b1Smrg	    xcb_get_property_reply_t *classprop;
1349863f95b1Smrg#endif
1350863f95b1Smrg	    xcb_get_geometry_reply_t *geometry;
1351863f95b1Smrg
1352863f95b1Smrg	    printf ("     ");
135383283e49Smrg	    for (int j = 0; j < level; j++) printf ("   ");
1354863f95b1Smrg	    Display_Window_Id (cw, False);
1355863f95b1Smrg	    printf (": (");
1356863f95b1Smrg
1357863f95b1Smrg#ifdef USE_XCB_ICCCM
1358853aa076Smrg	    if (xcb_icccm_get_wm_class_reply (dpy, cw->wm_class_cookie,
1359863f95b1Smrg					&classhint, NULL)) {
1360863f95b1Smrg		got_wm_class = True;
1361863f95b1Smrg		instance_name = classhint.instance_name;
1362863f95b1Smrg		class_name = classhint.class_name;
1363863f95b1Smrg		instance_name_len = strlen(instance_name);
1364863f95b1Smrg		class_name_len = strlen(class_name);
1365863f95b1Smrg	    }
1366863f95b1Smrg#else
1367863f95b1Smrg	    classprop = xcb_get_property_reply
1368863f95b1Smrg		(dpy, cw->wm_class_cookie, NULL);
1369863f95b1Smrg	    if (classprop) {
1370863f95b1Smrg		if (classprop->type == XCB_ATOM_STRING &&
1371863f95b1Smrg		    classprop->format == 8) {
1372863f95b1Smrg		    int proplen = xcb_get_property_value_length (classprop);
1373863f95b1Smrg
1374863f95b1Smrg		    instance_name = xcb_get_property_value (classprop);
1375863f95b1Smrg		    instance_name_len = strnlen (instance_name, proplen);
1376863f95b1Smrg		    if (instance_name_len < proplen) {
1377863f95b1Smrg			class_name = instance_name + instance_name_len + 1;
1378863f95b1Smrg			class_name_len = strnlen
1379863f95b1Smrg			    (class_name, proplen - (instance_name_len + 1));
1380863f95b1Smrg		    } else
1381863f95b1Smrg			class_name_len = 0;
1382863f95b1Smrg		    got_wm_class = True;
1383863f95b1Smrg		}
1384863f95b1Smrg		else
1385863f95b1Smrg		    free (classprop);
1386863f95b1Smrg	    }
1387863f95b1Smrg#endif
1388863f95b1Smrg
1389863f95b1Smrg	    if (got_wm_class) {
1390863f95b1Smrg		if (instance_name)
1391863f95b1Smrg		    printf ("\"%.*s\" ", instance_name_len, instance_name);
1392863f95b1Smrg		else
1393863f95b1Smrg		    printf ("(none) ");
1394863f95b1Smrg
1395863f95b1Smrg		if (class_name)
1396863f95b1Smrg		    printf ("\"%.*s\") ",  class_name_len, class_name);
1397863f95b1Smrg		else
1398863f95b1Smrg		    printf ("(none)) ");
1399863f95b1Smrg
1400863f95b1Smrg#ifdef USE_XCB_ICCCM
1401853aa076Smrg		xcb_icccm_get_wm_class_reply_wipe (&classhint);
1402863f95b1Smrg#else
1403863f95b1Smrg		free (classprop);
1404863f95b1Smrg#endif
1405863f95b1Smrg	    } else
1406863f95b1Smrg		printf (") ");
1407863f95b1Smrg
1408863f95b1Smrg	    geometry = xcb_get_geometry_reply(dpy, cw->geometry_cookie, &err);
1409863f95b1Smrg	    if (geometry) {
1410863f95b1Smrg		xcb_translate_coordinates_reply_t *trans_coords;
1411863f95b1Smrg
1412863f95b1Smrg		printf (" %ux%u+%d+%d", geometry->width, geometry->height,
1413863f95b1Smrg					geometry->x, geometry->y);
1414863f95b1Smrg
1415863f95b1Smrg		trans_coords = xcb_translate_coordinates_reply
1416863f95b1Smrg		    (dpy, cw->trans_coords_cookie, &err);
1417863f95b1Smrg
1418863f95b1Smrg		if (trans_coords) {
1419863f95b1Smrg		    int16_t abs_x = (int16_t) trans_coords->dst_x;
1420863f95b1Smrg		    int16_t abs_y = (int16_t) trans_coords->dst_y;
1421863f95b1Smrg		    int border = geometry->border_width;
1422863f95b1Smrg
1423863f95b1Smrg		    printf ("  +%d+%d", abs_x - border, abs_y - border);
1424863f95b1Smrg		    free (trans_coords);
1425863f95b1Smrg		} else if (err) {
1426863f95b1Smrg		    Print_X_Error (dpy, err);
1427863f95b1Smrg		}
1428863f95b1Smrg
1429863f95b1Smrg		free (geometry);
1430863f95b1Smrg	    } else if (err) {
1431863f95b1Smrg		Print_X_Error (dpy, err);
1432863f95b1Smrg	    }
1433863f95b1Smrg	    printf ("\n");
1434863f95b1Smrg
1435863f95b1Smrg	    if (recurse)
1436863f95b1Smrg		display_tree_info_1 (cw, 1, level+1);
1437863f95b1Smrg
1438863f95b1Smrg	    wininfo_wipe (cw);
1439863f95b1Smrg	}
1440863f95b1Smrg	free (children);
1441ff7e0accSmrg    }
1442ff7e0accSmrg
1443863f95b1Smrg    free (tree); /* includes storage for child_list[] */
1444ff7e0accSmrg}
1445ff7e0accSmrg
1446ff7e0accSmrg
1447ff7e0accSmrg/*
1448ff7e0accSmrg * Display a set of size hints
1449ff7e0accSmrg */
1450ff7e0accSmrgstatic void
1451863f95b1SmrgDisplay_Hints (xcb_size_hints_t *hints)
1452ff7e0accSmrg{
1453863f95b1Smrg    long flags;
1454ff7e0accSmrg
1455863f95b1Smrg    flags = hints->flags;
1456ff7e0accSmrg
1457853aa076Smrg    if (flags & XCB_ICCCM_SIZE_HINT_US_POSITION)
1458863f95b1Smrg	printf ("      User supplied location: %s, %s\n",
1459863f95b1Smrg		xscale (hints->x), yscale (hints->y));
1460ff7e0accSmrg
1461853aa076Smrg    if (flags & XCB_ICCCM_SIZE_HINT_P_POSITION)
1462863f95b1Smrg	printf ("      Program supplied location: %s, %s\n",
1463863f95b1Smrg		xscale (hints->x), yscale (hints->y));
1464ff7e0accSmrg
1465853aa076Smrg    if (flags & XCB_ICCCM_SIZE_HINT_US_SIZE) {
1466863f95b1Smrg	printf ("      User supplied size: %s by %s\n",
1467863f95b1Smrg		xscale (hints->width), yscale (hints->height));
1468863f95b1Smrg    }
1469ff7e0accSmrg
1470853aa076Smrg    if (flags & XCB_ICCCM_SIZE_HINT_P_SIZE)
1471863f95b1Smrg	printf ("      Program supplied size: %s by %s\n",
1472863f95b1Smrg		xscale (hints->width), yscale (hints->height));
1473ff7e0accSmrg
1474853aa076Smrg    if (flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)
1475863f95b1Smrg	printf ("      Program supplied minimum size: %s by %s\n",
1476863f95b1Smrg		xscale (hints->min_width), yscale (hints->min_height));
1477ff7e0accSmrg
1478853aa076Smrg    if (flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)
1479863f95b1Smrg	printf ("      Program supplied maximum size: %s by %s\n",
1480863f95b1Smrg		xscale (hints->max_width), yscale (hints->max_height));
1481ff7e0accSmrg
1482853aa076Smrg    if (flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) {
1483863f95b1Smrg	printf ("      Program supplied base size: %s by %s\n",
1484863f95b1Smrg		xscale (hints->base_width), yscale (hints->base_height));
1485863f95b1Smrg    }
1486863f95b1Smrg
1487853aa076Smrg    if (flags & XCB_ICCCM_SIZE_HINT_P_RESIZE_INC) {
1488863f95b1Smrg	printf ("      Program supplied x resize increment: %s\n",
1489863f95b1Smrg		xscale (hints->width_inc));
1490863f95b1Smrg	printf ("      Program supplied y resize increment: %s\n",
1491863f95b1Smrg		yscale (hints->height_inc));
1492863f95b1Smrg	if (hints->width_inc != 0 && hints->height_inc != 0) {
1493853aa076Smrg	    if (flags & XCB_ICCCM_SIZE_HINT_US_SIZE)
1494863f95b1Smrg		printf ("      User supplied size in resize increments:  %s by %s\n",
1495863f95b1Smrg			(xscale (hints->width / hints->width_inc)),
1496863f95b1Smrg			(yscale (hints->height / hints->height_inc)));
1497853aa076Smrg	    if (flags & XCB_ICCCM_SIZE_HINT_P_SIZE)
1498863f95b1Smrg		printf ("      Program supplied size in resize increments:  %s by %s\n",
1499863f95b1Smrg			(xscale (hints->width / hints->width_inc)),
1500863f95b1Smrg			(yscale (hints->height / hints->height_inc)));
1501853aa076Smrg	    if (flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)
1502863f95b1Smrg		printf ("      Program supplied minimum size in resize increments: %s by %s\n",
1503863f95b1Smrg			xscale (hints->min_width / hints->width_inc), yscale (hints->min_height / hints->height_inc));
1504853aa076Smrg	    if (flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE)
1505863f95b1Smrg		printf ("      Program supplied base size in resize increments:  %s by %s\n",
1506863f95b1Smrg			(xscale (hints->base_width / hints->width_inc)),
1507863f95b1Smrg			(yscale (hints->base_height / hints->height_inc)));
1508ff7e0accSmrg	}
1509863f95b1Smrg    }
1510863f95b1Smrg
1511853aa076Smrg    if (flags & XCB_ICCCM_SIZE_HINT_P_ASPECT) {
1512863f95b1Smrg	printf ("      Program supplied min aspect ratio: %s/%s\n",
1513863f95b1Smrg		xscale (hints->min_aspect_num), yscale (hints->min_aspect_den));
1514863f95b1Smrg	printf ("      Program supplied max aspect ratio: %s/%s\n",
1515863f95b1Smrg		xscale (hints->max_aspect_num), yscale (hints->max_aspect_den));
1516863f95b1Smrg    }
1517863f95b1Smrg
1518853aa076Smrg    if (flags & XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY) {
1519863f95b1Smrg	printf ("      Program supplied window gravity: %s\n",
1520863f95b1Smrg		Lookup (hints->win_gravity, _gravities));
1521863f95b1Smrg    }
1522ff7e0accSmrg}
1523ff7e0accSmrg
1524ff7e0accSmrg
1525ff7e0accSmrg/*
1526ff7e0accSmrg * Display Size Hints info
1527ff7e0accSmrg */
1528ff7e0accSmrgstatic void
1529863f95b1SmrgDisplay_Size_Hints (struct wininfo *w)
1530ff7e0accSmrg{
1531863f95b1Smrg    xcb_size_hints_t hints;
1532ff7e0accSmrg
1533863f95b1Smrg    printf ("\n");
1534863f95b1Smrg    if (!fetch_normal_hints (w, &hints))
1535863f95b1Smrg	printf ("  No normal window size hints defined\n");
1536863f95b1Smrg    else {
1537863f95b1Smrg	printf ("  Normal window size hints:\n");
1538863f95b1Smrg	Display_Hints (&hints);
1539863f95b1Smrg    }
1540863f95b1Smrg
1541853aa076Smrg    if (!xcb_icccm_get_wm_size_hints_reply (dpy, w->zoom_cookie, &hints, NULL))
1542863f95b1Smrg	printf ("  No zoom window size hints defined\n");
1543863f95b1Smrg    else {
1544863f95b1Smrg	printf ("  Zoom window size hints:\n");
1545863f95b1Smrg	Display_Hints (&hints);
1546863f95b1Smrg    }
1547ff7e0accSmrg}
1548ff7e0accSmrg
1549ff7e0accSmrg
1550ff7e0accSmrgstatic void
1551863f95b1SmrgDisplay_Window_Shape (xcb_window_t window)
1552ff7e0accSmrg{
1553863f95b1Smrg    const xcb_query_extension_reply_t *shape_query;
1554863f95b1Smrg    xcb_shape_query_extents_cookie_t extents_cookie;
1555863f95b1Smrg    xcb_shape_query_extents_reply_t *extents;
1556ff7e0accSmrg
1557863f95b1Smrg    shape_query = xcb_get_extension_data (dpy, &xcb_shape_id);
1558863f95b1Smrg    if (!shape_query->present)
1559ff7e0accSmrg	return;
1560ff7e0accSmrg
1561863f95b1Smrg    printf ("\n");
1562863f95b1Smrg
1563863f95b1Smrg    extents_cookie = xcb_shape_query_extents (dpy, window);
1564863f95b1Smrg    extents = xcb_shape_query_extents_reply (dpy, extents_cookie, &err);
1565863f95b1Smrg
1566863f95b1Smrg    if (!extents) {
1567863f95b1Smrg	if (err)
1568863f95b1Smrg	    Print_X_Error (dpy, err);
1569863f95b1Smrg	else
1570863f95b1Smrg	{
1571863f95b1Smrg	    printf ("  No window shape defined\n");
1572863f95b1Smrg	    printf ("  No border shape defined\n");
1573863f95b1Smrg	}
1574863f95b1Smrg	return;
1575863f95b1Smrg    }
1576863f95b1Smrg
1577863f95b1Smrg    if (!extents->bounding_shaped)
1578863f95b1Smrg	printf ("  No window shape defined\n");
1579ff7e0accSmrg    else {
1580863f95b1Smrg	printf ("  Window shape extents:  %sx%s",
1581863f95b1Smrg		xscale (extents->bounding_shape_extents_width),
1582863f95b1Smrg		yscale (extents->bounding_shape_extents_height));
1583863f95b1Smrg	printf ("+%s+%s\n",
1584863f95b1Smrg		xscale (extents->bounding_shape_extents_x),
1585863f95b1Smrg		yscale (extents->bounding_shape_extents_y));
1586ff7e0accSmrg    }
1587863f95b1Smrg    if (!extents->clip_shaped)
1588863f95b1Smrg	printf ("  No border shape defined\n");
1589ff7e0accSmrg    else {
1590863f95b1Smrg	printf ("  Border shape extents:  %sx%s",
1591863f95b1Smrg		xscale (extents->clip_shape_extents_width),
1592863f95b1Smrg		yscale (extents->clip_shape_extents_height));
1593863f95b1Smrg	printf ("+%s+%s\n",
1594863f95b1Smrg		xscale (extents->clip_shape_extents_x),
1595863f95b1Smrg		yscale (extents->clip_shape_extents_y));
1596ff7e0accSmrg    }
1597863f95b1Smrg
1598863f95b1Smrg    free (extents);
1599ff7e0accSmrg}
1600ff7e0accSmrg
1601ff7e0accSmrg/*
1602ff7e0accSmrg * Display Window Manager Info
1603863f95b1Smrg *
1604863f95b1Smrg * Requires wininfo members initialized:
1605863f95b1Smrg *   window, hints_cookie
1606ff7e0accSmrg */
1607ff7e0accSmrgstatic const binding _state_hints[] = {
1608853aa076Smrg	{ XCB_ICCCM_WM_STATE_WITHDRAWN, "Withdrawn State" },
1609853aa076Smrg	{ XCB_ICCCM_WM_STATE_NORMAL, "Normal State" },
1610853aa076Smrg	{ XCB_ICCCM_WM_STATE_ICONIC, "Iconic State" },
1611863f95b1Smrg/* xwininfo previously also reported the ZoomState & InactiveState,
1612863f95b1Smrg   but ICCCM declared those obsolete long ago */
161310998002Smrg	{ 0, NULL } };
1614ff7e0accSmrg
1615863f95b1Smrg#ifndef USE_XCB_ICCCM
1616863f95b1Smrgstatic Bool
1617cf2cd791Smrgwm_hints_reply (xcb_connection_t *whr_dpy, xcb_get_property_cookie_t cookie,
1618cf2cd791Smrg		wm_hints_t *hints_return, xcb_generic_error_t **whr_err)
1619863f95b1Smrg{
1620cf2cd791Smrg    xcb_get_property_reply_t *prop = xcb_get_property_reply (whr_dpy, cookie, whr_err);
1621e8f4a63fSmrg    size_t length;
1622863f95b1Smrg
1623863f95b1Smrg    if (!prop || (prop->type != XCB_ATOM_WM_HINTS) || (prop->format != 32)) {
1624863f95b1Smrg	free (prop);
1625863f95b1Smrg	return False;
1626863f95b1Smrg    }
1627863f95b1Smrg
1628863f95b1Smrg    memset (hints_return, 0, sizeof(wm_hints_t));
1629863f95b1Smrg
1630e8f4a63fSmrg    length = (size_t) xcb_get_property_value_length(prop);
1631863f95b1Smrg    if (length > sizeof(wm_hints_t))
1632863f95b1Smrg	length = sizeof(wm_hints_t);
1633863f95b1Smrg    memcpy (hints_return, xcb_get_property_value (prop), length);
1634863f95b1Smrg
1635863f95b1Smrg    free (prop);
1636863f95b1Smrg    return True;
1637863f95b1Smrg}
1638863f95b1Smrg
1639853aa076Smrg#define xcb_icccm_get_wm_hints_reply wm_hints_reply
1640863f95b1Smrg#endif
1641863f95b1Smrg
1642cf2cd791Smrgstatic void
1643cf2cd791SmrgDisplay_Atom_Name (xcb_atom_t atom, const char *prefix)
1644cf2cd791Smrg{
1645cf2cd791Smrg    const char *atom_name = Get_Atom_Name (dpy, atom);
1646cf2cd791Smrg
1647cf2cd791Smrg    if (atom_name) {
1648cf2cd791Smrg	char *friendly_name = get_friendly_name (atom_name, prefix);
1649cf2cd791Smrg	printf ("          %s\n", friendly_name);
1650cf2cd791Smrg	free (friendly_name);
1651cf2cd791Smrg    } else {
1652cf2cd791Smrg	printf ("          (unresolvable ATOM 0x%x)\n", atom);
1653cf2cd791Smrg    }
1654cf2cd791Smrg}
1655cf2cd791Smrg
1656ff7e0accSmrgstatic void
1657863f95b1SmrgDisplay_WM_Info (struct wininfo *w)
1658ff7e0accSmrg{
1659853aa076Smrg    xcb_icccm_wm_hints_t wmhints;
1660863f95b1Smrg    long flags;
1661863f95b1Smrg
1662863f95b1Smrg    printf ("\n");
1663853aa076Smrg    if (!xcb_icccm_get_wm_hints_reply(dpy, w->hints_cookie, &wmhints, &err))
1664863f95b1Smrg    {
1665863f95b1Smrg	printf ("  No window manager hints defined\n");
1666863f95b1Smrg	if (err)
1667863f95b1Smrg	    Print_X_Error (dpy, err);
1668863f95b1Smrg	flags = 0;
1669863f95b1Smrg    } else
1670863f95b1Smrg	flags = wmhints.flags;
1671863f95b1Smrg
1672863f95b1Smrg    printf ("  Window manager hints:\n");
1673863f95b1Smrg
1674853aa076Smrg    if (flags & XCB_ICCCM_WM_HINT_INPUT)
1675863f95b1Smrg	printf ("      Client accepts input or input focus: %s\n",
1676863f95b1Smrg		Lookup (wmhints.input, _bool));
1677863f95b1Smrg
1678853aa076Smrg    if (flags & XCB_ICCCM_WM_HINT_ICON_WINDOW) {
1679863f95b1Smrg	struct wininfo iw;
1680863f95b1Smrg	iw.window = wmhints.icon_window;
1681863f95b1Smrg	iw.net_wm_name_cookie = get_net_wm_name (dpy, iw.window);
1682853aa076Smrg	iw.wm_name_cookie = xcb_icccm_get_wm_name (dpy, iw.window);
1683863f95b1Smrg
1684863f95b1Smrg	printf ("      Icon window id: ");
1685863f95b1Smrg	Display_Window_Id (&iw, True);
1686863f95b1Smrg    }
1687863f95b1Smrg
1688853aa076Smrg    if (flags & XCB_ICCCM_WM_HINT_ICON_POSITION)
1689863f95b1Smrg	printf ("      Initial icon position: %s, %s\n",
1690863f95b1Smrg		xscale (wmhints.icon_x), yscale (wmhints.icon_y));
1691863f95b1Smrg
1692853aa076Smrg    if (flags & XCB_ICCCM_WM_HINT_STATE)
1693863f95b1Smrg	printf ("      Initial state is %s\n",
1694863f95b1Smrg		Lookup (wmhints.initial_state, _state_hints));
1695863f95b1Smrg
1696863f95b1Smrg    if (atom_net_wm_desktop) {
169783283e49Smrg	xcb_get_property_reply_t *prop;
169883283e49Smrg
1699863f95b1Smrg	prop = xcb_get_property_reply (dpy, w->wm_desktop_cookie, NULL);
1700863f95b1Smrg	if (prop && (prop->type != XCB_NONE)) {
1701863f95b1Smrg	    uint32_t *desktop = xcb_get_property_value (prop);
1702863f95b1Smrg	    if (*desktop == 0xFFFFFFFF) {
1703863f95b1Smrg		printf ("      Displayed on all desktops\n");
1704863f95b1Smrg	    } else {
1705863f95b1Smrg		printf ("      Displayed on desktop %d\n", *desktop);
1706863f95b1Smrg	    }
1707ff7e0accSmrg	}
1708863f95b1Smrg	free (prop);
1709863f95b1Smrg    }
1710ff7e0accSmrg
1711863f95b1Smrg    if (atom_net_wm_window_type) {
171283283e49Smrg	xcb_get_property_reply_t *prop;
171383283e49Smrg
1714863f95b1Smrg	prop = xcb_get_property_reply (dpy, w->wm_window_type_cookie,
1715863f95b1Smrg				       NULL);
1716863f95b1Smrg	if (prop && (prop->type != XCB_NONE) && (prop->value_len > 0)) {
1717863f95b1Smrg	    xcb_atom_t *atoms = xcb_get_property_value (prop);
1718863f95b1Smrg	    int atom_count = prop->value_len;
1719863f95b1Smrg
1720863f95b1Smrg	    if (atom_count > 0) {
1721863f95b1Smrg		printf ("      Window type:\n");
172283283e49Smrg		for (int i = 0; i < atom_count; i++)
1723cf2cd791Smrg		    Display_Atom_Name (atoms[i], "_NET_WM_WINDOW_TYPE_");
1724863f95b1Smrg	    }
1725863f95b1Smrg	}
1726863f95b1Smrg	free (prop);
1727863f95b1Smrg    }
1728863f95b1Smrg
1729863f95b1Smrg    if (atom_net_wm_state) {
173083283e49Smrg	xcb_get_property_reply_t *prop;
173183283e49Smrg
1732863f95b1Smrg	prop = xcb_get_property_reply (dpy, w->wm_state_cookie, NULL);
1733863f95b1Smrg	if (prop && (prop->type != XCB_NONE) && (prop->value_len > 0)) {
1734863f95b1Smrg	    xcb_atom_t *atoms = xcb_get_property_value (prop);
1735863f95b1Smrg	    int atom_count = prop->value_len;
1736863f95b1Smrg
1737863f95b1Smrg	    if (atom_count > 0) {
1738863f95b1Smrg		printf ("      Window state:\n");
173983283e49Smrg		for (int i = 0; i < atom_count; i++)
1740cf2cd791Smrg		    Display_Atom_Name (atoms[i], "_NET_WM_STATE_");
1741863f95b1Smrg	    }
1742863f95b1Smrg	}
1743863f95b1Smrg	free (prop);
1744863f95b1Smrg    }
1745ff7e0accSmrg
1746863f95b1Smrg    if (atom_net_wm_pid) {
174783283e49Smrg	xcb_get_property_reply_t *prop;
174883283e49Smrg
1749863f95b1Smrg	printf ("      Process id: ");
1750863f95b1Smrg	prop = xcb_get_property_reply (dpy, w->wm_pid_cookie, NULL);
1751863f95b1Smrg	if (prop && (prop->type == XCB_ATOM_CARDINAL)) {
1752863f95b1Smrg	    uint32_t *pid = xcb_get_property_value (prop);
1753863f95b1Smrg	    printf ("%d", *pid);
1754863f95b1Smrg	} else {
1755863f95b1Smrg	    printf ("(unknown)");
1756863f95b1Smrg	}
1757863f95b1Smrg	free (prop);
1758ff7e0accSmrg
1759863f95b1Smrg	prop = xcb_get_property_reply (dpy, w->wm_client_machine_cookie, NULL);
1760863f95b1Smrg	if (prop && (prop->type == XCB_ATOM_STRING)) {
1761863f95b1Smrg	    const char *hostname = xcb_get_property_value (prop);
1762863f95b1Smrg	    int hostname_len = xcb_get_property_value_length (prop);
1763863f95b1Smrg	    printf (" on host %.*s", hostname_len, hostname);
1764ff7e0accSmrg	}
1765863f95b1Smrg	printf ("\n");
1766863f95b1Smrg	free (prop);
1767863f95b1Smrg    }
1768863f95b1Smrg
1769863f95b1Smrg    if (atom_net_frame_extents) {
177083283e49Smrg	xcb_get_property_reply_t *prop;
177183283e49Smrg
1772863f95b1Smrg	prop = xcb_get_property_reply (dpy, w->frame_extents_cookie, NULL);
1773863f95b1Smrg	if (prop && (prop->type == XCB_ATOM_CARDINAL)
1774863f95b1Smrg	    && (prop->value_len == 4)) {
1775863f95b1Smrg	    uint32_t *extents = xcb_get_property_value (prop);
1776863f95b1Smrg
1777863f95b1Smrg	    printf ("      Frame extents: %d, %d, %d, %d\n",
1778863f95b1Smrg		    extents[0], extents[1], extents[2], extents[3]);
1779863f95b1Smrg	}
1780863f95b1Smrg	free (prop);
1781863f95b1Smrg    }
1782863f95b1Smrg}
1783863f95b1Smrg
1784863f95b1Smrg/* Frees all members of a wininfo struct, but not the struct itself */
1785863f95b1Smrgstatic void
1786863f95b1Smrgwininfo_wipe (struct wininfo *w)
1787863f95b1Smrg{
1788863f95b1Smrg    free (w->geometry);
1789863f95b1Smrg    free (w->win_attributes);
1790863f95b1Smrg    free (w->normal_hints);
1791863f95b1Smrg}
1792863f95b1Smrg
1793863f95b1Smrg/* Gets UTF-8 encoded EMWH property _NET_WM_NAME for a window */
1794863f95b1Smrgstatic xcb_get_property_cookie_t
1795cf2cd791Smrgget_net_wm_name (xcb_connection_t *gnwn_dpy, xcb_window_t win)
1796863f95b1Smrg{
1797863f95b1Smrg    if (!atom_net_wm_name)
1798cf2cd791Smrg	atom_net_wm_name = Get_Atom (gnwn_dpy, "_NET_WM_NAME");
1799863f95b1Smrg
1800863f95b1Smrg    if (!atom_utf8_string)
1801cf2cd791Smrg	atom_utf8_string = Get_Atom (gnwn_dpy, "UTF8_STRING");
1802863f95b1Smrg
1803863f95b1Smrg    if (atom_net_wm_name && atom_utf8_string)
1804cf2cd791Smrg	return xcb_get_property (gnwn_dpy, False, win, atom_net_wm_name,
1805863f95b1Smrg				 atom_utf8_string, 0, BUFSIZ);
1806863f95b1Smrg    else {
1807863f95b1Smrg	xcb_get_property_cookie_t dummy = { 0 };
1808863f95b1Smrg	return dummy;
1809863f95b1Smrg    }
1810863f95b1Smrg}
1811863f95b1Smrg
1812863f95b1Smrg/* [Copied from code added by Yang Zhao to xprop/xprop.c]
1813863f95b1Smrg *
1814863f95b1Smrg * Validate a string as UTF-8 encoded according to RFC 3629
1815863f95b1Smrg *
1816863f95b1Smrg * Simply, a unicode code point (up to 21-bits long) is encoded as follows:
1817863f95b1Smrg *
1818863f95b1Smrg *    Char. number range  |        UTF-8 octet sequence
1819863f95b1Smrg *       (hexadecimal)    |              (binary)
1820863f95b1Smrg *    --------------------+---------------------------------------------
1821863f95b1Smrg *    0000 0000-0000 007F | 0xxxxxxx
1822863f95b1Smrg *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
1823863f95b1Smrg *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
1824863f95b1Smrg *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1825863f95b1Smrg *
1826863f95b1Smrg * Validation is done left-to-right, and an error condition, if any, refers to
1827863f95b1Smrg * only the left-most problem in the string.
1828863f95b1Smrg *
1829863f95b1Smrg * Return values:
1830863f95b1Smrg *   UTF8_VALID: Valid UTF-8 encoded string
1831863f95b1Smrg *   UTF8_OVERLONG: Using more bytes than needed for a code point
1832863f95b1Smrg *   UTF8_SHORT_TAIL: Not enough bytes in a multi-byte sequence
1833863f95b1Smrg *   UTF8_LONG_TAIL: Too many bytes in a multi-byte sequence
1834863f95b1Smrg *   UTF8_FORBIDDEN_VALUE: Forbidden prefix or code point outside 0x10FFFF
1835863f95b1Smrg */
1836863f95b1Smrg#define UTF8_VALID 0
1837863f95b1Smrg#define UTF8_FORBIDDEN_VALUE 1
1838863f95b1Smrg#define UTF8_OVERLONG 2
1839863f95b1Smrg#define UTF8_SHORT_TAIL 3
1840863f95b1Smrg#define UTF8_LONG_TAIL 4
1841863f95b1Smrgstatic int
1842cf2cd791Smrgis_valid_utf8 (const char *string, size_t len)
1843863f95b1Smrg{
1844863f95b1Smrg    unsigned long codepoint;
184583283e49Smrg    int rem = 0;
1846863f95b1Smrg
184783283e49Smrg    for (size_t i = 0; i < len; i++) {
184883283e49Smrg	unsigned char c = (unsigned char) string[i];
1849863f95b1Smrg
1850863f95b1Smrg	/* Order of type check:
1851863f95b1Smrg	 *   - Single byte code point
1852863f95b1Smrg	 *   - Non-starting byte of multi-byte sequence
1853863f95b1Smrg	 *   - Start of 2-byte sequence
1854863f95b1Smrg	 *   - Start of 3-byte sequence
1855863f95b1Smrg	 *   - Start of 4-byte sequence
1856863f95b1Smrg	 */
1857863f95b1Smrg	if (!(c & 0x80)) {
1858863f95b1Smrg	    if (rem > 0) return UTF8_SHORT_TAIL;
1859863f95b1Smrg	    rem = 0;
1860863f95b1Smrg	    codepoint = c;
1861863f95b1Smrg	} else if ((c & 0xC0) == 0x80) {
1862863f95b1Smrg	    if (rem == 0) return UTF8_LONG_TAIL;
1863863f95b1Smrg	    rem--;
1864863f95b1Smrg	    codepoint |= (c & 0x3F) << (rem * 6);
1865863f95b1Smrg	    if (codepoint == 0) return UTF8_OVERLONG;
1866863f95b1Smrg	} else if ((c & 0xE0) == 0xC0) {
1867863f95b1Smrg	    if (rem > 0) return UTF8_SHORT_TAIL;
1868863f95b1Smrg	    rem = 1;
1869863f95b1Smrg	    codepoint = (c & 0x1F) << 6;
1870863f95b1Smrg	    if (codepoint == 0) return UTF8_OVERLONG;
1871863f95b1Smrg	} else if ((c & 0xF0) == 0xE0) {
1872863f95b1Smrg	    if (rem > 0) return UTF8_SHORT_TAIL;
1873863f95b1Smrg	    rem = 2;
1874863f95b1Smrg	    codepoint = (c & 0x0F) << 12;
1875863f95b1Smrg	} else if ((c & 0xF8) == 0xF0) {
1876863f95b1Smrg	    if (rem > 0) return UTF8_SHORT_TAIL;
1877863f95b1Smrg	    rem = 3;
1878863f95b1Smrg	    codepoint = (c & 0x07) << 18;
1879863f95b1Smrg	    if (codepoint > 0x10FFFF) return UTF8_FORBIDDEN_VALUE;
1880863f95b1Smrg	} else
1881863f95b1Smrg	    return UTF8_FORBIDDEN_VALUE;
1882863f95b1Smrg    }
1883ff7e0accSmrg
1884863f95b1Smrg    return UTF8_VALID;
1885863f95b1Smrg}
1886863f95b1Smrg
1887863f95b1Smrg/*
1888863f95b1Smrg * Converts a UTF-8 encoded string to the current locale encoding,
1889863f95b1Smrg * if possible, and prints it, with prefix before and suffix after.
1890863f95b1Smrg * Length of the string is specified in bytes, or -1 for going until '\0'
1891863f95b1Smrg */
1892863f95b1Smrgstatic void
1893857c29c5Skamilprint_utf8 (const char *prefix, char *u8str, size_t length, const char *suffix)
1894863f95b1Smrg{
1895863f95b1Smrg    size_t inlen = length;
1896863f95b1Smrg
1897863f95b1Smrg    if (is_valid_utf8 (u8str, inlen) != UTF8_VALID) {
1898863f95b1Smrg	printf (" (invalid UTF8_STRING)");
1899863f95b1Smrg	return;
1900863f95b1Smrg    }
1901863f95b1Smrg
1902863f95b1Smrg    if (strcmp (user_encoding, "UTF-8") == 0) {
1903863f95b1Smrg	/* Don't need to convert */
1904863f95b1Smrg	printf ("%s", prefix);
1905863f95b1Smrg	fwrite (u8str, 1, inlen, stdout);
1906863f95b1Smrg	printf ("%s", suffix);
1907863f95b1Smrg	return;
1908863f95b1Smrg    }
1909863f95b1Smrg
1910863f95b1Smrg#ifdef HAVE_ICONV
1911863f95b1Smrg    if (!iconv_from_utf8) {
1912863f95b1Smrg	iconv_from_utf8 = iconv_open (user_encoding, "UTF-8");
1913863f95b1Smrg    }
1914863f95b1Smrg
1915863f95b1Smrg    if (iconv_from_utf8 != (iconv_t) -1) {
1916863f95b1Smrg	Bool done = True;
191783283e49Smrg	ICONV_CONST char *inp = (ICONV_CONST char *) u8str;
1918863f95b1Smrg	char convbuf[BUFSIZ];
1919863f95b1Smrg
1920863f95b1Smrg	printf ("%s", prefix);
1921863f95b1Smrg	do {
1922863f95b1Smrg	    char *outp = convbuf;
1923863f95b1Smrg	    size_t outlen = sizeof(convbuf);
1924863f95b1Smrg
192583283e49Smrg	    int convres = iconv (iconv_from_utf8, &inp, &inlen, &outp, &outlen);
1926ff7e0accSmrg
1927863f95b1Smrg	    if ((convres == -1) && (errno == E2BIG)) {
1928863f95b1Smrg		done = False;
1929863f95b1Smrg		convres = 0;
1930863f95b1Smrg	    }
1931863f95b1Smrg
1932863f95b1Smrg	    if (convres == 0) {
1933863f95b1Smrg		fwrite (convbuf, 1, sizeof(convbuf) - outlen, stdout);
1934863f95b1Smrg	    } else {
1935863f95b1Smrg		printf (" (failure in conversion from UTF8_STRING to %s)",
1936863f95b1Smrg			user_encoding);
1937863f95b1Smrg	    }
1938863f95b1Smrg	} while (!done);
1939863f95b1Smrg	printf ("%s", suffix);
1940863f95b1Smrg    } else {
1941863f95b1Smrg	printf (" (can't load iconv conversion for UTF8_STRING to %s)",
1942863f95b1Smrg		user_encoding);
1943863f95b1Smrg    }
1944863f95b1Smrg#else
1945863f95b1Smrg    printf (" (can't convert UTF8_STRING to %s)", user_encoding);
1946863f95b1Smrg#endif
1947863f95b1Smrg}
1948863f95b1Smrg
1949863f95b1Smrg/*
1950863f95b1Smrg * Takes a string such as an atom name, strips the prefix, converts
1951863f95b1Smrg * underscores to spaces, lowercases all but the first letter of each word,
1952cf2cd791Smrg * and returns it. The returned string should be freed by the caller.
1953863f95b1Smrg */
1954cf2cd791Smrgstatic char *
1955cf2cd791Smrgget_friendly_name (const char *string, const char *prefix)
1956863f95b1Smrg{
1957863f95b1Smrg    const char *name_start = string;
195883283e49Smrg    char *lowered_name;
1959cf2cd791Smrg    Bool first = True;
1960cf2cd791Smrg    size_t prefix_len = strlen (prefix);
1961863f95b1Smrg
1962863f95b1Smrg    if (strncmp (name_start, prefix, prefix_len) == 0) {
1963863f95b1Smrg	name_start += prefix_len;
1964863f95b1Smrg    }
1965863f95b1Smrg
1966863f95b1Smrg    lowered_name = strdup (name_start);
1967cf2cd791Smrg    if (lowered_name == NULL)
1968cf2cd791Smrg	Fatal_Error ("Failed to allocate memory in get_friendly_name");
1969cf2cd791Smrg
197083283e49Smrg    for (char *n = lowered_name ; *n != 0 ; n++) {
1971cf2cd791Smrg	if (*n == '_') {
1972cf2cd791Smrg	    *n = ' ';
1973cf2cd791Smrg	    first = True;
1974cf2cd791Smrg	} else if (first) {
1975cf2cd791Smrg	    first = False;
1976cf2cd791Smrg	} else {
1977cf2cd791Smrg	    *n = tolower((unsigned char)*n);
1978863f95b1Smrg	}
1979863f95b1Smrg    }
1980ff7e0accSmrg
1981cf2cd791Smrg    return lowered_name;
1982ff7e0accSmrg}
1983