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