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