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