xwininfo.c revision b419652f
1/* 2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23/* 24 25Copyright 1987, 1998 The Open Group 26 27Permission to use, copy, modify, distribute, and sell this software and its 28documentation for any purpose is hereby granted without fee, provided that 29the above copyright notice appear in all copies and that both that 30copyright notice and this permission notice appear in supporting 31documentation. 32 33The above copyright notice and this permission notice shall be included 34in all copies or substantial portions of the Software. 35 36THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 37OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 38MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 39OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 40HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL 41INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING 42FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 43NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 44WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 45 46Except as contained in this notice, the name of a copyright holder 47shall not be used in advertising or otherwise to promote the sale, use 48or other dealings in this Software without prior written authorization 49of the copyright holder. 50 51*/ 52 53 54/* 55 * xwininfo.c - MIT Project Athena, X Window system window 56 * information utility. 57 * 58 * 59 * This program will report all relevant information 60 * about a specific window. 61 * 62 * Author: Mark Lillibridge, MIT Project Athena 63 * 16-Jun-87 64 */ 65 66#include "config.h" 67 68#include <xcb/xcb.h> 69#include <xcb/xproto.h> 70#ifdef USE_XCB_ICCCM 71# include <xcb/xcb_icccm.h> 72#endif 73#include <xcb/shape.h> 74 75#include <stdio.h> 76#include <stdlib.h> 77#include <string.h> 78#include <locale.h> 79#include <langinfo.h> 80#ifdef HAVE_ICONV 81# include <iconv.h> 82#endif 83#include <ctype.h> 84#include <errno.h> 85 86#ifndef HAVE_STRNLEN 87#include "strnlen.h" 88#endif 89 90/* Include routines to handle parsing defaults */ 91#include "dsimple.h" 92 93typedef struct { 94 long code; 95 const char *name; 96} binding; 97 98#ifndef USE_XCB_ICCCM 99/* Once xcb-icccm's API is stable, this should be replaced by 100 xcb_size_hints_t & xcb_size_hints_flags_t */ 101typedef struct { 102 /** User specified flags */ 103 uint32_t flags; 104 /** User-specified position */ 105 int32_t x, y; 106 /** User-specified size */ 107 int32_t width, height; 108 /** Program-specified minimum size */ 109 int32_t min_width, min_height; 110 /** Program-specified maximum size */ 111 int32_t max_width, max_height; 112 /** Program-specified resize increments */ 113 int32_t width_inc, height_inc; 114 /** Program-specified minimum aspect ratios */ 115 int32_t min_aspect_num, min_aspect_den; 116 /** Program-specified maximum aspect ratios */ 117 int32_t max_aspect_num, max_aspect_den; 118 /** Program-specified base size */ 119 int32_t base_width, base_height; 120 /** Program-specified window gravity */ 121 uint32_t win_gravity; 122} wm_size_hints_t; 123 124# define xcb_size_hints_t wm_size_hints_t 125 126typedef struct { 127 /** Marks which fields in this structure are defined */ 128 int32_t flags; 129 /** Does this application rely on the window manager to get keyboard 130 input? */ 131 uint32_t input; 132 /** See below */ 133 int32_t initial_state; 134 /** Pixmap to be used as icon */ 135 xcb_pixmap_t icon_pixmap; 136 /** Window to be used as icon */ 137 xcb_window_t icon_window; 138 /** Initial position of icon */ 139 int32_t icon_x, icon_y; 140 /** Icon mask bitmap */ 141 xcb_pixmap_t icon_mask; 142 /* Identifier of related window group */ 143 xcb_window_t window_group; 144} wm_hints_t; 145 146#define xcb_wm_hints_t wm_hints_t 147 148enum { 149 /* xcb_size_hints_flags_t */ 150 XCB_SIZE_HINT_US_POSITION = 1 << 0, 151 XCB_SIZE_HINT_US_SIZE = 1 << 1, 152 XCB_SIZE_HINT_P_POSITION = 1 << 2, 153 XCB_SIZE_HINT_P_SIZE = 1 << 3, 154 XCB_SIZE_HINT_P_MIN_SIZE = 1 << 4, 155 XCB_SIZE_HINT_P_MAX_SIZE = 1 << 5, 156 XCB_SIZE_HINT_P_RESIZE_INC = 1 << 6, 157 XCB_SIZE_HINT_P_ASPECT = 1 << 7, 158 XCB_SIZE_HINT_BASE_SIZE = 1 << 8, 159 XCB_SIZE_HINT_P_WIN_GRAVITY = 1 << 9, 160 /* xcb_wm_state_t */ 161 XCB_WM_STATE_WITHDRAWN = 0, 162 XCB_WM_STATE_NORMAL = 1, 163 XCB_WM_STATE_ICONIC = 3, 164 /* xcb_wm_t */ 165 XCB_WM_HINT_INPUT = (1L << 0), 166 XCB_WM_HINT_STATE = (1L << 1), 167 XCB_WM_HINT_ICON_PIXMAP = (1L << 2), 168 XCB_WM_HINT_ICON_WINDOW = (1L << 3), 169 XCB_WM_HINT_ICON_POSITION = (1L << 4), 170 XCB_WM_HINT_ICON_MASK = (1L << 5), 171 XCB_WM_HINT_WINDOW_GROUP = (1L << 6), 172 XCB_WM_HINT_X_URGENCY = (1L << 8) 173}; 174 175/* Once xcb-icccm's API is stable, these should be replaced by calls to it */ 176# define GET_TEXT_PROPERTY(Dpy, Win, Atom) \ 177 xcb_get_property (Dpy, False, Win, Atom, XCB_GET_PROPERTY_TYPE_ANY, 0, BUFSIZ) 178# define xcb_get_wm_name(Dpy, Win) GET_TEXT_PROPERTY(Dpy, Win, XCB_ATOM_WM_NAME) 179 180# define xcb_get_wm_class(Dpy, Win) \ 181 xcb_get_property (Dpy, False, Win, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, BUFSIZ) 182# define xcb_get_wm_hints(Dpy, Win) \ 183 xcb_get_property(Dpy, False, Win, XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 0, 9) 184 185# define xcb_get_wm_size_hints(Dpy, Win, Atom) \ 186 xcb_get_property (Dpy, False, Win, Atom, XCB_ATOM_WM_SIZE_HINTS, 0, 18) 187# define xcb_get_wm_normal_hints(Dpy, Win) \ 188 xcb_get_wm_size_hints(Dpy, Win, XCB_ATOM_WM_NORMAL_HINTS) 189#endif 190 191/* Possibly in xcb-emwh in the future? */ 192static xcb_atom_t atom_net_wm_name, atom_utf8_string; 193static xcb_atom_t atom_net_wm_desktop, atom_net_wm_window_type, 194 atom_net_wm_state, atom_net_wm_pid, atom_net_frame_extents; 195static xcb_get_property_cookie_t get_net_wm_name (xcb_connection_t *, 196 xcb_window_t); 197 198/* Information we keep track of for each window to allow prefetching/reusing */ 199struct wininfo { 200 xcb_window_t window; 201 202 /* cookies for requests we've sent */ 203 xcb_get_geometry_cookie_t geometry_cookie; 204 xcb_get_property_cookie_t net_wm_name_cookie; 205 xcb_get_property_cookie_t wm_name_cookie; 206 xcb_get_property_cookie_t wm_class_cookie; 207 xcb_translate_coordinates_cookie_t trans_coords_cookie; 208 xcb_query_tree_cookie_t tree_cookie; 209 xcb_get_window_attributes_cookie_t attr_cookie; 210 xcb_get_property_cookie_t normal_hints_cookie; 211 xcb_get_property_cookie_t hints_cookie; 212 xcb_get_property_cookie_t wm_desktop_cookie; 213 xcb_get_property_cookie_t wm_window_type_cookie; 214 xcb_get_property_cookie_t wm_state_cookie; 215 xcb_get_property_cookie_t wm_pid_cookie; 216 xcb_get_property_cookie_t wm_client_machine_cookie; 217 xcb_get_property_cookie_t frame_extents_cookie; 218 xcb_get_property_cookie_t zoom_cookie; 219 220 /* cached results from previous requests */ 221 xcb_get_geometry_reply_t * geometry; 222 xcb_get_window_attributes_reply_t * win_attributes; 223 xcb_size_hints_t * normal_hints; 224}; 225 226static void scale_init (xcb_screen_t *scrn); 227static char *nscale (int, int, int, char *, size_t); 228static char *xscale (int); 229static char *yscale (int); 230static char *bscale (int); 231int main (int, char **); 232static const char *LookupL (long, const binding *); 233static const char *Lookup (int, const binding *); 234static void Display_Window_Id (struct wininfo *, Bool); 235static void Display_Stats_Info (struct wininfo *); 236static void Display_Bits_Info (struct wininfo *); 237static void Display_Event_Mask (long); 238static void Display_Events_Info (struct wininfo *); 239static void Display_Tree_Info (struct wininfo *, int); 240static void display_tree_info_1 (struct wininfo *, int, int); 241static void Display_Hints (xcb_size_hints_t *); 242static void Display_Size_Hints (struct wininfo *); 243static void Display_Window_Shape (xcb_window_t); 244static void Display_WM_Info (struct wininfo *); 245static void wininfo_wipe (struct wininfo *); 246 247static const char *window_id_format = "0x%lx"; 248 249#ifdef HAVE_ICONV 250static iconv_t iconv_from_utf8; 251#endif 252static const char *user_encoding; 253static void print_utf8 (const char *, char *, size_t, const char *); 254static void print_friendly_name (const char *, const char *, const char *); 255 256static xcb_connection_t *dpy; 257static xcb_screen_t *screen; 258static xcb_generic_error_t *err; 259 260#ifndef HAVE_STRLCAT 261static size_t strlcat (char *dst, const char *src, size_t dstsize) 262{ 263 size_t sd = strlen (dst); 264 size_t ss = strlen (src); 265 size_t s = sd + ss; 266 267 if (s < dstsize) { 268 strcpy (dst + sd, src); 269 } else { 270 strncpy (dst + sd, src, dstsize-sd-1); 271 dst[dstsize] = '\0'; 272 } 273 return s; 274} 275#endif 276 277/* 278 * Report the syntax for calling xwininfo: 279 */ 280void 281usage (void) 282{ 283 fprintf (stderr, 284 "usage: %s [-options ...]\n\n" 285 "where options include:\n" 286 " -help print this message\n" 287 " -display host:dpy X server to contact\n" 288 " -root use the root window\n" 289 " -id windowid use the window with the specified id\n" 290 " -name windowname use the window with the specified name\n" 291 " -int print window id in decimal\n" 292 " -children print parent and child identifiers\n" 293 " -tree print children identifiers recursively\n" 294 " -stats print window geometry [DEFAULT]\n" 295 " -bits print window pixel information\n" 296 " -events print events selected for on window\n" 297 " -size print size hints\n" 298 " -wm print window manager hints\n" 299 " -shape print shape extents\n" 300 " -frame don't ignore window manager frames\n" 301 " -english print sizes in english units\n" 302 " -metric print sizes in metric units\n" 303 " -all -tree, -stats, -bits, -events, -wm, -size, -shape\n" 304 "\n", 305 program_name); 306 exit (1); 307} 308 309/* 310 * pixel to inch, metric converter. 311 * Hacked in by Mark W. Eichin <eichin@athena> [eichin:19880619.1509EST] 312 * 313 * Simply put: replace the old numbers with string print calls. 314 * Returning a local string is ok, since we only ever get called to 315 * print one x and one y, so as long as they don't collide, they're 316 * fine. This is not meant to be a general purpose routine. 317 * 318 */ 319 320static int xp = 0, xmm = 0; 321static int yp = 0, ymm = 0; 322static int bp = 0, bmm = 0; 323static int english = 0, metric = 0; 324 325static void 326scale_init (xcb_screen_t *screen) 327{ 328 xp = screen->width_in_pixels; 329 yp = screen->height_in_pixels; 330 xmm = screen->width_in_millimeters; 331 ymm = screen->height_in_millimeters; 332 bp = xp + yp; 333 bmm = xmm + ymm; 334} 335 336#define MILE (5280*12) 337#define YARD (3*12) 338#define FOOT (12) 339 340static char * 341nscale (int n, int np, int nmm, char *nbuf, size_t nbufsize) 342{ 343 int s; 344 snprintf (nbuf, nbufsize, "%d", n); 345 346 if (metric||english) { 347 s = strlcat (nbuf, " (", nbufsize); 348 349 if (metric) { 350 snprintf (nbuf+s, nbufsize-s, "%.2f mm%s", 351 ((double) n) * nmm/np , english ? "; " : ""); 352 } 353 if (english) { 354 double inch_frac; 355 Bool printed_anything = False; 356 int mi, yar, ft, inr; 357 358 inch_frac = ((double) n)*(nmm/25.4)/np; 359 inr = (int)inch_frac; 360 inch_frac -= (double)inr; 361 if (inr >= MILE) { 362 mi = inr/MILE; 363 inr %= MILE; 364 s = strlen (nbuf); 365 snprintf (nbuf+s, nbufsize-s, "%d %s(?!?)", 366 mi, (mi == 1) ? "mile" : "miles"); 367 printed_anything = True; 368 } 369 if (inr >= YARD) { 370 yar = inr/YARD; 371 inr %= YARD; 372 if (printed_anything) 373 strlcat (nbuf, ", ", nbufsize); 374 s = strlen (nbuf); 375 snprintf (nbuf+s, nbufsize-s, "%d %s", 376 yar, (yar==1) ? "yard" : "yards"); 377 printed_anything = True; 378 } 379 if (inr >= FOOT) { 380 ft = inr/FOOT; 381 inr %= FOOT; 382 if (printed_anything) 383 strlcat (nbuf, ", ", nbufsize); 384 s = strlen (nbuf); 385 snprintf (nbuf+s, nbufsize-s, "%d %s", 386 ft, (ft==1) ? "foot" : "feet"); 387 printed_anything = True; 388 } 389 if (!printed_anything || inch_frac != 0.0 || inr != 0) { 390 if (printed_anything) 391 strlcat (nbuf, ", ", nbufsize); 392 s = strlen (nbuf); 393 snprintf (nbuf+s, nbufsize-s, "%.2f inches", inr+inch_frac); 394 } 395 } 396 strlcat (nbuf, ")", nbufsize); 397 } 398 return (nbuf); 399} 400 401static char xbuf[BUFSIZ]; 402static char * 403xscale (int x) 404{ 405 return (nscale (x, xp, xmm, xbuf, sizeof(xbuf))); 406} 407 408static char ybuf[BUFSIZ]; 409static char * 410yscale (int y) 411{ 412 return (nscale (y, yp, ymm, ybuf, sizeof(ybuf))); 413} 414 415static char bbuf[BUFSIZ]; 416static char * 417bscale (int b) 418{ 419 return (nscale (b, bp, bmm, bbuf, sizeof(bbuf))); 420} 421 422/* end of pixel to inch, metric converter */ 423 424int 425main (int argc, char **argv) 426{ 427 register int i; 428 int tree = 0, stats = 0, bits = 0, events = 0, wm = 0, size = 0, shape = 0; 429 int frame = 0, children = 0; 430 int use_root = 0; 431 xcb_window_t window = 0; 432 char *display_name = NULL; 433 const char *window_name = NULL; 434 struct wininfo wininfo; 435 struct wininfo *w = &wininfo; 436 437 program_name = argv[0]; 438 439 if (!setlocale (LC_ALL, "")) 440 fprintf (stderr, "%s: can not set locale properly\n", program_name); 441 user_encoding = nl_langinfo (CODESET); 442 if (user_encoding == NULL) 443 user_encoding = "unknown encoding"; 444 445 memset (w, 0, sizeof(struct wininfo)); 446 447 /* Handle our command line arguments */ 448 for (i = 1; i < argc; i++) { 449 if (!strcmp (argv[i], "-help")) 450 usage (); 451 if (!strcmp (argv[i], "-display") || !strcmp (argv[i], "-d")) { 452 if (++i >= argc) 453 Fatal_Error("-display requires argument"); 454 display_name = argv[i]; 455 continue; 456 } 457 if (!strcmp (argv[i], "-root")) { 458 use_root = 1; 459 continue; 460 } 461 if (!strcmp (argv[i], "-id")) { 462 if (++i >= argc) 463 Fatal_Error("-id requires argument"); 464 window = strtoul(argv[i], NULL, 0); 465 continue; 466 } 467 if (!strcmp (argv[i], "-name")) { 468 if (++i >= argc) 469 Fatal_Error("-name requires argument"); 470 window_name = argv[i]; 471 continue; 472 } 473 if (!strcmp (argv[i], "-int")) { 474 window_id_format = "%ld"; 475 continue; 476 } 477 if (!strcmp (argv[i], "-children")) { 478 children = 1; 479 continue; 480 } 481 if (!strcmp (argv[i], "-tree")) { 482 tree = 1; 483 continue; 484 } 485 if (!strcmp (argv[i], "-stats")) { 486 stats = 1; 487 continue; 488 } 489 if (!strcmp (argv[i], "-bits")) { 490 bits = 1; 491 continue; 492 } 493 if (!strcmp (argv[i], "-events")) { 494 events = 1; 495 continue; 496 } 497 if (!strcmp (argv[i], "-wm")) { 498 wm = 1; 499 continue; 500 } 501 if (!strcmp (argv[i], "-frame")) { 502 frame = 1; 503 continue; 504 } 505 if (!strcmp (argv[i], "-size")) { 506 size = 1; 507 continue; 508 } 509 if (!strcmp (argv[i], "-shape")) { 510 shape = 1; 511 continue; 512 } 513 if (!strcmp (argv[i], "-english")) { 514 english = 1; 515 continue; 516 } 517 if (!strcmp (argv[i], "-metric")) { 518 metric = 1; 519 continue; 520 } 521 if (!strcmp (argv[i], "-all")) { 522 tree = stats = bits = events = wm = size = shape = 1; 523 continue; 524 } 525 usage (); 526 } 527 528 Setup_Display_And_Screen (display_name, &dpy, &screen); 529 530 /* preload atoms we may need later */ 531 Intern_Atom (dpy, "_NET_WM_NAME"); 532 Intern_Atom (dpy, "UTF8_STRING"); 533 if (wm) { 534 Intern_Atom (dpy, "_NET_WM_DESKTOP"); 535 Intern_Atom (dpy, "_NET_WM_WINDOW_TYPE"); 536 Intern_Atom (dpy, "_NET_WM_STATE"); 537 Intern_Atom (dpy, "_NET_WM_PID"); 538 Intern_Atom (dpy, "_NET_FRAME_EXTENTS"); 539 } 540 /* initialize scaling data */ 541 scale_init(screen); 542 543 if (use_root) 544 window = screen->root; 545 else if (window_name) { 546 window = Window_With_Name (dpy, screen->root, window_name); 547 if (!window) 548 Fatal_Error ("No window with name \"%s\" exists!", window_name); 549 } 550 551 /* If no window selected on command line, let user pick one the hard way */ 552 if (!window) { 553 printf ("\n" 554 "xwininfo: Please select the window about which you\n" 555 " would like information by clicking the\n" 556 " mouse in that window.\n"); 557 Intern_Atom (dpy, "_NET_VIRTUAL_ROOTS"); 558 Intern_Atom (dpy, "WM_STATE"); 559 window = Select_Window (dpy, screen, !frame); 560 } 561 562 /* 563 * Do the actual displaying as per parameters 564 */ 565 if (!(children || tree || bits || events || wm || size)) 566 stats = 1; 567 568 /* 569 * make sure that the window is valid 570 */ 571 { 572 xcb_get_geometry_cookie_t gg_cookie = 573 xcb_get_geometry (dpy, window); 574 575 w->geometry = xcb_get_geometry_reply(dpy, gg_cookie, &err); 576 577 if (!w->geometry) { 578 char badid[20]; 579 580 if (err) 581 Print_X_Error (dpy, err); 582 583 snprintf (badid, sizeof(badid), window_id_format, window); 584 Fatal_Error ("No such window with id %s.", badid); 585 } 586 } 587 588 /* Send requests to prefetch data we'll need */ 589 w->window = window; 590 w->net_wm_name_cookie = get_net_wm_name (dpy, window); 591 w->wm_name_cookie = xcb_get_wm_name (dpy, window); 592 if (children || tree) 593 w->tree_cookie = xcb_query_tree (dpy, window); 594 if (stats) { 595 w->trans_coords_cookie = 596 xcb_translate_coordinates (dpy, window, w->geometry->root, 597 -(w->geometry->border_width), 598 -(w->geometry->border_width)); 599 } 600 if (stats || bits || events) 601 w->attr_cookie = xcb_get_window_attributes (dpy, window); 602 if (stats || size) 603 w->normal_hints_cookie = xcb_get_wm_normal_hints (dpy, window); 604 if (wm) { 605 w->hints_cookie = xcb_get_wm_hints(dpy, window); 606 607 atom_net_wm_desktop = Get_Atom (dpy, "_NET_WM_DESKTOP"); 608 if (atom_net_wm_desktop) { 609 w->wm_desktop_cookie = xcb_get_property 610 (dpy, False, window, atom_net_wm_desktop, 611 XCB_ATOM_CARDINAL, 0, 4); 612 } 613 614 atom_net_wm_window_type = Get_Atom (dpy, "_NET_WM_WINDOW_TYPE"); 615 if (atom_net_wm_window_type) { 616 w->wm_window_type_cookie = xcb_get_property 617 (dpy, False, window, atom_net_wm_window_type, 618 XCB_ATOM_ATOM, 0, BUFSIZ); 619 } 620 621 atom_net_wm_state = Get_Atom (dpy, "_NET_WM_STATE"); 622 if (atom_net_wm_state) { 623 w->wm_state_cookie = xcb_get_property 624 (dpy, False, window, atom_net_wm_state, 625 XCB_ATOM_ATOM, 0, BUFSIZ); 626 } 627 628 atom_net_wm_pid = Get_Atom (dpy, "_NET_WM_PID"); 629 if (atom_net_wm_pid) { 630 w->wm_pid_cookie = xcb_get_property 631 (dpy, False, window, atom_net_wm_pid, 632 XCB_ATOM_CARDINAL, 0, BUFSIZ); 633 w->wm_client_machine_cookie = xcb_get_property 634 (dpy, False, window, XCB_ATOM_WM_CLIENT_MACHINE, 635 XCB_GET_PROPERTY_TYPE_ANY, 0, BUFSIZ); 636 } 637 638 atom_net_frame_extents = Get_Atom (dpy, "_NET_FRAME_EXTENTS"); 639 if (atom_net_frame_extents) { 640 w->frame_extents_cookie = xcb_get_property 641 (dpy, False, window, atom_net_frame_extents, 642 XCB_ATOM_CARDINAL, 0, 4 * 4); 643 } 644 } 645 if (size) 646 w->zoom_cookie = xcb_get_wm_size_hints (dpy, window, 647 XCB_ATOM_WM_ZOOM_HINTS); 648 xcb_flush (dpy); 649 650 printf ("\nxwininfo: Window id: "); 651 Display_Window_Id (w, True); 652 if (children || tree) 653 Display_Tree_Info (w, tree); 654 if (stats) 655 Display_Stats_Info (w); 656 if (bits) 657 Display_Bits_Info (w); 658 if (events) 659 Display_Events_Info (w); 660 if (wm) 661 Display_WM_Info (w); 662 if (size) 663 Display_Size_Hints (w); 664 if (shape) 665 Display_Window_Shape (window); 666 printf ("\n"); 667 668 wininfo_wipe (w); 669 xcb_disconnect (dpy); 670#ifdef HAVE_ICONV 671 if (iconv_from_utf8 && (iconv_from_utf8 != (iconv_t) -1)) { 672 iconv_close (iconv_from_utf8); 673 } 674#endif 675 exit (0); 676} 677 678/* Ensure win_attributes field is filled in */ 679static xcb_get_window_attributes_reply_t * 680fetch_win_attributes (struct wininfo *w) 681{ 682 if (!w->win_attributes) { 683 w->win_attributes = 684 xcb_get_window_attributes_reply (dpy, w->attr_cookie, &err); 685 686 if (!w->win_attributes) { 687 Print_X_Error (dpy, err); 688 Fatal_Error ("Can't get window attributes."); 689 } 690 } 691 return w->win_attributes; 692} 693 694#ifndef USE_XCB_ICCCM 695static Bool 696wm_size_hints_reply (xcb_connection_t *dpy, xcb_get_property_cookie_t cookie, 697 wm_size_hints_t *hints_return, xcb_generic_error_t **err) 698{ 699 xcb_get_property_reply_t *prop = xcb_get_property_reply (dpy, cookie, err); 700 int length; 701 702 if (!prop || (prop->type != XCB_ATOM_WM_SIZE_HINTS) || 703 (prop->format != 32)) { 704 free (prop); 705 return False; 706 } 707 708 memset (hints_return, 0, sizeof(wm_size_hints_t)); 709 710 length = xcb_get_property_value_length(prop); 711 if (length > sizeof(wm_size_hints_t)) 712 length = sizeof(wm_size_hints_t); 713 memcpy (hints_return, xcb_get_property_value (prop), length); 714 715 free (prop); 716 return True; 717} 718 719#define xcb_get_wm_normal_hints_reply wm_size_hints_reply 720#define xcb_get_wm_size_hints_reply wm_size_hints_reply 721#endif 722 723 724 725/* Ensure normal_hints field is filled in */ 726static xcb_size_hints_t * 727fetch_normal_hints (struct wininfo *w, xcb_size_hints_t *hints_return) 728{ 729 xcb_size_hints_t hints; 730 731 if (!w->normal_hints) { 732 if (xcb_get_wm_normal_hints_reply (dpy, w->normal_hints_cookie, 733 &hints, NULL)) { 734 w->normal_hints = malloc (sizeof(xcb_size_hints_t)); 735 if (w->normal_hints) 736 memcpy(w->normal_hints, &hints, sizeof(xcb_size_hints_t)); 737 } 738 } 739 if (hints_return && w->normal_hints) 740 memcpy(hints_return, w->normal_hints, sizeof(xcb_size_hints_t)); 741 return w->normal_hints; 742} 743 744 745/* 746 * Lookup: lookup a code in a table. 747 */ 748static char _lookup_buffer[100]; 749 750static const char * 751LookupL (long code, const binding *table) 752{ 753 const char *name = NULL; 754 755 while (table->name) { 756 if (table->code == code) { 757 name = table->name; 758 break; 759 } 760 table++; 761 } 762 763 if (name == NULL) { 764 snprintf (_lookup_buffer, sizeof(_lookup_buffer), 765 "unknown (code = %ld. = 0x%lx)", code, code); 766 name = _lookup_buffer; 767 } 768 769 return (name); 770} 771 772static const char * 773Lookup (int code, const binding *table) 774{ 775 return LookupL ((long)code, table); 776} 777 778/* 779 * Routine to display a window id in dec/hex with name if window has one 780 * 781 * Requires wininfo members initialized: window, wm_name_cookie 782 */ 783 784static void 785Display_Window_Id (struct wininfo *w, Bool newline_wanted) 786{ 787#ifdef USE_XCB_ICCCM 788 xcb_get_text_property_reply_t wmn_reply; 789#endif 790 xcb_get_property_reply_t *prop; 791 uint8_t got_reply = False; 792 const char *wm_name = NULL; 793 unsigned int wm_name_len = 0; 794 xcb_atom_t wm_name_encoding = XCB_NONE; 795 796 printf (window_id_format, w->window); /* print id # in hex/dec */ 797 798 if (!w->window) { 799 printf (" (none)"); 800 } else { 801 if (w->window == screen->root) { 802 printf (" (the root window)"); 803 } 804 /* Get window name if any */ 805 prop = xcb_get_property_reply (dpy, w->net_wm_name_cookie, NULL); 806 if (prop && (prop->type != XCB_NONE)) { 807 wm_name = xcb_get_property_value (prop); 808 wm_name_len = xcb_get_property_value_length (prop); 809 wm_name_encoding = prop->type; 810 got_reply = True; 811 } 812 813 if (!got_reply) { /* No _NET_WM_NAME, check WM_NAME */ 814#ifdef USE_XCB_ICCCM 815 got_reply = xcb_get_wm_name_reply (dpy, w->wm_name_cookie, 816 &wmn_reply, NULL); 817 if (got_reply) { 818 wm_name = wmn_reply.name; 819 wm_name_len = wmn_reply.name_len; 820 wm_name_encoding = wmn_reply.encoding; 821 } 822#else 823 prop = xcb_get_property_reply (dpy, w->wm_name_cookie, NULL); 824 if (prop && (prop->type != XCB_NONE)) { 825 wm_name = xcb_get_property_value (prop); 826 wm_name_len = xcb_get_property_value_length (prop); 827 wm_name_encoding = prop->type; 828 got_reply = True; 829 } 830#endif 831 } 832 if (!got_reply || wm_name_len == 0) { 833 printf (" (has no name)"); 834 } else { 835 if (wm_name_encoding == XCB_ATOM_STRING) { 836 printf (" \"%.*s\"", wm_name_len, wm_name); 837 } else if (wm_name_encoding == atom_utf8_string) { 838 print_utf8 (" \"", (char *) wm_name, wm_name_len, "\""); 839 } else { 840 /* Encodings we don't support, including COMPOUND_TEXT */ 841 const char *enc_name = Get_Atom_Name (dpy, wm_name_encoding); 842 if (enc_name) { 843 printf (" (name in unsupported encoding %s)", enc_name); 844 } else { 845 printf (" (name in unsupported encoding ATOM 0x%x)", 846 wm_name_encoding); 847 } 848 } 849 } 850#ifdef USE_XCB_ICCCM 851 if (got_reply) 852 xcb_get_text_property_reply_wipe (&wmn_reply); 853#else 854 free (prop); 855#endif 856 } 857 858 if (newline_wanted) 859 printf ("\n"); 860 861 return; 862} 863 864 865/* 866 * Display Stats on window 867 */ 868static const binding _window_classes[] = { 869 { XCB_WINDOW_CLASS_INPUT_OUTPUT, "InputOutput" }, 870 { XCB_WINDOW_CLASS_INPUT_ONLY, "InputOnly" }, 871 { 0, NULL } }; 872 873static const binding _map_states[] = { 874 { XCB_MAP_STATE_UNMAPPED, "IsUnMapped" }, 875 { XCB_MAP_STATE_UNVIEWABLE, "IsUnviewable" }, 876 { XCB_MAP_STATE_VIEWABLE, "IsViewable" }, 877 { 0, NULL } }; 878 879static const binding _backing_store_states[] = { 880 { XCB_BACKING_STORE_NOT_USEFUL, "NotUseful" }, 881 { XCB_BACKING_STORE_WHEN_MAPPED,"WhenMapped" }, 882 { XCB_BACKING_STORE_ALWAYS, "Always" }, 883 { 0, NULL } }; 884 885static const binding _bit_gravity_states[] = { 886 { XCB_GRAVITY_BIT_FORGET, "ForgetGravity" }, 887 { XCB_GRAVITY_NORTH_WEST, "NorthWestGravity" }, 888 { XCB_GRAVITY_NORTH, "NorthGravity" }, 889 { XCB_GRAVITY_NORTH_EAST, "NorthEastGravity" }, 890 { XCB_GRAVITY_WEST, "WestGravity" }, 891 { XCB_GRAVITY_CENTER, "CenterGravity" }, 892 { XCB_GRAVITY_EAST, "EastGravity" }, 893 { XCB_GRAVITY_SOUTH_WEST, "SouthWestGravity" }, 894 { XCB_GRAVITY_SOUTH, "SouthGravity" }, 895 { XCB_GRAVITY_SOUTH_EAST, "SouthEastGravity" }, 896 { XCB_GRAVITY_STATIC, "StaticGravity" }, 897 { 0, NULL }}; 898 899static const binding _window_gravity_states[] = { 900 { XCB_GRAVITY_WIN_UNMAP, "UnmapGravity" }, 901 { XCB_GRAVITY_NORTH_WEST, "NorthWestGravity" }, 902 { XCB_GRAVITY_NORTH, "NorthGravity" }, 903 { XCB_GRAVITY_NORTH_EAST, "NorthEastGravity" }, 904 { XCB_GRAVITY_WEST, "WestGravity" }, 905 { XCB_GRAVITY_CENTER, "CenterGravity" }, 906 { XCB_GRAVITY_EAST, "EastGravity" }, 907 { XCB_GRAVITY_SOUTH_WEST, "SouthWestGravity" }, 908 { XCB_GRAVITY_SOUTH, "SouthGravity" }, 909 { XCB_GRAVITY_SOUTH_EAST, "SouthEastGravity" }, 910 { XCB_GRAVITY_STATIC, "StaticGravity" }, 911 { 0, NULL }}; 912 913static const binding _visual_classes[] = { 914 { XCB_VISUAL_CLASS_STATIC_GRAY, "StaticGray" }, 915 { XCB_VISUAL_CLASS_GRAY_SCALE, "GrayScale" }, 916 { XCB_VISUAL_CLASS_STATIC_COLOR,"StaticColor" }, 917 { XCB_VISUAL_CLASS_PSEUDO_COLOR,"PseudoColor" }, 918 { XCB_VISUAL_CLASS_TRUE_COLOR, "TrueColor" }, 919 { XCB_VISUAL_CLASS_DIRECT_COLOR,"DirectColor" }, 920 { 0, NULL }}; 921 922/* 923 * Requires wininfo members initialized: 924 * window, geometry, attr_cookie, trans_coords_cookie, normal_hints_cookie 925 */ 926static void 927Display_Stats_Info (struct wininfo *w) 928{ 929 xcb_translate_coordinates_reply_t *trans_coords; 930 xcb_get_window_attributes_reply_t *win_attributes; 931 xcb_size_hints_t hints; 932 933 int dw = screen->width_in_pixels, dh = screen->height_in_pixels; 934 int rx, ry, xright, ybelow; 935 int showright = 0, showbelow = 0; 936 xcb_window_t wmframe, parent; 937 938 trans_coords = 939 xcb_translate_coordinates_reply (dpy, w->trans_coords_cookie, NULL); 940 if (!trans_coords) 941 Fatal_Error ("Can't get translated coordinates."); 942 943 rx = (int16_t)trans_coords->dst_x; 944 ry = (int16_t)trans_coords->dst_y; 945 free (trans_coords); 946 947 xright = (dw - rx - w->geometry->border_width * 2 - 948 w->geometry->width); 949 ybelow = (dh - ry - w->geometry->border_width * 2 - 950 w->geometry->height); 951 952 953 printf ("\n"); 954 printf (" Absolute upper-left X: %s\n", xscale (rx)); 955 printf (" Absolute upper-left Y: %s\n", yscale (ry)); 956 printf (" Relative upper-left X: %s\n", xscale (w->geometry->x)); 957 printf (" Relative upper-left Y: %s\n", yscale (w->geometry->y)); 958 printf (" Width: %s\n", xscale (w->geometry->width)); 959 printf (" Height: %s\n", yscale (w->geometry->height)); 960 printf (" Depth: %d\n", w->geometry->depth); 961 962 win_attributes = fetch_win_attributes (w); 963 964 printf (" Visual: 0x%lx\n", (unsigned long) win_attributes->visual); 965 if (screen) 966 { 967 xcb_depth_iterator_t depth_iter; 968 xcb_visualtype_t *visual_type = NULL; 969 970 depth_iter = xcb_screen_allowed_depths_iterator (screen); 971 for (; depth_iter.rem; xcb_depth_next (&depth_iter)) { 972 xcb_visualtype_iterator_t visual_iter; 973 974 visual_iter = xcb_depth_visuals_iterator (depth_iter.data); 975 for (; visual_iter.rem; xcb_visualtype_next (&visual_iter)) { 976 if (screen->root_visual == visual_iter.data->visual_id) { 977 visual_type = visual_iter.data; 978 break; 979 } 980 } 981 } 982 if (visual_type) 983 printf (" Visual Class: %s\n", Lookup (visual_type->_class, 984 _visual_classes)); 985 } 986 987 printf (" Border width: %s\n", bscale (w->geometry->border_width)); 988 printf (" Class: %s\n", 989 Lookup (win_attributes->_class, _window_classes)); 990 printf (" Colormap: 0x%lx (%sinstalled)\n", 991 (unsigned long) win_attributes->colormap, 992 win_attributes->map_is_installed ? "" : "not "); 993 printf (" Bit Gravity State: %s\n", 994 Lookup (win_attributes->bit_gravity, _bit_gravity_states)); 995 printf (" Window Gravity State: %s\n", 996 Lookup (win_attributes->win_gravity, _window_gravity_states)); 997 printf (" Backing Store State: %s\n", 998 Lookup (win_attributes->backing_store, _backing_store_states)); 999 printf (" Save Under State: %s\n", 1000 win_attributes->save_under ? "yes" : "no"); 1001 printf (" Map State: %s\n", 1002 Lookup (win_attributes->map_state, _map_states)); 1003 printf (" Override Redirect State: %s\n", 1004 win_attributes->override_redirect ? "yes" : "no"); 1005 printf (" Corners: +%d+%d -%d+%d -%d-%d +%d-%d\n", 1006 rx, ry, xright, ry, xright, ybelow, rx, ybelow); 1007 1008 /* 1009 * compute geometry string that would recreate window 1010 */ 1011 printf (" -geometry "); 1012 1013 /* compute size in appropriate units */ 1014 if (!fetch_normal_hints (w, &hints)) 1015 hints.flags = 0; 1016 1017 if ((hints.flags & XCB_SIZE_HINT_P_RESIZE_INC) && 1018 (hints.width_inc != 0) && (hints.height_inc != 0)) { 1019 if (hints.flags & (XCB_SIZE_HINT_BASE_SIZE|XCB_SIZE_HINT_P_MIN_SIZE)) { 1020 if (hints.flags & XCB_SIZE_HINT_BASE_SIZE) { 1021 w->geometry->width -= hints.base_width; 1022 w->geometry->height -= hints.base_height; 1023 } else { 1024 /* ICCCM says MinSize is default for BaseSize */ 1025 w->geometry->width -= hints.min_width; 1026 w->geometry->height -= hints.min_height; 1027 } 1028 } 1029 printf ("%dx%d", w->geometry->width/hints.width_inc, 1030 w->geometry->height/hints.height_inc); 1031 } else 1032 printf ("%dx%d", w->geometry->width, w->geometry->height); 1033 1034 if (!(hints.flags & XCB_SIZE_HINT_P_WIN_GRAVITY)) 1035 hints.win_gravity = XCB_GRAVITY_NORTH_WEST; /* per ICCCM */ 1036 /* find our window manager frame, if any */ 1037 for (wmframe = parent = w->window; parent != 0 ; wmframe = parent) { 1038 xcb_query_tree_cookie_t qt_cookie; 1039 xcb_query_tree_reply_t *tree; 1040 1041 qt_cookie = xcb_query_tree (dpy, wmframe); 1042 tree = xcb_query_tree_reply (dpy, qt_cookie, &err); 1043 if (!tree) { 1044 Print_X_Error (dpy, err); 1045 Fatal_Error ("Can't query window tree."); 1046 } 1047 parent = tree->parent; 1048 free (tree); 1049 if (parent == w->geometry->root || !parent) 1050 break; 1051 } 1052 if (wmframe != w->window) { 1053 /* WM reparented, so find edges of the frame */ 1054 /* Only works for ICCCM-compliant WMs, and then only if the 1055 window has corner gravity. We would need to know the original width 1056 of the window to correctly handle the other gravities. */ 1057 xcb_get_geometry_cookie_t geom_cookie; 1058 xcb_get_geometry_reply_t *frame_geometry; 1059 1060 geom_cookie = xcb_get_geometry (dpy, wmframe); 1061 frame_geometry = xcb_get_geometry_reply (dpy, geom_cookie, &err); 1062 1063 if (!frame_geometry) { 1064 Print_X_Error (dpy, err); 1065 Fatal_Error ("Can't get frame geometry."); 1066 } 1067 switch (hints.win_gravity) { 1068 case XCB_GRAVITY_NORTH_WEST: case XCB_GRAVITY_SOUTH_WEST: 1069 case XCB_GRAVITY_NORTH_EAST: case XCB_GRAVITY_SOUTH_EAST: 1070 case XCB_GRAVITY_WEST: 1071 rx = frame_geometry->x; 1072 } 1073 switch (hints.win_gravity) { 1074 case XCB_GRAVITY_NORTH_WEST: case XCB_GRAVITY_SOUTH_WEST: 1075 case XCB_GRAVITY_NORTH_EAST: case XCB_GRAVITY_SOUTH_EAST: 1076 case XCB_GRAVITY_EAST: 1077 xright = dw - frame_geometry->x - frame_geometry->width - 1078 (2 * frame_geometry->border_width); 1079 } 1080 switch (hints.win_gravity) { 1081 case XCB_GRAVITY_NORTH_WEST: case XCB_GRAVITY_SOUTH_WEST: 1082 case XCB_GRAVITY_NORTH_EAST: case XCB_GRAVITY_SOUTH_EAST: 1083 case XCB_GRAVITY_NORTH: 1084 ry = frame_geometry->y; 1085 } 1086 switch (hints.win_gravity) { 1087 case XCB_GRAVITY_NORTH_WEST: case XCB_GRAVITY_SOUTH_WEST: 1088 case XCB_GRAVITY_NORTH_EAST: case XCB_GRAVITY_SOUTH_EAST: 1089 case XCB_GRAVITY_SOUTH: 1090 ybelow = dh - frame_geometry->y - frame_geometry->height - 1091 (2 * frame_geometry->border_width); 1092 } 1093 free (frame_geometry); 1094 } 1095 /* If edge gravity, offer a corner on that edge (because the application 1096 programmer cares about that edge), otherwise offer upper left unless 1097 some other corner is close to an edge of the screen. 1098 (For corner gravity, assume gravity was set by XWMGeometry. 1099 For CenterGravity, it doesn't matter.) */ 1100 if (hints.win_gravity == XCB_GRAVITY_EAST || 1101 (abs (xright) <= 100 && abs (xright) < abs (rx) 1102 && hints.win_gravity != XCB_GRAVITY_WEST)) 1103 showright = 1; 1104 if (hints.win_gravity == XCB_GRAVITY_SOUTH || 1105 (abs (ybelow) <= 100 && abs (ybelow) < abs (ry) 1106 && hints.win_gravity != XCB_GRAVITY_NORTH)) 1107 showbelow = 1; 1108 1109 if (showright) 1110 printf ("-%d", xright); 1111 else 1112 printf ("+%d", rx); 1113 if (showbelow) 1114 printf ("-%d", ybelow); 1115 else 1116 printf ("+%d", ry); 1117 printf ("\n"); 1118} 1119 1120 1121/* 1122 * Display bits info: 1123 */ 1124static const binding _gravities[] = { 1125 /* WARNING: the first two of these have the same value - see code */ 1126 { XCB_GRAVITY_WIN_UNMAP, "UnMapGravity" }, 1127 { XCB_GRAVITY_BIT_FORGET, "ForgetGravity" }, 1128 { XCB_GRAVITY_NORTH_WEST, "NorthWestGravity" }, 1129 { XCB_GRAVITY_NORTH, "NorthGravity" }, 1130 { XCB_GRAVITY_NORTH_EAST, "NorthEastGravity" }, 1131 { XCB_GRAVITY_WEST, "WestGravity" }, 1132 { XCB_GRAVITY_CENTER, "CenterGravity" }, 1133 { XCB_GRAVITY_EAST, "EastGravity" }, 1134 { XCB_GRAVITY_SOUTH_WEST, "SouthWestGravity" }, 1135 { XCB_GRAVITY_SOUTH, "SouthGravity" }, 1136 { XCB_GRAVITY_SOUTH_EAST, "SouthEastGravity" }, 1137 { XCB_GRAVITY_STATIC, "StaticGravity" }, 1138 { 0, NULL } }; 1139 1140static const binding _backing_store_hint[] = { 1141 { XCB_BACKING_STORE_NOT_USEFUL, "NotUseful" }, 1142 { XCB_BACKING_STORE_WHEN_MAPPED,"WhenMapped" }, 1143 { XCB_BACKING_STORE_ALWAYS, "Always" }, 1144 { 0, NULL } }; 1145 1146static const binding _bool[] = { 1147 { 0, "No" }, 1148 { 1, "Yes" }, 1149 { 0, NULL } }; 1150 1151/* 1152 * Requires wininfo members initialized: 1153 * window, attr_cookie (or win_attributes) 1154 */ 1155static void 1156Display_Bits_Info (struct wininfo * w) 1157{ 1158 xcb_get_window_attributes_reply_t *win_attributes 1159 = fetch_win_attributes (w); 1160 1161 printf ("\n"); 1162 printf (" Bit gravity: %s\n", 1163 Lookup (win_attributes->bit_gravity, _gravities+1)); 1164 printf (" Window gravity: %s\n", 1165 Lookup (win_attributes->win_gravity, _gravities)); 1166 printf (" Backing-store hint: %s\n", 1167 Lookup (win_attributes->backing_store, _backing_store_hint)); 1168 printf (" Backing-planes to be preserved: 0x%lx\n", 1169 (unsigned long) win_attributes->backing_planes); 1170 printf (" Backing pixel: %ld\n", 1171 (unsigned long) win_attributes->backing_pixel); 1172 printf (" Save-unders: %s\n", 1173 Lookup (win_attributes->save_under, _bool)); 1174} 1175 1176 1177/* 1178 * Routine to display all events in an event mask 1179 */ 1180static const binding _event_mask_names[] = { 1181 { XCB_EVENT_MASK_KEY_PRESS, "KeyPress" }, 1182 { XCB_EVENT_MASK_KEY_RELEASE, "KeyRelease" }, 1183 { XCB_EVENT_MASK_BUTTON_PRESS, "ButtonPress" }, 1184 { XCB_EVENT_MASK_BUTTON_RELEASE, "ButtonRelease" }, 1185 { XCB_EVENT_MASK_ENTER_WINDOW, "EnterWindow" }, 1186 { XCB_EVENT_MASK_LEAVE_WINDOW, "LeaveWindow" }, 1187 { XCB_EVENT_MASK_POINTER_MOTION, "PointerMotion" }, 1188 { XCB_EVENT_MASK_POINTER_MOTION_HINT, "PointerMotionHint" }, 1189 { XCB_EVENT_MASK_BUTTON_1_MOTION, "Button1Motion" }, 1190 { XCB_EVENT_MASK_BUTTON_2_MOTION, "Button2Motion" }, 1191 { XCB_EVENT_MASK_BUTTON_3_MOTION, "Button3Motion" }, 1192 { XCB_EVENT_MASK_BUTTON_4_MOTION, "Button4Motion" }, 1193 { XCB_EVENT_MASK_BUTTON_5_MOTION, "Button5Motion" }, 1194 { XCB_EVENT_MASK_BUTTON_MOTION, "ButtonMotion" }, 1195 { XCB_EVENT_MASK_KEYMAP_STATE, "KeymapState" }, 1196 { XCB_EVENT_MASK_EXPOSURE, "Exposure" }, 1197 { XCB_EVENT_MASK_VISIBILITY_CHANGE, "VisibilityChange" }, 1198 { XCB_EVENT_MASK_STRUCTURE_NOTIFY, "StructureNotify" }, 1199 { XCB_EVENT_MASK_RESIZE_REDIRECT, "ResizeRedirect" }, 1200 { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, "SubstructureNotify" }, 1201 { XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, "SubstructureRedirect" }, 1202 { XCB_EVENT_MASK_FOCUS_CHANGE, "FocusChange" }, 1203 { XCB_EVENT_MASK_PROPERTY_CHANGE, "PropertyChange" }, 1204 { XCB_EVENT_MASK_COLOR_MAP_CHANGE, "ColormapChange" }, 1205 { XCB_EVENT_MASK_OWNER_GRAB_BUTTON, "OwnerGrabButton" }, 1206 { 0, NULL } }; 1207 1208static void 1209Display_Event_Mask (long mask) 1210{ 1211 long bit, bit_mask; 1212 1213 for (bit=0, bit_mask=1; bit < sizeof(long)*8; bit++, bit_mask <<= 1) 1214 if (mask & bit_mask) 1215 printf (" %s\n", 1216 LookupL (bit_mask, _event_mask_names)); 1217} 1218 1219 1220/* 1221 * Display info on events 1222 * 1223 * Requires wininfo members initialized: 1224 * window, attr_cookie (or win_attributes) 1225 */ 1226static void 1227Display_Events_Info (struct wininfo *w) 1228{ 1229 xcb_get_window_attributes_reply_t *win_attributes 1230 = fetch_win_attributes (w); 1231 1232 printf ("\n"); 1233 printf (" Someone wants these events:\n"); 1234 Display_Event_Mask (win_attributes->all_event_masks); 1235 1236 printf (" Do not propagate these events:\n"); 1237 Display_Event_Mask (win_attributes->do_not_propagate_mask); 1238 1239 printf (" Override redirection?: %s\n", 1240 Lookup (win_attributes->override_redirect, _bool)); 1241} 1242 1243 1244 /* left out visual stuff */ 1245 /* left out colormap */ 1246 /* left out map_installed */ 1247 1248 1249/* 1250 * Display root, parent, and (recursively) children information 1251 * recurse - true to show children information 1252 * 1253 * Requires wininfo members initialized: window, tree_cookie 1254 */ 1255static void 1256Display_Tree_Info (struct wininfo *w, int recurse) 1257{ 1258 display_tree_info_1 (w, recurse, 0); 1259} 1260 1261/* 1262 * level - recursion level 1263 */ 1264static void 1265display_tree_info_1 (struct wininfo *w, int recurse, int level) 1266{ 1267 int i, j; 1268 unsigned int num_children; 1269 xcb_query_tree_reply_t *tree; 1270 1271 tree = xcb_query_tree_reply (dpy, w->tree_cookie, &err); 1272 if (!tree) { 1273 Print_X_Error (dpy, err); 1274 Fatal_Error ("Can't query window tree."); 1275 } 1276 1277 if (level == 0) { 1278 struct wininfo rw, pw; 1279 rw.window = tree->root; 1280 rw.net_wm_name_cookie = get_net_wm_name (dpy, rw.window); 1281 rw.wm_name_cookie = xcb_get_wm_name (dpy, rw.window); 1282 pw.window = tree->parent; 1283 pw.net_wm_name_cookie = get_net_wm_name (dpy, pw.window); 1284 pw.wm_name_cookie = xcb_get_wm_name (dpy, pw.window); 1285 xcb_flush (dpy); 1286 1287 printf ("\n"); 1288 printf (" Root window id: "); 1289 Display_Window_Id (&rw, True); 1290 printf (" Parent window id: "); 1291 Display_Window_Id (&pw, True); 1292 } 1293 1294 num_children = xcb_query_tree_children_length (tree); 1295 1296 if (level == 0 || num_children > 0) { 1297 printf (" "); 1298 for (j = 0; j < level; j++) printf (" "); 1299 printf ("%d child%s%s\n", num_children, num_children == 1 ? "" : "ren", 1300 num_children ? ":" : "."); 1301 } 1302 1303 if (num_children > 0) { 1304 xcb_window_t *child_list = xcb_query_tree_children (tree); 1305 struct wininfo *children 1306 = calloc (num_children, sizeof(struct wininfo)); 1307 1308 if (children == NULL) 1309 Fatal_Error ("Failed to allocate memory in display_tree_info"); 1310 1311 for (i = (int)num_children - 1; i >= 0; i--) { 1312 struct wininfo *cw = &children[i]; 1313 1314 cw->window = child_list[i]; 1315 cw->net_wm_name_cookie = get_net_wm_name (dpy, child_list[i]); 1316 cw->wm_name_cookie = xcb_get_wm_name (dpy, child_list[i]); 1317 cw->wm_class_cookie = xcb_get_wm_class (dpy, child_list[i]); 1318 cw->geometry_cookie = xcb_get_geometry (dpy, child_list[i]); 1319 cw->trans_coords_cookie = xcb_translate_coordinates 1320 (dpy, child_list[i], tree->root, 0, 0); 1321 if (recurse) 1322 cw->tree_cookie = xcb_query_tree (dpy, child_list[i]); 1323 } 1324 xcb_flush (dpy); 1325 1326 for (i = (int)num_children - 1; i >= 0; i--) { 1327 struct wininfo *cw = &children[i]; 1328 Bool got_wm_class = False; 1329 char *instance_name = NULL, *class_name = NULL; 1330 int instance_name_len, class_name_len; 1331#ifdef USE_XCB_ICCCM 1332 xcb_get_wm_class_reply_t classhint; 1333#else 1334 xcb_get_property_reply_t *classprop; 1335#endif 1336 xcb_get_geometry_reply_t *geometry; 1337 1338 printf (" "); 1339 for (j = 0; j < level; j++) printf (" "); 1340 Display_Window_Id (cw, False); 1341 printf (": ("); 1342 1343#ifdef USE_XCB_ICCCM 1344 if (xcb_get_wm_class_reply (dpy, cw->wm_class_cookie, 1345 &classhint, NULL)) { 1346 got_wm_class = True; 1347 instance_name = classhint.instance_name; 1348 class_name = classhint.class_name; 1349 instance_name_len = strlen(instance_name); 1350 class_name_len = strlen(class_name); 1351 } 1352#else 1353 classprop = xcb_get_property_reply 1354 (dpy, cw->wm_class_cookie, NULL); 1355 if (classprop) { 1356 if (classprop->type == XCB_ATOM_STRING && 1357 classprop->format == 8) { 1358 int proplen = xcb_get_property_value_length (classprop); 1359 1360 instance_name = xcb_get_property_value (classprop); 1361 instance_name_len = strnlen (instance_name, proplen); 1362 if (instance_name_len < proplen) { 1363 class_name = instance_name + instance_name_len + 1; 1364 class_name_len = strnlen 1365 (class_name, proplen - (instance_name_len + 1)); 1366 } else 1367 class_name_len = 0; 1368 got_wm_class = True; 1369 } 1370 else 1371 free (classprop); 1372 } 1373#endif 1374 1375 if (got_wm_class) { 1376 if (instance_name) 1377 printf ("\"%.*s\" ", instance_name_len, instance_name); 1378 else 1379 printf ("(none) "); 1380 1381 if (class_name) 1382 printf ("\"%.*s\") ", class_name_len, class_name); 1383 else 1384 printf ("(none)) "); 1385 1386#ifdef USE_XCB_ICCCM 1387 xcb_get_wm_class_reply_wipe (&classhint); 1388#else 1389 free (classprop); 1390#endif 1391 } else 1392 printf (") "); 1393 1394 geometry = xcb_get_geometry_reply(dpy, cw->geometry_cookie, &err); 1395 if (geometry) { 1396 xcb_translate_coordinates_reply_t *trans_coords; 1397 1398 printf (" %ux%u+%d+%d", geometry->width, geometry->height, 1399 geometry->x, geometry->y); 1400 1401 trans_coords = xcb_translate_coordinates_reply 1402 (dpy, cw->trans_coords_cookie, &err); 1403 1404 if (trans_coords) { 1405 int16_t abs_x = (int16_t) trans_coords->dst_x; 1406 int16_t abs_y = (int16_t) trans_coords->dst_y; 1407 int border = geometry->border_width; 1408 1409 printf (" +%d+%d", abs_x - border, abs_y - border); 1410 free (trans_coords); 1411 } else if (err) { 1412 Print_X_Error (dpy, err); 1413 } 1414 1415 free (geometry); 1416 } else if (err) { 1417 Print_X_Error (dpy, err); 1418 } 1419 printf ("\n"); 1420 1421 if (recurse) 1422 display_tree_info_1 (cw, 1, level+1); 1423 1424 wininfo_wipe (cw); 1425 } 1426 free (children); 1427 } 1428 1429 free (tree); /* includes storage for child_list[] */ 1430} 1431 1432 1433/* 1434 * Display a set of size hints 1435 */ 1436static void 1437Display_Hints (xcb_size_hints_t *hints) 1438{ 1439 long flags; 1440 1441 flags = hints->flags; 1442 1443 if (flags & XCB_SIZE_HINT_US_POSITION) 1444 printf (" User supplied location: %s, %s\n", 1445 xscale (hints->x), yscale (hints->y)); 1446 1447 if (flags & XCB_SIZE_HINT_P_POSITION) 1448 printf (" Program supplied location: %s, %s\n", 1449 xscale (hints->x), yscale (hints->y)); 1450 1451 if (flags & XCB_SIZE_HINT_US_SIZE) { 1452 printf (" User supplied size: %s by %s\n", 1453 xscale (hints->width), yscale (hints->height)); 1454 } 1455 1456 if (flags & XCB_SIZE_HINT_P_SIZE) 1457 printf (" Program supplied size: %s by %s\n", 1458 xscale (hints->width), yscale (hints->height)); 1459 1460 if (flags & XCB_SIZE_HINT_P_MIN_SIZE) 1461 printf (" Program supplied minimum size: %s by %s\n", 1462 xscale (hints->min_width), yscale (hints->min_height)); 1463 1464 if (flags & XCB_SIZE_HINT_P_MAX_SIZE) 1465 printf (" Program supplied maximum size: %s by %s\n", 1466 xscale (hints->max_width), yscale (hints->max_height)); 1467 1468 if (flags & XCB_SIZE_HINT_BASE_SIZE) { 1469 printf (" Program supplied base size: %s by %s\n", 1470 xscale (hints->base_width), yscale (hints->base_height)); 1471 } 1472 1473 if (flags & XCB_SIZE_HINT_P_RESIZE_INC) { 1474 printf (" Program supplied x resize increment: %s\n", 1475 xscale (hints->width_inc)); 1476 printf (" Program supplied y resize increment: %s\n", 1477 yscale (hints->height_inc)); 1478 if (hints->width_inc != 0 && hints->height_inc != 0) { 1479 if (flags & XCB_SIZE_HINT_US_SIZE) 1480 printf (" User supplied size in resize increments: %s by %s\n", 1481 (xscale (hints->width / hints->width_inc)), 1482 (yscale (hints->height / hints->height_inc))); 1483 if (flags & XCB_SIZE_HINT_P_SIZE) 1484 printf (" Program supplied size in resize increments: %s by %s\n", 1485 (xscale (hints->width / hints->width_inc)), 1486 (yscale (hints->height / hints->height_inc))); 1487 if (flags & XCB_SIZE_HINT_P_MIN_SIZE) 1488 printf (" Program supplied minimum size in resize increments: %s by %s\n", 1489 xscale (hints->min_width / hints->width_inc), yscale (hints->min_height / hints->height_inc)); 1490 if (flags & XCB_SIZE_HINT_BASE_SIZE) 1491 printf (" Program supplied base size in resize increments: %s by %s\n", 1492 (xscale (hints->base_width / hints->width_inc)), 1493 (yscale (hints->base_height / hints->height_inc))); 1494 } 1495 } 1496 1497 if (flags & XCB_SIZE_HINT_P_ASPECT) { 1498 printf (" Program supplied min aspect ratio: %s/%s\n", 1499 xscale (hints->min_aspect_num), yscale (hints->min_aspect_den)); 1500 printf (" Program supplied max aspect ratio: %s/%s\n", 1501 xscale (hints->max_aspect_num), yscale (hints->max_aspect_den)); 1502 } 1503 1504 if (flags & XCB_SIZE_HINT_P_WIN_GRAVITY) { 1505 printf (" Program supplied window gravity: %s\n", 1506 Lookup (hints->win_gravity, _gravities)); 1507 } 1508} 1509 1510 1511/* 1512 * Display Size Hints info 1513 */ 1514static void 1515Display_Size_Hints (struct wininfo *w) 1516{ 1517 xcb_size_hints_t hints; 1518 1519 printf ("\n"); 1520 if (!fetch_normal_hints (w, &hints)) 1521 printf (" No normal window size hints defined\n"); 1522 else { 1523 printf (" Normal window size hints:\n"); 1524 Display_Hints (&hints); 1525 } 1526 1527 if (!xcb_get_wm_size_hints_reply (dpy, w->zoom_cookie, &hints, NULL)) 1528 printf (" No zoom window size hints defined\n"); 1529 else { 1530 printf (" Zoom window size hints:\n"); 1531 Display_Hints (&hints); 1532 } 1533} 1534 1535 1536static void 1537Display_Window_Shape (xcb_window_t window) 1538{ 1539 const xcb_query_extension_reply_t *shape_query; 1540 xcb_shape_query_extents_cookie_t extents_cookie; 1541 xcb_shape_query_extents_reply_t *extents; 1542 1543 shape_query = xcb_get_extension_data (dpy, &xcb_shape_id); 1544 if (!shape_query->present) 1545 return; 1546 1547 printf ("\n"); 1548 1549 extents_cookie = xcb_shape_query_extents (dpy, window); 1550 extents = xcb_shape_query_extents_reply (dpy, extents_cookie, &err); 1551 1552 if (!extents) { 1553 if (err) 1554 Print_X_Error (dpy, err); 1555 else 1556 { 1557 printf (" No window shape defined\n"); 1558 printf (" No border shape defined\n"); 1559 } 1560 return; 1561 } 1562 1563 if (!extents->bounding_shaped) 1564 printf (" No window shape defined\n"); 1565 else { 1566 printf (" Window shape extents: %sx%s", 1567 xscale (extents->bounding_shape_extents_width), 1568 yscale (extents->bounding_shape_extents_height)); 1569 printf ("+%s+%s\n", 1570 xscale (extents->bounding_shape_extents_x), 1571 yscale (extents->bounding_shape_extents_y)); 1572 } 1573 if (!extents->clip_shaped) 1574 printf (" No border shape defined\n"); 1575 else { 1576 printf (" Border shape extents: %sx%s", 1577 xscale (extents->clip_shape_extents_width), 1578 yscale (extents->clip_shape_extents_height)); 1579 printf ("+%s+%s\n", 1580 xscale (extents->clip_shape_extents_x), 1581 yscale (extents->clip_shape_extents_y)); 1582 } 1583 1584 free (extents); 1585} 1586 1587/* 1588 * Display Window Manager Info 1589 * 1590 * Requires wininfo members initialized: 1591 * window, hints_cookie 1592 */ 1593static const binding _state_hints[] = { 1594 { XCB_WM_STATE_WITHDRAWN, "Withdrawn State" }, 1595 { XCB_WM_STATE_NORMAL, "Normal State" }, 1596 { XCB_WM_STATE_ICONIC, "Iconic State" }, 1597/* xwininfo previously also reported the ZoomState & InactiveState, 1598 but ICCCM declared those obsolete long ago */ 1599 { 0, NULL } }; 1600 1601#ifndef USE_XCB_ICCCM 1602static Bool 1603wm_hints_reply (xcb_connection_t *dpy, xcb_get_property_cookie_t cookie, 1604 wm_hints_t *hints_return, xcb_generic_error_t **err) 1605{ 1606 xcb_get_property_reply_t *prop = xcb_get_property_reply (dpy, cookie, err); 1607 int length; 1608 1609 if (!prop || (prop->type != XCB_ATOM_WM_HINTS) || (prop->format != 32)) { 1610 free (prop); 1611 return False; 1612 } 1613 1614 memset (hints_return, 0, sizeof(wm_hints_t)); 1615 1616 length = xcb_get_property_value_length(prop); 1617 if (length > sizeof(wm_hints_t)) 1618 length = sizeof(wm_hints_t); 1619 memcpy (hints_return, xcb_get_property_value (prop), length); 1620 1621 free (prop); 1622 return True; 1623} 1624 1625#define xcb_get_wm_hints_reply wm_hints_reply 1626#endif 1627 1628static void 1629Display_WM_Info (struct wininfo *w) 1630{ 1631 xcb_wm_hints_t wmhints; 1632 long flags; 1633 xcb_get_property_reply_t *prop; 1634 int i; 1635 1636 printf ("\n"); 1637 if (!xcb_get_wm_hints_reply(dpy, w->hints_cookie, &wmhints, &err)) 1638 { 1639 printf (" No window manager hints defined\n"); 1640 if (err) 1641 Print_X_Error (dpy, err); 1642 flags = 0; 1643 } else 1644 flags = wmhints.flags; 1645 1646 printf (" Window manager hints:\n"); 1647 1648 if (flags & XCB_WM_HINT_INPUT) 1649 printf (" Client accepts input or input focus: %s\n", 1650 Lookup (wmhints.input, _bool)); 1651 1652 if (flags & XCB_WM_HINT_ICON_WINDOW) { 1653 struct wininfo iw; 1654 iw.window = wmhints.icon_window; 1655 iw.net_wm_name_cookie = get_net_wm_name (dpy, iw.window); 1656 iw.wm_name_cookie = xcb_get_wm_name (dpy, iw.window); 1657 1658 printf (" Icon window id: "); 1659 Display_Window_Id (&iw, True); 1660 } 1661 1662 if (flags & XCB_WM_HINT_ICON_POSITION) 1663 printf (" Initial icon position: %s, %s\n", 1664 xscale (wmhints.icon_x), yscale (wmhints.icon_y)); 1665 1666 if (flags & XCB_WM_HINT_STATE) 1667 printf (" Initial state is %s\n", 1668 Lookup (wmhints.initial_state, _state_hints)); 1669 1670 if (atom_net_wm_desktop) { 1671 prop = xcb_get_property_reply (dpy, w->wm_desktop_cookie, NULL); 1672 if (prop && (prop->type != XCB_NONE)) { 1673 uint32_t *desktop = xcb_get_property_value (prop); 1674 if (*desktop == 0xFFFFFFFF) { 1675 printf (" Displayed on all desktops\n"); 1676 } else { 1677 printf (" Displayed on desktop %d\n", *desktop); 1678 } 1679 } 1680 free (prop); 1681 } 1682 1683 if (atom_net_wm_window_type) { 1684 prop = xcb_get_property_reply (dpy, w->wm_window_type_cookie, 1685 NULL); 1686 if (prop && (prop->type != XCB_NONE) && (prop->value_len > 0)) { 1687 xcb_atom_t *atoms = xcb_get_property_value (prop); 1688 int atom_count = prop->value_len; 1689 1690 if (atom_count > 0) { 1691 printf (" Window type:\n"); 1692 for (i = 0; i < atom_count; i++) { 1693 const char *atom_name = Get_Atom_Name (dpy, atoms[i]); 1694 1695 if (atom_name) { 1696 print_friendly_name (" %s\n", atom_name, 1697 "_NET_WM_WINDOW_TYPE_"); 1698 } else { 1699 printf (" (unresolvable ATOM 0x%x)\n", 1700 atoms[i]); 1701 } 1702 } 1703 } 1704 } 1705 free (prop); 1706 } 1707 1708 if (atom_net_wm_state) { 1709 prop = xcb_get_property_reply (dpy, w->wm_state_cookie, NULL); 1710 if (prop && (prop->type != XCB_NONE) && (prop->value_len > 0)) { 1711 xcb_atom_t *atoms = xcb_get_property_value (prop); 1712 int atom_count = prop->value_len; 1713 1714 if (atom_count > 0) { 1715 printf (" Window state:\n"); 1716 for (i = 0; i < atom_count; i++) { 1717 const char *atom_name = Get_Atom_Name (dpy, atoms[i]); 1718 1719 if (atom_name) { 1720 print_friendly_name (" %s\n", atom_name, 1721 "_NET_WM_STATE_"); 1722 } else { 1723 printf (" (unresolvable ATOM 0x%x)\n", 1724 atoms[i]); 1725 } 1726 } 1727 } 1728 } 1729 free (prop); 1730 } 1731 1732 if (atom_net_wm_pid) { 1733 printf (" Process id: "); 1734 prop = xcb_get_property_reply (dpy, w->wm_pid_cookie, NULL); 1735 if (prop && (prop->type == XCB_ATOM_CARDINAL)) { 1736 uint32_t *pid = xcb_get_property_value (prop); 1737 printf ("%d", *pid); 1738 } else { 1739 printf ("(unknown)"); 1740 } 1741 free (prop); 1742 1743 prop = xcb_get_property_reply (dpy, w->wm_client_machine_cookie, NULL); 1744 if (prop && (prop->type == XCB_ATOM_STRING)) { 1745 const char *hostname = xcb_get_property_value (prop); 1746 int hostname_len = xcb_get_property_value_length (prop); 1747 printf (" on host %.*s", hostname_len, hostname); 1748 } 1749 printf ("\n"); 1750 free (prop); 1751 } 1752 1753 if (atom_net_frame_extents) { 1754 prop = xcb_get_property_reply (dpy, w->frame_extents_cookie, NULL); 1755 if (prop && (prop->type == XCB_ATOM_CARDINAL) 1756 && (prop->value_len == 4)) { 1757 uint32_t *extents = xcb_get_property_value (prop); 1758 1759 printf (" Frame extents: %d, %d, %d, %d\n", 1760 extents[0], extents[1], extents[2], extents[3]); 1761 } 1762 free (prop); 1763 } 1764} 1765 1766/* Frees all members of a wininfo struct, but not the struct itself */ 1767static void 1768wininfo_wipe (struct wininfo *w) 1769{ 1770 free (w->geometry); 1771 free (w->win_attributes); 1772 free (w->normal_hints); 1773} 1774 1775/* Gets UTF-8 encoded EMWH property _NET_WM_NAME for a window */ 1776static xcb_get_property_cookie_t 1777get_net_wm_name (xcb_connection_t *dpy, xcb_window_t win) 1778{ 1779 if (!atom_net_wm_name) 1780 atom_net_wm_name = Get_Atom (dpy, "_NET_WM_NAME"); 1781 1782 if (!atom_utf8_string) 1783 atom_utf8_string = Get_Atom (dpy, "UTF8_STRING"); 1784 1785 if (atom_net_wm_name && atom_utf8_string) 1786 return xcb_get_property (dpy, False, win, atom_net_wm_name, 1787 atom_utf8_string, 0, BUFSIZ); 1788 else { 1789 xcb_get_property_cookie_t dummy = { 0 }; 1790 return dummy; 1791 } 1792} 1793 1794/* [Copied from code added by Yang Zhao to xprop/xprop.c] 1795 * 1796 * Validate a string as UTF-8 encoded according to RFC 3629 1797 * 1798 * Simply, a unicode code point (up to 21-bits long) is encoded as follows: 1799 * 1800 * Char. number range | UTF-8 octet sequence 1801 * (hexadecimal) | (binary) 1802 * --------------------+--------------------------------------------- 1803 * 0000 0000-0000 007F | 0xxxxxxx 1804 * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 1805 * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 1806 * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 1807 * 1808 * Validation is done left-to-right, and an error condition, if any, refers to 1809 * only the left-most problem in the string. 1810 * 1811 * Return values: 1812 * UTF8_VALID: Valid UTF-8 encoded string 1813 * UTF8_OVERLONG: Using more bytes than needed for a code point 1814 * UTF8_SHORT_TAIL: Not enough bytes in a multi-byte sequence 1815 * UTF8_LONG_TAIL: Too many bytes in a multi-byte sequence 1816 * UTF8_FORBIDDEN_VALUE: Forbidden prefix or code point outside 0x10FFFF 1817 */ 1818#define UTF8_VALID 0 1819#define UTF8_FORBIDDEN_VALUE 1 1820#define UTF8_OVERLONG 2 1821#define UTF8_SHORT_TAIL 3 1822#define UTF8_LONG_TAIL 4 1823static int 1824is_valid_utf8 (const char *string, int len) 1825{ 1826 unsigned long codepoint; 1827 int rem, i; 1828 unsigned char c; 1829 1830 rem = 0; 1831 for (i = 0; i < len; i++) { 1832 c = (unsigned char) string[i]; 1833 1834 /* Order of type check: 1835 * - Single byte code point 1836 * - Non-starting byte of multi-byte sequence 1837 * - Start of 2-byte sequence 1838 * - Start of 3-byte sequence 1839 * - Start of 4-byte sequence 1840 */ 1841 if (!(c & 0x80)) { 1842 if (rem > 0) return UTF8_SHORT_TAIL; 1843 rem = 0; 1844 codepoint = c; 1845 } else if ((c & 0xC0) == 0x80) { 1846 if (rem == 0) return UTF8_LONG_TAIL; 1847 rem--; 1848 codepoint |= (c & 0x3F) << (rem * 6); 1849 if (codepoint == 0) return UTF8_OVERLONG; 1850 } else if ((c & 0xE0) == 0xC0) { 1851 if (rem > 0) return UTF8_SHORT_TAIL; 1852 rem = 1; 1853 codepoint = (c & 0x1F) << 6; 1854 if (codepoint == 0) return UTF8_OVERLONG; 1855 } else if ((c & 0xF0) == 0xE0) { 1856 if (rem > 0) return UTF8_SHORT_TAIL; 1857 rem = 2; 1858 codepoint = (c & 0x0F) << 12; 1859 } else if ((c & 0xF8) == 0xF0) { 1860 if (rem > 0) return UTF8_SHORT_TAIL; 1861 rem = 3; 1862 codepoint = (c & 0x07) << 18; 1863 if (codepoint > 0x10FFFF) return UTF8_FORBIDDEN_VALUE; 1864 } else 1865 return UTF8_FORBIDDEN_VALUE; 1866 } 1867 1868 return UTF8_VALID; 1869} 1870 1871/* 1872 * Converts a UTF-8 encoded string to the current locale encoding, 1873 * if possible, and prints it, with prefix before and suffix after. 1874 * Length of the string is specified in bytes, or -1 for going until '\0' 1875 */ 1876static void 1877print_utf8 (const char *prefix, char *u8str, size_t length, const char *suffix) 1878{ 1879 size_t inlen = length; 1880 1881 if (inlen < 0) { 1882 inlen = strlen (u8str); 1883 } 1884 1885 if (is_valid_utf8 (u8str, inlen) != UTF8_VALID) { 1886 printf (" (invalid UTF8_STRING)"); 1887 return; 1888 } 1889 1890 if (strcmp (user_encoding, "UTF-8") == 0) { 1891 /* Don't need to convert */ 1892 printf ("%s", prefix); 1893 fwrite (u8str, 1, inlen, stdout); 1894 printf ("%s", suffix); 1895 return; 1896 } 1897 1898#ifdef HAVE_ICONV 1899 if (!iconv_from_utf8) { 1900 iconv_from_utf8 = iconv_open (user_encoding, "UTF-8"); 1901 } 1902 1903 if (iconv_from_utf8 != (iconv_t) -1) { 1904 Bool done = True; 1905 char *inp = u8str; 1906 char convbuf[BUFSIZ]; 1907 int convres; 1908 1909 printf ("%s", prefix); 1910 do { 1911 char *outp = convbuf; 1912 size_t outlen = sizeof(convbuf); 1913 1914 convres = iconv (iconv_from_utf8, (const char **)&inp, &inlen, &outp, &outlen); 1915 1916 if ((convres == -1) && (errno == E2BIG)) { 1917 done = False; 1918 convres = 0; 1919 } 1920 1921 if (convres == 0) { 1922 fwrite (convbuf, 1, sizeof(convbuf) - outlen, stdout); 1923 } else { 1924 printf (" (failure in conversion from UTF8_STRING to %s)", 1925 user_encoding); 1926 } 1927 } while (!done); 1928 printf ("%s", suffix); 1929 } else { 1930 printf (" (can't load iconv conversion for UTF8_STRING to %s)", 1931 user_encoding); 1932 } 1933#else 1934 printf (" (can't convert UTF8_STRING to %s)", user_encoding); 1935#endif 1936} 1937 1938/* 1939 * Takes a string such as an atom name, strips the prefix, converts 1940 * underscores to spaces, lowercases all but the first letter of each word, 1941 * and prints it. 1942 */ 1943static void 1944print_friendly_name (const char *format, const char *string, 1945 const char *prefix) 1946{ 1947 const char *name_start = string; 1948 char *lowered_name, *n; 1949 int prefix_len = strlen (prefix); 1950 1951 if (strncmp (name_start, prefix, prefix_len) == 0) { 1952 name_start += prefix_len; 1953 } 1954 1955 lowered_name = strdup (name_start); 1956 if (lowered_name) { 1957 Bool first = True; 1958 1959 for (n = lowered_name ; *n != 0 ; n++) { 1960 if (*n == '_') { 1961 *n = ' '; 1962 first = True; 1963 } else if (first) { 1964 first = False; 1965 } else { 1966 *n = tolower(*n); 1967 } 1968 } 1969 name_start = lowered_name; 1970 } 1971 1972 printf (format, name_start); 1973 free (lowered_name); 1974} 1975