xlsclients.c revision 9511053f
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
5170f7c90cSmrgstatic char *ProgramName;
52a850946eSmrg
537dff02feSmrgstatic xcb_atom_t WM_STATE;
547dff02feSmrg
557dff02feSmrgstatic void lookat (xcb_connection_t *dpy, xcb_window_t root, int verbose, int maxcmdlen);
567dff02feSmrgstatic void print_client_properties (xcb_connection_t *dpy, xcb_window_t w,
577dff02feSmrg				     int verbose, int maxcmdlen );
589511053fSmrgstatic void print_text_field (xcb_connection_t *dpy, const char *s, xcb_get_property_reply_t *tp );
597dff02feSmrgstatic int print_quoted_word (char *s, int maxlen);
607dff02feSmrgstatic void unknown (xcb_connection_t *dpy, xcb_atom_t actual_type, int actual_format );
617dff02feSmrg
627dff02feSmrg/* For convenience: */
637dff02feSmrgtypedef int Bool;
647dff02feSmrg#define False (0)
657dff02feSmrg#define True (!False)
66a850946eSmrg
67a850946eSmrgstatic void
68a850946eSmrgusage(void)
69a850946eSmrg{
70a850946eSmrg    fprintf (stderr,
719511053fSmrg	     "usage:  %s  [-display dpy] [-m len] [-[a][l]] [-version]\n",
729511053fSmrg	     ProgramName);
73a850946eSmrg    exit (1);
74a850946eSmrg}
75a850946eSmrg
767dff02feSmrgtypedef void (*queue_func)(void *closure);
777dff02feSmrgtypedef struct queue_blob {
787dff02feSmrg    queue_func func;
797dff02feSmrg    void *closure;
807dff02feSmrg    struct queue_blob *next;
817dff02feSmrg} queue_blob;
827dff02feSmrg
837dff02feSmrgstatic queue_blob *head = NULL;
847dff02feSmrgstatic queue_blob **tail = &head;
857dff02feSmrg
867dff02feSmrgstatic void enqueue(queue_func func, void *closure)
877dff02feSmrg{
887dff02feSmrg    queue_blob *blob = malloc(sizeof(*blob));
897dff02feSmrg    if (!blob)
907dff02feSmrg	return; /* TODO: print OOM error */
917dff02feSmrg
927dff02feSmrg    blob->func = func;
937dff02feSmrg    blob->closure = closure;
947dff02feSmrg    blob->next = NULL;
957dff02feSmrg    *tail = blob;
967dff02feSmrg    tail = &blob->next;
977dff02feSmrg}
987dff02feSmrg
997dff02feSmrgstatic void run_queue(void)
1007dff02feSmrg{
1017dff02feSmrg    while (head) {
1027dff02feSmrg	queue_blob *blob = head;
1037dff02feSmrg	blob->func(blob->closure);
1047dff02feSmrg	head = blob->next;
1057dff02feSmrg	free(blob);
1067dff02feSmrg    }
1077dff02feSmrg    tail = &head;
1087dff02feSmrg}
1097dff02feSmrg
1107dff02feSmrgtypedef struct {
1117dff02feSmrg    xcb_connection_t *c;
1127dff02feSmrg    xcb_intern_atom_cookie_t cookie;
1137dff02feSmrg    xcb_atom_t *atom;
1147dff02feSmrg} atom_state;
1157dff02feSmrg
1167dff02feSmrgstatic void atom_done(void *closure)
1177dff02feSmrg{
1187dff02feSmrg    xcb_intern_atom_reply_t *reply;
1197dff02feSmrg    atom_state *as = closure;
1207dff02feSmrg
1217dff02feSmrg    reply = xcb_intern_atom_reply(as->c, as->cookie, NULL);
1227dff02feSmrg    if (!reply)
1237dff02feSmrg	goto done; /* TODO: print Error message */
1247dff02feSmrg
1257dff02feSmrg    *(as->atom) = reply->atom;
1267dff02feSmrg    free(reply);
1277dff02feSmrg
1287dff02feSmrgdone:
1297dff02feSmrg    free(as);
1307dff02feSmrg}
1317dff02feSmrg
1327dff02feSmrgstatic void init_atoms(xcb_connection_t *c)
1337dff02feSmrg{
1347dff02feSmrg    atom_state *as;
1357dff02feSmrg
1367dff02feSmrg    as = malloc(sizeof(*as));
1377dff02feSmrg    as->c = c;
1387dff02feSmrg    as->atom = &WM_STATE;
1397dff02feSmrg    as->cookie = xcb_intern_atom(c, 0, strlen("WM_STATE"), "WM_STATE");
1407dff02feSmrg    enqueue(atom_done, as);
1417dff02feSmrg}
1427dff02feSmrg
143a850946eSmrgint
144a850946eSmrgmain(int argc, char *argv[])
145a850946eSmrg{
146a850946eSmrg    int i;
147a850946eSmrg    char *displayname = NULL;
148a850946eSmrg    Bool all_screens = False;
149a850946eSmrg    Bool verbose = False;
1507dff02feSmrg    xcb_connection_t *dpy;
1517dff02feSmrg    const xcb_setup_t *setup;
1527dff02feSmrg    int screen_number = 0;
153a850946eSmrg    int maxcmdlen = 10000;
154a850946eSmrg
155a850946eSmrg    ProgramName = argv[0];
156a850946eSmrg
157a850946eSmrg    for (i = 1; i < argc; i++) {
158a850946eSmrg	char *arg = argv[i];
159a850946eSmrg
160a850946eSmrg	if (arg[0] == '-') {
161a850946eSmrg	    char *cp;
162a850946eSmrg
163a850946eSmrg	    switch (arg[1]) {
164a850946eSmrg	      case 'd':			/* -display dpyname */
165a850946eSmrg		if (++i >= argc) usage ();
166a850946eSmrg		displayname = argv[i];
167a850946eSmrg		continue;
168a850946eSmrg	      case 'm':			/* -max maxcmdlen */
169a850946eSmrg		if (++i >= argc) usage ();
170a850946eSmrg		maxcmdlen = atoi (argv[i]);
171a850946eSmrg		continue;
1729511053fSmrg	      case 'v':			/* -version */
1739511053fSmrg		printf("%s\n", PACKAGE_STRING);
1749511053fSmrg		exit(0);
175a850946eSmrg	    }
176a850946eSmrg
177a850946eSmrg	    for (cp = &arg[1]; *cp; cp++) {
178a850946eSmrg		switch (*cp) {
179a850946eSmrg		  case 'a':		/* -all */
180a850946eSmrg		    all_screens = True;
181a850946eSmrg		    continue;
182a850946eSmrg		  case 'l':		/* -long */
183a850946eSmrg		    verbose = True;
184a850946eSmrg		    continue;
185a850946eSmrg		  default:
186a850946eSmrg		    usage ();
187a850946eSmrg		}
188a850946eSmrg	    }
189a850946eSmrg	} else {
190a850946eSmrg	    usage ();
191a850946eSmrg	}
192a850946eSmrg    }
193a850946eSmrg
1947dff02feSmrg    dpy = xcb_connect(displayname, &screen_number);
1957dff02feSmrg    if (xcb_connection_has_error(dpy)) {
1969511053fSmrg	const char *name = displayname;
1977dff02feSmrg	if (!name)
1987dff02feSmrg	    name = getenv("DISPLAY");
1997dff02feSmrg	if (!name)
2007dff02feSmrg	    name = "";
201a850946eSmrg	fprintf (stderr, "%s:  unable to open display \"%s\"\r\n",
2027dff02feSmrg		 ProgramName, name);
203a850946eSmrg	exit (1);
204a850946eSmrg    }
205a850946eSmrg
2067dff02feSmrg    init_atoms(dpy);
2077dff02feSmrg
2087dff02feSmrg    setup = xcb_get_setup(dpy);
209a850946eSmrg    if (all_screens) {
2107dff02feSmrg	xcb_screen_iterator_t screen;
2117dff02feSmrg
2127dff02feSmrg	screen = xcb_setup_roots_iterator(setup);
2137dff02feSmrg	do {
2147dff02feSmrg	    lookat(dpy, screen.data->root, verbose, maxcmdlen);
2157dff02feSmrg	    xcb_screen_next(&screen);
2167dff02feSmrg	} while (screen.rem);
217a850946eSmrg    } else {
2187dff02feSmrg	xcb_screen_iterator_t screen;
2197dff02feSmrg
2207dff02feSmrg	screen = xcb_setup_roots_iterator(setup);
2217dff02feSmrg	for (i = 0; i < screen_number; i++)
2227dff02feSmrg	    xcb_screen_next(&screen);
2237dff02feSmrg
2247dff02feSmrg	lookat (dpy, screen.data->root, verbose, maxcmdlen);
225a850946eSmrg    }
226a850946eSmrg
2277dff02feSmrg    run_queue();
2287dff02feSmrg
2297dff02feSmrg    xcb_disconnect(dpy);
230a850946eSmrg    exit (0);
231a850946eSmrg}
232a850946eSmrg
2337dff02feSmrgtypedef struct {
2347dff02feSmrg    xcb_connection_t *c;
2357dff02feSmrg    xcb_get_property_cookie_t *prop_cookie;
2367dff02feSmrg    xcb_query_tree_cookie_t *tree_cookie;
2377dff02feSmrg    xcb_window_t *win;
2387dff02feSmrg    xcb_window_t orig_win;
2397dff02feSmrg    int list_length;
2407dff02feSmrg    int verbose;
2417dff02feSmrg    int maxcmdlen;
2427dff02feSmrg} child_wm_state;
2437dff02feSmrg
2447dff02feSmrgstatic void child_info(void *closure)
2457dff02feSmrg{
2467dff02feSmrg    child_wm_state *cs = closure;
2477dff02feSmrg    xcb_window_t orig = cs->orig_win;
2487dff02feSmrg    xcb_connection_t *c = cs->c;
2497dff02feSmrg    int verbose = cs->verbose;
2507dff02feSmrg    int maxcmdlen = cs->maxcmdlen;
2517dff02feSmrg    int i, j;
2527dff02feSmrg
2537dff02feSmrg    int child_count, num_rep;
2549511053fSmrg    xcb_query_tree_reply_t **qt_reply;
2557dff02feSmrg
2567dff02feSmrg    for (i = 0; i < cs->list_length; i++) {
2579511053fSmrg	xcb_get_property_reply_t *gp_reply;
2589511053fSmrg	gp_reply = xcb_get_property_reply(c, cs->prop_cookie[i], NULL);
2599511053fSmrg	if (gp_reply) {
2609511053fSmrg	    if (gp_reply->type) {
2617dff02feSmrg		/* Show information for this window */
2627dff02feSmrg		print_client_properties(c, cs->win[i], cs->verbose, cs->maxcmdlen);
2637dff02feSmrg
2649511053fSmrg		free(gp_reply);
2657dff02feSmrg
2667dff02feSmrg		/* drain stale replies */
2677dff02feSmrg		for (j = i+1; j < cs->list_length; j++) {
2689511053fSmrg		    gp_reply = xcb_get_property_reply(c, cs->prop_cookie[j], NULL);
2699511053fSmrg		    if (gp_reply)
2709511053fSmrg			free(gp_reply);
2717dff02feSmrg		}
2727dff02feSmrg		for (j = 0; j < cs->list_length; j++) {
2737dff02feSmrg		    xcb_query_tree_reply_t *rep;
2747dff02feSmrg		    rep = xcb_query_tree_reply(c, cs->tree_cookie[j], NULL);
2757dff02feSmrg		    if (rep)
2767dff02feSmrg			free(rep);
2777dff02feSmrg		}
2787dff02feSmrg		goto done;
2797dff02feSmrg	    }
2809511053fSmrg	    free(gp_reply);
2817dff02feSmrg	}
2827dff02feSmrg    }
2837dff02feSmrg
2847dff02feSmrg    /* WM_STATE not found. Recurse into children: */
2857dff02feSmrg    num_rep = 0;
2869511053fSmrg    qt_reply = malloc(sizeof(*qt_reply) * cs->list_length);
2879511053fSmrg    if (!qt_reply)
2887dff02feSmrg	goto done; /* TODO: print OOM message, drain reply queue */
2897dff02feSmrg
2907dff02feSmrg    for (i = 0; i < cs->list_length; i++) {
2919511053fSmrg	qt_reply[num_rep] = xcb_query_tree_reply(c, cs->tree_cookie[i], NULL);
2929511053fSmrg	if (qt_reply[num_rep])
2937dff02feSmrg	    num_rep++;
2947dff02feSmrg    }
2957dff02feSmrg
2967dff02feSmrg    child_count = 0;
2977dff02feSmrg    for (i = 0; i < num_rep; i++)
2989511053fSmrg	child_count += qt_reply[i]->children_len;
2997dff02feSmrg
3007dff02feSmrg    if (!child_count) {
3017dff02feSmrg	/* No children have CS_STATE; try the parent window */
3027dff02feSmrg	print_client_properties(c, cs->orig_win, cs->verbose, cs->maxcmdlen);
3037dff02feSmrg	goto reply_done;
3047dff02feSmrg    }
3057dff02feSmrg
3067dff02feSmrg    cs = malloc(sizeof(*cs) + child_count * (sizeof(*cs->prop_cookie) + sizeof(*cs->tree_cookie) + sizeof(*cs->win)));
3077dff02feSmrg    if (!cs)
3087dff02feSmrg	goto reply_done; /* TODO: print OOM message */
3097dff02feSmrg
3107dff02feSmrg    cs->c = c;
3117dff02feSmrg    cs->verbose = verbose;
3127dff02feSmrg    cs->maxcmdlen = maxcmdlen;
3137dff02feSmrg    cs->orig_win = orig;
3147dff02feSmrg    cs->prop_cookie = (void *)&cs[1];
3157dff02feSmrg    cs->tree_cookie = (void *)&cs->prop_cookie[child_count];
3167dff02feSmrg    cs->win = (void *)&cs->tree_cookie[child_count];
3177dff02feSmrg    cs->list_length = child_count;
3187dff02feSmrg
3197dff02feSmrg    child_count = 0;
3207dff02feSmrg    for (i = 0; i < num_rep; i++) {
3219511053fSmrg	xcb_window_t *child = xcb_query_tree_children(qt_reply[i]);
3229511053fSmrg	for (j = 0; j < qt_reply[i]->children_len; j++) {
3237dff02feSmrg	    cs->win[child_count] = child[j];
3247dff02feSmrg	    cs->prop_cookie[child_count] = xcb_get_property(c, 0, child[j],
3257dff02feSmrg			    WM_STATE, XCB_GET_PROPERTY_TYPE_ANY,
3267dff02feSmrg			    0, 0);
3277dff02feSmrg	    /* Just in case the property isn't there, get the tree too */
3287dff02feSmrg	    cs->tree_cookie[child_count++] = xcb_query_tree(c, child[j]);
3297dff02feSmrg	}
3307dff02feSmrg    }
3317dff02feSmrg
3327dff02feSmrg    enqueue(child_info, cs);
3337dff02feSmrg
3347dff02feSmrgreply_done:
3357dff02feSmrg    for (i = 0; i < num_rep; i++)
3369511053fSmrg	free(qt_reply[i]);
3379511053fSmrg    free(qt_reply);
3387dff02feSmrg
3397dff02feSmrgdone:
3407dff02feSmrg    free(closure);
3417dff02feSmrg}
3427dff02feSmrg
3437dff02feSmrgtypedef struct {
3447dff02feSmrg    xcb_connection_t *c;
3457dff02feSmrg    xcb_query_tree_cookie_t cookie;
3467dff02feSmrg    int verbose;
3477dff02feSmrg    int maxcmdlen;
3487dff02feSmrg} root_list_state;
3497dff02feSmrg
3507dff02feSmrgstatic void root_list(void *closure)
3517dff02feSmrg{
3527dff02feSmrg    int i;
3537dff02feSmrg    xcb_window_t *child;
3547dff02feSmrg    xcb_query_tree_reply_t *reply;
3557dff02feSmrg    root_list_state *rl = closure;
3567dff02feSmrg
3577dff02feSmrg    reply = xcb_query_tree_reply(rl->c, rl->cookie, NULL);
3587dff02feSmrg    if (!reply)
3597dff02feSmrg	goto done;
3607dff02feSmrg
3617dff02feSmrg    child = xcb_query_tree_children(reply);
3627dff02feSmrg    for (i = 0; i < reply->children_len; i++) {
3637dff02feSmrg	/* Get information about each child */
3647dff02feSmrg	child_wm_state *cs = malloc(sizeof(*cs) + sizeof(*cs->prop_cookie) + sizeof(*cs->tree_cookie) + sizeof(*cs->win));
3657dff02feSmrg	if (!cs)
3667dff02feSmrg	    goto done; /* TODO: print OOM message */
3677dff02feSmrg	cs->c = rl->c;
3687dff02feSmrg	cs->verbose = rl->verbose;
3697dff02feSmrg	cs->maxcmdlen = rl->maxcmdlen;
3707dff02feSmrg	cs->prop_cookie = (void *)&cs[1];
3717dff02feSmrg	cs->tree_cookie = (void *)&cs->prop_cookie[1];
3727dff02feSmrg	cs->win = (void *)&cs->tree_cookie[1];
3737dff02feSmrg
3747dff02feSmrg	cs->orig_win = child[i];
3757dff02feSmrg	cs->win[0] = child[i];
3767dff02feSmrg
3777dff02feSmrg	cs->prop_cookie[0] = xcb_get_property(rl->c, 0, child[i],
3787dff02feSmrg			WM_STATE, XCB_GET_PROPERTY_TYPE_ANY,
3797dff02feSmrg			0, 0);
3807dff02feSmrg	/* Just in case the property isn't there, get the tree too */
3817dff02feSmrg	cs->tree_cookie[0] = xcb_query_tree(rl->c, child[i]);
3827dff02feSmrg
3837dff02feSmrg	cs->list_length = 1;
3847dff02feSmrg	enqueue(child_info, cs);
3857dff02feSmrg    }
3867dff02feSmrg    free(reply);
3877dff02feSmrg
3887dff02feSmrgdone:
3897dff02feSmrg    free(rl);
3907dff02feSmrg}
3917dff02feSmrg
392a850946eSmrgstatic void
3937dff02feSmrglookat(xcb_connection_t *dpy, xcb_window_t root, int verbose, int maxcmdlen)
394a850946eSmrg{
3957dff02feSmrg    root_list_state *rl = malloc(sizeof(*rl));
396a850946eSmrg
3977dff02feSmrg    if (!rl)
3987dff02feSmrg	return; /* TODO: OOM message */
399a850946eSmrg
400a850946eSmrg    /*
4017dff02feSmrg     * get the list of windows
402a850946eSmrg     */
403a850946eSmrg
4047dff02feSmrg    rl->c = dpy;
4057dff02feSmrg    rl->cookie = xcb_query_tree(dpy, root);
4067dff02feSmrg    rl->verbose = verbose;
4077dff02feSmrg    rl->maxcmdlen = maxcmdlen;
4087dff02feSmrg    enqueue(root_list, rl);
409a850946eSmrg}
410a850946eSmrg
4119511053fSmrgstatic const char *Nil = "(nil)";
412a850946eSmrg
4137dff02feSmrgtypedef struct {
4147dff02feSmrg    xcb_connection_t *c;
4157dff02feSmrg    xcb_get_property_cookie_t client_machine;
4167dff02feSmrg    xcb_get_property_cookie_t command;
4177dff02feSmrg    xcb_get_property_cookie_t name;
4187dff02feSmrg    xcb_get_property_cookie_t icon_name;
4197dff02feSmrg    xcb_get_property_cookie_t wm_class;
4207dff02feSmrg    xcb_window_t w;
4217dff02feSmrg    int verbose;
4227dff02feSmrg    int maxcmdlen;
4237dff02feSmrg} client_state;
4247dff02feSmrg
425a850946eSmrgstatic void
4267dff02feSmrgshow_client_properties(void *closure)
427a850946eSmrg{
4287dff02feSmrg    client_state *cs = closure;
4297dff02feSmrg    xcb_get_property_reply_t *client_machine;
4307dff02feSmrg    xcb_get_property_reply_t *command;
4317dff02feSmrg    xcb_get_property_reply_t *name;
4327dff02feSmrg    xcb_get_property_reply_t *icon_name;
4337dff02feSmrg    xcb_get_property_reply_t *wm_class;
4347dff02feSmrg    char *argv;
4357dff02feSmrg    int charsleft = cs->maxcmdlen;
4367dff02feSmrg    int i;
437a850946eSmrg
438a850946eSmrg    /*
439a850946eSmrg     * get the WM_MACHINE and WM_COMMAND list of strings
440a850946eSmrg     */
4417dff02feSmrg    client_machine = xcb_get_property_reply(cs->c, cs->client_machine, NULL);
4427dff02feSmrg    command = xcb_get_property_reply(cs->c, cs->command, NULL);
4437dff02feSmrg    if (cs->verbose) {
4447dff02feSmrg	name = xcb_get_property_reply(cs->c, cs->name, NULL);
4457dff02feSmrg	icon_name = xcb_get_property_reply(cs->c, cs->icon_name, NULL);
4467dff02feSmrg	wm_class = xcb_get_property_reply(cs->c, cs->wm_class, NULL);
447a850946eSmrg    }
448a850946eSmrg
4497dff02feSmrg    if (!command || !command->type)
4507dff02feSmrg	goto done;
451a850946eSmrg
452a850946eSmrg    /*
453a850946eSmrg     * do header information
454a850946eSmrg     */
4557dff02feSmrg    if (cs->verbose) {
4567dff02feSmrg	printf ("Window 0x%" PRIx32 ":\n", cs->w);
4577dff02feSmrg	print_text_field (cs->c, "  Machine:  ", client_machine);
4587dff02feSmrg	if (name && name->type)
4597dff02feSmrg	    print_text_field (cs->c, "  Name:  ", name);
460a850946eSmrg    } else {
4617dff02feSmrg	print_text_field (cs->c, NULL, client_machine);
462a850946eSmrg	putchar (' ');
463a850946eSmrg	putchar (' ');
464a850946eSmrg    }
465a850946eSmrg
4667dff02feSmrg
4677dff02feSmrg    if (cs->verbose)
4687dff02feSmrg	if (icon_name && icon_name->type)
4697dff02feSmrg	    print_text_field (cs->c, "  Icon Name:  ", icon_name);
470a850946eSmrg
471a850946eSmrg
472a850946eSmrg    /*
473a850946eSmrg     * do the command
474a850946eSmrg     */
4757dff02feSmrg    if (cs->verbose)
476a850946eSmrg	printf ("  Command:  ");
4777dff02feSmrg    argv = xcb_get_property_value(command);
4787dff02feSmrg    for (i = 0; i < command->value_len && charsleft > 0; ) {
4797dff02feSmrg	charsleft -= print_quoted_word (argv + i, charsleft);
4807dff02feSmrg	i += strnlen(argv + i, command->value_len - i) + 1;
4817dff02feSmrg	if (i < command->value_len && charsleft > 0) {
4827dff02feSmrg	    putchar (' ');
4837dff02feSmrg	    charsleft--;
484a850946eSmrg	}
485a850946eSmrg    }
486a850946eSmrg    putchar ('\n');
487a850946eSmrg
488a850946eSmrg
489a850946eSmrg    /*
490a850946eSmrg     * do trailer information
491a850946eSmrg     */
4927dff02feSmrg    if (cs->verbose) {
4937dff02feSmrg	if (wm_class && wm_class->type) {
4949511053fSmrg	    const char *res_name, *res_class;
4957dff02feSmrg	    int name_len, class_len;
4967dff02feSmrg	    res_name = xcb_get_property_value(wm_class);
4977dff02feSmrg	    name_len = strnlen(res_name, wm_class->value_len) + 1;
4987dff02feSmrg	    class_len = wm_class->value_len - name_len;
4997dff02feSmrg	    if (class_len > 0) {
5007dff02feSmrg		res_class = res_name + name_len;
5017dff02feSmrg	    } else {
5027dff02feSmrg		res_class = Nil;
5037dff02feSmrg		class_len = strlen(res_class);
5047dff02feSmrg	    }
5057dff02feSmrg
5067dff02feSmrg	    printf ("  Instance/Class:  %.*s/%.*s",
5077dff02feSmrg		    name_len, res_name,
5087dff02feSmrg		    class_len, res_class);
509a850946eSmrg	    putchar ('\n');
510a850946eSmrg	}
511a850946eSmrg    }
5127dff02feSmrg
5137dff02feSmrgdone:
5147dff02feSmrg    if (client_machine)
5157dff02feSmrg	free(client_machine);
5167dff02feSmrg    if (command)
5177dff02feSmrg	free(command);
5187dff02feSmrg    if (cs->verbose) {
5197dff02feSmrg	if (name)
5207dff02feSmrg	    free(name);
5217dff02feSmrg	if (icon_name)
5227dff02feSmrg	    free(icon_name);
5237dff02feSmrg	if (wm_class)
5247dff02feSmrg	    free(wm_class);
5257dff02feSmrg    }
5267dff02feSmrg    free(cs);
527a850946eSmrg}
528a850946eSmrg
529a850946eSmrgstatic void
5307dff02feSmrgprint_client_properties(xcb_connection_t *dpy, xcb_window_t w, int verbose, int maxcmdlen)
531a850946eSmrg{
5327dff02feSmrg    client_state *cs = malloc(sizeof(*cs));
5337dff02feSmrg    if (!cs)
5347dff02feSmrg	return; /* TODO: print OOM message */
5357dff02feSmrg
5367dff02feSmrg    cs->c = dpy;
5377dff02feSmrg    cs->w = w;
5387dff02feSmrg    cs->verbose = verbose;
5397dff02feSmrg    cs->maxcmdlen = maxcmdlen;
5407dff02feSmrg
5417dff02feSmrg    /*
5427dff02feSmrg     * get the WM_CLIENT_MACHINE and WM_COMMAND list of strings
5437dff02feSmrg     */
5447dff02feSmrg    cs->client_machine = xcb_get_property(dpy, 0, w,
5458e46b049Smrg			    XCB_ATOM_WM_CLIENT_MACHINE, XCB_GET_PROPERTY_TYPE_ANY,
5467dff02feSmrg			    0, 1000000L);
5477dff02feSmrg    cs->command = xcb_get_property(dpy, 0, w,
5488e46b049Smrg			    XCB_ATOM_WM_COMMAND, XCB_GET_PROPERTY_TYPE_ANY,
5497dff02feSmrg			    0, 1000000L);
5507dff02feSmrg
5517dff02feSmrg    if (verbose) {
5527dff02feSmrg	cs->name = xcb_get_property(dpy, 0, w,
5538e46b049Smrg			    XCB_ATOM_WM_NAME, XCB_GET_PROPERTY_TYPE_ANY,
5547dff02feSmrg			    0, 1000000L);
5557dff02feSmrg	cs->icon_name = xcb_get_property(dpy, 0, w,
5568e46b049Smrg			    XCB_ATOM_WM_ICON_NAME, XCB_GET_PROPERTY_TYPE_ANY,
5577dff02feSmrg			    0, 1000000L);
5587dff02feSmrg	cs->wm_class = xcb_get_property(dpy, 0, w,
5598e46b049Smrg			    XCB_ATOM_WM_CLASS, XCB_ATOM_STRING,
5607dff02feSmrg			    0, 1000000L);
5617dff02feSmrg    }
5627dff02feSmrg
5637dff02feSmrg    enqueue(show_client_properties, cs);
5647dff02feSmrg}
5657dff02feSmrg
5667dff02feSmrgstatic void
5679511053fSmrgprint_text_field(xcb_connection_t *dpy, const char *s, xcb_get_property_reply_t *tp)
5687dff02feSmrg{
5697dff02feSmrg    if (tp->type == XCB_NONE || tp->format == 0) {  /* Or XCB_ATOM_NONE after libxcb 1.5 */
570a850946eSmrg	printf ("''");
571a850946eSmrg	return;
572a850946eSmrg    }
573a850946eSmrg
574a850946eSmrg    if (s) printf ("%s", s);
5758e46b049Smrg    if (tp->type == XCB_ATOM_STRING && tp->format == 8) {
5767dff02feSmrg	printf ("%.*s", (int)tp->value_len, (char *)xcb_get_property_value(tp));
577a850946eSmrg    } else {
5787dff02feSmrg	unknown (dpy, tp->type, tp->format);
579a850946eSmrg    }
580a850946eSmrg    if (s) putchar ('\n');
581a850946eSmrg}
582a850946eSmrg
583a850946eSmrg/* returns the number of characters printed */
584a850946eSmrgstatic int
585a850946eSmrgprint_quoted_word(char *s,
586a850946eSmrg		  int maxlen)		/* max number of chars we can print */
587a850946eSmrg{
588a850946eSmrg    register char *cp;
589a850946eSmrg    Bool need_quote = False, in_quote = False;
590a850946eSmrg    char quote_char = '\'', other_quote = '"';
591a850946eSmrg    int charsprinted = 0;
592a850946eSmrg
593a850946eSmrg    /*
594a850946eSmrg     * walk down seeing whether or not we need to quote
595a850946eSmrg     */
596a850946eSmrg    for (cp = s; *cp; cp++) {
597a850946eSmrg
598a850946eSmrg	if (! ((isascii(*cp) && isalnum(*cp)) ||
599a850946eSmrg	       (*cp == '-' || *cp == '_' || *cp == '.' || *cp == '+' ||
600a850946eSmrg		*cp == '/' || *cp == '=' || *cp == ':' || *cp == ','))) {
601a850946eSmrg	    need_quote = True;
602a850946eSmrg	    break;
603a850946eSmrg	}
604a850946eSmrg    }
605a850946eSmrg
606a850946eSmrg    /*
607a850946eSmrg     * write out the string: if we hit a quote, then close any previous quote,
608a850946eSmrg     * emit the other quote, swap quotes and continue on.
609a850946eSmrg     */
610a850946eSmrg    in_quote = need_quote;
611a850946eSmrg    if (need_quote) {
612a850946eSmrg	putchar (quote_char);
613a850946eSmrg	charsprinted++; maxlen--;
614a850946eSmrg    }
615a850946eSmrg    for (cp = s; *cp && maxlen>0; cp++) {
616a850946eSmrg	if (*cp == quote_char) {
617a850946eSmrg	    if (in_quote) {
618a850946eSmrg		putchar (quote_char);
619a850946eSmrg		charsprinted++; maxlen--;
620a850946eSmrg	    }
621a850946eSmrg	    putchar (other_quote);
622a850946eSmrg	    charsprinted++; maxlen--;
623a850946eSmrg	    {
624a850946eSmrg		char tmp = other_quote;
625a850946eSmrg		other_quote = quote_char; quote_char = tmp;
626a850946eSmrg	    }
627a850946eSmrg	    in_quote = True;
628a850946eSmrg	}
629a850946eSmrg	putchar (*cp);
630a850946eSmrg	charsprinted++; maxlen--;
631a850946eSmrg    }
632a850946eSmrg    /* close the quote if we opened one and if we printed the whole string */
633a850946eSmrg    if (in_quote && maxlen>0) {
634a850946eSmrg	putchar (quote_char);
635a850946eSmrg	charsprinted++; maxlen--;
636a850946eSmrg    }
637a850946eSmrg
638a850946eSmrg    return charsprinted;
639a850946eSmrg}
640a850946eSmrg
641a850946eSmrgstatic void
6427dff02feSmrgunknown(xcb_connection_t *dpy, xcb_atom_t actual_type, int actual_format)
643a850946eSmrg{
644a850946eSmrg    printf ("<unknown type ");
6457dff02feSmrg    if (actual_type == XCB_NONE)
6467dff02feSmrg	printf ("None");
6477dff02feSmrg    else {
6487dff02feSmrg	/* This should happen so rarely as to make no odds. Eat a round-trip: */
6497dff02feSmrg	xcb_get_atom_name_reply_t *atom =
6507dff02feSmrg	    xcb_get_atom_name_reply(dpy,
6517dff02feSmrg		xcb_get_atom_name(dpy, actual_type), NULL);
6527dff02feSmrg	if (atom) {
6537dff02feSmrg	    printf("%.*s", xcb_get_atom_name_name_length(atom),
6547dff02feSmrg			  xcb_get_atom_name_name(atom));
6557dff02feSmrg	    free(atom);
6567dff02feSmrg	} else
6577dff02feSmrg	    fputs (Nil, stdout);
658a850946eSmrg    }
6597dff02feSmrg    printf (" (%" PRIu32 ") or format %d>", actual_type, actual_format);
660a850946eSmrg}
661