xwd.c revision 74b97a6c
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    Setup_Display_And_Screen(&argc, argv);
152
153    /* Get window select on command line, if any */
154    target_win = Select_Window_Args(&argc, argv);
155
156    for (i = 1; i < argc; i++) {
157        if (!strcmp(argv[i], "-nobdrs")) {
158            nobdrs = True;
159            continue;
160        }
161        if (!strcmp(argv[i], "-debug")) {
162            debug = True;
163            continue;
164        }
165        if (!strcmp(argv[i], "-help"))
166            usage(NULL);
167        if (!strcmp(argv[i], "-out")) {
168            if (++i >= argc)
169                usage("-out requires an argument");
170            if (!(out_file = fopen(argv[i], "wb")))
171                Fatal_Error("Can't open output file as specified.");
172            standard_out = False;
173            continue;
174        }
175        if (!strcmp(argv[i], "-xy")) {
176            format = XYPixmap;
177            continue;
178        }
179        if (!strcmp(argv[i], "-screen")) {
180            on_root = True;
181            continue;
182        }
183        if (!strcmp(argv[i], "-icmap")) {
184            use_installed = True;
185            continue;
186        }
187        if (!strcmp(argv[i], "-add")) {
188            if (++i >= argc)
189                usage("-add requires an argument");
190            add_pixel_value = parse_long(argv[i]);
191            continue;
192        }
193        if (!strcmp(argv[i], "-frame")) {
194            frame_only = True;
195            continue;
196        }
197        if (!strcmp(argv[i], "-silent")) {
198            silent = True;
199            continue;
200        }
201        if (!strcmp(argv[i], "-version")) {
202            puts(PACKAGE_STRING);
203            exit(0);
204        }
205        fprintf(stderr, "%s: unrecognized argument '%s'\n",
206                program_name, argv[i]);
207        usage(NULL);
208    }
209#ifdef WIN32
210    if (standard_out)
211        _setmode(fileno(out_file), _O_BINARY);
212#endif
213
214    /*
215     * Let the user select the target window.
216     */
217    if (target_win == None)
218        target_win = Select_Window(dpy, !frame_only);
219
220    /*
221     * Dump it!
222     */
223    Window_Dump(target_win, out_file);
224
225    XCloseDisplay(dpy);
226    if (fclose(out_file)) {
227        perror("xwd");
228        exit(1);
229    }
230    exit(0);
231}
232
233static int
234Get24bitDirectColors(XColor **colors)
235{
236    int i, ncolors = 256;
237    XColor *tcol;
238
239    *colors = tcol = (XColor *) malloc(sizeof(XColor) * ncolors);
240
241    for (i = 0; i < ncolors; i++) {
242        tcol[i].pixel = i << 16 | i << 8 | i;
243        tcol[i].red = tcol[i].green = tcol[i].blue = i << 8 | i;
244        tcol[i].flags = 0;
245    }
246
247    return ncolors;
248}
249
250/*
251 * Window_Dump: dump a window to a file which must already be open for
252 *              writing.
253 */
254
255void
256Window_Dump(Window window, FILE *out)
257{
258    unsigned long swaptest = 1;
259    XColor *colors;
260    unsigned buffer_size;
261    size_t win_name_size;
262    CARD32 header_size;
263    int ncolors, i;
264    char *win_name;
265    char default_win_name[] = "xwdump";
266    Bool got_win_name;
267    XWindowAttributes win_info;
268    XImage *image;
269    int absx, absy, x, y;
270    unsigned width, height;
271    int dwidth, dheight;
272    Window dummywin;
273    XWDFileHeader header;
274    XWDColor xwdcolor;
275
276    int transparentOverlays, multiVis;
277    int numVisuals;
278    XVisualInfo *pVisuals;
279    int numOverlayVisuals;
280    OverlayInfo *pOverlayVisuals;
281    int numImageVisuals;
282    XVisualInfo **pImageVisuals;
283    list_ptr vis_regions;       /* list of regions to read from */
284    list_ptr vis_image_regions;
285    Visual vis_h, *vis;
286    int allImage = 0;
287
288    /*
289     * Inform the user not to alter the screen.
290     */
291    if (!silent) {
292#ifdef XKB
293        XkbStdBell(dpy, None, 50, XkbBI_Wait);
294#else
295        XBell(dpy, FEEP_VOLUME);
296#endif
297        XFlush(dpy);
298    }
299
300    /*
301     * Get the parameters of the window being dumped.
302     */
303    if (debug)
304        outl("xwd: Getting target window information.\n");
305    if (!XGetWindowAttributes(dpy, window, &win_info))
306        Fatal_Error("Can't get target window attributes.");
307
308    /* handle any frame window */
309    if (!XTranslateCoordinates(dpy, window, RootWindow(dpy, screen), 0, 0,
310                               &absx, &absy, &dummywin)) {
311        fprintf(stderr,
312                "%s:  unable to translate window coordinates (%d,%d)\n",
313                program_name, absx, absy);
314        exit(1);
315    }
316    win_info.x = absx;
317    win_info.y = absy;
318    width = win_info.width;
319    height = win_info.height;
320
321    if (!nobdrs) {
322        absx -= win_info.border_width;
323        absy -= win_info.border_width;
324        width += (2 * win_info.border_width);
325        height += (2 * win_info.border_width);
326    }
327    dwidth = DisplayWidth(dpy, screen);
328    dheight = DisplayHeight(dpy, screen);
329
330    /* clip to window */
331    if (absx < 0)
332        width += absx, absx = 0;
333    if (absy < 0)
334        height += absy, absy = 0;
335    if (absx + width > dwidth)
336        width = dwidth - absx;
337    if (absy + height > dheight)
338        height = dheight - absy;
339
340    if (!XFetchName(dpy, window, &win_name) || !win_name || !win_name[0]) {
341        win_name = default_win_name;
342        got_win_name = False;
343    }
344    else {
345        got_win_name = True;
346    }
347
348    /* sizeof(char) is included for the null string terminator. */
349    win_name_size = strlen(win_name) + sizeof(char);
350
351    /*
352     * Snarf the pixmap with XGetImage.
353     */
354
355    x = absx - win_info.x;
356    y = absy - win_info.y;
357
358    multiVis = GetMultiVisualRegions(dpy, RootWindow(dpy, screen),
359                                     absx, absy,
360                                     width, height, &transparentOverlays,
361                                     &numVisuals, &pVisuals,
362                                     &numOverlayVisuals, &pOverlayVisuals,
363                                     &numImageVisuals, &pImageVisuals,
364                                     &vis_regions, &vis_image_regions,
365                                     &allImage);
366    if (on_root || multiVis) {
367        if (!multiVis)
368            image = XGetImage(dpy, RootWindow(dpy, screen), absx, absy,
369                              width, height, AllPlanes, format);
370        else
371            image = ReadAreaToImage(dpy, RootWindow(dpy, screen), absx, absy,
372                                    width, height,
373                                    numVisuals, pVisuals,
374                                    numOverlayVisuals, pOverlayVisuals,
375                                    numImageVisuals, pImageVisuals,
376                                    vis_regions, vis_image_regions,
377                                    format, allImage);
378    }
379    else
380        image = XGetImage(dpy, window, x, y, width, height, AllPlanes, format);
381    if (!image) {
382        fprintf(stderr, "%s:  unable to get image at %dx%d+%d+%d\n",
383                program_name, width, height, x, y);
384        exit(1);
385    }
386
387    if (add_pixel_value != 0)
388        XAddPixel(image, add_pixel_value);
389
390    /*
391     * Determine the pixmap size.
392     */
393    buffer_size = Image_Size(image);
394
395    if (debug)
396        outl("xwd: Getting Colors.\n");
397
398    if (!multiVis) {
399        ncolors = Get_XColors(&win_info, &colors);
400        vis = win_info.visual;
401    }
402    else {
403        ncolors = Get24bitDirectColors(&colors);
404        initFakeVisual(&vis_h);
405        vis = &vis_h;
406    }
407    /*
408     * Inform the user that the image has been retrieved.
409     */
410    if (!silent) {
411#ifdef XKB
412        XkbStdBell(dpy, window, FEEP_VOLUME, XkbBI_Proceed);
413        XkbStdBell(dpy, window, FEEP_VOLUME, XkbBI_RepeatingLastBell);
414#else
415        XBell(dpy, FEEP_VOLUME);
416        XBell(dpy, FEEP_VOLUME);
417#endif
418        XFlush(dpy);
419    }
420
421    /*
422     * Calculate header size.
423     */
424    if (debug)
425        outl("xwd: Calculating header size.\n");
426    header_size = SIZEOF(XWDheader) + (CARD32) win_name_size;
427
428    /*
429     * Write out header information.
430     */
431    if (debug)
432        outl("xwd: Constructing and dumping file header.\n");
433    memset(&header, 0, SIZEOF(XWDheader));
434    header.header_size = (CARD32) header_size;
435    header.file_version = (CARD32) XWD_FILE_VERSION;
436    header.pixmap_format = (CARD32) format;
437    header.pixmap_depth = (CARD32) image->depth;
438    header.pixmap_width = (CARD32) image->width;
439    header.pixmap_height = (CARD32) image->height;
440    header.xoffset = (CARD32) image->xoffset;
441    header.byte_order = (CARD32) image->byte_order;
442    header.bitmap_unit = (CARD32) image->bitmap_unit;
443    header.bitmap_bit_order = (CARD32) image->bitmap_bit_order;
444    header.bitmap_pad = (CARD32) image->bitmap_pad;
445    header.bits_per_pixel = (CARD32) image->bits_per_pixel;
446    header.bytes_per_line = (CARD32) image->bytes_per_line;
447    /****
448    header.visual_class = (CARD32) win_info.visual->class;
449    header.red_mask = (CARD32) win_info.visual->red_mask;
450    header.green_mask = (CARD32) win_info.visual->green_mask;
451    header.blue_mask = (CARD32) win_info.visual->blue_mask;
452    header.bits_per_rgb = (CARD32) win_info.visual->bits_per_rgb;
453    header.colormap_entries = (CARD32) win_info.visual->map_entries;
454    *****/
455    header.visual_class = (CARD32) vis->class;
456    header.red_mask = (CARD32) vis->red_mask;
457    header.green_mask = (CARD32) vis->green_mask;
458    header.blue_mask = (CARD32) vis->blue_mask;
459    header.bits_per_rgb = (CARD32) vis->bits_per_rgb;
460    header.colormap_entries = (CARD32) vis->map_entries;
461
462    header.ncolors = ncolors;
463    header.window_width = (CARD32) win_info.width;
464    header.window_height = (CARD32) win_info.height;
465    header.window_x = absx;
466    header.window_y = absy;
467    header.window_bdrwidth = (CARD32) win_info.border_width;
468
469    if (*(char *) &swaptest) {
470        _swaplong((char *) &header, sizeof(header));
471        for (i = 0; i < ncolors; i++) {
472            _swaplong((char *) &colors[i].pixel, sizeof(CARD32));
473            _swapshort((char *) &colors[i].red, 3 * sizeof(short));
474        }
475    }
476
477    if (fwrite((char *) &header, SIZEOF(XWDheader), 1, out) != 1 ||
478        fwrite(win_name, win_name_size, 1, out) != 1) {
479        perror("xwd");
480        exit(1);
481    }
482
483    /*
484     * Write out the color maps, if any
485     */
486
487    if (debug)
488        outl("xwd: Dumping %d colors.\n", ncolors);
489    for (i = 0; i < ncolors; i++) {
490        xwdcolor.pixel = colors[i].pixel;
491        xwdcolor.red = colors[i].red;
492        xwdcolor.green = colors[i].green;
493        xwdcolor.blue = colors[i].blue;
494        xwdcolor.flags = colors[i].flags;
495        if (fwrite((char *) &xwdcolor, SIZEOF(XWDColor), 1, out) != 1) {
496            perror("xwd");
497            exit(1);
498        }
499    }
500
501    /*
502     * Write out the buffer.
503     */
504    if (debug)
505        outl("xwd: Dumping pixmap.  bufsize=%d\n", buffer_size);
506
507    /*
508     *  This copying of the bit stream (data) to a file is to be replaced
509     *  by an Xlib call which hasn't been written yet.  It is not clear
510     *  what other functions of xwd will be taken over by this (as yet)
511     *  non-existant X function.
512     */
513    if (fwrite(image->data, (int) buffer_size, 1, out) != 1) {
514        perror("xwd");
515        exit(1);
516    }
517
518    /*
519     * free the color buffer.
520     */
521
522    if (debug && ncolors > 0)
523        outl("xwd: Freeing colors.\n");
524    if (ncolors > 0)
525        free(colors);
526
527    /*
528     * Free window name string.
529     */
530    if (debug)
531        outl("xwd: Freeing window name string.\n");
532    if (got_win_name)
533        XFree(win_name);
534
535    /*
536     * Free image
537     */
538    XDestroyImage(image);
539}
540
541/*
542 * Report the syntax for calling xwd.
543 */
544void
545usage(const char *errmsg)
546{
547    if (errmsg != NULL)
548        fprintf(stderr, "%s: %s\n", program_name, errmsg);
549
550    fprintf(stderr,
551            "Usage: %s [options] [-root| -id <wdid>| -name <wdname>] > mywddump\n",
552            program_name);
553    fprintf(stderr,
554            "       %s [options] [-root| -id <wdid>| -name <wdname>] -out mywddump\n",
555            program_name);
556    fputs("Options:\n" "  -help                    Print this message\n"
557          "  -version                 Print the program version and exit\n"
558          "  -debug                   Enable debug mode\n"
559          "  -d, -display <host:dpy>  Specify server to connect\n"
560          "  -nobdrs                  Exclude window borders\n"
561          "  -out <file>              Specify an output file\n"
562          "  -xy                      Select XY dumping format for color displays\n"
563          "  -add <value>             Add a signed value to every pixel\n"
564          "  -frame                   Include window manager frame\n"
565          "  -root                    Select the root window\n"
566          "  -id <wdid>               Select a window by its resource id\n"
567          "  -name <wdname>           Select a window by its WM_NAME property\n"
568          "  -icmap                   Use the first colormap of the screen\n"
569          "  -screen                  Send the request against the root window\n"
570          "  -silent                  Don't ring any bells\n", stderr);
571    exit(1);
572}
573
574/*
575 * Determine the pixmap size.
576 */
577
578int
579Image_Size(XImage *image)
580{
581    if (image->format != ZPixmap)
582        return (image->bytes_per_line * image->height * image->depth);
583
584    return (image->bytes_per_line * image->height);
585}
586
587#define lowbit(x) ((x) & (~(x) + 1))
588
589static int
590ReadColors(Visual *vis, Colormap cmap, XColor **colors)
591{
592    int i, ncolors;
593
594    ncolors = vis->map_entries;
595
596    if (!(*colors = (XColor *) malloc(sizeof(XColor) * ncolors)))
597        Fatal_Error("Out of memory!");
598
599    if (vis->class == DirectColor || vis->class == TrueColor) {
600        Pixel red, green, blue, red1, green1, blue1;
601
602        red = green = blue = 0;
603        red1 = lowbit(vis->red_mask);
604        green1 = lowbit(vis->green_mask);
605        blue1 = lowbit(vis->blue_mask);
606        for (i = 0; i < ncolors; i++) {
607            (*colors)[i].pixel = red | green | blue;
608            (*colors)[i].pad = 0;
609            red += red1;
610            if (red > vis->red_mask)
611                red = 0;
612            green += green1;
613            if (green > vis->green_mask)
614                green = 0;
615            blue += blue1;
616            if (blue > vis->blue_mask)
617                blue = 0;
618        }
619    }
620    else {
621        for (i = 0; i < ncolors; i++) {
622            (*colors)[i].pixel = i;
623            (*colors)[i].pad = 0;
624        }
625    }
626
627    XQueryColors(dpy, cmap, *colors, ncolors);
628
629    return (ncolors);
630}
631
632/*
633 * Get the XColors of all pixels in image - returns # of colors
634 */
635int
636Get_XColors(XWindowAttributes *win_info, XColor **colors)
637{
638    int i, ncolors;
639    Colormap cmap = win_info->colormap;
640
641    if (use_installed)
642        /* assume the visual will be OK ... */
643        cmap = XListInstalledColormaps(dpy, win_info->root, &i)[0];
644    if (!cmap)
645        return (0);
646    ncolors = ReadColors(win_info->visual, cmap, colors);
647    return ncolors;
648}
649
650void
651_swapshort(register char *bp, register unsigned n)
652{
653    register char c;
654    register char *ep = bp + n;
655
656    while (bp < ep) {
657        c = *bp;
658        *bp = *(bp + 1);
659        bp++;
660        *bp++ = c;
661    }
662}
663
664void
665_swaplong(register char *bp, register unsigned n)
666{
667    register char c;
668    register char *ep = bp + n;
669
670    while (bp < ep) {
671        c = bp[3];
672        bp[3] = bp[0];
673        bp[0] = c;
674        c = bp[2];
675        bp[2] = bp[1];
676        bp[1] = c;
677        bp += 4;
678    }
679}
680