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