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