xlsclients.c revision 7dff02fe
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#include <xcb/xcb_atom.h> 41#ifndef HAVE_STRNLEN 42#include "strnlen.h" 43#endif 44 45#ifndef PRIx32 46#define PRIx32 "x" 47#endif 48#ifndef PRIu32 49#define PRIu32 "u" 50#endif 51 52static char *ProgramName; 53 54static xcb_atom_t WM_STATE; 55 56static void lookat (xcb_connection_t *dpy, xcb_window_t root, int verbose, int maxcmdlen); 57static void print_client_properties (xcb_connection_t *dpy, xcb_window_t w, 58 int verbose, int maxcmdlen ); 59static void print_text_field (xcb_connection_t *dpy, char *s, xcb_get_property_reply_t *tp ); 60static int print_quoted_word (char *s, int maxlen); 61static void unknown (xcb_connection_t *dpy, xcb_atom_t actual_type, int actual_format ); 62 63/* For convenience: */ 64typedef int Bool; 65#define False (0) 66#define True (!False) 67 68static void 69usage(void) 70{ 71 fprintf (stderr, 72 "usage: %s [-display dpy] [-m len] [-[a][l]]\n", 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 } 173 174 for (cp = &arg[1]; *cp; cp++) { 175 switch (*cp) { 176 case 'a': /* -all */ 177 all_screens = True; 178 continue; 179 case 'l': /* -long */ 180 verbose = True; 181 continue; 182 default: 183 usage (); 184 } 185 } 186 } else { 187 usage (); 188 } 189 } 190 191 dpy = xcb_connect(displayname, &screen_number); 192 if (xcb_connection_has_error(dpy)) { 193 char *name = displayname; 194 if (!name) 195 name = getenv("DISPLAY"); 196 if (!name) 197 name = ""; 198 fprintf (stderr, "%s: unable to open display \"%s\"\r\n", 199 ProgramName, name); 200 exit (1); 201 } 202 203 init_atoms(dpy); 204 205 setup = xcb_get_setup(dpy); 206 if (all_screens) { 207 xcb_screen_iterator_t screen; 208 209 screen = xcb_setup_roots_iterator(setup); 210 do { 211 lookat(dpy, screen.data->root, verbose, maxcmdlen); 212 xcb_screen_next(&screen); 213 } while (screen.rem); 214 } else { 215 xcb_screen_iterator_t screen; 216 217 screen = xcb_setup_roots_iterator(setup); 218 for (i = 0; i < screen_number; i++) 219 xcb_screen_next(&screen); 220 221 lookat (dpy, screen.data->root, verbose, maxcmdlen); 222 } 223 224 run_queue(); 225 226 xcb_disconnect(dpy); 227 exit (0); 228} 229 230typedef struct { 231 xcb_connection_t *c; 232 xcb_get_property_cookie_t *prop_cookie; 233 xcb_query_tree_cookie_t *tree_cookie; 234 xcb_window_t *win; 235 xcb_window_t orig_win; 236 int list_length; 237 int verbose; 238 int maxcmdlen; 239} child_wm_state; 240 241static void child_info(void *closure) 242{ 243 child_wm_state *cs = closure; 244 xcb_window_t orig = cs->orig_win; 245 xcb_connection_t *c = cs->c; 246 int verbose = cs->verbose; 247 int maxcmdlen = cs->maxcmdlen; 248 int i, j; 249 250 int child_count, num_rep; 251 xcb_query_tree_reply_t **reply; 252 253 for (i = 0; i < cs->list_length; i++) { 254 xcb_get_property_reply_t *reply; 255 reply = xcb_get_property_reply(c, cs->prop_cookie[i], NULL); 256 if (reply) { 257 if (reply->type) { 258 /* Show information for this window */ 259 print_client_properties(c, cs->win[i], cs->verbose, cs->maxcmdlen); 260 261 free(reply); 262 263 /* drain stale replies */ 264 for (j = i+1; j < cs->list_length; j++) { 265 reply = xcb_get_property_reply(c, cs->prop_cookie[j], NULL); 266 if (reply) 267 free(reply); 268 } 269 for (j = 0; j < cs->list_length; j++) { 270 xcb_query_tree_reply_t *rep; 271 rep = xcb_query_tree_reply(c, cs->tree_cookie[j], NULL); 272 if (rep) 273 free(rep); 274 } 275 goto done; 276 } 277 free(reply); 278 } 279 } 280 281 /* WM_STATE not found. Recurse into children: */ 282 num_rep = 0; 283 reply = malloc(sizeof(*reply) * cs->list_length); 284 if (!reply) 285 goto done; /* TODO: print OOM message, drain reply queue */ 286 287 for (i = 0; i < cs->list_length; i++) { 288 reply[num_rep] = xcb_query_tree_reply(c, cs->tree_cookie[i], NULL); 289 if (reply[num_rep]) 290 num_rep++; 291 } 292 293 child_count = 0; 294 for (i = 0; i < num_rep; i++) 295 child_count += reply[i]->children_len; 296 297 if (!child_count) { 298 /* No children have CS_STATE; try the parent window */ 299 print_client_properties(c, cs->orig_win, cs->verbose, cs->maxcmdlen); 300 goto reply_done; 301 } 302 303 cs = malloc(sizeof(*cs) + child_count * (sizeof(*cs->prop_cookie) + sizeof(*cs->tree_cookie) + sizeof(*cs->win))); 304 if (!cs) 305 goto reply_done; /* TODO: print OOM message */ 306 307 cs->c = c; 308 cs->verbose = verbose; 309 cs->maxcmdlen = maxcmdlen; 310 cs->orig_win = orig; 311 cs->prop_cookie = (void *)&cs[1]; 312 cs->tree_cookie = (void *)&cs->prop_cookie[child_count]; 313 cs->win = (void *)&cs->tree_cookie[child_count]; 314 cs->list_length = child_count; 315 316 child_count = 0; 317 for (i = 0; i < num_rep; i++) { 318 xcb_window_t *child = xcb_query_tree_children(reply[i]); 319 for (j = 0; j < reply[i]->children_len; j++) { 320 cs->win[child_count] = child[j]; 321 cs->prop_cookie[child_count] = xcb_get_property(c, 0, child[j], 322 WM_STATE, XCB_GET_PROPERTY_TYPE_ANY, 323 0, 0); 324 /* Just in case the property isn't there, get the tree too */ 325 cs->tree_cookie[child_count++] = xcb_query_tree(c, child[j]); 326 } 327 } 328 329 enqueue(child_info, cs); 330 331reply_done: 332 for (i = 0; i < num_rep; i++) 333 free(reply[i]); 334 free(reply); 335 336done: 337 free(closure); 338} 339 340typedef struct { 341 xcb_connection_t *c; 342 xcb_query_tree_cookie_t cookie; 343 int verbose; 344 int maxcmdlen; 345} root_list_state; 346 347static void root_list(void *closure) 348{ 349 int i; 350 xcb_window_t *child; 351 xcb_query_tree_reply_t *reply; 352 root_list_state *rl = closure; 353 354 reply = xcb_query_tree_reply(rl->c, rl->cookie, NULL); 355 if (!reply) 356 goto done; 357 358 child = xcb_query_tree_children(reply); 359 for (i = 0; i < reply->children_len; i++) { 360 /* Get information about each child */ 361 child_wm_state *cs = malloc(sizeof(*cs) + sizeof(*cs->prop_cookie) + sizeof(*cs->tree_cookie) + sizeof(*cs->win)); 362 if (!cs) 363 goto done; /* TODO: print OOM message */ 364 cs->c = rl->c; 365 cs->verbose = rl->verbose; 366 cs->maxcmdlen = rl->maxcmdlen; 367 cs->prop_cookie = (void *)&cs[1]; 368 cs->tree_cookie = (void *)&cs->prop_cookie[1]; 369 cs->win = (void *)&cs->tree_cookie[1]; 370 371 cs->orig_win = child[i]; 372 cs->win[0] = child[i]; 373 374 cs->prop_cookie[0] = xcb_get_property(rl->c, 0, child[i], 375 WM_STATE, XCB_GET_PROPERTY_TYPE_ANY, 376 0, 0); 377 /* Just in case the property isn't there, get the tree too */ 378 cs->tree_cookie[0] = xcb_query_tree(rl->c, child[i]); 379 380 cs->list_length = 1; 381 enqueue(child_info, cs); 382 } 383 free(reply); 384 385done: 386 free(rl); 387} 388 389static void 390lookat(xcb_connection_t *dpy, xcb_window_t root, int verbose, int maxcmdlen) 391{ 392 root_list_state *rl = malloc(sizeof(*rl)); 393 394 if (!rl) 395 return; /* TODO: OOM message */ 396 397 /* 398 * get the list of windows 399 */ 400 401 rl->c = dpy; 402 rl->cookie = xcb_query_tree(dpy, root); 403 rl->verbose = verbose; 404 rl->maxcmdlen = maxcmdlen; 405 enqueue(root_list, rl); 406} 407 408static char *Nil = "(nil)"; 409 410typedef struct { 411 xcb_connection_t *c; 412 xcb_get_property_cookie_t client_machine; 413 xcb_get_property_cookie_t command; 414 xcb_get_property_cookie_t name; 415 xcb_get_property_cookie_t icon_name; 416 xcb_get_property_cookie_t wm_class; 417 xcb_window_t w; 418 int verbose; 419 int maxcmdlen; 420} client_state; 421 422static void 423show_client_properties(void *closure) 424{ 425 client_state *cs = closure; 426 xcb_get_property_reply_t *client_machine; 427 xcb_get_property_reply_t *command; 428 xcb_get_property_reply_t *name; 429 xcb_get_property_reply_t *icon_name; 430 xcb_get_property_reply_t *wm_class; 431 char *argv; 432 int charsleft = cs->maxcmdlen; 433 int i; 434 435 /* 436 * get the WM_MACHINE and WM_COMMAND list of strings 437 */ 438 client_machine = xcb_get_property_reply(cs->c, cs->client_machine, NULL); 439 command = xcb_get_property_reply(cs->c, cs->command, NULL); 440 if (cs->verbose) { 441 name = xcb_get_property_reply(cs->c, cs->name, NULL); 442 icon_name = xcb_get_property_reply(cs->c, cs->icon_name, NULL); 443 wm_class = xcb_get_property_reply(cs->c, cs->wm_class, NULL); 444 } 445 446 if (!command || !command->type) 447 goto done; 448 449 /* 450 * do header information 451 */ 452 if (cs->verbose) { 453 printf ("Window 0x%" PRIx32 ":\n", cs->w); 454 print_text_field (cs->c, " Machine: ", client_machine); 455 if (name && name->type) 456 print_text_field (cs->c, " Name: ", name); 457 } else { 458 print_text_field (cs->c, NULL, client_machine); 459 putchar (' '); 460 putchar (' '); 461 } 462 463 464 if (cs->verbose) 465 if (icon_name && icon_name->type) 466 print_text_field (cs->c, " Icon Name: ", icon_name); 467 468 469 /* 470 * do the command 471 */ 472 if (cs->verbose) 473 printf (" Command: "); 474 argv = xcb_get_property_value(command); 475 for (i = 0; i < command->value_len && charsleft > 0; ) { 476 charsleft -= print_quoted_word (argv + i, charsleft); 477 i += strnlen(argv + i, command->value_len - i) + 1; 478 if (i < command->value_len && charsleft > 0) { 479 putchar (' '); 480 charsleft--; 481 } 482 } 483 putchar ('\n'); 484 485 486 /* 487 * do trailer information 488 */ 489 if (cs->verbose) { 490 if (wm_class && wm_class->type) { 491 char *res_name, *res_class; 492 int name_len, class_len; 493 res_name = xcb_get_property_value(wm_class); 494 name_len = strnlen(res_name, wm_class->value_len) + 1; 495 class_len = wm_class->value_len - name_len; 496 if (class_len > 0) { 497 res_class = res_name + name_len; 498 } else { 499 res_class = Nil; 500 class_len = strlen(res_class); 501 } 502 503 printf (" Instance/Class: %.*s/%.*s", 504 name_len, res_name, 505 class_len, res_class); 506 putchar ('\n'); 507 } 508 } 509 510done: 511 if (client_machine) 512 free(client_machine); 513 if (command) 514 free(command); 515 if (cs->verbose) { 516 if (name) 517 free(name); 518 if (icon_name) 519 free(icon_name); 520 if (wm_class) 521 free(wm_class); 522 } 523 free(cs); 524} 525 526static void 527print_client_properties(xcb_connection_t *dpy, xcb_window_t w, int verbose, int maxcmdlen) 528{ 529 client_state *cs = malloc(sizeof(*cs)); 530 if (!cs) 531 return; /* TODO: print OOM message */ 532 533 cs->c = dpy; 534 cs->w = w; 535 cs->verbose = verbose; 536 cs->maxcmdlen = maxcmdlen; 537 538 /* 539 * get the WM_CLIENT_MACHINE and WM_COMMAND list of strings 540 */ 541 cs->client_machine = xcb_get_property(dpy, 0, w, 542 WM_CLIENT_MACHINE, XCB_GET_PROPERTY_TYPE_ANY, 543 0, 1000000L); 544 cs->command = xcb_get_property(dpy, 0, w, 545 WM_COMMAND, XCB_GET_PROPERTY_TYPE_ANY, 546 0, 1000000L); 547 548 if (verbose) { 549 cs->name = xcb_get_property(dpy, 0, w, 550 WM_NAME, XCB_GET_PROPERTY_TYPE_ANY, 551 0, 1000000L); 552 cs->icon_name = xcb_get_property(dpy, 0, w, 553 WM_ICON_NAME, XCB_GET_PROPERTY_TYPE_ANY, 554 0, 1000000L); 555 cs->wm_class = xcb_get_property(dpy, 0, w, 556 WM_CLASS, STRING, 557 0, 1000000L); 558 } 559 560 enqueue(show_client_properties, cs); 561} 562 563static void 564print_text_field(xcb_connection_t *dpy, char *s, xcb_get_property_reply_t *tp) 565{ 566 if (tp->type == XCB_NONE || tp->format == 0) { /* Or XCB_ATOM_NONE after libxcb 1.5 */ 567 printf ("''"); 568 return; 569 } 570 571 if (s) printf ("%s", s); 572 if (tp->type == STRING && tp->format == 8) { 573 printf ("%.*s", (int)tp->value_len, (char *)xcb_get_property_value(tp)); 574 } else { 575 unknown (dpy, tp->type, tp->format); 576 } 577 if (s) putchar ('\n'); 578} 579 580/* returns the number of characters printed */ 581static int 582print_quoted_word(char *s, 583 int maxlen) /* max number of chars we can print */ 584{ 585 register char *cp; 586 Bool need_quote = False, in_quote = False; 587 char quote_char = '\'', other_quote = '"'; 588 int charsprinted = 0; 589 590 /* 591 * walk down seeing whether or not we need to quote 592 */ 593 for (cp = s; *cp; cp++) { 594 595 if (! ((isascii(*cp) && isalnum(*cp)) || 596 (*cp == '-' || *cp == '_' || *cp == '.' || *cp == '+' || 597 *cp == '/' || *cp == '=' || *cp == ':' || *cp == ','))) { 598 need_quote = True; 599 break; 600 } 601 } 602 603 /* 604 * write out the string: if we hit a quote, then close any previous quote, 605 * emit the other quote, swap quotes and continue on. 606 */ 607 in_quote = need_quote; 608 if (need_quote) { 609 putchar (quote_char); 610 charsprinted++; maxlen--; 611 } 612 for (cp = s; *cp && maxlen>0; cp++) { 613 if (*cp == quote_char) { 614 if (in_quote) { 615 putchar (quote_char); 616 charsprinted++; maxlen--; 617 } 618 putchar (other_quote); 619 charsprinted++; maxlen--; 620 { 621 char tmp = other_quote; 622 other_quote = quote_char; quote_char = tmp; 623 } 624 in_quote = True; 625 } 626 putchar (*cp); 627 charsprinted++; maxlen--; 628 } 629 /* close the quote if we opened one and if we printed the whole string */ 630 if (in_quote && maxlen>0) { 631 putchar (quote_char); 632 charsprinted++; maxlen--; 633 } 634 635 return charsprinted; 636} 637 638static void 639unknown(xcb_connection_t *dpy, xcb_atom_t actual_type, int actual_format) 640{ 641 printf ("<unknown type "); 642 if (actual_type == XCB_NONE) 643 printf ("None"); 644 else { 645 /* This should happen so rarely as to make no odds. Eat a round-trip: */ 646 xcb_get_atom_name_reply_t *atom = 647 xcb_get_atom_name_reply(dpy, 648 xcb_get_atom_name(dpy, actual_type), NULL); 649 if (atom) { 650 printf("%.*s", xcb_get_atom_name_name_length(atom), 651 xcb_get_atom_name_name(atom)); 652 free(atom); 653 } else 654 fputs (Nil, stdout); 655 } 656 printf (" (%" PRIu32 ") or format %d>", actual_type, actual_format); 657} 658