xlsclients.c revision c44a0236
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
68c44a0236Smrgusage(const char *errmsg)
69a850946eSmrg{
70c44a0236Smrg    if (errmsg != NULL)
71c44a0236Smrg	fprintf (stderr, "%s: %s\n\n", ProgramName, errmsg);
72c44a0236Smrg
73a850946eSmrg    fprintf (stderr,
749511053fSmrg	     "usage:  %s  [-display dpy] [-m len] [-[a][l]] [-version]\n",
759511053fSmrg	     ProgramName);
76a850946eSmrg    exit (1);
77a850946eSmrg}
78a850946eSmrg
797dff02feSmrgtypedef void (*queue_func)(void *closure);
807dff02feSmrgtypedef struct queue_blob {
817dff02feSmrg    queue_func func;
827dff02feSmrg    void *closure;
837dff02feSmrg    struct queue_blob *next;
847dff02feSmrg} queue_blob;
857dff02feSmrg
867dff02feSmrgstatic queue_blob *head = NULL;
877dff02feSmrgstatic queue_blob **tail = &head;
887dff02feSmrg
897dff02feSmrgstatic void enqueue(queue_func func, void *closure)
907dff02feSmrg{
917dff02feSmrg    queue_blob *blob = malloc(sizeof(*blob));
927dff02feSmrg    if (!blob)
937dff02feSmrg	return; /* TODO: print OOM error */
947dff02feSmrg
957dff02feSmrg    blob->func = func;
967dff02feSmrg    blob->closure = closure;
977dff02feSmrg    blob->next = NULL;
987dff02feSmrg    *tail = blob;
997dff02feSmrg    tail = &blob->next;
1007dff02feSmrg}
1017dff02feSmrg
1027dff02feSmrgstatic void run_queue(void)
1037dff02feSmrg{
1047dff02feSmrg    while (head) {
1057dff02feSmrg	queue_blob *blob = head;
1067dff02feSmrg	blob->func(blob->closure);
1077dff02feSmrg	head = blob->next;
1087dff02feSmrg	free(blob);
1097dff02feSmrg    }
1107dff02feSmrg    tail = &head;
1117dff02feSmrg}
1127dff02feSmrg
1137dff02feSmrgtypedef struct {
1147dff02feSmrg    xcb_connection_t *c;
1157dff02feSmrg    xcb_intern_atom_cookie_t cookie;
1167dff02feSmrg    xcb_atom_t *atom;
1177dff02feSmrg} atom_state;
1187dff02feSmrg
1197dff02feSmrgstatic void atom_done(void *closure)
1207dff02feSmrg{
1217dff02feSmrg    xcb_intern_atom_reply_t *reply;
1227dff02feSmrg    atom_state *as = closure;
1237dff02feSmrg
1247dff02feSmrg    reply = xcb_intern_atom_reply(as->c, as->cookie, NULL);
1257dff02feSmrg    if (!reply)
1267dff02feSmrg	goto done; /* TODO: print Error message */
1277dff02feSmrg
1287dff02feSmrg    *(as->atom) = reply->atom;
1297dff02feSmrg    free(reply);
1307dff02feSmrg
1317dff02feSmrgdone:
1327dff02feSmrg    free(as);
1337dff02feSmrg}
1347dff02feSmrg
1357dff02feSmrgstatic void init_atoms(xcb_connection_t *c)
1367dff02feSmrg{
1377dff02feSmrg    atom_state *as;
1387dff02feSmrg
1397dff02feSmrg    as = malloc(sizeof(*as));
1407dff02feSmrg    as->c = c;
1417dff02feSmrg    as->atom = &WM_STATE;
1427dff02feSmrg    as->cookie = xcb_intern_atom(c, 0, strlen("WM_STATE"), "WM_STATE");
1437dff02feSmrg    enqueue(atom_done, as);
1447dff02feSmrg}
1457dff02feSmrg
146a850946eSmrgint
147a850946eSmrgmain(int argc, char *argv[])
148a850946eSmrg{
149a850946eSmrg    int i;
150a850946eSmrg    char *displayname = NULL;
151a850946eSmrg    Bool all_screens = False;
152a850946eSmrg    Bool verbose = False;
1537dff02feSmrg    xcb_connection_t *dpy;
1547dff02feSmrg    const xcb_setup_t *setup;
1557dff02feSmrg    int screen_number = 0;
156a850946eSmrg    int maxcmdlen = 10000;
157a850946eSmrg
158a850946eSmrg    ProgramName = argv[0];
159a850946eSmrg
160a850946eSmrg    for (i = 1; i < argc; i++) {
161a850946eSmrg	char *arg = argv[i];
162a850946eSmrg
163a850946eSmrg	if (arg[0] == '-') {
164a850946eSmrg	    char *cp;
165a850946eSmrg
166a850946eSmrg	    switch (arg[1]) {
167a850946eSmrg	      case 'd':			/* -display dpyname */
168c44a0236Smrg		if (++i >= argc) usage ("-display requires an argument");
169a850946eSmrg		displayname = argv[i];
170a850946eSmrg		continue;
171a850946eSmrg	      case 'm':			/* -max maxcmdlen */
172c44a0236Smrg		if (++i >= argc) usage ("-max requires an argument");
173a850946eSmrg		maxcmdlen = atoi (argv[i]);
174a850946eSmrg		continue;
1759511053fSmrg	      case 'v':			/* -version */
1769511053fSmrg		printf("%s\n", PACKAGE_STRING);
1779511053fSmrg		exit(0);
178a850946eSmrg	    }
179a850946eSmrg
180a850946eSmrg	    for (cp = &arg[1]; *cp; cp++) {
181a850946eSmrg		switch (*cp) {
182a850946eSmrg		  case 'a':		/* -all */
183a850946eSmrg		    all_screens = True;
184a850946eSmrg		    continue;
185a850946eSmrg		  case 'l':		/* -long */
186a850946eSmrg		    verbose = True;
187a850946eSmrg		    continue;
188a850946eSmrg		  default:
189c44a0236Smrg		    fprintf (stderr, "%s: unrecognized argument -%s\n\n",
190c44a0236Smrg			     ProgramName, cp);
191c44a0236Smrg		    usage (NULL);
192a850946eSmrg		}
193a850946eSmrg	    }
194a850946eSmrg	} else {
195c44a0236Smrg	    fprintf (stderr, "%s: unrecognized argument %s\n\n",
196c44a0236Smrg		     ProgramName, arg);
197c44a0236Smrg	    usage (NULL);
198a850946eSmrg	}
199a850946eSmrg    }
200a850946eSmrg
2017dff02feSmrg    dpy = xcb_connect(displayname, &screen_number);
2027dff02feSmrg    if (xcb_connection_has_error(dpy)) {
2039511053fSmrg	const char *name = displayname;
2047dff02feSmrg	if (!name)
2057dff02feSmrg	    name = getenv("DISPLAY");
2067dff02feSmrg	if (!name)
2077dff02feSmrg	    name = "";
208a850946eSmrg	fprintf (stderr, "%s:  unable to open display \"%s\"\r\n",
2097dff02feSmrg		 ProgramName, name);
210a850946eSmrg	exit (1);
211a850946eSmrg    }
212a850946eSmrg
2137dff02feSmrg    init_atoms(dpy);
2147dff02feSmrg
2157dff02feSmrg    setup = xcb_get_setup(dpy);
216a850946eSmrg    if (all_screens) {
2177dff02feSmrg	xcb_screen_iterator_t screen;
2187dff02feSmrg
2197dff02feSmrg	screen = xcb_setup_roots_iterator(setup);
2207dff02feSmrg	do {
2217dff02feSmrg	    lookat(dpy, screen.data->root, verbose, maxcmdlen);
2227dff02feSmrg	    xcb_screen_next(&screen);
2237dff02feSmrg	} while (screen.rem);
224a850946eSmrg    } else {
2257dff02feSmrg	xcb_screen_iterator_t screen;
2267dff02feSmrg
2277dff02feSmrg	screen = xcb_setup_roots_iterator(setup);
2287dff02feSmrg	for (i = 0; i < screen_number; i++)
2297dff02feSmrg	    xcb_screen_next(&screen);
2307dff02feSmrg
2317dff02feSmrg	lookat (dpy, screen.data->root, verbose, maxcmdlen);
232a850946eSmrg    }
233a850946eSmrg
2347dff02feSmrg    run_queue();
2357dff02feSmrg
2367dff02feSmrg    xcb_disconnect(dpy);
237a850946eSmrg    exit (0);
238a850946eSmrg}
239a850946eSmrg
2407dff02feSmrgtypedef struct {
2417dff02feSmrg    xcb_connection_t *c;
2427dff02feSmrg    xcb_get_property_cookie_t *prop_cookie;
2437dff02feSmrg    xcb_query_tree_cookie_t *tree_cookie;
2447dff02feSmrg    xcb_window_t *win;
2457dff02feSmrg    xcb_window_t orig_win;
2467dff02feSmrg    int list_length;
2477dff02feSmrg    int verbose;
2487dff02feSmrg    int maxcmdlen;
2497dff02feSmrg} child_wm_state;
2507dff02feSmrg
2517dff02feSmrgstatic void child_info(void *closure)
2527dff02feSmrg{
2537dff02feSmrg    child_wm_state *cs = closure;
2547dff02feSmrg    xcb_window_t orig = cs->orig_win;
2557dff02feSmrg    xcb_connection_t *c = cs->c;
2567dff02feSmrg    int verbose = cs->verbose;
2577dff02feSmrg    int maxcmdlen = cs->maxcmdlen;
2587dff02feSmrg    int i, j;
2597dff02feSmrg
2607dff02feSmrg    int child_count, num_rep;
2619511053fSmrg    xcb_query_tree_reply_t **qt_reply;
2627dff02feSmrg
2637dff02feSmrg    for (i = 0; i < cs->list_length; i++) {
2649511053fSmrg	xcb_get_property_reply_t *gp_reply;
2659511053fSmrg	gp_reply = xcb_get_property_reply(c, cs->prop_cookie[i], NULL);
2669511053fSmrg	if (gp_reply) {
2679511053fSmrg	    if (gp_reply->type) {
2687dff02feSmrg		/* Show information for this window */
2697dff02feSmrg		print_client_properties(c, cs->win[i], cs->verbose, cs->maxcmdlen);
2707dff02feSmrg
2719511053fSmrg		free(gp_reply);
2727dff02feSmrg
2737dff02feSmrg		/* drain stale replies */
2747dff02feSmrg		for (j = i+1; j < cs->list_length; j++) {
2759511053fSmrg		    gp_reply = xcb_get_property_reply(c, cs->prop_cookie[j], NULL);
2769511053fSmrg		    if (gp_reply)
2779511053fSmrg			free(gp_reply);
2787dff02feSmrg		}
2797dff02feSmrg		for (j = 0; j < cs->list_length; j++) {
2807dff02feSmrg		    xcb_query_tree_reply_t *rep;
2817dff02feSmrg		    rep = xcb_query_tree_reply(c, cs->tree_cookie[j], NULL);
2827dff02feSmrg		    if (rep)
2837dff02feSmrg			free(rep);
2847dff02feSmrg		}
2857dff02feSmrg		goto done;
2867dff02feSmrg	    }
2879511053fSmrg	    free(gp_reply);
2887dff02feSmrg	}
2897dff02feSmrg    }
2907dff02feSmrg
2917dff02feSmrg    /* WM_STATE not found. Recurse into children: */
2927dff02feSmrg    num_rep = 0;
2939511053fSmrg    qt_reply = malloc(sizeof(*qt_reply) * cs->list_length);
2949511053fSmrg    if (!qt_reply)
2957dff02feSmrg	goto done; /* TODO: print OOM message, drain reply queue */
2967dff02feSmrg
2977dff02feSmrg    for (i = 0; i < cs->list_length; i++) {
2989511053fSmrg	qt_reply[num_rep] = xcb_query_tree_reply(c, cs->tree_cookie[i], NULL);
2999511053fSmrg	if (qt_reply[num_rep])
3007dff02feSmrg	    num_rep++;
3017dff02feSmrg    }
3027dff02feSmrg
3037dff02feSmrg    child_count = 0;
3047dff02feSmrg    for (i = 0; i < num_rep; i++)
3059511053fSmrg	child_count += qt_reply[i]->children_len;
3067dff02feSmrg
3077dff02feSmrg    if (!child_count) {
3087dff02feSmrg	/* No children have CS_STATE; try the parent window */
3097dff02feSmrg	print_client_properties(c, cs->orig_win, cs->verbose, cs->maxcmdlen);
3107dff02feSmrg	goto reply_done;
3117dff02feSmrg    }
3127dff02feSmrg
3137dff02feSmrg    cs = malloc(sizeof(*cs) + child_count * (sizeof(*cs->prop_cookie) + sizeof(*cs->tree_cookie) + sizeof(*cs->win)));
3147dff02feSmrg    if (!cs)
3157dff02feSmrg	goto reply_done; /* TODO: print OOM message */
3167dff02feSmrg
3177dff02feSmrg    cs->c = c;
3187dff02feSmrg    cs->verbose = verbose;
3197dff02feSmrg    cs->maxcmdlen = maxcmdlen;
3207dff02feSmrg    cs->orig_win = orig;
3217dff02feSmrg    cs->prop_cookie = (void *)&cs[1];
3227dff02feSmrg    cs->tree_cookie = (void *)&cs->prop_cookie[child_count];
3237dff02feSmrg    cs->win = (void *)&cs->tree_cookie[child_count];
3247dff02feSmrg    cs->list_length = child_count;
3257dff02feSmrg
3267dff02feSmrg    child_count = 0;
3277dff02feSmrg    for (i = 0; i < num_rep; i++) {
3289511053fSmrg	xcb_window_t *child = xcb_query_tree_children(qt_reply[i]);
3299511053fSmrg	for (j = 0; j < qt_reply[i]->children_len; j++) {
3307dff02feSmrg	    cs->win[child_count] = child[j];
3317dff02feSmrg	    cs->prop_cookie[child_count] = xcb_get_property(c, 0, child[j],
3327dff02feSmrg			    WM_STATE, XCB_GET_PROPERTY_TYPE_ANY,
3337dff02feSmrg			    0, 0);
3347dff02feSmrg	    /* Just in case the property isn't there, get the tree too */
3357dff02feSmrg	    cs->tree_cookie[child_count++] = xcb_query_tree(c, child[j]);
3367dff02feSmrg	}
3377dff02feSmrg    }
3387dff02feSmrg
3397dff02feSmrg    enqueue(child_info, cs);
3407dff02feSmrg
3417dff02feSmrgreply_done:
3427dff02feSmrg    for (i = 0; i < num_rep; i++)
3439511053fSmrg	free(qt_reply[i]);
3449511053fSmrg    free(qt_reply);
3457dff02feSmrg
3467dff02feSmrgdone:
3477dff02feSmrg    free(closure);
3487dff02feSmrg}
3497dff02feSmrg
3507dff02feSmrgtypedef struct {
3517dff02feSmrg    xcb_connection_t *c;
3527dff02feSmrg    xcb_query_tree_cookie_t cookie;
3537dff02feSmrg    int verbose;
3547dff02feSmrg    int maxcmdlen;
3557dff02feSmrg} root_list_state;
3567dff02feSmrg
3577dff02feSmrgstatic void root_list(void *closure)
3587dff02feSmrg{
3597dff02feSmrg    int i;
3607dff02feSmrg    xcb_window_t *child;
3617dff02feSmrg    xcb_query_tree_reply_t *reply;
3627dff02feSmrg    root_list_state *rl = closure;
3637dff02feSmrg
3647dff02feSmrg    reply = xcb_query_tree_reply(rl->c, rl->cookie, NULL);
3657dff02feSmrg    if (!reply)
3667dff02feSmrg	goto done;
3677dff02feSmrg
3687dff02feSmrg    child = xcb_query_tree_children(reply);
3697dff02feSmrg    for (i = 0; i < reply->children_len; i++) {
3707dff02feSmrg	/* Get information about each child */
3717dff02feSmrg	child_wm_state *cs = malloc(sizeof(*cs) + sizeof(*cs->prop_cookie) + sizeof(*cs->tree_cookie) + sizeof(*cs->win));
3727dff02feSmrg	if (!cs)
3737dff02feSmrg	    goto done; /* TODO: print OOM message */
3747dff02feSmrg	cs->c = rl->c;
3757dff02feSmrg	cs->verbose = rl->verbose;
3767dff02feSmrg	cs->maxcmdlen = rl->maxcmdlen;
3777dff02feSmrg	cs->prop_cookie = (void *)&cs[1];
3787dff02feSmrg	cs->tree_cookie = (void *)&cs->prop_cookie[1];
3797dff02feSmrg	cs->win = (void *)&cs->tree_cookie[1];
3807dff02feSmrg
3817dff02feSmrg	cs->orig_win = child[i];
3827dff02feSmrg	cs->win[0] = child[i];
3837dff02feSmrg
3847dff02feSmrg	cs->prop_cookie[0] = xcb_get_property(rl->c, 0, child[i],
3857dff02feSmrg			WM_STATE, XCB_GET_PROPERTY_TYPE_ANY,
3867dff02feSmrg			0, 0);
3877dff02feSmrg	/* Just in case the property isn't there, get the tree too */
3887dff02feSmrg	cs->tree_cookie[0] = xcb_query_tree(rl->c, child[i]);
3897dff02feSmrg
3907dff02feSmrg	cs->list_length = 1;
3917dff02feSmrg	enqueue(child_info, cs);
3927dff02feSmrg    }
3937dff02feSmrg    free(reply);
3947dff02feSmrg
3957dff02feSmrgdone:
3967dff02feSmrg    free(rl);
3977dff02feSmrg}
3987dff02feSmrg
399a850946eSmrgstatic void
4007dff02feSmrglookat(xcb_connection_t *dpy, xcb_window_t root, int verbose, int maxcmdlen)
401a850946eSmrg{
4027dff02feSmrg    root_list_state *rl = malloc(sizeof(*rl));
403a850946eSmrg
4047dff02feSmrg    if (!rl)
4057dff02feSmrg	return; /* TODO: OOM message */
406a850946eSmrg
407a850946eSmrg    /*
4087dff02feSmrg     * get the list of windows
409a850946eSmrg     */
410a850946eSmrg
4117dff02feSmrg    rl->c = dpy;
4127dff02feSmrg    rl->cookie = xcb_query_tree(dpy, root);
4137dff02feSmrg    rl->verbose = verbose;
4147dff02feSmrg    rl->maxcmdlen = maxcmdlen;
4157dff02feSmrg    enqueue(root_list, rl);
416a850946eSmrg}
417a850946eSmrg
4189511053fSmrgstatic const char *Nil = "(nil)";
419a850946eSmrg
4207dff02feSmrgtypedef struct {
4217dff02feSmrg    xcb_connection_t *c;
4227dff02feSmrg    xcb_get_property_cookie_t client_machine;
4237dff02feSmrg    xcb_get_property_cookie_t command;
4247dff02feSmrg    xcb_get_property_cookie_t name;
4257dff02feSmrg    xcb_get_property_cookie_t icon_name;
4267dff02feSmrg    xcb_get_property_cookie_t wm_class;
4277dff02feSmrg    xcb_window_t w;
4287dff02feSmrg    int verbose;
4297dff02feSmrg    int maxcmdlen;
4307dff02feSmrg} client_state;
4317dff02feSmrg
432a850946eSmrgstatic void
4337dff02feSmrgshow_client_properties(void *closure)
434a850946eSmrg{
4357dff02feSmrg    client_state *cs = closure;
4367dff02feSmrg    xcb_get_property_reply_t *client_machine;
4377dff02feSmrg    xcb_get_property_reply_t *command;
4387dff02feSmrg    xcb_get_property_reply_t *name;
4397dff02feSmrg    xcb_get_property_reply_t *icon_name;
4407dff02feSmrg    xcb_get_property_reply_t *wm_class;
4417dff02feSmrg    char *argv;
4427dff02feSmrg    int charsleft = cs->maxcmdlen;
4437dff02feSmrg    int i;
444a850946eSmrg
445a850946eSmrg    /*
446a850946eSmrg     * get the WM_MACHINE and WM_COMMAND list of strings
447a850946eSmrg     */
4487dff02feSmrg    client_machine = xcb_get_property_reply(cs->c, cs->client_machine, NULL);
4497dff02feSmrg    command = xcb_get_property_reply(cs->c, cs->command, NULL);
4507dff02feSmrg    if (cs->verbose) {
4517dff02feSmrg	name = xcb_get_property_reply(cs->c, cs->name, NULL);
4527dff02feSmrg	icon_name = xcb_get_property_reply(cs->c, cs->icon_name, NULL);
4537dff02feSmrg	wm_class = xcb_get_property_reply(cs->c, cs->wm_class, NULL);
454a850946eSmrg    }
455a850946eSmrg
4567dff02feSmrg    if (!command || !command->type)
4577dff02feSmrg	goto done;
458a850946eSmrg
459a850946eSmrg    /*
460a850946eSmrg     * do header information
461a850946eSmrg     */
4627dff02feSmrg    if (cs->verbose) {
4637dff02feSmrg	printf ("Window 0x%" PRIx32 ":\n", cs->w);
4647dff02feSmrg	print_text_field (cs->c, "  Machine:  ", client_machine);
4657dff02feSmrg	if (name && name->type)
4667dff02feSmrg	    print_text_field (cs->c, "  Name:  ", name);
467a850946eSmrg    } else {
4687dff02feSmrg	print_text_field (cs->c, NULL, client_machine);
469a850946eSmrg	putchar (' ');
470a850946eSmrg	putchar (' ');
471a850946eSmrg    }
472a850946eSmrg
4737dff02feSmrg
4747dff02feSmrg    if (cs->verbose)
4757dff02feSmrg	if (icon_name && icon_name->type)
4767dff02feSmrg	    print_text_field (cs->c, "  Icon Name:  ", icon_name);
477a850946eSmrg
478a850946eSmrg
479a850946eSmrg    /*
480a850946eSmrg     * do the command
481a850946eSmrg     */
4827dff02feSmrg    if (cs->verbose)
483a850946eSmrg	printf ("  Command:  ");
4847dff02feSmrg    argv = xcb_get_property_value(command);
4857dff02feSmrg    for (i = 0; i < command->value_len && charsleft > 0; ) {
4867dff02feSmrg	charsleft -= print_quoted_word (argv + i, charsleft);
4877dff02feSmrg	i += strnlen(argv + i, command->value_len - i) + 1;
4887dff02feSmrg	if (i < command->value_len && charsleft > 0) {
4897dff02feSmrg	    putchar (' ');
4907dff02feSmrg	    charsleft--;
491a850946eSmrg	}
492a850946eSmrg    }
493a850946eSmrg    putchar ('\n');
494a850946eSmrg
495a850946eSmrg
496a850946eSmrg    /*
497a850946eSmrg     * do trailer information
498a850946eSmrg     */
4997dff02feSmrg    if (cs->verbose) {
5007dff02feSmrg	if (wm_class && wm_class->type) {
5019511053fSmrg	    const char *res_name, *res_class;
5027dff02feSmrg	    int name_len, class_len;
5037dff02feSmrg	    res_name = xcb_get_property_value(wm_class);
5047dff02feSmrg	    name_len = strnlen(res_name, wm_class->value_len) + 1;
5057dff02feSmrg	    class_len = wm_class->value_len - name_len;
5067dff02feSmrg	    if (class_len > 0) {
5077dff02feSmrg		res_class = res_name + name_len;
5087dff02feSmrg	    } else {
5097dff02feSmrg		res_class = Nil;
5107dff02feSmrg		class_len = strlen(res_class);
5117dff02feSmrg	    }
5127dff02feSmrg
5137dff02feSmrg	    printf ("  Instance/Class:  %.*s/%.*s",
5147dff02feSmrg		    name_len, res_name,
5157dff02feSmrg		    class_len, res_class);
516a850946eSmrg	    putchar ('\n');
517a850946eSmrg	}
518a850946eSmrg    }
5197dff02feSmrg
5207dff02feSmrgdone:
5217dff02feSmrg    if (client_machine)
5227dff02feSmrg	free(client_machine);
5237dff02feSmrg    if (command)
5247dff02feSmrg	free(command);
5257dff02feSmrg    if (cs->verbose) {
5267dff02feSmrg	if (name)
5277dff02feSmrg	    free(name);
5287dff02feSmrg	if (icon_name)
5297dff02feSmrg	    free(icon_name);
5307dff02feSmrg	if (wm_class)
5317dff02feSmrg	    free(wm_class);
5327dff02feSmrg    }
5337dff02feSmrg    free(cs);
534a850946eSmrg}
535a850946eSmrg
536a850946eSmrgstatic void
5377dff02feSmrgprint_client_properties(xcb_connection_t *dpy, xcb_window_t w, int verbose, int maxcmdlen)
538a850946eSmrg{
5397dff02feSmrg    client_state *cs = malloc(sizeof(*cs));
5407dff02feSmrg    if (!cs)
5417dff02feSmrg	return; /* TODO: print OOM message */
5427dff02feSmrg
5437dff02feSmrg    cs->c = dpy;
5447dff02feSmrg    cs->w = w;
5457dff02feSmrg    cs->verbose = verbose;
5467dff02feSmrg    cs->maxcmdlen = maxcmdlen;
5477dff02feSmrg
5487dff02feSmrg    /*
5497dff02feSmrg     * get the WM_CLIENT_MACHINE and WM_COMMAND list of strings
5507dff02feSmrg     */
5517dff02feSmrg    cs->client_machine = xcb_get_property(dpy, 0, w,
5528e46b049Smrg			    XCB_ATOM_WM_CLIENT_MACHINE, XCB_GET_PROPERTY_TYPE_ANY,
5537dff02feSmrg			    0, 1000000L);
5547dff02feSmrg    cs->command = xcb_get_property(dpy, 0, w,
5558e46b049Smrg			    XCB_ATOM_WM_COMMAND, XCB_GET_PROPERTY_TYPE_ANY,
5567dff02feSmrg			    0, 1000000L);
5577dff02feSmrg
5587dff02feSmrg    if (verbose) {
5597dff02feSmrg	cs->name = xcb_get_property(dpy, 0, w,
5608e46b049Smrg			    XCB_ATOM_WM_NAME, XCB_GET_PROPERTY_TYPE_ANY,
5617dff02feSmrg			    0, 1000000L);
5627dff02feSmrg	cs->icon_name = xcb_get_property(dpy, 0, w,
5638e46b049Smrg			    XCB_ATOM_WM_ICON_NAME, XCB_GET_PROPERTY_TYPE_ANY,
5647dff02feSmrg			    0, 1000000L);
5657dff02feSmrg	cs->wm_class = xcb_get_property(dpy, 0, w,
5668e46b049Smrg			    XCB_ATOM_WM_CLASS, XCB_ATOM_STRING,
5677dff02feSmrg			    0, 1000000L);
5687dff02feSmrg    }
5697dff02feSmrg
5707dff02feSmrg    enqueue(show_client_properties, cs);
5717dff02feSmrg}
5727dff02feSmrg
5737dff02feSmrgstatic void
5749511053fSmrgprint_text_field(xcb_connection_t *dpy, const char *s, xcb_get_property_reply_t *tp)
5757dff02feSmrg{
5767dff02feSmrg    if (tp->type == XCB_NONE || tp->format == 0) {  /* Or XCB_ATOM_NONE after libxcb 1.5 */
577a850946eSmrg	printf ("''");
578a850946eSmrg	return;
579a850946eSmrg    }
580a850946eSmrg
581a850946eSmrg    if (s) printf ("%s", s);
5828e46b049Smrg    if (tp->type == XCB_ATOM_STRING && tp->format == 8) {
5837dff02feSmrg	printf ("%.*s", (int)tp->value_len, (char *)xcb_get_property_value(tp));
584a850946eSmrg    } else {
5857dff02feSmrg	unknown (dpy, tp->type, tp->format);
586a850946eSmrg    }
587a850946eSmrg    if (s) putchar ('\n');
588a850946eSmrg}
589a850946eSmrg
590a850946eSmrg/* returns the number of characters printed */
591a850946eSmrgstatic int
592a850946eSmrgprint_quoted_word(char *s,
593a850946eSmrg		  int maxlen)		/* max number of chars we can print */
594a850946eSmrg{
595a850946eSmrg    register char *cp;
596a850946eSmrg    Bool need_quote = False, in_quote = False;
597a850946eSmrg    char quote_char = '\'', other_quote = '"';
598a850946eSmrg    int charsprinted = 0;
599a850946eSmrg
600a850946eSmrg    /*
601a850946eSmrg     * walk down seeing whether or not we need to quote
602a850946eSmrg     */
603a850946eSmrg    for (cp = s; *cp; cp++) {
604a850946eSmrg
605a850946eSmrg	if (! ((isascii(*cp) && isalnum(*cp)) ||
606a850946eSmrg	       (*cp == '-' || *cp == '_' || *cp == '.' || *cp == '+' ||
607a850946eSmrg		*cp == '/' || *cp == '=' || *cp == ':' || *cp == ','))) {
608a850946eSmrg	    need_quote = True;
609a850946eSmrg	    break;
610a850946eSmrg	}
611a850946eSmrg    }
612a850946eSmrg
613a850946eSmrg    /*
614a850946eSmrg     * write out the string: if we hit a quote, then close any previous quote,
615a850946eSmrg     * emit the other quote, swap quotes and continue on.
616a850946eSmrg     */
617a850946eSmrg    in_quote = need_quote;
618a850946eSmrg    if (need_quote) {
619a850946eSmrg	putchar (quote_char);
620a850946eSmrg	charsprinted++; maxlen--;
621a850946eSmrg    }
622a850946eSmrg    for (cp = s; *cp && maxlen>0; cp++) {
623a850946eSmrg	if (*cp == quote_char) {
624a850946eSmrg	    if (in_quote) {
625a850946eSmrg		putchar (quote_char);
626a850946eSmrg		charsprinted++; maxlen--;
627a850946eSmrg	    }
628a850946eSmrg	    putchar (other_quote);
629a850946eSmrg	    charsprinted++; maxlen--;
630a850946eSmrg	    {
631a850946eSmrg		char tmp = other_quote;
632a850946eSmrg		other_quote = quote_char; quote_char = tmp;
633a850946eSmrg	    }
634a850946eSmrg	    in_quote = True;
635a850946eSmrg	}
636a850946eSmrg	putchar (*cp);
637a850946eSmrg	charsprinted++; maxlen--;
638a850946eSmrg    }
639a850946eSmrg    /* close the quote if we opened one and if we printed the whole string */
640a850946eSmrg    if (in_quote && maxlen>0) {
641a850946eSmrg	putchar (quote_char);
642a850946eSmrg	charsprinted++; maxlen--;
643a850946eSmrg    }
644a850946eSmrg
645a850946eSmrg    return charsprinted;
646a850946eSmrg}
647a850946eSmrg
648a850946eSmrgstatic void
6497dff02feSmrgunknown(xcb_connection_t *dpy, xcb_atom_t actual_type, int actual_format)
650a850946eSmrg{
651a850946eSmrg    printf ("<unknown type ");
6527dff02feSmrg    if (actual_type == XCB_NONE)
6537dff02feSmrg	printf ("None");
6547dff02feSmrg    else {
6557dff02feSmrg	/* This should happen so rarely as to make no odds. Eat a round-trip: */
6567dff02feSmrg	xcb_get_atom_name_reply_t *atom =
6577dff02feSmrg	    xcb_get_atom_name_reply(dpy,
6587dff02feSmrg		xcb_get_atom_name(dpy, actual_type), NULL);
6597dff02feSmrg	if (atom) {
6607dff02feSmrg	    printf("%.*s", xcb_get_atom_name_name_length(atom),
6617dff02feSmrg			  xcb_get_atom_name_name(atom));
6627dff02feSmrg	    free(atom);
6637dff02feSmrg	} else
6647dff02feSmrg	    fputs (Nil, stdout);
665a850946eSmrg    }
6667dff02feSmrg    printf (" (%" PRIu32 ") or format %d>", actual_type, actual_format);
667a850946eSmrg}
668