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