xlsclients.c revision 8e46b049
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 );
587dff02feSmrgstatic void print_text_field (xcb_connection_t *dpy, 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,
71a850946eSmrg	     "usage:  %s  [-display dpy] [-m len] [-[a][l]]\n", ProgramName);
72a850946eSmrg    exit (1);
73a850946eSmrg}
74a850946eSmrg
757dff02feSmrgtypedef void (*queue_func)(void *closure);
767dff02feSmrgtypedef struct queue_blob {
777dff02feSmrg    queue_func func;
787dff02feSmrg    void *closure;
797dff02feSmrg    struct queue_blob *next;
807dff02feSmrg} queue_blob;
817dff02feSmrg
827dff02feSmrgstatic queue_blob *head = NULL;
837dff02feSmrgstatic queue_blob **tail = &head;
847dff02feSmrg
857dff02feSmrgstatic void enqueue(queue_func func, void *closure)
867dff02feSmrg{
877dff02feSmrg    queue_blob *blob = malloc(sizeof(*blob));
887dff02feSmrg    if (!blob)
897dff02feSmrg	return; /* TODO: print OOM error */
907dff02feSmrg
917dff02feSmrg    blob->func = func;
927dff02feSmrg    blob->closure = closure;
937dff02feSmrg    blob->next = NULL;
947dff02feSmrg    *tail = blob;
957dff02feSmrg    tail = &blob->next;
967dff02feSmrg}
977dff02feSmrg
987dff02feSmrgstatic void run_queue(void)
997dff02feSmrg{
1007dff02feSmrg    while (head) {
1017dff02feSmrg	queue_blob *blob = head;
1027dff02feSmrg	blob->func(blob->closure);
1037dff02feSmrg	head = blob->next;
1047dff02feSmrg	free(blob);
1057dff02feSmrg    }
1067dff02feSmrg    tail = &head;
1077dff02feSmrg}
1087dff02feSmrg
1097dff02feSmrgtypedef struct {
1107dff02feSmrg    xcb_connection_t *c;
1117dff02feSmrg    xcb_intern_atom_cookie_t cookie;
1127dff02feSmrg    xcb_atom_t *atom;
1137dff02feSmrg} atom_state;
1147dff02feSmrg
1157dff02feSmrgstatic void atom_done(void *closure)
1167dff02feSmrg{
1177dff02feSmrg    xcb_intern_atom_reply_t *reply;
1187dff02feSmrg    atom_state *as = closure;
1197dff02feSmrg
1207dff02feSmrg    reply = xcb_intern_atom_reply(as->c, as->cookie, NULL);
1217dff02feSmrg    if (!reply)
1227dff02feSmrg	goto done; /* TODO: print Error message */
1237dff02feSmrg
1247dff02feSmrg    *(as->atom) = reply->atom;
1257dff02feSmrg    free(reply);
1267dff02feSmrg
1277dff02feSmrgdone:
1287dff02feSmrg    free(as);
1297dff02feSmrg}
1307dff02feSmrg
1317dff02feSmrgstatic void init_atoms(xcb_connection_t *c)
1327dff02feSmrg{
1337dff02feSmrg    atom_state *as;
1347dff02feSmrg
1357dff02feSmrg    as = malloc(sizeof(*as));
1367dff02feSmrg    as->c = c;
1377dff02feSmrg    as->atom = &WM_STATE;
1387dff02feSmrg    as->cookie = xcb_intern_atom(c, 0, strlen("WM_STATE"), "WM_STATE");
1397dff02feSmrg    enqueue(atom_done, as);
1407dff02feSmrg}
1417dff02feSmrg
142a850946eSmrgint
143a850946eSmrgmain(int argc, char *argv[])
144a850946eSmrg{
145a850946eSmrg    int i;
146a850946eSmrg    char *displayname = NULL;
147a850946eSmrg    Bool all_screens = False;
148a850946eSmrg    Bool verbose = False;
1497dff02feSmrg    xcb_connection_t *dpy;
1507dff02feSmrg    const xcb_setup_t *setup;
1517dff02feSmrg    int screen_number = 0;
152a850946eSmrg    int maxcmdlen = 10000;
153a850946eSmrg
154a850946eSmrg    ProgramName = argv[0];
155a850946eSmrg
156a850946eSmrg    for (i = 1; i < argc; i++) {
157a850946eSmrg	char *arg = argv[i];
158a850946eSmrg
159a850946eSmrg	if (arg[0] == '-') {
160a850946eSmrg	    char *cp;
161a850946eSmrg
162a850946eSmrg	    switch (arg[1]) {
163a850946eSmrg	      case 'd':			/* -display dpyname */
164a850946eSmrg		if (++i >= argc) usage ();
165a850946eSmrg		displayname = argv[i];
166a850946eSmrg		continue;
167a850946eSmrg	      case 'm':			/* -max maxcmdlen */
168a850946eSmrg		if (++i >= argc) usage ();
169a850946eSmrg		maxcmdlen = atoi (argv[i]);
170a850946eSmrg		continue;
171a850946eSmrg	    }
172a850946eSmrg
173a850946eSmrg	    for (cp = &arg[1]; *cp; cp++) {
174a850946eSmrg		switch (*cp) {
175a850946eSmrg		  case 'a':		/* -all */
176a850946eSmrg		    all_screens = True;
177a850946eSmrg		    continue;
178a850946eSmrg		  case 'l':		/* -long */
179a850946eSmrg		    verbose = True;
180a850946eSmrg		    continue;
181a850946eSmrg		  default:
182a850946eSmrg		    usage ();
183a850946eSmrg		}
184a850946eSmrg	    }
185a850946eSmrg	} else {
186a850946eSmrg	    usage ();
187a850946eSmrg	}
188a850946eSmrg    }
189a850946eSmrg
1907dff02feSmrg    dpy = xcb_connect(displayname, &screen_number);
1917dff02feSmrg    if (xcb_connection_has_error(dpy)) {
1927dff02feSmrg	char *name = displayname;
1937dff02feSmrg	if (!name)
1947dff02feSmrg	    name = getenv("DISPLAY");
1957dff02feSmrg	if (!name)
1967dff02feSmrg	    name = "";
197a850946eSmrg	fprintf (stderr, "%s:  unable to open display \"%s\"\r\n",
1987dff02feSmrg		 ProgramName, name);
199a850946eSmrg	exit (1);
200a850946eSmrg    }
201a850946eSmrg
2027dff02feSmrg    init_atoms(dpy);
2037dff02feSmrg
2047dff02feSmrg    setup = xcb_get_setup(dpy);
205a850946eSmrg    if (all_screens) {
2067dff02feSmrg	xcb_screen_iterator_t screen;
2077dff02feSmrg
2087dff02feSmrg	screen = xcb_setup_roots_iterator(setup);
2097dff02feSmrg	do {
2107dff02feSmrg	    lookat(dpy, screen.data->root, verbose, maxcmdlen);
2117dff02feSmrg	    xcb_screen_next(&screen);
2127dff02feSmrg	} while (screen.rem);
213a850946eSmrg    } else {
2147dff02feSmrg	xcb_screen_iterator_t screen;
2157dff02feSmrg
2167dff02feSmrg	screen = xcb_setup_roots_iterator(setup);
2177dff02feSmrg	for (i = 0; i < screen_number; i++)
2187dff02feSmrg	    xcb_screen_next(&screen);
2197dff02feSmrg
2207dff02feSmrg	lookat (dpy, screen.data->root, verbose, maxcmdlen);
221a850946eSmrg    }
222a850946eSmrg
2237dff02feSmrg    run_queue();
2247dff02feSmrg
2257dff02feSmrg    xcb_disconnect(dpy);
226a850946eSmrg    exit (0);
227a850946eSmrg}
228a850946eSmrg
2297dff02feSmrgtypedef struct {
2307dff02feSmrg    xcb_connection_t *c;
2317dff02feSmrg    xcb_get_property_cookie_t *prop_cookie;
2327dff02feSmrg    xcb_query_tree_cookie_t *tree_cookie;
2337dff02feSmrg    xcb_window_t *win;
2347dff02feSmrg    xcb_window_t orig_win;
2357dff02feSmrg    int list_length;
2367dff02feSmrg    int verbose;
2377dff02feSmrg    int maxcmdlen;
2387dff02feSmrg} child_wm_state;
2397dff02feSmrg
2407dff02feSmrgstatic void child_info(void *closure)
2417dff02feSmrg{
2427dff02feSmrg    child_wm_state *cs = closure;
2437dff02feSmrg    xcb_window_t orig = cs->orig_win;
2447dff02feSmrg    xcb_connection_t *c = cs->c;
2457dff02feSmrg    int verbose = cs->verbose;
2467dff02feSmrg    int maxcmdlen = cs->maxcmdlen;
2477dff02feSmrg    int i, j;
2487dff02feSmrg
2497dff02feSmrg    int child_count, num_rep;
2507dff02feSmrg    xcb_query_tree_reply_t **reply;
2517dff02feSmrg
2527dff02feSmrg    for (i = 0; i < cs->list_length; i++) {
2537dff02feSmrg	xcb_get_property_reply_t *reply;
2547dff02feSmrg	reply = xcb_get_property_reply(c, cs->prop_cookie[i], NULL);
2557dff02feSmrg	if (reply) {
2567dff02feSmrg	    if (reply->type) {
2577dff02feSmrg		/* Show information for this window */
2587dff02feSmrg		print_client_properties(c, cs->win[i], cs->verbose, cs->maxcmdlen);
2597dff02feSmrg
2607dff02feSmrg		free(reply);
2617dff02feSmrg
2627dff02feSmrg		/* drain stale replies */
2637dff02feSmrg		for (j = i+1; j < cs->list_length; j++) {
2647dff02feSmrg		    reply = xcb_get_property_reply(c, cs->prop_cookie[j], NULL);
2657dff02feSmrg		    if (reply)
2667dff02feSmrg			free(reply);
2677dff02feSmrg		}
2687dff02feSmrg		for (j = 0; j < cs->list_length; j++) {
2697dff02feSmrg		    xcb_query_tree_reply_t *rep;
2707dff02feSmrg		    rep = xcb_query_tree_reply(c, cs->tree_cookie[j], NULL);
2717dff02feSmrg		    if (rep)
2727dff02feSmrg			free(rep);
2737dff02feSmrg		}
2747dff02feSmrg		goto done;
2757dff02feSmrg	    }
2767dff02feSmrg	    free(reply);
2777dff02feSmrg	}
2787dff02feSmrg    }
2797dff02feSmrg
2807dff02feSmrg    /* WM_STATE not found. Recurse into children: */
2817dff02feSmrg    num_rep = 0;
2827dff02feSmrg    reply = malloc(sizeof(*reply) * cs->list_length);
2837dff02feSmrg    if (!reply)
2847dff02feSmrg	goto done; /* TODO: print OOM message, drain reply queue */
2857dff02feSmrg
2867dff02feSmrg    for (i = 0; i < cs->list_length; i++) {
2877dff02feSmrg	reply[num_rep] = xcb_query_tree_reply(c, cs->tree_cookie[i], NULL);
2887dff02feSmrg	if (reply[num_rep])
2897dff02feSmrg	    num_rep++;
2907dff02feSmrg    }
2917dff02feSmrg
2927dff02feSmrg    child_count = 0;
2937dff02feSmrg    for (i = 0; i < num_rep; i++)
2947dff02feSmrg	child_count += reply[i]->children_len;
2957dff02feSmrg
2967dff02feSmrg    if (!child_count) {
2977dff02feSmrg	/* No children have CS_STATE; try the parent window */
2987dff02feSmrg	print_client_properties(c, cs->orig_win, cs->verbose, cs->maxcmdlen);
2997dff02feSmrg	goto reply_done;
3007dff02feSmrg    }
3017dff02feSmrg
3027dff02feSmrg    cs = malloc(sizeof(*cs) + child_count * (sizeof(*cs->prop_cookie) + sizeof(*cs->tree_cookie) + sizeof(*cs->win)));
3037dff02feSmrg    if (!cs)
3047dff02feSmrg	goto reply_done; /* TODO: print OOM message */
3057dff02feSmrg
3067dff02feSmrg    cs->c = c;
3077dff02feSmrg    cs->verbose = verbose;
3087dff02feSmrg    cs->maxcmdlen = maxcmdlen;
3097dff02feSmrg    cs->orig_win = orig;
3107dff02feSmrg    cs->prop_cookie = (void *)&cs[1];
3117dff02feSmrg    cs->tree_cookie = (void *)&cs->prop_cookie[child_count];
3127dff02feSmrg    cs->win = (void *)&cs->tree_cookie[child_count];
3137dff02feSmrg    cs->list_length = child_count;
3147dff02feSmrg
3157dff02feSmrg    child_count = 0;
3167dff02feSmrg    for (i = 0; i < num_rep; i++) {
3177dff02feSmrg	xcb_window_t *child = xcb_query_tree_children(reply[i]);
3187dff02feSmrg	for (j = 0; j < reply[i]->children_len; j++) {
3197dff02feSmrg	    cs->win[child_count] = child[j];
3207dff02feSmrg	    cs->prop_cookie[child_count] = xcb_get_property(c, 0, child[j],
3217dff02feSmrg			    WM_STATE, XCB_GET_PROPERTY_TYPE_ANY,
3227dff02feSmrg			    0, 0);
3237dff02feSmrg	    /* Just in case the property isn't there, get the tree too */
3247dff02feSmrg	    cs->tree_cookie[child_count++] = xcb_query_tree(c, child[j]);
3257dff02feSmrg	}
3267dff02feSmrg    }
3277dff02feSmrg
3287dff02feSmrg    enqueue(child_info, cs);
3297dff02feSmrg
3307dff02feSmrgreply_done:
3317dff02feSmrg    for (i = 0; i < num_rep; i++)
3327dff02feSmrg	free(reply[i]);
3337dff02feSmrg    free(reply);
3347dff02feSmrg
3357dff02feSmrgdone:
3367dff02feSmrg    free(closure);
3377dff02feSmrg}
3387dff02feSmrg
3397dff02feSmrgtypedef struct {
3407dff02feSmrg    xcb_connection_t *c;
3417dff02feSmrg    xcb_query_tree_cookie_t cookie;
3427dff02feSmrg    int verbose;
3437dff02feSmrg    int maxcmdlen;
3447dff02feSmrg} root_list_state;
3457dff02feSmrg
3467dff02feSmrgstatic void root_list(void *closure)
3477dff02feSmrg{
3487dff02feSmrg    int i;
3497dff02feSmrg    xcb_window_t *child;
3507dff02feSmrg    xcb_query_tree_reply_t *reply;
3517dff02feSmrg    root_list_state *rl = closure;
3527dff02feSmrg
3537dff02feSmrg    reply = xcb_query_tree_reply(rl->c, rl->cookie, NULL);
3547dff02feSmrg    if (!reply)
3557dff02feSmrg	goto done;
3567dff02feSmrg
3577dff02feSmrg    child = xcb_query_tree_children(reply);
3587dff02feSmrg    for (i = 0; i < reply->children_len; i++) {
3597dff02feSmrg	/* Get information about each child */
3607dff02feSmrg	child_wm_state *cs = malloc(sizeof(*cs) + sizeof(*cs->prop_cookie) + sizeof(*cs->tree_cookie) + sizeof(*cs->win));
3617dff02feSmrg	if (!cs)
3627dff02feSmrg	    goto done; /* TODO: print OOM message */
3637dff02feSmrg	cs->c = rl->c;
3647dff02feSmrg	cs->verbose = rl->verbose;
3657dff02feSmrg	cs->maxcmdlen = rl->maxcmdlen;
3667dff02feSmrg	cs->prop_cookie = (void *)&cs[1];
3677dff02feSmrg	cs->tree_cookie = (void *)&cs->prop_cookie[1];
3687dff02feSmrg	cs->win = (void *)&cs->tree_cookie[1];
3697dff02feSmrg
3707dff02feSmrg	cs->orig_win = child[i];
3717dff02feSmrg	cs->win[0] = child[i];
3727dff02feSmrg
3737dff02feSmrg	cs->prop_cookie[0] = xcb_get_property(rl->c, 0, child[i],
3747dff02feSmrg			WM_STATE, XCB_GET_PROPERTY_TYPE_ANY,
3757dff02feSmrg			0, 0);
3767dff02feSmrg	/* Just in case the property isn't there, get the tree too */
3777dff02feSmrg	cs->tree_cookie[0] = xcb_query_tree(rl->c, child[i]);
3787dff02feSmrg
3797dff02feSmrg	cs->list_length = 1;
3807dff02feSmrg	enqueue(child_info, cs);
3817dff02feSmrg    }
3827dff02feSmrg    free(reply);
3837dff02feSmrg
3847dff02feSmrgdone:
3857dff02feSmrg    free(rl);
3867dff02feSmrg}
3877dff02feSmrg
388a850946eSmrgstatic void
3897dff02feSmrglookat(xcb_connection_t *dpy, xcb_window_t root, int verbose, int maxcmdlen)
390a850946eSmrg{
3917dff02feSmrg    root_list_state *rl = malloc(sizeof(*rl));
392a850946eSmrg
3937dff02feSmrg    if (!rl)
3947dff02feSmrg	return; /* TODO: OOM message */
395a850946eSmrg
396a850946eSmrg    /*
3977dff02feSmrg     * get the list of windows
398a850946eSmrg     */
399a850946eSmrg
4007dff02feSmrg    rl->c = dpy;
4017dff02feSmrg    rl->cookie = xcb_query_tree(dpy, root);
4027dff02feSmrg    rl->verbose = verbose;
4037dff02feSmrg    rl->maxcmdlen = maxcmdlen;
4047dff02feSmrg    enqueue(root_list, rl);
405a850946eSmrg}
406a850946eSmrg
407a850946eSmrgstatic char *Nil = "(nil)";
408a850946eSmrg
4097dff02feSmrgtypedef struct {
4107dff02feSmrg    xcb_connection_t *c;
4117dff02feSmrg    xcb_get_property_cookie_t client_machine;
4127dff02feSmrg    xcb_get_property_cookie_t command;
4137dff02feSmrg    xcb_get_property_cookie_t name;
4147dff02feSmrg    xcb_get_property_cookie_t icon_name;
4157dff02feSmrg    xcb_get_property_cookie_t wm_class;
4167dff02feSmrg    xcb_window_t w;
4177dff02feSmrg    int verbose;
4187dff02feSmrg    int maxcmdlen;
4197dff02feSmrg} client_state;
4207dff02feSmrg
421a850946eSmrgstatic void
4227dff02feSmrgshow_client_properties(void *closure)
423a850946eSmrg{
4247dff02feSmrg    client_state *cs = closure;
4257dff02feSmrg    xcb_get_property_reply_t *client_machine;
4267dff02feSmrg    xcb_get_property_reply_t *command;
4277dff02feSmrg    xcb_get_property_reply_t *name;
4287dff02feSmrg    xcb_get_property_reply_t *icon_name;
4297dff02feSmrg    xcb_get_property_reply_t *wm_class;
4307dff02feSmrg    char *argv;
4317dff02feSmrg    int charsleft = cs->maxcmdlen;
4327dff02feSmrg    int i;
433a850946eSmrg
434a850946eSmrg    /*
435a850946eSmrg     * get the WM_MACHINE and WM_COMMAND list of strings
436a850946eSmrg     */
4377dff02feSmrg    client_machine = xcb_get_property_reply(cs->c, cs->client_machine, NULL);
4387dff02feSmrg    command = xcb_get_property_reply(cs->c, cs->command, NULL);
4397dff02feSmrg    if (cs->verbose) {
4407dff02feSmrg	name = xcb_get_property_reply(cs->c, cs->name, NULL);
4417dff02feSmrg	icon_name = xcb_get_property_reply(cs->c, cs->icon_name, NULL);
4427dff02feSmrg	wm_class = xcb_get_property_reply(cs->c, cs->wm_class, NULL);
443a850946eSmrg    }
444a850946eSmrg
4457dff02feSmrg    if (!command || !command->type)
4467dff02feSmrg	goto done;
447a850946eSmrg
448a850946eSmrg    /*
449a850946eSmrg     * do header information
450a850946eSmrg     */
4517dff02feSmrg    if (cs->verbose) {
4527dff02feSmrg	printf ("Window 0x%" PRIx32 ":\n", cs->w);
4537dff02feSmrg	print_text_field (cs->c, "  Machine:  ", client_machine);
4547dff02feSmrg	if (name && name->type)
4557dff02feSmrg	    print_text_field (cs->c, "  Name:  ", name);
456a850946eSmrg    } else {
4577dff02feSmrg	print_text_field (cs->c, NULL, client_machine);
458a850946eSmrg	putchar (' ');
459a850946eSmrg	putchar (' ');
460a850946eSmrg    }
461a850946eSmrg
4627dff02feSmrg
4637dff02feSmrg    if (cs->verbose)
4647dff02feSmrg	if (icon_name && icon_name->type)
4657dff02feSmrg	    print_text_field (cs->c, "  Icon Name:  ", icon_name);
466a850946eSmrg
467a850946eSmrg
468a850946eSmrg    /*
469a850946eSmrg     * do the command
470a850946eSmrg     */
4717dff02feSmrg    if (cs->verbose)
472a850946eSmrg	printf ("  Command:  ");
4737dff02feSmrg    argv = xcb_get_property_value(command);
4747dff02feSmrg    for (i = 0; i < command->value_len && charsleft > 0; ) {
4757dff02feSmrg	charsleft -= print_quoted_word (argv + i, charsleft);
4767dff02feSmrg	i += strnlen(argv + i, command->value_len - i) + 1;
4777dff02feSmrg	if (i < command->value_len && charsleft > 0) {
4787dff02feSmrg	    putchar (' ');
4797dff02feSmrg	    charsleft--;
480a850946eSmrg	}
481a850946eSmrg    }
482a850946eSmrg    putchar ('\n');
483a850946eSmrg
484a850946eSmrg
485a850946eSmrg    /*
486a850946eSmrg     * do trailer information
487a850946eSmrg     */
4887dff02feSmrg    if (cs->verbose) {
4897dff02feSmrg	if (wm_class && wm_class->type) {
4907dff02feSmrg	    char *res_name, *res_class;
4917dff02feSmrg	    int name_len, class_len;
4927dff02feSmrg	    res_name = xcb_get_property_value(wm_class);
4937dff02feSmrg	    name_len = strnlen(res_name, wm_class->value_len) + 1;
4947dff02feSmrg	    class_len = wm_class->value_len - name_len;
4957dff02feSmrg	    if (class_len > 0) {
4967dff02feSmrg		res_class = res_name + name_len;
4977dff02feSmrg	    } else {
4987dff02feSmrg		res_class = Nil;
4997dff02feSmrg		class_len = strlen(res_class);
5007dff02feSmrg	    }
5017dff02feSmrg
5027dff02feSmrg	    printf ("  Instance/Class:  %.*s/%.*s",
5037dff02feSmrg		    name_len, res_name,
5047dff02feSmrg		    class_len, res_class);
505a850946eSmrg	    putchar ('\n');
506a850946eSmrg	}
507a850946eSmrg    }
5087dff02feSmrg
5097dff02feSmrgdone:
5107dff02feSmrg    if (client_machine)
5117dff02feSmrg	free(client_machine);
5127dff02feSmrg    if (command)
5137dff02feSmrg	free(command);
5147dff02feSmrg    if (cs->verbose) {
5157dff02feSmrg	if (name)
5167dff02feSmrg	    free(name);
5177dff02feSmrg	if (icon_name)
5187dff02feSmrg	    free(icon_name);
5197dff02feSmrg	if (wm_class)
5207dff02feSmrg	    free(wm_class);
5217dff02feSmrg    }
5227dff02feSmrg    free(cs);
523a850946eSmrg}
524a850946eSmrg
525a850946eSmrgstatic void
5267dff02feSmrgprint_client_properties(xcb_connection_t *dpy, xcb_window_t w, int verbose, int maxcmdlen)
527a850946eSmrg{
5287dff02feSmrg    client_state *cs = malloc(sizeof(*cs));
5297dff02feSmrg    if (!cs)
5307dff02feSmrg	return; /* TODO: print OOM message */
5317dff02feSmrg
5327dff02feSmrg    cs->c = dpy;
5337dff02feSmrg    cs->w = w;
5347dff02feSmrg    cs->verbose = verbose;
5357dff02feSmrg    cs->maxcmdlen = maxcmdlen;
5367dff02feSmrg
5377dff02feSmrg    /*
5387dff02feSmrg     * get the WM_CLIENT_MACHINE and WM_COMMAND list of strings
5397dff02feSmrg     */
5407dff02feSmrg    cs->client_machine = xcb_get_property(dpy, 0, w,
5418e46b049Smrg			    XCB_ATOM_WM_CLIENT_MACHINE, XCB_GET_PROPERTY_TYPE_ANY,
5427dff02feSmrg			    0, 1000000L);
5437dff02feSmrg    cs->command = xcb_get_property(dpy, 0, w,
5448e46b049Smrg			    XCB_ATOM_WM_COMMAND, XCB_GET_PROPERTY_TYPE_ANY,
5457dff02feSmrg			    0, 1000000L);
5467dff02feSmrg
5477dff02feSmrg    if (verbose) {
5487dff02feSmrg	cs->name = xcb_get_property(dpy, 0, w,
5498e46b049Smrg			    XCB_ATOM_WM_NAME, XCB_GET_PROPERTY_TYPE_ANY,
5507dff02feSmrg			    0, 1000000L);
5517dff02feSmrg	cs->icon_name = xcb_get_property(dpy, 0, w,
5528e46b049Smrg			    XCB_ATOM_WM_ICON_NAME, XCB_GET_PROPERTY_TYPE_ANY,
5537dff02feSmrg			    0, 1000000L);
5547dff02feSmrg	cs->wm_class = xcb_get_property(dpy, 0, w,
5558e46b049Smrg			    XCB_ATOM_WM_CLASS, XCB_ATOM_STRING,
5567dff02feSmrg			    0, 1000000L);
5577dff02feSmrg    }
5587dff02feSmrg
5597dff02feSmrg    enqueue(show_client_properties, cs);
5607dff02feSmrg}
5617dff02feSmrg
5627dff02feSmrgstatic void
5637dff02feSmrgprint_text_field(xcb_connection_t *dpy, char *s, xcb_get_property_reply_t *tp)
5647dff02feSmrg{
5657dff02feSmrg    if (tp->type == XCB_NONE || tp->format == 0) {  /* Or XCB_ATOM_NONE after libxcb 1.5 */
566a850946eSmrg	printf ("''");
567a850946eSmrg	return;
568a850946eSmrg    }
569a850946eSmrg
570a850946eSmrg    if (s) printf ("%s", s);
5718e46b049Smrg    if (tp->type == XCB_ATOM_STRING && tp->format == 8) {
5727dff02feSmrg	printf ("%.*s", (int)tp->value_len, (char *)xcb_get_property_value(tp));
573a850946eSmrg    } else {
5747dff02feSmrg	unknown (dpy, tp->type, tp->format);
575a850946eSmrg    }
576a850946eSmrg    if (s) putchar ('\n');
577a850946eSmrg}
578a850946eSmrg
579a850946eSmrg/* returns the number of characters printed */
580a850946eSmrgstatic int
581a850946eSmrgprint_quoted_word(char *s,
582a850946eSmrg		  int maxlen)		/* max number of chars we can print */
583a850946eSmrg{
584a850946eSmrg    register char *cp;
585a850946eSmrg    Bool need_quote = False, in_quote = False;
586a850946eSmrg    char quote_char = '\'', other_quote = '"';
587a850946eSmrg    int charsprinted = 0;
588a850946eSmrg
589a850946eSmrg    /*
590a850946eSmrg     * walk down seeing whether or not we need to quote
591a850946eSmrg     */
592a850946eSmrg    for (cp = s; *cp; cp++) {
593a850946eSmrg
594a850946eSmrg	if (! ((isascii(*cp) && isalnum(*cp)) ||
595a850946eSmrg	       (*cp == '-' || *cp == '_' || *cp == '.' || *cp == '+' ||
596a850946eSmrg		*cp == '/' || *cp == '=' || *cp == ':' || *cp == ','))) {
597a850946eSmrg	    need_quote = True;
598a850946eSmrg	    break;
599a850946eSmrg	}
600a850946eSmrg    }
601a850946eSmrg
602a850946eSmrg    /*
603a850946eSmrg     * write out the string: if we hit a quote, then close any previous quote,
604a850946eSmrg     * emit the other quote, swap quotes and continue on.
605a850946eSmrg     */
606a850946eSmrg    in_quote = need_quote;
607a850946eSmrg    if (need_quote) {
608a850946eSmrg	putchar (quote_char);
609a850946eSmrg	charsprinted++; maxlen--;
610a850946eSmrg    }
611a850946eSmrg    for (cp = s; *cp && maxlen>0; cp++) {
612a850946eSmrg	if (*cp == quote_char) {
613a850946eSmrg	    if (in_quote) {
614a850946eSmrg		putchar (quote_char);
615a850946eSmrg		charsprinted++; maxlen--;
616a850946eSmrg	    }
617a850946eSmrg	    putchar (other_quote);
618a850946eSmrg	    charsprinted++; maxlen--;
619a850946eSmrg	    {
620a850946eSmrg		char tmp = other_quote;
621a850946eSmrg		other_quote = quote_char; quote_char = tmp;
622a850946eSmrg	    }
623a850946eSmrg	    in_quote = True;
624a850946eSmrg	}
625a850946eSmrg	putchar (*cp);
626a850946eSmrg	charsprinted++; maxlen--;
627a850946eSmrg    }
628a850946eSmrg    /* close the quote if we opened one and if we printed the whole string */
629a850946eSmrg    if (in_quote && maxlen>0) {
630a850946eSmrg	putchar (quote_char);
631a850946eSmrg	charsprinted++; maxlen--;
632a850946eSmrg    }
633a850946eSmrg
634a850946eSmrg    return charsprinted;
635a850946eSmrg}
636a850946eSmrg
637a850946eSmrgstatic void
6387dff02feSmrgunknown(xcb_connection_t *dpy, xcb_atom_t actual_type, int actual_format)
639a850946eSmrg{
640a850946eSmrg    printf ("<unknown type ");
6417dff02feSmrg    if (actual_type == XCB_NONE)
6427dff02feSmrg	printf ("None");
6437dff02feSmrg    else {
6447dff02feSmrg	/* This should happen so rarely as to make no odds. Eat a round-trip: */
6457dff02feSmrg	xcb_get_atom_name_reply_t *atom =
6467dff02feSmrg	    xcb_get_atom_name_reply(dpy,
6477dff02feSmrg		xcb_get_atom_name(dpy, actual_type), NULL);
6487dff02feSmrg	if (atom) {
6497dff02feSmrg	    printf("%.*s", xcb_get_atom_name_name_length(atom),
6507dff02feSmrg			  xcb_get_atom_name_name(atom));
6517dff02feSmrg	    free(atom);
6527dff02feSmrg	} else
6537dff02feSmrg	    fputs (Nil, stdout);
654a850946eSmrg    }
6557dff02feSmrg    printf (" (%" PRIu32 ") or format %d>", actual_type, actual_format);
656a850946eSmrg}
657