Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2 Copyright 1989, 1998  The Open Group
      3 Copyright 2009  Open Text Corporation
      4 
      5 Permission to use, copy, modify, distribute, and sell this software and its
      6 documentation for any purpose is hereby granted without fee, provided that
      7 the above copyright notice appear in all copies and that both that
      8 copyright notice and this permission notice appear in supporting
      9 documentation.
     10 
     11 The above copyright notice and this permission notice shall be included in
     12 all copies or substantial portions of the Software.
     13 
     14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     20 
     21 Except as contained in this notice, the name of The Open Group shall not be
     22 used in advertising or otherwise to promote the sale, use or other dealings
     23 in this Software without prior written authorization from The Open Group.
     24  * *
     25  * Author:  Jim Fulton, MIT X Consortium
     26  * Author:  Peter Harris, Open Text Corporation
     27  */
     28 
     29 #ifdef HAVE_CONFIG_H
     30 #include "config.h"
     31 #endif
     32 
     33 #include <stdio.h>
     34 #include <stdlib.h>
     35 #include <string.h>
     36 #include <ctype.h>
     37 #include <inttypes.h>
     38 #include <xcb/xcb.h>
     39 #include <xcb/xproto.h>
     40 #ifndef HAVE_STRNLEN
     41 #include "strnlen.h"
     42 #endif
     43 
     44 #ifndef PRIx32
     45 #define PRIx32 "x"
     46 #endif
     47 #ifndef PRIu32
     48 #define PRIu32 "u"
     49 #endif
     50 
     51 #ifndef __has_attribute
     52 # define __has_attribute(x) 0  /* Compatibility with older compilers. */
     53 #endif
     54 
     55 static char *ProgramName;
     56 
     57 static xcb_atom_t WM_STATE;
     58 
     59 static void lookat (xcb_connection_t *dpy, xcb_window_t root, int verbose, int maxcmdlen);
     60 static void print_client_properties (xcb_connection_t *dpy, xcb_window_t w,
     61 				     int verbose, int maxcmdlen );
     62 static void print_text_field (xcb_connection_t *dpy, const char *s, xcb_get_property_reply_t *tp );
     63 static int print_quoted_word (char *s, int maxlen);
     64 static void unknown (xcb_connection_t *dpy, xcb_atom_t actual_type, int actual_format );
     65 
     66 /* For convenience: */
     67 typedef int Bool;
     68 #define False (0)
     69 #define True (!False)
     70 
     71 static void
     72 #if __has_attribute(__cold__)
     73 __attribute__((__cold__))
     74 #endif
     75 #if __has_attribute(noreturn)
     76 __attribute__((noreturn))
     77 #endif
     78 usage(const char *errmsg)
     79 {
     80     if (errmsg != NULL)
     81 	fprintf (stderr, "%s: %s\n\n", ProgramName, errmsg);
     82 
     83     fprintf (stderr,
     84 	     "usage:  %s  [-display dpy] [-m len] [-[a][l]] [-version]\n",
     85 	     ProgramName);
     86     exit (1);
     87 }
     88 
     89 typedef void (*queue_func)(void *closure);
     90 typedef struct queue_blob {
     91     queue_func func;
     92     void *closure;
     93     struct queue_blob *next;
     94 } queue_blob;
     95 
     96 static queue_blob *head = NULL;
     97 static queue_blob **tail = &head;
     98 
     99 static void enqueue(queue_func func, void *closure)
    100 {
    101     queue_blob *blob = malloc(sizeof(*blob));
    102     if (!blob)
    103 	return; /* TODO: print OOM error */
    104 
    105     blob->func = func;
    106     blob->closure = closure;
    107     blob->next = NULL;
    108     *tail = blob;
    109     tail = &blob->next;
    110 }
    111 
    112 static void run_queue(void)
    113 {
    114     while (head) {
    115 	queue_blob *blob = head;
    116 	blob->func(blob->closure);
    117 	head = blob->next;
    118 	free(blob);
    119     }
    120     tail = &head;
    121 }
    122 
    123 typedef struct {
    124     xcb_connection_t *c;
    125     xcb_intern_atom_cookie_t cookie;
    126     xcb_atom_t *atom;
    127 } atom_state;
    128 
    129 static void atom_done(void *closure)
    130 {
    131     xcb_intern_atom_reply_t *reply;
    132     atom_state *as = closure;
    133 
    134     reply = xcb_intern_atom_reply(as->c, as->cookie, NULL);
    135     if (!reply)
    136 	goto done; /* TODO: print Error message */
    137 
    138     *(as->atom) = reply->atom;
    139     free(reply);
    140 
    141 done:
    142     free(as);
    143 }
    144 
    145 static void init_atoms(xcb_connection_t *c)
    146 {
    147     atom_state *as;
    148 
    149     as = malloc(sizeof(*as));
    150     as->c = c;
    151     as->atom = &WM_STATE;
    152     as->cookie = xcb_intern_atom(c, 0, strlen("WM_STATE"), "WM_STATE");
    153     enqueue(atom_done, as);
    154 }
    155 
    156 int
    157 main(int argc, char *argv[])
    158 {
    159     int i;
    160     char *displayname = NULL;
    161     Bool all_screens = False;
    162     Bool verbose = False;
    163     xcb_connection_t *dpy;
    164     const xcb_setup_t *setup;
    165     int screen_number = 0;
    166     int maxcmdlen = 10000;
    167 
    168     ProgramName = argv[0];
    169 
    170     for (i = 1; i < argc; i++) {
    171 	char *arg = argv[i];
    172 
    173 	if (arg[0] == '-') {
    174 	    char *cp;
    175 
    176 	    switch (arg[1]) {
    177 	      case 'd':			/* -display dpyname */
    178 		if (++i >= argc) usage ("-display requires an argument");
    179 		displayname = argv[i];
    180 		continue;
    181 	      case 'm':			/* -max maxcmdlen */
    182 		if (++i >= argc) usage ("-max requires an argument");
    183 		maxcmdlen = atoi (argv[i]);
    184 		continue;
    185 	      case 'v':			/* -version */
    186 		printf("%s\n", PACKAGE_STRING);
    187 		exit(0);
    188 	    }
    189 
    190 	    for (cp = &arg[1]; *cp; cp++) {
    191 		switch (*cp) {
    192 		  case 'a':		/* -all */
    193 		    all_screens = True;
    194 		    continue;
    195 		  case 'l':		/* -long */
    196 		    verbose = True;
    197 		    continue;
    198 		  default:
    199 		    fprintf (stderr, "%s: unrecognized argument -%s\n\n",
    200 			     ProgramName, cp);
    201 		    usage (NULL);
    202 		}
    203 	    }
    204 	} else {
    205 	    fprintf (stderr, "%s: unrecognized argument %s\n\n",
    206 		     ProgramName, arg);
    207 	    usage (NULL);
    208 	}
    209     }
    210 
    211     dpy = xcb_connect(displayname, &screen_number);
    212     if (xcb_connection_has_error(dpy)) {
    213 	const char *name = displayname;
    214 	if (!name)
    215 	    name = getenv("DISPLAY");
    216 	if (!name)
    217 	    name = "";
    218 	fprintf (stderr, "%s:  unable to open display \"%s\"\r\n",
    219 		 ProgramName, name);
    220 	exit (1);
    221     }
    222 
    223     init_atoms(dpy);
    224 
    225     setup = xcb_get_setup(dpy);
    226     if (all_screens) {
    227 	xcb_screen_iterator_t screen;
    228 
    229 	screen = xcb_setup_roots_iterator(setup);
    230 	do {
    231 	    lookat(dpy, screen.data->root, verbose, maxcmdlen);
    232 	    xcb_screen_next(&screen);
    233 	} while (screen.rem);
    234     } else {
    235 	xcb_screen_iterator_t screen;
    236 
    237 	screen = xcb_setup_roots_iterator(setup);
    238 	for (i = 0; i < screen_number; i++)
    239 	    xcb_screen_next(&screen);
    240 
    241 	lookat (dpy, screen.data->root, verbose, maxcmdlen);
    242     }
    243 
    244     run_queue();
    245 
    246     xcb_disconnect(dpy);
    247     exit (0);
    248 }
    249 
    250 typedef struct {
    251     xcb_connection_t *c;
    252     xcb_get_property_cookie_t *prop_cookie;
    253     xcb_query_tree_cookie_t *tree_cookie;
    254     xcb_window_t *win;
    255     xcb_window_t orig_win;
    256     unsigned int list_length;
    257     int verbose;
    258     int maxcmdlen;
    259 } child_wm_state;
    260 
    261 static void child_info(void *closure)
    262 {
    263     child_wm_state *cs = closure;
    264     xcb_window_t orig = cs->orig_win;
    265     xcb_connection_t *c = cs->c;
    266     int verbose = cs->verbose;
    267     int maxcmdlen = cs->maxcmdlen;
    268     unsigned int i, j;
    269 
    270     unsigned int child_count, num_rep;
    271     xcb_query_tree_reply_t **qt_reply;
    272 
    273     for (i = 0; i < cs->list_length; i++) {
    274 	xcb_get_property_reply_t *gp_reply;
    275 	gp_reply = xcb_get_property_reply(c, cs->prop_cookie[i], NULL);
    276 	if (gp_reply) {
    277 	    if (gp_reply->type) {
    278 		/* Show information for this window */
    279 		print_client_properties(c, cs->win[i], cs->verbose, cs->maxcmdlen);
    280 
    281 		free(gp_reply);
    282 
    283 		/* drain stale replies */
    284 		for (j = i+1; j < cs->list_length; j++) {
    285 		    gp_reply = xcb_get_property_reply(c, cs->prop_cookie[j], NULL);
    286 		    if (gp_reply)
    287 			free(gp_reply);
    288 		}
    289 		for (j = 0; j < cs->list_length; j++) {
    290 		    xcb_query_tree_reply_t *rep;
    291 		    rep = xcb_query_tree_reply(c, cs->tree_cookie[j], NULL);
    292 		    if (rep)
    293 			free(rep);
    294 		}
    295 		goto done;
    296 	    }
    297 	    free(gp_reply);
    298 	}
    299     }
    300 
    301     /* WM_STATE not found. Recurse into children: */
    302     num_rep = 0;
    303     qt_reply = malloc(sizeof(*qt_reply) * cs->list_length);
    304     if (!qt_reply)
    305 	goto done; /* TODO: print OOM message, drain reply queue */
    306 
    307     for (i = 0; i < cs->list_length; i++) {
    308 	qt_reply[num_rep] = xcb_query_tree_reply(c, cs->tree_cookie[i], NULL);
    309 	if (qt_reply[num_rep])
    310 	    num_rep++;
    311     }
    312 
    313     child_count = 0;
    314     for (i = 0; i < num_rep; i++)
    315 	child_count += qt_reply[i]->children_len;
    316 
    317     if (!child_count) {
    318 	/* No children have CS_STATE; try the parent window */
    319 	print_client_properties(c, cs->orig_win, cs->verbose, cs->maxcmdlen);
    320 	goto reply_done;
    321     }
    322 
    323     cs = malloc(sizeof(*cs) + child_count * (sizeof(*cs->prop_cookie) + sizeof(*cs->tree_cookie) + sizeof(*cs->win)));
    324     if (!cs)
    325 	goto reply_done; /* TODO: print OOM message */
    326 
    327     cs->c = c;
    328     cs->verbose = verbose;
    329     cs->maxcmdlen = maxcmdlen;
    330     cs->orig_win = orig;
    331     cs->prop_cookie = (void *)&cs[1];
    332     cs->tree_cookie = (void *)&cs->prop_cookie[child_count];
    333     cs->win = (void *)&cs->tree_cookie[child_count];
    334     cs->list_length = child_count;
    335 
    336     child_count = 0;
    337     for (i = 0; i < num_rep; i++) {
    338 	xcb_window_t *child = xcb_query_tree_children(qt_reply[i]);
    339 	for (j = 0; j < qt_reply[i]->children_len; j++) {
    340 	    cs->win[child_count] = child[j];
    341 	    cs->prop_cookie[child_count] = xcb_get_property(c, 0, child[j],
    342 			    WM_STATE, XCB_GET_PROPERTY_TYPE_ANY,
    343 			    0, 0);
    344 	    /* Just in case the property isn't there, get the tree too */
    345 	    cs->tree_cookie[child_count++] = xcb_query_tree(c, child[j]);
    346 	}
    347     }
    348 
    349     enqueue(child_info, cs);
    350 
    351 reply_done:
    352     for (i = 0; i < num_rep; i++)
    353 	free(qt_reply[i]);
    354     free(qt_reply);
    355 
    356 done:
    357     free(closure);
    358 }
    359 
    360 typedef struct {
    361     xcb_connection_t *c;
    362     xcb_query_tree_cookie_t cookie;
    363     int verbose;
    364     int maxcmdlen;
    365 } root_list_state;
    366 
    367 static void root_list(void *closure)
    368 {
    369     int i;
    370     xcb_window_t *child;
    371     xcb_query_tree_reply_t *reply;
    372     root_list_state *rl = closure;
    373 
    374     reply = xcb_query_tree_reply(rl->c, rl->cookie, NULL);
    375     if (!reply)
    376 	goto done;
    377 
    378     child = xcb_query_tree_children(reply);
    379     for (i = 0; i < reply->children_len; i++) {
    380 	/* Get information about each child */
    381 	child_wm_state *cs = malloc(sizeof(*cs) + sizeof(*cs->prop_cookie) + sizeof(*cs->tree_cookie) + sizeof(*cs->win));
    382 	if (!cs)
    383 	    goto done; /* TODO: print OOM message */
    384 	cs->c = rl->c;
    385 	cs->verbose = rl->verbose;
    386 	cs->maxcmdlen = rl->maxcmdlen;
    387 	cs->prop_cookie = (void *)&cs[1];
    388 	cs->tree_cookie = (void *)&cs->prop_cookie[1];
    389 	cs->win = (void *)&cs->tree_cookie[1];
    390 
    391 	cs->orig_win = child[i];
    392 	cs->win[0] = child[i];
    393 
    394 	cs->prop_cookie[0] = xcb_get_property(rl->c, 0, child[i],
    395 			WM_STATE, XCB_GET_PROPERTY_TYPE_ANY,
    396 			0, 0);
    397 	/* Just in case the property isn't there, get the tree too */
    398 	cs->tree_cookie[0] = xcb_query_tree(rl->c, child[i]);
    399 
    400 	cs->list_length = 1;
    401 	enqueue(child_info, cs);
    402     }
    403     free(reply);
    404 
    405 done:
    406     free(rl);
    407 }
    408 
    409 static void
    410 lookat(xcb_connection_t *dpy, xcb_window_t root, int verbose, int maxcmdlen)
    411 {
    412     root_list_state *rl = malloc(sizeof(*rl));
    413 
    414     if (!rl)
    415 	return; /* TODO: OOM message */
    416 
    417     /*
    418      * get the list of windows
    419      */
    420 
    421     rl->c = dpy;
    422     rl->cookie = xcb_query_tree(dpy, root);
    423     rl->verbose = verbose;
    424     rl->maxcmdlen = maxcmdlen;
    425     enqueue(root_list, rl);
    426 }
    427 
    428 static const char *Nil = "(nil)";
    429 
    430 typedef struct {
    431     xcb_connection_t *c;
    432     xcb_get_property_cookie_t client_machine;
    433     xcb_get_property_cookie_t command;
    434     xcb_get_property_cookie_t name;
    435     xcb_get_property_cookie_t icon_name;
    436     xcb_get_property_cookie_t wm_class;
    437     xcb_window_t w;
    438     int verbose;
    439     int maxcmdlen;
    440 } client_state;
    441 
    442 static void
    443 show_client_properties(void *closure)
    444 {
    445     client_state *cs = closure;
    446     xcb_get_property_reply_t *client_machine;
    447     xcb_get_property_reply_t *command;
    448     xcb_get_property_reply_t *name = NULL;
    449     xcb_get_property_reply_t *icon_name = NULL;
    450     xcb_get_property_reply_t *wm_class = NULL;
    451     char *argv;
    452     int charsleft = cs->maxcmdlen;
    453     unsigned int i;
    454 
    455     /*
    456      * get the WM_MACHINE and WM_COMMAND list of strings
    457      */
    458     client_machine = xcb_get_property_reply(cs->c, cs->client_machine, NULL);
    459     command = xcb_get_property_reply(cs->c, cs->command, NULL);
    460     if (cs->verbose) {
    461 	name = xcb_get_property_reply(cs->c, cs->name, NULL);
    462 	icon_name = xcb_get_property_reply(cs->c, cs->icon_name, NULL);
    463 	wm_class = xcb_get_property_reply(cs->c, cs->wm_class, NULL);
    464     }
    465 
    466     if (!command || !command->type)
    467 	goto done;
    468 
    469     /*
    470      * do header information
    471      */
    472     if (cs->verbose) {
    473 	printf ("Window 0x%" PRIx32 ":\n", cs->w);
    474 	print_text_field (cs->c, "  Machine:  ", client_machine);
    475 	if (name && name->type)
    476 	    print_text_field (cs->c, "  Name:  ", name);
    477     } else {
    478 	print_text_field (cs->c, NULL, client_machine);
    479 	putchar (' ');
    480 	putchar (' ');
    481     }
    482 
    483 
    484     if (cs->verbose)
    485 	if (icon_name && icon_name->type)
    486 	    print_text_field (cs->c, "  Icon Name:  ", icon_name);
    487 
    488 
    489     /*
    490      * do the command
    491      */
    492     if (cs->verbose)
    493 	printf ("  Command:  ");
    494     argv = xcb_get_property_value(command);
    495     for (i = 0; i < command->value_len && charsleft > 0; ) {
    496 	charsleft -= print_quoted_word (argv + i, charsleft);
    497 	i += strnlen(argv + i, command->value_len - i) + 1;
    498 	if (i < command->value_len && charsleft > 0) {
    499 	    putchar (' ');
    500 	    charsleft--;
    501 	}
    502     }
    503     putchar ('\n');
    504 
    505 
    506     /*
    507      * do trailer information
    508      */
    509     if (cs->verbose) {
    510 	if (wm_class && wm_class->type) {
    511 	    const char *res_name, *res_class;
    512 	    int name_len, class_len; /* Must be int for use with %.*s */
    513 	    res_name = xcb_get_property_value(wm_class);
    514 	    name_len = (int) strnlen(res_name, wm_class->value_len) + 1;
    515 	    class_len = (int) wm_class->value_len - name_len;
    516 	    if (class_len > 0) {
    517 		res_class = res_name + name_len;
    518 	    } else {
    519 		res_class = Nil;
    520 		class_len = (int) strlen(res_class);
    521 	    }
    522 
    523 	    printf ("  Instance/Class:  %.*s/%.*s",
    524 		    name_len, res_name,
    525 		    class_len, res_class);
    526 	    putchar ('\n');
    527 	}
    528     }
    529 
    530 done:
    531     if (client_machine)
    532 	free(client_machine);
    533     if (command)
    534 	free(command);
    535     if (cs->verbose) {
    536 	if (name)
    537 	    free(name);
    538 	if (icon_name)
    539 	    free(icon_name);
    540 	if (wm_class)
    541 	    free(wm_class);
    542     }
    543     free(cs);
    544 }
    545 
    546 static void
    547 print_client_properties(xcb_connection_t *dpy, xcb_window_t w, int verbose, int maxcmdlen)
    548 {
    549     client_state *cs = malloc(sizeof(*cs));
    550     if (!cs)
    551 	return; /* TODO: print OOM message */
    552 
    553     cs->c = dpy;
    554     cs->w = w;
    555     cs->verbose = verbose;
    556     cs->maxcmdlen = maxcmdlen;
    557 
    558     /*
    559      * get the WM_CLIENT_MACHINE and WM_COMMAND list of strings
    560      */
    561     cs->client_machine = xcb_get_property(dpy, 0, w,
    562 			    XCB_ATOM_WM_CLIENT_MACHINE, XCB_GET_PROPERTY_TYPE_ANY,
    563 			    0, 1000000L);
    564     cs->command = xcb_get_property(dpy, 0, w,
    565 			    XCB_ATOM_WM_COMMAND, XCB_GET_PROPERTY_TYPE_ANY,
    566 			    0, 1000000L);
    567 
    568     if (verbose) {
    569 	cs->name = xcb_get_property(dpy, 0, w,
    570 			    XCB_ATOM_WM_NAME, XCB_GET_PROPERTY_TYPE_ANY,
    571 			    0, 1000000L);
    572 	cs->icon_name = xcb_get_property(dpy, 0, w,
    573 			    XCB_ATOM_WM_ICON_NAME, XCB_GET_PROPERTY_TYPE_ANY,
    574 			    0, 1000000L);
    575 	cs->wm_class = xcb_get_property(dpy, 0, w,
    576 			    XCB_ATOM_WM_CLASS, XCB_ATOM_STRING,
    577 			    0, 1000000L);
    578     }
    579 
    580     enqueue(show_client_properties, cs);
    581 }
    582 
    583 static void
    584 print_text_field(xcb_connection_t *dpy, const char *s, xcb_get_property_reply_t *tp)
    585 {
    586     if (tp->type == XCB_NONE || tp->format == 0) {  /* Or XCB_ATOM_NONE after libxcb 1.5 */
    587 	printf ("''");
    588 	return;
    589     }
    590 
    591     if (s) printf ("%s", s);
    592     if (tp->type == XCB_ATOM_STRING && tp->format == 8) {
    593 	printf ("%.*s", (int)tp->value_len, (char *)xcb_get_property_value(tp));
    594     } else {
    595 	unknown (dpy, tp->type, tp->format);
    596     }
    597     if (s) putchar ('\n');
    598 }
    599 
    600 /* returns the number of characters printed */
    601 static int
    602 print_quoted_word(char *s,
    603 		  int maxlen)		/* max number of chars we can print */
    604 {
    605     register char *cp;
    606     Bool need_quote = False, in_quote = False;
    607     char quote_char = '\'', other_quote = '"';
    608     int charsprinted = 0;
    609 
    610     /*
    611      * walk down seeing whether or not we need to quote
    612      */
    613     for (cp = s; *cp; cp++) {
    614 
    615 	if (! ((isascii(*cp) && isalnum(*cp)) ||
    616 	       (*cp == '-' || *cp == '_' || *cp == '.' || *cp == '+' ||
    617 		*cp == '/' || *cp == '=' || *cp == ':' || *cp == ','))) {
    618 	    need_quote = True;
    619 	    break;
    620 	}
    621     }
    622 
    623     /*
    624      * write out the string: if we hit a quote, then close any previous quote,
    625      * emit the other quote, swap quotes and continue on.
    626      */
    627     in_quote = need_quote;
    628     if (need_quote) {
    629 	putchar (quote_char);
    630 	charsprinted++; maxlen--;
    631     }
    632     for (cp = s; *cp && maxlen>0; cp++) {
    633 	if (*cp == quote_char) {
    634 	    if (in_quote) {
    635 		putchar (quote_char);
    636 		charsprinted++; maxlen--;
    637 	    }
    638 	    putchar (other_quote);
    639 	    charsprinted++; maxlen--;
    640 	    {
    641 		char tmp = other_quote;
    642 		other_quote = quote_char; quote_char = tmp;
    643 	    }
    644 	    in_quote = True;
    645 	}
    646 	putchar (*cp);
    647 	charsprinted++; maxlen--;
    648     }
    649     /* close the quote if we opened one and if we printed the whole string */
    650     if (in_quote && maxlen>0) {
    651 	putchar (quote_char);
    652 	charsprinted++; maxlen--;
    653     }
    654 
    655     return charsprinted;
    656 }
    657 
    658 static void
    659 unknown(xcb_connection_t *dpy, xcb_atom_t actual_type, int actual_format)
    660 {
    661     printf ("<unknown type ");
    662     if (actual_type == XCB_NONE)
    663 	printf ("None");
    664     else {
    665 	/* This should happen so rarely as to make no odds. Eat a round-trip: */
    666 	xcb_get_atom_name_reply_t *atom =
    667 	    xcb_get_atom_name_reply(dpy,
    668 		xcb_get_atom_name(dpy, actual_type), NULL);
    669 	if (atom) {
    670 	    printf("%.*s", xcb_get_atom_name_name_length(atom),
    671 			  xcb_get_atom_name_name(atom));
    672 	    free(atom);
    673 	} else
    674 	    fputs (Nil, stdout);
    675     }
    676     printf (" (%" PRIu32 ") or format %d>", actual_type, actual_format);
    677 }
    678