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