1a850946eSmrg/*
2a850946eSmrgCopyright 1989, 1998  The Open Group
37dff02feSmrgCopyright 2009  Open Text Corporation
4a850946eSmrg
5a850946eSmrgPermission to use, copy, modify, distribute, and sell this software and its
6a850946eSmrgdocumentation for any purpose is hereby granted without fee, provided that
7a850946eSmrgthe above copyright notice appear in all copies and that both that
8a850946eSmrgcopyright notice and this permission notice appear in supporting
9a850946eSmrgdocumentation.
10a850946eSmrg
11a850946eSmrgThe above copyright notice and this permission notice shall be included in
12a850946eSmrgall copies or substantial portions of the Software.
13a850946eSmrg
14a850946eSmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15a850946eSmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16a850946eSmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17a850946eSmrgOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18a850946eSmrgAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19a850946eSmrgCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20a850946eSmrg
21a850946eSmrgExcept as contained in this notice, the name of The Open Group shall not be
22a850946eSmrgused in advertising or otherwise to promote the sale, use or other dealings
23a850946eSmrgin this Software without prior written authorization from The Open Group.
24a850946eSmrg * *
25a850946eSmrg * Author:  Jim Fulton, MIT X Consortium
267dff02feSmrg * Author:  Peter Harris, Open Text Corporation
27a850946eSmrg */
287dff02feSmrg
297dff02feSmrg#ifdef HAVE_CONFIG_H
307dff02feSmrg#include "config.h"
317dff02feSmrg#endif
32a850946eSmrg
33a850946eSmrg#include <stdio.h>
34a850946eSmrg#include <stdlib.h>
357dff02feSmrg#include <string.h>
36a850946eSmrg#include <ctype.h>
377dff02feSmrg#include <inttypes.h>
387dff02feSmrg#include <xcb/xcb.h>
397dff02feSmrg#include <xcb/xproto.h>
407dff02feSmrg#ifndef HAVE_STRNLEN
417dff02feSmrg#include "strnlen.h"
427dff02feSmrg#endif
437dff02feSmrg
447dff02feSmrg#ifndef PRIx32
457dff02feSmrg#define PRIx32 "x"
467dff02feSmrg#endif
477dff02feSmrg#ifndef PRIu32
487dff02feSmrg#define PRIu32 "u"
497dff02feSmrg#endif
50a850946eSmrg
51da4b5163Smrg#ifndef __has_attribute
52da4b5163Smrg# define __has_attribute(x) 0  /* Compatibility with older compilers. */
53da4b5163Smrg#endif
54da4b5163Smrg
5570f7c90cSmrgstatic char *ProgramName;
56a850946eSmrg
577dff02feSmrgstatic xcb_atom_t WM_STATE;
587dff02feSmrg
597dff02feSmrgstatic void lookat (xcb_connection_t *dpy, xcb_window_t root, int verbose, int maxcmdlen);
607dff02feSmrgstatic void print_client_properties (xcb_connection_t *dpy, xcb_window_t w,
617dff02feSmrg				     int verbose, int maxcmdlen );
629511053fSmrgstatic void print_text_field (xcb_connection_t *dpy, const char *s, xcb_get_property_reply_t *tp );
637dff02feSmrgstatic int print_quoted_word (char *s, int maxlen);
647dff02feSmrgstatic void unknown (xcb_connection_t *dpy, xcb_atom_t actual_type, int actual_format );
657dff02feSmrg
667dff02feSmrg/* For convenience: */
677dff02feSmrgtypedef int Bool;
687dff02feSmrg#define False (0)
697dff02feSmrg#define True (!False)
70a850946eSmrg
71da4b5163Smrgstatic void
72da4b5163Smrg#if __has_attribute(__cold__)
73da4b5163Smrg__attribute__((__cold__))
74da4b5163Smrg#endif
75da4b5163Smrg#if __has_attribute(noreturn)
76da4b5163Smrg__attribute__((noreturn))
77da4b5163Smrg#endif
78c44a0236Smrgusage(const char *errmsg)
79a850946eSmrg{
80c44a0236Smrg    if (errmsg != NULL)
81c44a0236Smrg	fprintf (stderr, "%s: %s\n\n", ProgramName, errmsg);
82c44a0236Smrg
83a850946eSmrg    fprintf (stderr,
849511053fSmrg	     "usage:  %s  [-display dpy] [-m len] [-[a][l]] [-version]\n",
859511053fSmrg	     ProgramName);
86a850946eSmrg    exit (1);
87a850946eSmrg}
88a850946eSmrg
897dff02feSmrgtypedef void (*queue_func)(void *closure);
907dff02feSmrgtypedef struct queue_blob {
917dff02feSmrg    queue_func func;
927dff02feSmrg    void *closure;
937dff02feSmrg    struct queue_blob *next;
947dff02feSmrg} queue_blob;
957dff02feSmrg
967dff02feSmrgstatic queue_blob *head = NULL;
977dff02feSmrgstatic queue_blob **tail = &head;
987dff02feSmrg
997dff02feSmrgstatic void enqueue(queue_func func, void *closure)
1007dff02feSmrg{
1017dff02feSmrg    queue_blob *blob = malloc(sizeof(*blob));
1027dff02feSmrg    if (!blob)
1037dff02feSmrg	return; /* TODO: print OOM error */
1047dff02feSmrg
1057dff02feSmrg    blob->func = func;
1067dff02feSmrg    blob->closure = closure;
1077dff02feSmrg    blob->next = NULL;
1087dff02feSmrg    *tail = blob;
1097dff02feSmrg    tail = &blob->next;
1107dff02feSmrg}
1117dff02feSmrg
1127dff02feSmrgstatic void run_queue(void)
1137dff02feSmrg{
1147dff02feSmrg    while (head) {
1157dff02feSmrg	queue_blob *blob = head;
1167dff02feSmrg	blob->func(blob->closure);
1177dff02feSmrg	head = blob->next;
1187dff02feSmrg	free(blob);
1197dff02feSmrg    }
1207dff02feSmrg    tail = &head;
1217dff02feSmrg}
1227dff02feSmrg
1237dff02feSmrgtypedef struct {
1247dff02feSmrg    xcb_connection_t *c;
1257dff02feSmrg    xcb_intern_atom_cookie_t cookie;
1267dff02feSmrg    xcb_atom_t *atom;
1277dff02feSmrg} atom_state;
1287dff02feSmrg
1297dff02feSmrgstatic void atom_done(void *closure)
1307dff02feSmrg{
1317dff02feSmrg    xcb_intern_atom_reply_t *reply;
1327dff02feSmrg    atom_state *as = closure;
1337dff02feSmrg
1347dff02feSmrg    reply = xcb_intern_atom_reply(as->c, as->cookie, NULL);
1357dff02feSmrg    if (!reply)
1367dff02feSmrg	goto done; /* TODO: print Error message */
1377dff02feSmrg
1387dff02feSmrg    *(as->atom) = reply->atom;
1397dff02feSmrg    free(reply);
1407dff02feSmrg
1417dff02feSmrgdone:
1427dff02feSmrg    free(as);
1437dff02feSmrg}
1447dff02feSmrg
1457dff02feSmrgstatic void init_atoms(xcb_connection_t *c)
1467dff02feSmrg{
1477dff02feSmrg    atom_state *as;
1487dff02feSmrg
1497dff02feSmrg    as = malloc(sizeof(*as));
1507dff02feSmrg    as->c = c;
1517dff02feSmrg    as->atom = &WM_STATE;
1527dff02feSmrg    as->cookie = xcb_intern_atom(c, 0, strlen("WM_STATE"), "WM_STATE");
1537dff02feSmrg    enqueue(atom_done, as);
1547dff02feSmrg}
1557dff02feSmrg
156a850946eSmrgint
157a850946eSmrgmain(int argc, char *argv[])
158a850946eSmrg{
159a850946eSmrg    int i;
160a850946eSmrg    char *displayname = NULL;
161a850946eSmrg    Bool all_screens = False;
162a850946eSmrg    Bool verbose = False;
1637dff02feSmrg    xcb_connection_t *dpy;
1647dff02feSmrg    const xcb_setup_t *setup;
1657dff02feSmrg    int screen_number = 0;
166a850946eSmrg    int maxcmdlen = 10000;
167a850946eSmrg
168a850946eSmrg    ProgramName = argv[0];
169a850946eSmrg
170a850946eSmrg    for (i = 1; i < argc; i++) {
171a850946eSmrg	char *arg = argv[i];
172a850946eSmrg
173a850946eSmrg	if (arg[0] == '-') {
174a850946eSmrg	    char *cp;
175a850946eSmrg
176a850946eSmrg	    switch (arg[1]) {
177a850946eSmrg	      case 'd':			/* -display dpyname */
178c44a0236Smrg		if (++i >= argc) usage ("-display requires an argument");
179a850946eSmrg		displayname = argv[i];
180a850946eSmrg		continue;
181a850946eSmrg	      case 'm':			/* -max maxcmdlen */
182c44a0236Smrg		if (++i >= argc) usage ("-max requires an argument");
183a850946eSmrg		maxcmdlen = atoi (argv[i]);
184a850946eSmrg		continue;
1859511053fSmrg	      case 'v':			/* -version */
1869511053fSmrg		printf("%s\n", PACKAGE_STRING);
1879511053fSmrg		exit(0);
188a850946eSmrg	    }
189a850946eSmrg
190a850946eSmrg	    for (cp = &arg[1]; *cp; cp++) {
191a850946eSmrg		switch (*cp) {
192a850946eSmrg		  case 'a':		/* -all */
193a850946eSmrg		    all_screens = True;
194a850946eSmrg		    continue;
195a850946eSmrg		  case 'l':		/* -long */
196a850946eSmrg		    verbose = True;
197a850946eSmrg		    continue;
198a850946eSmrg		  default:
199c44a0236Smrg		    fprintf (stderr, "%s: unrecognized argument -%s\n\n",
200c44a0236Smrg			     ProgramName, cp);
201c44a0236Smrg		    usage (NULL);
202a850946eSmrg		}
203a850946eSmrg	    }
204a850946eSmrg	} else {
205c44a0236Smrg	    fprintf (stderr, "%s: unrecognized argument %s\n\n",
206c44a0236Smrg		     ProgramName, arg);
207c44a0236Smrg	    usage (NULL);
208a850946eSmrg	}
209a850946eSmrg    }
210a850946eSmrg
2117dff02feSmrg    dpy = xcb_connect(displayname, &screen_number);
2127dff02feSmrg    if (xcb_connection_has_error(dpy)) {
2139511053fSmrg	const char *name = displayname;
2147dff02feSmrg	if (!name)
2157dff02feSmrg	    name = getenv("DISPLAY");
2167dff02feSmrg	if (!name)
2177dff02feSmrg	    name = "";
218a850946eSmrg	fprintf (stderr, "%s:  unable to open display \"%s\"\r\n",
2197dff02feSmrg		 ProgramName, name);
220a850946eSmrg	exit (1);
221a850946eSmrg    }
222a850946eSmrg
2237dff02feSmrg    init_atoms(dpy);
2247dff02feSmrg
2257dff02feSmrg    setup = xcb_get_setup(dpy);
226a850946eSmrg    if (all_screens) {
2277dff02feSmrg	xcb_screen_iterator_t screen;
2287dff02feSmrg
2297dff02feSmrg	screen = xcb_setup_roots_iterator(setup);
2307dff02feSmrg	do {
2317dff02feSmrg	    lookat(dpy, screen.data->root, verbose, maxcmdlen);
2327dff02feSmrg	    xcb_screen_next(&screen);
2337dff02feSmrg	} while (screen.rem);
234a850946eSmrg    } else {
2357dff02feSmrg	xcb_screen_iterator_t screen;
2367dff02feSmrg
2377dff02feSmrg	screen = xcb_setup_roots_iterator(setup);
2387dff02feSmrg	for (i = 0; i < screen_number; i++)
2397dff02feSmrg	    xcb_screen_next(&screen);
2407dff02feSmrg
2417dff02feSmrg	lookat (dpy, screen.data->root, verbose, maxcmdlen);
242a850946eSmrg    }
243a850946eSmrg
2447dff02feSmrg    run_queue();
2457dff02feSmrg
2467dff02feSmrg    xcb_disconnect(dpy);
247a850946eSmrg    exit (0);
248a850946eSmrg}
249a850946eSmrg
2507dff02feSmrgtypedef struct {
2517dff02feSmrg    xcb_connection_t *c;
2527dff02feSmrg    xcb_get_property_cookie_t *prop_cookie;
2537dff02feSmrg    xcb_query_tree_cookie_t *tree_cookie;
2547dff02feSmrg    xcb_window_t *win;
2557dff02feSmrg    xcb_window_t orig_win;
256da4b5163Smrg    unsigned int list_length;
2577dff02feSmrg    int verbose;
2587dff02feSmrg    int maxcmdlen;
2597dff02feSmrg} child_wm_state;
2607dff02feSmrg
2617dff02feSmrgstatic void child_info(void *closure)
2627dff02feSmrg{
2637dff02feSmrg    child_wm_state *cs = closure;
2647dff02feSmrg    xcb_window_t orig = cs->orig_win;
2657dff02feSmrg    xcb_connection_t *c = cs->c;
2667dff02feSmrg    int verbose = cs->verbose;
2677dff02feSmrg    int maxcmdlen = cs->maxcmdlen;
268da4b5163Smrg    unsigned int i, j;
2697dff02feSmrg
270da4b5163Smrg    unsigned int child_count, num_rep;
2719511053fSmrg    xcb_query_tree_reply_t **qt_reply;
2727dff02feSmrg
2737dff02feSmrg    for (i = 0; i < cs->list_length; i++) {
2749511053fSmrg	xcb_get_property_reply_t *gp_reply;
2759511053fSmrg	gp_reply = xcb_get_property_reply(c, cs->prop_cookie[i], NULL);
2769511053fSmrg	if (gp_reply) {
2779511053fSmrg	    if (gp_reply->type) {
2787dff02feSmrg		/* Show information for this window */
2797dff02feSmrg		print_client_properties(c, cs->win[i], cs->verbose, cs->maxcmdlen);
2807dff02feSmrg
2819511053fSmrg		free(gp_reply);
2827dff02feSmrg
2837dff02feSmrg		/* drain stale replies */
2847dff02feSmrg		for (j = i+1; j < cs->list_length; j++) {
2859511053fSmrg		    gp_reply = xcb_get_property_reply(c, cs->prop_cookie[j], NULL);
2869511053fSmrg		    if (gp_reply)
2879511053fSmrg			free(gp_reply);
2887dff02feSmrg		}
2897dff02feSmrg		for (j = 0; j < cs->list_length; j++) {
2907dff02feSmrg		    xcb_query_tree_reply_t *rep;
2917dff02feSmrg		    rep = xcb_query_tree_reply(c, cs->tree_cookie[j], NULL);
2927dff02feSmrg		    if (rep)
2937dff02feSmrg			free(rep);
2947dff02feSmrg		}
2957dff02feSmrg		goto done;
2967dff02feSmrg	    }
2979511053fSmrg	    free(gp_reply);
2987dff02feSmrg	}
2997dff02feSmrg    }
3007dff02feSmrg
3017dff02feSmrg    /* WM_STATE not found. Recurse into children: */
3027dff02feSmrg    num_rep = 0;
3039511053fSmrg    qt_reply = malloc(sizeof(*qt_reply) * cs->list_length);
3049511053fSmrg    if (!qt_reply)
3057dff02feSmrg	goto done; /* TODO: print OOM message, drain reply queue */
3067dff02feSmrg
3077dff02feSmrg    for (i = 0; i < cs->list_length; i++) {
3089511053fSmrg	qt_reply[num_rep] = xcb_query_tree_reply(c, cs->tree_cookie[i], NULL);
3099511053fSmrg	if (qt_reply[num_rep])
3107dff02feSmrg	    num_rep++;
3117dff02feSmrg    }
3127dff02feSmrg
3137dff02feSmrg    child_count = 0;
3147dff02feSmrg    for (i = 0; i < num_rep; i++)
3159511053fSmrg	child_count += qt_reply[i]->children_len;
3167dff02feSmrg
3177dff02feSmrg    if (!child_count) {
3187dff02feSmrg	/* No children have CS_STATE; try the parent window */
3197dff02feSmrg	print_client_properties(c, cs->orig_win, cs->verbose, cs->maxcmdlen);
3207dff02feSmrg	goto reply_done;
3217dff02feSmrg    }
3227dff02feSmrg
3237dff02feSmrg    cs = malloc(sizeof(*cs) + child_count * (sizeof(*cs->prop_cookie) + sizeof(*cs->tree_cookie) + sizeof(*cs->win)));
3247dff02feSmrg    if (!cs)
3257dff02feSmrg	goto reply_done; /* TODO: print OOM message */
3267dff02feSmrg
3277dff02feSmrg    cs->c = c;
3287dff02feSmrg    cs->verbose = verbose;
3297dff02feSmrg    cs->maxcmdlen = maxcmdlen;
3307dff02feSmrg    cs->orig_win = orig;
3317dff02feSmrg    cs->prop_cookie = (void *)&cs[1];
3327dff02feSmrg    cs->tree_cookie = (void *)&cs->prop_cookie[child_count];
3337dff02feSmrg    cs->win = (void *)&cs->tree_cookie[child_count];
3347dff02feSmrg    cs->list_length = child_count;
3357dff02feSmrg
3367dff02feSmrg    child_count = 0;
3377dff02feSmrg    for (i = 0; i < num_rep; i++) {
3389511053fSmrg	xcb_window_t *child = xcb_query_tree_children(qt_reply[i]);
3399511053fSmrg	for (j = 0; j < qt_reply[i]->children_len; j++) {
3407dff02feSmrg	    cs->win[child_count] = child[j];
3417dff02feSmrg	    cs->prop_cookie[child_count] = xcb_get_property(c, 0, child[j],
3427dff02feSmrg			    WM_STATE, XCB_GET_PROPERTY_TYPE_ANY,
3437dff02feSmrg			    0, 0);
3447dff02feSmrg	    /* Just in case the property isn't there, get the tree too */
3457dff02feSmrg	    cs->tree_cookie[child_count++] = xcb_query_tree(c, child[j]);
3467dff02feSmrg	}
3477dff02feSmrg    }
3487dff02feSmrg
3497dff02feSmrg    enqueue(child_info, cs);
3507dff02feSmrg
3517dff02feSmrgreply_done:
3527dff02feSmrg    for (i = 0; i < num_rep; i++)
3539511053fSmrg	free(qt_reply[i]);
3549511053fSmrg    free(qt_reply);
3557dff02feSmrg
3567dff02feSmrgdone:
3577dff02feSmrg    free(closure);
3587dff02feSmrg}
3597dff02feSmrg
3607dff02feSmrgtypedef struct {
3617dff02feSmrg    xcb_connection_t *c;
3627dff02feSmrg    xcb_query_tree_cookie_t cookie;
3637dff02feSmrg    int verbose;
3647dff02feSmrg    int maxcmdlen;
3657dff02feSmrg} root_list_state;
3667dff02feSmrg
3677dff02feSmrgstatic void root_list(void *closure)
3687dff02feSmrg{
3697dff02feSmrg    int i;
3707dff02feSmrg    xcb_window_t *child;
3717dff02feSmrg    xcb_query_tree_reply_t *reply;
3727dff02feSmrg    root_list_state *rl = closure;
3737dff02feSmrg
3747dff02feSmrg    reply = xcb_query_tree_reply(rl->c, rl->cookie, NULL);
3757dff02feSmrg    if (!reply)
3767dff02feSmrg	goto done;
3777dff02feSmrg
3787dff02feSmrg    child = xcb_query_tree_children(reply);
3797dff02feSmrg    for (i = 0; i < reply->children_len; i++) {
3807dff02feSmrg	/* Get information about each child */
3817dff02feSmrg	child_wm_state *cs = malloc(sizeof(*cs) + sizeof(*cs->prop_cookie) + sizeof(*cs->tree_cookie) + sizeof(*cs->win));
3827dff02feSmrg	if (!cs)
3837dff02feSmrg	    goto done; /* TODO: print OOM message */
3847dff02feSmrg	cs->c = rl->c;
3857dff02feSmrg	cs->verbose = rl->verbose;
3867dff02feSmrg	cs->maxcmdlen = rl->maxcmdlen;
3877dff02feSmrg	cs->prop_cookie = (void *)&cs[1];
3887dff02feSmrg	cs->tree_cookie = (void *)&cs->prop_cookie[1];
3897dff02feSmrg	cs->win = (void *)&cs->tree_cookie[1];
3907dff02feSmrg
3917dff02feSmrg	cs->orig_win = child[i];
3927dff02feSmrg	cs->win[0] = child[i];
3937dff02feSmrg
3947dff02feSmrg	cs->prop_cookie[0] = xcb_get_property(rl->c, 0, child[i],
3957dff02feSmrg			WM_STATE, XCB_GET_PROPERTY_TYPE_ANY,
3967dff02feSmrg			0, 0);
3977dff02feSmrg	/* Just in case the property isn't there, get the tree too */
3987dff02feSmrg	cs->tree_cookie[0] = xcb_query_tree(rl->c, child[i]);
3997dff02feSmrg
4007dff02feSmrg	cs->list_length = 1;
4017dff02feSmrg	enqueue(child_info, cs);
4027dff02feSmrg    }
4037dff02feSmrg    free(reply);
4047dff02feSmrg
4057dff02feSmrgdone:
4067dff02feSmrg    free(rl);
4077dff02feSmrg}
4087dff02feSmrg
409a850946eSmrgstatic void
4107dff02feSmrglookat(xcb_connection_t *dpy, xcb_window_t root, int verbose, int maxcmdlen)
411a850946eSmrg{
4127dff02feSmrg    root_list_state *rl = malloc(sizeof(*rl));
413a850946eSmrg
4147dff02feSmrg    if (!rl)
4157dff02feSmrg	return; /* TODO: OOM message */
416a850946eSmrg
417a850946eSmrg    /*
4187dff02feSmrg     * get the list of windows
419a850946eSmrg     */
420a850946eSmrg
4217dff02feSmrg    rl->c = dpy;
4227dff02feSmrg    rl->cookie = xcb_query_tree(dpy, root);
4237dff02feSmrg    rl->verbose = verbose;
4247dff02feSmrg    rl->maxcmdlen = maxcmdlen;
4257dff02feSmrg    enqueue(root_list, rl);
426a850946eSmrg}
427a850946eSmrg
4289511053fSmrgstatic const char *Nil = "(nil)";
429a850946eSmrg
4307dff02feSmrgtypedef struct {
4317dff02feSmrg    xcb_connection_t *c;
4327dff02feSmrg    xcb_get_property_cookie_t client_machine;
4337dff02feSmrg    xcb_get_property_cookie_t command;
4347dff02feSmrg    xcb_get_property_cookie_t name;
4357dff02feSmrg    xcb_get_property_cookie_t icon_name;
4367dff02feSmrg    xcb_get_property_cookie_t wm_class;
4377dff02feSmrg    xcb_window_t w;
4387dff02feSmrg    int verbose;
4397dff02feSmrg    int maxcmdlen;
4407dff02feSmrg} client_state;
4417dff02feSmrg
442a850946eSmrgstatic void
4437dff02feSmrgshow_client_properties(void *closure)
444a850946eSmrg{
4457dff02feSmrg    client_state *cs = closure;
4467dff02feSmrg    xcb_get_property_reply_t *client_machine;
4477dff02feSmrg    xcb_get_property_reply_t *command;
448da4b5163Smrg    xcb_get_property_reply_t *name = NULL;
449da4b5163Smrg    xcb_get_property_reply_t *icon_name = NULL;
450da4b5163Smrg    xcb_get_property_reply_t *wm_class = NULL;
4517dff02feSmrg    char *argv;
4527dff02feSmrg    int charsleft = cs->maxcmdlen;
453da4b5163Smrg    unsigned int i;
454a850946eSmrg
455a850946eSmrg    /*
456a850946eSmrg     * get the WM_MACHINE and WM_COMMAND list of strings
457a850946eSmrg     */
4587dff02feSmrg    client_machine = xcb_get_property_reply(cs->c, cs->client_machine, NULL);
4597dff02feSmrg    command = xcb_get_property_reply(cs->c, cs->command, NULL);
4607dff02feSmrg    if (cs->verbose) {
4617dff02feSmrg	name = xcb_get_property_reply(cs->c, cs->name, NULL);
4627dff02feSmrg	icon_name = xcb_get_property_reply(cs->c, cs->icon_name, NULL);
4637dff02feSmrg	wm_class = xcb_get_property_reply(cs->c, cs->wm_class, NULL);
464a850946eSmrg    }
465a850946eSmrg
4667dff02feSmrg    if (!command || !command->type)
4677dff02feSmrg	goto done;
468a850946eSmrg
469a850946eSmrg    /*
470a850946eSmrg     * do header information
471a850946eSmrg     */
4727dff02feSmrg    if (cs->verbose) {
4737dff02feSmrg	printf ("Window 0x%" PRIx32 ":\n", cs->w);
4747dff02feSmrg	print_text_field (cs->c, "  Machine:  ", client_machine);
4757dff02feSmrg	if (name && name->type)
4767dff02feSmrg	    print_text_field (cs->c, "  Name:  ", name);
477a850946eSmrg    } else {
4787dff02feSmrg	print_text_field (cs->c, NULL, client_machine);
479a850946eSmrg	putchar (' ');
480a850946eSmrg	putchar (' ');
481a850946eSmrg    }
482a850946eSmrg
4837dff02feSmrg
4847dff02feSmrg    if (cs->verbose)
4857dff02feSmrg	if (icon_name && icon_name->type)
4867dff02feSmrg	    print_text_field (cs->c, "  Icon Name:  ", icon_name);
487a850946eSmrg
488a850946eSmrg
489a850946eSmrg    /*
490a850946eSmrg     * do the command
491a850946eSmrg     */
4927dff02feSmrg    if (cs->verbose)
493a850946eSmrg	printf ("  Command:  ");
4947dff02feSmrg    argv = xcb_get_property_value(command);
4957dff02feSmrg    for (i = 0; i < command->value_len && charsleft > 0; ) {
4967dff02feSmrg	charsleft -= print_quoted_word (argv + i, charsleft);
4977dff02feSmrg	i += strnlen(argv + i, command->value_len - i) + 1;
4987dff02feSmrg	if (i < command->value_len && charsleft > 0) {
4997dff02feSmrg	    putchar (' ');
5007dff02feSmrg	    charsleft--;
501a850946eSmrg	}
502a850946eSmrg    }
503a850946eSmrg    putchar ('\n');
504a850946eSmrg
505a850946eSmrg
506a850946eSmrg    /*
507a850946eSmrg     * do trailer information
508a850946eSmrg     */
5097dff02feSmrg    if (cs->verbose) {
5107dff02feSmrg	if (wm_class && wm_class->type) {
5119511053fSmrg	    const char *res_name, *res_class;
512da4b5163Smrg	    int name_len, class_len; /* Must be int for use with %.*s */
5137dff02feSmrg	    res_name = xcb_get_property_value(wm_class);
514da4b5163Smrg	    name_len = (int) strnlen(res_name, wm_class->value_len) + 1;
515da4b5163Smrg	    class_len = (int) wm_class->value_len - name_len;
5167dff02feSmrg	    if (class_len > 0) {
5177dff02feSmrg		res_class = res_name + name_len;
5187dff02feSmrg	    } else {
5197dff02feSmrg		res_class = Nil;
520da4b5163Smrg		class_len = (int) strlen(res_class);
5217dff02feSmrg	    }
5227dff02feSmrg
5237dff02feSmrg	    printf ("  Instance/Class:  %.*s/%.*s",
5247dff02feSmrg		    name_len, res_name,
5257dff02feSmrg		    class_len, res_class);
526a850946eSmrg	    putchar ('\n');
527a850946eSmrg	}
528a850946eSmrg    }
5297dff02feSmrg
5307dff02feSmrgdone:
5317dff02feSmrg    if (client_machine)
5327dff02feSmrg	free(client_machine);
5337dff02feSmrg    if (command)
5347dff02feSmrg	free(command);
5357dff02feSmrg    if (cs->verbose) {
5367dff02feSmrg	if (name)
5377dff02feSmrg	    free(name);
5387dff02feSmrg	if (icon_name)
5397dff02feSmrg	    free(icon_name);
5407dff02feSmrg	if (wm_class)
5417dff02feSmrg	    free(wm_class);
5427dff02feSmrg    }
5437dff02feSmrg    free(cs);
544a850946eSmrg}
545a850946eSmrg
546a850946eSmrgstatic void
5477dff02feSmrgprint_client_properties(xcb_connection_t *dpy, xcb_window_t w, int verbose, int maxcmdlen)
548a850946eSmrg{
5497dff02feSmrg    client_state *cs = malloc(sizeof(*cs));
5507dff02feSmrg    if (!cs)
5517dff02feSmrg	return; /* TODO: print OOM message */
5527dff02feSmrg
5537dff02feSmrg    cs->c = dpy;
5547dff02feSmrg    cs->w = w;
5557dff02feSmrg    cs->verbose = verbose;
5567dff02feSmrg    cs->maxcmdlen = maxcmdlen;
5577dff02feSmrg
5587dff02feSmrg    /*
5597dff02feSmrg     * get the WM_CLIENT_MACHINE and WM_COMMAND list of strings
5607dff02feSmrg     */
5617dff02feSmrg    cs->client_machine = xcb_get_property(dpy, 0, w,
5628e46b049Smrg			    XCB_ATOM_WM_CLIENT_MACHINE, XCB_GET_PROPERTY_TYPE_ANY,
5637dff02feSmrg			    0, 1000000L);
5647dff02feSmrg    cs->command = xcb_get_property(dpy, 0, w,
5658e46b049Smrg			    XCB_ATOM_WM_COMMAND, XCB_GET_PROPERTY_TYPE_ANY,
5667dff02feSmrg			    0, 1000000L);
5677dff02feSmrg
5687dff02feSmrg    if (verbose) {
5697dff02feSmrg	cs->name = xcb_get_property(dpy, 0, w,
5708e46b049Smrg			    XCB_ATOM_WM_NAME, XCB_GET_PROPERTY_TYPE_ANY,
5717dff02feSmrg			    0, 1000000L);
5727dff02feSmrg	cs->icon_name = xcb_get_property(dpy, 0, w,
5738e46b049Smrg			    XCB_ATOM_WM_ICON_NAME, XCB_GET_PROPERTY_TYPE_ANY,
5747dff02feSmrg			    0, 1000000L);
5757dff02feSmrg	cs->wm_class = xcb_get_property(dpy, 0, w,
5768e46b049Smrg			    XCB_ATOM_WM_CLASS, XCB_ATOM_STRING,
5777dff02feSmrg			    0, 1000000L);
5787dff02feSmrg    }
5797dff02feSmrg
5807dff02feSmrg    enqueue(show_client_properties, cs);
5817dff02feSmrg}
5827dff02feSmrg
5837dff02feSmrgstatic void
5849511053fSmrgprint_text_field(xcb_connection_t *dpy, const char *s, xcb_get_property_reply_t *tp)
5857dff02feSmrg{
5867dff02feSmrg    if (tp->type == XCB_NONE || tp->format == 0) {  /* Or XCB_ATOM_NONE after libxcb 1.5 */
587a850946eSmrg	printf ("''");
588a850946eSmrg	return;
589a850946eSmrg    }
590a850946eSmrg
591a850946eSmrg    if (s) printf ("%s", s);
5928e46b049Smrg    if (tp->type == XCB_ATOM_STRING && tp->format == 8) {
5937dff02feSmrg	printf ("%.*s", (int)tp->value_len, (char *)xcb_get_property_value(tp));
594a850946eSmrg    } else {
5957dff02feSmrg	unknown (dpy, tp->type, tp->format);
596a850946eSmrg    }
597a850946eSmrg    if (s) putchar ('\n');
598a850946eSmrg}
599a850946eSmrg
600a850946eSmrg/* returns the number of characters printed */
601a850946eSmrgstatic int
602a850946eSmrgprint_quoted_word(char *s,
603a850946eSmrg		  int maxlen)		/* max number of chars we can print */
604a850946eSmrg{
605a850946eSmrg    register char *cp;
606a850946eSmrg    Bool need_quote = False, in_quote = False;
607a850946eSmrg    char quote_char = '\'', other_quote = '"';
608a850946eSmrg    int charsprinted = 0;
609a850946eSmrg
610a850946eSmrg    /*
611a850946eSmrg     * walk down seeing whether or not we need to quote
612a850946eSmrg     */
613a850946eSmrg    for (cp = s; *cp; cp++) {
614a850946eSmrg
615a850946eSmrg	if (! ((isascii(*cp) && isalnum(*cp)) ||
616a850946eSmrg	       (*cp == '-' || *cp == '_' || *cp == '.' || *cp == '+' ||
617a850946eSmrg		*cp == '/' || *cp == '=' || *cp == ':' || *cp == ','))) {
618a850946eSmrg	    need_quote = True;
619a850946eSmrg	    break;
620a850946eSmrg	}
621a850946eSmrg    }
622a850946eSmrg
623a850946eSmrg    /*
624a850946eSmrg     * write out the string: if we hit a quote, then close any previous quote,
625a850946eSmrg     * emit the other quote, swap quotes and continue on.
626a850946eSmrg     */
627a850946eSmrg    in_quote = need_quote;
628a850946eSmrg    if (need_quote) {
629a850946eSmrg	putchar (quote_char);
630a850946eSmrg	charsprinted++; maxlen--;
631a850946eSmrg    }
632a850946eSmrg    for (cp = s; *cp && maxlen>0; cp++) {
633a850946eSmrg	if (*cp == quote_char) {
634a850946eSmrg	    if (in_quote) {
635a850946eSmrg		putchar (quote_char);
636a850946eSmrg		charsprinted++; maxlen--;
637a850946eSmrg	    }
638a850946eSmrg	    putchar (other_quote);
639a850946eSmrg	    charsprinted++; maxlen--;
640a850946eSmrg	    {
641a850946eSmrg		char tmp = other_quote;
642a850946eSmrg		other_quote = quote_char; quote_char = tmp;
643a850946eSmrg	    }
644a850946eSmrg	    in_quote = True;
645a850946eSmrg	}
646a850946eSmrg	putchar (*cp);
647a850946eSmrg	charsprinted++; maxlen--;
648a850946eSmrg    }
649a850946eSmrg    /* close the quote if we opened one and if we printed the whole string */
650a850946eSmrg    if (in_quote && maxlen>0) {
651a850946eSmrg	putchar (quote_char);
652a850946eSmrg	charsprinted++; maxlen--;
653a850946eSmrg    }
654a850946eSmrg
655a850946eSmrg    return charsprinted;
656a850946eSmrg}
657a850946eSmrg
658a850946eSmrgstatic void
6597dff02feSmrgunknown(xcb_connection_t *dpy, xcb_atom_t actual_type, int actual_format)
660a850946eSmrg{
661a850946eSmrg    printf ("<unknown type ");
6627dff02feSmrg    if (actual_type == XCB_NONE)
6637dff02feSmrg	printf ("None");
6647dff02feSmrg    else {
6657dff02feSmrg	/* This should happen so rarely as to make no odds. Eat a round-trip: */
6667dff02feSmrg	xcb_get_atom_name_reply_t *atom =
6677dff02feSmrg	    xcb_get_atom_name_reply(dpy,
6687dff02feSmrg		xcb_get_atom_name(dpy, actual_type), NULL);
6697dff02feSmrg	if (atom) {
6707dff02feSmrg	    printf("%.*s", xcb_get_atom_name_name_length(atom),
6717dff02feSmrg			  xcb_get_atom_name_name(atom));
6727dff02feSmrg	    free(atom);
6737dff02feSmrg	} else
6747dff02feSmrg	    fputs (Nil, stdout);
675a850946eSmrg    }
6767dff02feSmrg    printf (" (%" PRIu32 ") or format %d>", actual_type, actual_format);
677a850946eSmrg}
678