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