xwd.c revision 6728f30e
1/* 2 3Copyright 1987, 1998 The Open Group 4 5Permission to use, copy, modify, distribute, and sell this software and its 6documentation for any purpose is hereby granted without fee, provided that 7the above copyright notice appear in all copies and that both that 8copyright notice and this permission notice appear in supporting 9documentation. 10 11The above copyright notice and this permission notice shall be included in 12all copies or substantial portions of the Software. 13 14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 21Except as contained in this notice, the name of The Open Group shall not be 22used in advertising or otherwise to promote the sale, use or other dealings 23in this Software without prior written authorization from The Open Group. 24 25*/ 26 27/* 28 * xwd.c MIT Project Athena, X Window system window raster image dumper. 29 * 30 * This program will dump a raster image of the contents of a window into a 31 * file for output on graphics printers or for other uses. 32 * 33 * Author: Tony Della Fera, DEC 34 * 17-Jun-85 35 * 36 * Modification history: 37 * 38 * 11/14/86 Bill Wyatt, Smithsonian Astrophysical Observatory 39 * - Removed Z format option, changing it to an XY option. Monochrome 40 * windows will always dump in XY format. Color windows will dump 41 * in Z format by default, but can be dumped in XY format with the 42 * -xy option. 43 * 44 * 11/18/86 Bill Wyatt 45 * - VERSION 6 is same as version 5 for monchrome. For colors, the 46 * appropriate number of Color structs are dumped after the header, 47 * which has the number of colors (=0 for monochrome) in place of the 48 * V5 padding at the end. Up to 16-bit displays are supported. I 49 * don't yet know how 24- to 32-bit displays will be handled under 50 * the Version 11 protocol. 51 * 52 * 6/15/87 David Krikorian, MIT Project Athena 53 * - VERSION 7 runs under the X Version 11 servers, while the previous 54 * versions of xwd were are for X Version 10. This version is based 55 * on xwd version 6, and should eventually have the same color 56 * abilities. (Xwd V7 has yet to be tested on a color machine, so 57 * all color-related code is commented out until color support 58 * becomes practical.) 59 */ 60 61/*% 62 *% This is the format for commenting out color-related code until 63 *% color can be supported. 64%*/ 65 66#ifdef HAVE_CONFIG_H 67#include "config.h" 68#endif 69 70#include <stdio.h> 71#include <errno.h> 72#include <X11/Xos.h> 73#include <stdlib.h> 74 75#include <X11/Xlib.h> 76#include <X11/Xutil.h> 77 78#include "X11/XWDFile.h" 79 80#define FEEP_VOLUME 0 81 82/* Include routines to do parsing */ 83#include "dsimple.h" 84#include "list.h" 85#include "wsutils.h" 86#include "multiVis.h" 87 88#ifdef XKB 89#include <X11/extensions/XKBbells.h> 90#endif 91 92/* Setable Options */ 93 94static int format = ZPixmap; 95static Bool nobdrs = False; 96static Bool on_root = False; 97static Bool standard_out = True; 98static Bool debug = False; 99static Bool silent = False; 100static Bool use_installed = False; 101static long add_pixel_value = 0; 102 103 104extern int main(int, char **); 105extern void Window_Dump(Window, FILE *); 106extern int Image_Size(XImage *); 107extern int Get_XColors(XWindowAttributes *, XColor **); 108extern void _swapshort(register char *, register unsigned); 109extern void _swaplong(register char *, register unsigned); 110static long parse_long(char *); 111static int Get24bitDirectColors(XColor **); 112static int ReadColors(Visual *, Colormap, XColor **); 113 114static long 115parse_long(char *s) 116{ 117 long retval = 0L; 118 int thesign = 1; 119 120 if (s && s[0]) { 121 switch (s[0]) { 122 case '-': 123 (void) sscanf(s + 1, "%lu", &retval); 124 thesign = -1; 125 break; 126 case '0': 127 (void) sscanf(s + 1, "%lo", &retval); 128 break; 129 case 'x': 130 case 'X': 131 (void) sscanf(s + 1, "%lx", &retval); 132 break; 133 default: 134 (void) sscanf(s, "%lu", &retval); 135 break; 136 } 137 } 138 return (thesign * retval); 139} 140 141int 142main(int argc, char **argv) 143{ 144 register int i; 145 Window target_win; 146 FILE *out_file = stdout; 147 Bool frame_only = False; 148 149 INIT_NAME; 150 151 /* Handle args that don't require opening a display */ 152 for (int n = 1; n < argc; n++) { 153 const char *argn = argv[n]; 154 /* accept single or double dash for -help & -version */ 155 if (argn[0] == '-' && argn[1] == '-') { 156 argn++; 157 } 158 if (strcmp (argn, "-help") == 0) { 159 usage(NULL, EXIT_SUCCESS); 160 exit(0); 161 } 162 if (strcmp (argn, "-version") == 0) { 163 puts(PACKAGE_STRING); 164 exit(EXIT_SUCCESS); 165 } 166 } 167 168 Setup_Display_And_Screen(&argc, argv); 169 170 /* Get window select on command line, if any */ 171 target_win = Select_Window_Args(&argc, argv); 172 173 for (i = 1; i < argc; i++) { 174 if (!strcmp(argv[i], "-nobdrs")) { 175 nobdrs = True; 176 continue; 177 } 178 if (!strcmp(argv[i], "-debug")) { 179 debug = True; 180 continue; 181 } 182 if (!strcmp(argv[i], "-out")) { 183 if (++i >= argc) 184 usage("-out requires an argument", EXIT_FAILURE); 185 if (!(out_file = fopen(argv[i], "wb"))) 186 Fatal_Error("Can't open output file as specified."); 187 standard_out = False; 188 continue; 189 } 190 if (!strcmp(argv[i], "-xy")) { 191 format = XYPixmap; 192 continue; 193 } 194 if (!strcmp(argv[i], "-screen")) { 195 on_root = True; 196 continue; 197 } 198 if (!strcmp(argv[i], "-icmap")) { 199 use_installed = True; 200 continue; 201 } 202 if (!strcmp(argv[i], "-add")) { 203 if (++i >= argc) 204 usage("-add requires an argument", EXIT_FAILURE); 205 add_pixel_value = parse_long(argv[i]); 206 continue; 207 } 208 if (!strcmp(argv[i], "-frame")) { 209 frame_only = True; 210 continue; 211 } 212 if (!strcmp(argv[i], "-silent")) { 213 silent = True; 214 continue; 215 } 216 fprintf(stderr, "%s: unrecognized argument '%s'\n", 217 program_name, argv[i]); 218 usage(NULL, EXIT_FAILURE); 219 } 220#ifdef WIN32 221 if (standard_out) 222 _setmode(fileno(out_file), _O_BINARY); 223#endif 224 225 /* 226 * Let the user select the target window. 227 */ 228 if (target_win == None) 229 target_win = Select_Window(dpy, !frame_only); 230 231 /* 232 * Dump it! 233 */ 234 Window_Dump(target_win, out_file); 235 236 XCloseDisplay(dpy); 237 if (fclose(out_file)) { 238 perror("xwd"); 239 exit(1); 240 } 241 exit(0); 242} 243 244static int 245Get24bitDirectColors(XColor **colors) 246{ 247 int i, ncolors = 256; 248 XColor *tcol; 249 250 *colors = tcol = malloc(sizeof(XColor) * ncolors); 251 252 for (i = 0; i < ncolors; i++) { 253 tcol[i].pixel = i << 16 | i << 8 | i; 254 tcol[i].red = tcol[i].green = tcol[i].blue = i << 8 | i; 255 tcol[i].flags = 0; 256 } 257 258 return ncolors; 259} 260 261/* 262 * Window_Dump: dump a window to a file which must already be open for 263 * writing. 264 */ 265 266void 267Window_Dump(Window window, FILE *out) 268{ 269 unsigned long swaptest = 1; 270 XColor *colors; 271 unsigned buffer_size; 272 size_t win_name_size; 273 CARD32 header_size; 274 int ncolors, i; 275 char *win_name; 276 char default_win_name[] = "xwdump"; 277 Bool got_win_name; 278 XWindowAttributes win_info; 279 XImage *image; 280 int absx, absy, x, y; 281 unsigned width, height; 282 int dwidth, dheight; 283 Window dummywin; 284 XWDFileHeader header; 285 XWDColor xwdcolor; 286 287 int transparentOverlays, multiVis; 288 int numVisuals; 289 XVisualInfo *pVisuals; 290 int numOverlayVisuals; 291 OverlayInfo *pOverlayVisuals; 292 int numImageVisuals; 293 XVisualInfo **pImageVisuals; 294 list_ptr vis_regions; /* list of regions to read from */ 295 list_ptr vis_image_regions; 296 Visual vis_h, *vis; 297 int allImage = 0; 298 299 /* 300 * Inform the user not to alter the screen. 301 */ 302 if (!silent) { 303#ifdef XKB 304 XkbStdBell(dpy, None, 50, XkbBI_Wait); 305#else 306 XBell(dpy, FEEP_VOLUME); 307#endif 308 XFlush(dpy); 309 } 310 311 /* 312 * Get the parameters of the window being dumped. 313 */ 314 if (debug) 315 outl("xwd: Getting target window information.\n"); 316 if (!XGetWindowAttributes(dpy, window, &win_info)) 317 Fatal_Error("Can't get target window attributes."); 318 319 /* handle any frame window */ 320 if (!XTranslateCoordinates(dpy, window, RootWindow(dpy, screen), 0, 0, 321 &absx, &absy, &dummywin)) { 322 fprintf(stderr, 323 "%s: unable to translate window coordinates (%d,%d)\n", 324 program_name, absx, absy); 325 exit(1); 326 } 327 win_info.x = absx; 328 win_info.y = absy; 329 width = win_info.width; 330 height = win_info.height; 331 332 if (!nobdrs) { 333 absx -= win_info.border_width; 334 absy -= win_info.border_width; 335 width += (2 * win_info.border_width); 336 height += (2 * win_info.border_width); 337 } 338 dwidth = DisplayWidth(dpy, screen); 339 dheight = DisplayHeight(dpy, screen); 340 341 /* clip to window */ 342 if (absx < 0) { 343 width += absx; 344 absx = 0; 345 } 346 if (absy < 0) { 347 height += absy; 348 absy = 0; 349 } 350 if (absx + width > dwidth) 351 width = dwidth - absx; 352 if (absy + height > dheight) 353 height = dheight - absy; 354 355 if (!XFetchName(dpy, window, &win_name) || !win_name || !win_name[0]) { 356 win_name = default_win_name; 357 got_win_name = False; 358 } 359 else { 360 got_win_name = True; 361 } 362 363 /* sizeof(char) is included for the null string terminator. */ 364 win_name_size = strlen(win_name) + sizeof(char); 365 366 /* 367 * Snarf the pixmap with XGetImage. 368 */ 369 370 x = absx - win_info.x; 371 y = absy - win_info.y; 372 373 multiVis = GetMultiVisualRegions(dpy, RootWindow(dpy, screen), 374 absx, absy, 375 width, height, &transparentOverlays, 376 &numVisuals, &pVisuals, 377 &numOverlayVisuals, &pOverlayVisuals, 378 &numImageVisuals, &pImageVisuals, 379 &vis_regions, &vis_image_regions, 380 &allImage); 381 if (on_root || multiVis) { 382 if (!multiVis) 383 image = XGetImage(dpy, RootWindow(dpy, screen), absx, absy, 384 width, height, AllPlanes, format); 385 else 386 image = ReadAreaToImage(dpy, RootWindow(dpy, screen), absx, absy, 387 width, height, 388 numVisuals, pVisuals, 389 numOverlayVisuals, pOverlayVisuals, 390 numImageVisuals, pImageVisuals, 391 vis_regions, vis_image_regions, 392 format, allImage); 393 } 394 else 395 image = XGetImage(dpy, window, x, y, width, height, AllPlanes, format); 396 if (!image) { 397 fprintf(stderr, "%s: unable to get image at %dx%d+%d+%d\n", 398 program_name, width, height, x, y); 399 exit(1); 400 } 401 402 if (add_pixel_value != 0) 403 XAddPixel(image, add_pixel_value); 404 405 /* 406 * Determine the pixmap size. 407 */ 408 buffer_size = Image_Size(image); 409 410 if (debug) 411 outl("xwd: Getting Colors.\n"); 412 413 if (!multiVis) { 414 ncolors = Get_XColors(&win_info, &colors); 415 vis = win_info.visual; 416 } 417 else { 418 ncolors = Get24bitDirectColors(&colors); 419 initFakeVisual(&vis_h); 420 vis = &vis_h; 421 } 422 /* 423 * Inform the user that the image has been retrieved. 424 */ 425 if (!silent) { 426#ifdef XKB 427 XkbStdBell(dpy, window, FEEP_VOLUME, XkbBI_Proceed); 428 XkbStdBell(dpy, window, FEEP_VOLUME, XkbBI_RepeatingLastBell); 429#else 430 XBell(dpy, FEEP_VOLUME); 431 XBell(dpy, FEEP_VOLUME); 432#endif 433 XFlush(dpy); 434 } 435 436 /* 437 * Calculate header size. 438 */ 439 if (debug) 440 outl("xwd: Calculating header size.\n"); 441 header_size = SIZEOF(XWDheader) + (CARD32) win_name_size; 442 443 /* 444 * Write out header information. 445 */ 446 if (debug) 447 outl("xwd: Constructing and dumping file header.\n"); 448 memset(&header, 0, SIZEOF(XWDheader)); 449 header.header_size = (CARD32) header_size; 450 header.file_version = (CARD32) XWD_FILE_VERSION; 451 header.pixmap_format = (CARD32) format; 452 header.pixmap_depth = (CARD32) image->depth; 453 header.pixmap_width = (CARD32) image->width; 454 header.pixmap_height = (CARD32) image->height; 455 header.xoffset = (CARD32) image->xoffset; 456 header.byte_order = (CARD32) image->byte_order; 457 header.bitmap_unit = (CARD32) image->bitmap_unit; 458 header.bitmap_bit_order = (CARD32) image->bitmap_bit_order; 459 header.bitmap_pad = (CARD32) image->bitmap_pad; 460 header.bits_per_pixel = (CARD32) image->bits_per_pixel; 461 header.bytes_per_line = (CARD32) image->bytes_per_line; 462 /**** 463 header.visual_class = (CARD32) win_info.visual->class; 464 header.red_mask = (CARD32) win_info.visual->red_mask; 465 header.green_mask = (CARD32) win_info.visual->green_mask; 466 header.blue_mask = (CARD32) win_info.visual->blue_mask; 467 header.bits_per_rgb = (CARD32) win_info.visual->bits_per_rgb; 468 header.colormap_entries = (CARD32) win_info.visual->map_entries; 469 *****/ 470 header.visual_class = (CARD32) vis->class; 471 header.red_mask = (CARD32) vis->red_mask; 472 header.green_mask = (CARD32) vis->green_mask; 473 header.blue_mask = (CARD32) vis->blue_mask; 474 header.bits_per_rgb = (CARD32) vis->bits_per_rgb; 475 header.colormap_entries = (CARD32) vis->map_entries; 476 477 header.ncolors = ncolors; 478 header.window_width = (CARD32) win_info.width; 479 header.window_height = (CARD32) win_info.height; 480 header.window_x = absx; 481 header.window_y = absy; 482 header.window_bdrwidth = (CARD32) win_info.border_width; 483 484 if (*(char *) &swaptest) { 485 _swaplong((char *) &header, sizeof(header)); 486 for (i = 0; i < ncolors; i++) { 487 _swaplong((char *) &colors[i].pixel, sizeof(CARD32)); 488 _swapshort((char *) &colors[i].red, 3 * sizeof(short)); 489 } 490 } 491 492 if (fwrite((char *) &header, SIZEOF(XWDheader), 1, out) != 1 || 493 fwrite(win_name, win_name_size, 1, out) != 1) { 494 perror("xwd"); 495 exit(1); 496 } 497 498 /* 499 * Write out the color maps, if any 500 */ 501 502 if (debug) 503 outl("xwd: Dumping %d colors.\n", ncolors); 504 for (i = 0; i < ncolors; i++) { 505 xwdcolor.pixel = colors[i].pixel; 506 xwdcolor.red = colors[i].red; 507 xwdcolor.green = colors[i].green; 508 xwdcolor.blue = colors[i].blue; 509 xwdcolor.flags = colors[i].flags; 510 if (fwrite((char *) &xwdcolor, SIZEOF(XWDColor), 1, out) != 1) { 511 perror("xwd"); 512 exit(1); 513 } 514 } 515 516 /* 517 * Write out the buffer. 518 */ 519 if (debug) 520 outl("xwd: Dumping pixmap. bufsize=%d\n", buffer_size); 521 522 /* 523 * This copying of the bit stream (data) to a file is to be replaced 524 * by an Xlib call which hasn't been written yet. It is not clear 525 * what other functions of xwd will be taken over by this (as yet) 526 * non-existent X function. 527 */ 528 if (fwrite(image->data, (int) buffer_size, 1, out) != 1) { 529 perror("xwd"); 530 exit(1); 531 } 532 533 /* 534 * free the color buffer. 535 */ 536 537 if (debug && ncolors > 0) 538 outl("xwd: Freeing colors.\n"); 539 if (ncolors > 0) 540 free(colors); 541 542 /* 543 * Free window name string. 544 */ 545 if (debug) 546 outl("xwd: Freeing window name string.\n"); 547 if (got_win_name) 548 XFree(win_name); 549 550 /* 551 * Free image 552 */ 553 XDestroyImage(image); 554} 555 556/* 557 * Report the syntax for calling xwd. 558 */ 559void 560usage(const char *errmsg, int exitval) 561{ 562 if (errmsg != NULL) 563 fprintf(stderr, "%s: %s\n", program_name, errmsg); 564 565 fprintf(stderr, 566 "Usage: %s [options] [-root| -id <wdid>| -name <wdname>] > mywddump\n", 567 program_name); 568 fprintf(stderr, 569 " %s [options] [-root| -id <wdid>| -name <wdname>] -out mywddump\n", 570 program_name); 571 fputs("Options:\n" " -help Print this message\n" 572 " -version Print the program version and exit\n" 573 " -debug Enable debug mode\n" 574 " -d, -display <host:dpy> Specify server to connect\n" 575 " -nobdrs Exclude window borders\n" 576 " -out <file> Specify an output file\n" 577 " -xy Select XY dumping format for color displays\n" 578 " -add <value> Add a signed value to every pixel\n" 579 " -frame Include window manager frame\n" 580 " -root Select the root window\n" 581 " -id <wdid> Select a window by its resource id\n" 582 " -name <wdname> Select a window by its WM_NAME property\n" 583 " -icmap Use the first colormap of the screen\n" 584 " -screen Send the request against the root window\n" 585 " -silent Don't ring any bells\n", stderr); 586 exit(exitval); 587} 588 589/* 590 * Determine the pixmap size. 591 */ 592 593int 594Image_Size(XImage *image) 595{ 596 if (image->format != ZPixmap) 597 return (image->bytes_per_line * image->height * image->depth); 598 599 return (image->bytes_per_line * image->height); 600} 601 602#define lowbit(x) ((x) & (~(x) + 1)) 603 604static int 605ReadColors(Visual *vis, Colormap cmap, XColor **colors) 606{ 607 int i, ncolors; 608 609 ncolors = vis->map_entries; 610 611 if (!(*colors = malloc(sizeof(XColor) * ncolors))) 612 Fatal_Error("Out of memory!"); 613 614 if (vis->class == DirectColor || vis->class == TrueColor) { 615 Pixel red, green, blue, red1, green1, blue1; 616 617 red = green = blue = 0; 618 red1 = lowbit(vis->red_mask); 619 green1 = lowbit(vis->green_mask); 620 blue1 = lowbit(vis->blue_mask); 621 for (i = 0; i < ncolors; i++) { 622 (*colors)[i].pixel = red | green | blue; 623 (*colors)[i].pad = 0; 624 red += red1; 625 if (red > vis->red_mask) 626 red = 0; 627 green += green1; 628 if (green > vis->green_mask) 629 green = 0; 630 blue += blue1; 631 if (blue > vis->blue_mask) 632 blue = 0; 633 } 634 } 635 else { 636 for (i = 0; i < ncolors; i++) { 637 (*colors)[i].pixel = i; 638 (*colors)[i].pad = 0; 639 } 640 } 641 642 XQueryColors(dpy, cmap, *colors, ncolors); 643 644 return (ncolors); 645} 646 647/* 648 * Get the XColors of all pixels in image - returns # of colors 649 */ 650int 651Get_XColors(XWindowAttributes *win_info, XColor **colors) 652{ 653 int i, ncolors; 654 Colormap cmap = win_info->colormap; 655 656 if (use_installed) 657 /* assume the visual will be OK ... */ 658 cmap = XListInstalledColormaps(dpy, win_info->root, &i)[0]; 659 if (!cmap) 660 return (0); 661 ncolors = ReadColors(win_info->visual, cmap, colors); 662 return ncolors; 663} 664 665void 666_swapshort(register char *bp, register unsigned n) 667{ 668 char *ep = bp + n; 669 670 while (bp < ep) { 671 char c = *bp; 672 *bp = *(bp + 1); 673 bp++; 674 *bp++ = c; 675 } 676} 677 678void 679_swaplong(register char *bp, register unsigned n) 680{ 681 char *ep = bp + n; 682 683 while (bp < ep) { 684 char c = bp[3]; 685 bp[3] = bp[0]; 686 bp[0] = c; 687 c = bp[2]; 688 bp[2] = bp[1]; 689 bp[1] = c; 690 bp += 4; 691 } 692} 693