From: jonas-y@isy.liu.se (Jonas Yngvesson)
Subject: xdl - a .DL viewer for X-windows. [source]
Organization: Dept of EE, University of Linkoping
Date: 15 Aug 92 15:32:28 GMT

Since everybody keeps whining about the lack of DL-viewers and
dltogl doesn't work too well together with xgrasp (according to 
some people at least, I don't have xgrasp) but nobody seems to *do*
anything about it, here is something you can put your sweaty palms 
on: a DL-viewer for X-windows! 

I took dltogl.c and hacked out the GL-specific stuff and stuffed in
some X-things instead. I dont know anything really about neither the
DL format nor the GL format but I have interpreted George's code the
best I could. It is only tested on a SUN and it is a quick hack so it
is probably not very portable. Take it or leave it...

The program takes a DL-file as a command line argument or reads from
standard input if no file is given. Have fun!

--Jonas

------------------------------------------------------------------------------
 J o n a s   Y n g v e s s o n          email: jonas-y@isy.liu.se
Dept. of Electrical Engineering	        voice:  +46-(0)13-282162          
University of Linkoping, Sweden         fax  :  +46-(0)13-139282


-------------------------------- Cut Here --------------------------------
/*
 * xdl.c -- display a DL animation in an X-window.
 *
 * usage: xdl [file.dl]
 *
 * compile with: [g]cc -O<some level> -o xdl xdl.c -lX11
 *
 *
 * Author:
 *      Jonas Yngvesson <jonas-y@isy.liu.se>
 *
 * Derived from dltogl.c by:
 *	George Phillips <phillips@cs.ubc.ca>
 */

#include <stdio.h>
#include <signal.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/time.h>

#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

#define isneg16(x)	((x) & 0x8000)
#define neg16(x)	((~(x) + 1) & 0x7fff)

Display *x_display;
Window   x_window;
u_long   pixels[256];
Pixmap  *pixmap;
XImage  *x_image;
u_char  *x_image_data;
GC       gc_clear;


/*
 * This routine returns a pointer to a pseudo color visual.
 *            (an 8-bit deep color window)
 */
static Visual *
get_pseudo_visual()
{
    XVisualInfo  vtemplate;     /* template of the visual we want */
    XVisualInfo *visual_list;  /* list of matching XVisualInfo structs */
    int          i, visuals_matched;
   
    vtemplate.screen = DefaultScreen(x_display);
    vtemplate.depth = 8;
    visual_list = XGetVisualInfo(x_display, VisualScreenMask | VisualDepthMask,
                                &vtemplate, &visuals_matched);
    if (!visuals_matched) {
        fputs("Sorry, no PseudoColor on this display.\n", stderr);
        exit(1);
    }
 
    for (i = 0; i < visuals_matched; i++) {
        if (visual_list[i].visual->class == PseudoColor) {
            return(visual_list[i].visual);
        }
    }

    fputs("Sorry, no PseudoColor on this display.\n", stderr);
    exit(1);

    /*NOTREACHED*/
    return NULL;
}


/*
 * Initialize the colormap. I use a private one, I was
 * too tired to fiddle with allocating shared colors.
 */
static void
cmap_setup(colors)
    u_char *colors;
{
    Colormap cmap;
    XColor   color;
    int      i;

    cmap = XCreateColormap(x_display, x_window, get_pseudo_visual(), 
                           AllocNone);
    for (i = 0; i < 256; i++) {
        /*
         * X wants 16 bit color specs and VGA uses 6 ==> shift 10 bits.
         */
        color.red   = colors[3 * i    ] << 10;
        color.green = colors[3 * i + 1] << 10;
        color.blue  = colors[3 * i + 2] << 10;
        XAllocColor(x_display, cmap, &color);
        pixels[i] = color.pixel;
    }
    XSetWindowColormap(x_display, x_window, cmap);
}



/*
 * Wait for a key to be pressed. In "dltogl" it was
 * printed as a "waitkey" command to GL with a numeric
 * argument. I don't know any GL commands so I don't know
 * if the argument was a request to wait for a specific key
 * or a timeout for how long to wait or anything else.
 * I just wait forever for any key to be pressed.
 */
static void
wait_for_key()
{
    XEvent event;
    Bool   done;

    done = False;
    while (!done) {
        XNextEvent(x_display, &event);
        if (event.type == KeyPress) {
            done = True;
        }
    }
}



/*
 * Set up the X window.
 */
static void
x_window_setup(title)
    char *title;
{
    XSetWindowAttributes  win_attr;    /* storage for "window attributes" */
    XSizeHints            hints;       /* storage for "window hints" */
    XGCValues             gcval;
    int                   screen;
 
    /*
     * Open the display.
     */
    if (NULL == (x_display = XOpenDisplay(NULL))) {
        fputs("Can't open display.\n", stderr);
        exit(1);
    }

    /*
     * Create the window.
     */
    screen = DefaultScreen(x_display);
    x_window = XCreateSimpleWindow(x_display, DefaultRootWindow(x_display),  
                                   100, 100, 320, 200, 0, 
                                   BlackPixel(x_display, screen), 
                                   BlackPixel(x_display, screen));
 
    /* 
     * set up "window hints" so that we won't be allowed to
     * resize the window while it's running 
     */
    hints.flags = PSize | PMinSize | PMaxSize;
    hints.width = hints.min_width = hints.max_width = 320;
    hints.height = hints.min_height = hints.max_height = 200;
    XSetStandardProperties(x_display, x_window, title, title, 
                           None, NULL, 0, &hints);
 
    /*
     * Tell the server which events we want to recieve.
     * Enable backing store so we don't have to worry
     * about exposes (yes I know, backing store is not
     * guaranteed, but it works for me, hah).
     */
    win_attr.event_mask = KeyPressMask;
    win_attr.backing_store = Always;
    XChangeWindowAttributes(x_display, x_window, 
                            CWEventMask | CWBackingStore, &win_attr);

    gcval.foreground = BlackPixel(x_display, screen);
    gc_clear = XCreateGC(x_display, x_window, GCForeground, &gcval);

    XMapWindow(x_display, x_window);
    XSync(x_display, False);
}


static void die(s)char*s;{fprintf(stderr,"%s\n",s);exit(1);}


void
main(argc, argv)
    int		argc;
    char*	argv[];
{
    unsigned int i, j;
    struct itimerval timer;
    FILE* fp;
    int num_scrn;
    int ctl_len;
    int format;
    static char title[21];
    static char author[21];
    static unsigned char pal[256*3];
    u_char *tmp_data;
    int images_per_screen;
    int fx, fy;
    int* cmd;
    int labelpos, label;
    int cmdnum;
    int version;
    
    if (argc > 2)
        die("usage: xdl [file.dl]");
    
    if (1 == argc) {
        fp = stdin;
    } else if (NULL == (fp = fopen(argv[1], "r"))) {        
        fprintf(stderr, "xdl: can't open %s\n", argv[1]);
        exit(1);
    }
    
    switch (version = fgetc(fp)) {
      case 1: case 2: break;
      default:
        fprintf(stderr, "xdl: This file is in an unknown format.\n");
        fprintf(stderr, "     I can only do .DL version 1 and 2.\n", version);
        exit(1);
    }
    
    if (version == 1)
        format = 1;
    else
        format = fgetc(fp);
    
    switch (format) {
      case 0:
        fx = fy = 0;
        images_per_screen = 1;
        break;
      case 1:
        fx = 80;
        fy = 50;
        images_per_screen = 4;
        break;
      default:
        die("xdl: only large and medium formats are handled");
        break;
    }
    
    title[20] = author[20] = 0;
    for (i = 0; i < 20; i++)
        title[i] = fgetc(fp) ^ 255;
    
    for (i = 0; i < 20; i++)
        if (version == 2)
            author[i] = fgetc(fp) ^ 255;
        else
            author[i] = 255;
    
    for (i = 0; i < 20; i++) {
        if ((unsigned char)title[i] == 255) title[i] = 0;
        if ((unsigned char)author[i] == 255) author[i] = 0;
    }
    
    num_scrn = fgetc(fp);
    ctl_len = fgetc(fp);
    
    printf("%s is a %s sized version %d .DL file.\n", 
           (argc == 2 ? argv[1] : "stdin"), 
           (format == 0 ? "large" : "medium"), 
           version);
    printf("It containes %d images in a %d frame loop.\n",
           num_scrn * (format * 3 + 1), ctl_len);
    
    if (!(cmd = (int *)malloc(ctl_len * sizeof(int))))
        die("xdl: out of memory");
    
    /* mebbe this is the border colour? */
    if (version == 2)
        for (i = 0; i < 3; i++)
            fgetc(fp);
    else
        fgetc(fp);
    
    for (i = 0; i < 256; i++) {
        pal[i*3] = fgetc(fp);
        pal[i*3+1] = fgetc(fp);
        pal[i*3+2] = fgetc(fp);
    }
    
    x_window_setup(title);
    cmap_setup(pal);
    
    if (0 == format) {
        if (NULL == (x_image_data = (u_char *)malloc(320 * 200))) {
            die("xdl: not enough memory.");
        }
        x_image = XCreateImage(x_display, get_pseudo_visual(), 8, ZPixmap,
                               0, x_image_data, 320, 200, 8, 320);
    } else {
        if (NULL == (x_image_data = (u_char *)malloc(160 * 100))) {
            die("xdl: not enough memory.");
        }
        if (NULL == (tmp_data = (u_char *)malloc(320 * 200))) {
            die("xdl: not enough memory.");
        }
        x_image = XCreateImage(x_display, get_pseudo_visual(), 8, ZPixmap,
                               0, x_image_data, 160, 100, 8, 160);
    }
    
    pixmap = (Pixmap *)malloc(num_scrn * images_per_screen
                              * sizeof(Pixmap));
    
    for (j = 0; j < num_scrn; j++) {
        switch (format) {
          case 0: /* large */
            pixmap[j] = XCreatePixmap(x_display, x_window, 320, 200, 8);
            XFillRectangle(x_display, pixmap[j], gc_clear, 0, 0, 320, 200);
            fread(x_image_data, 1, 320 * 200, fp);
            for (i = 0; i < 320 * 200; ++i) {
                x_image_data[i] = (u_char)pixels[x_image_data[i]];
            }
            XPutImage(x_display, pixmap[j], 
                      DefaultGC(x_display, DefaultScreen(x_display)), 
                      x_image, 0, 0, 0, 0, 320, 200);
            break;
            
          case 1: /* medium */
            fread(tmp_data, 1, 320 * 200, fp);
            for (i = 0; i < 4; i++) {
                unsigned char*	src;
                unsigned char*	dst;
                int             row;
                int             col;

                pixmap[j * 4 + i] = XCreatePixmap(x_display, x_window, 
                                                  320, 200, 8);
                XFillRectangle(x_display, pixmap[j * 4 + i], gc_clear, 
                               0, 0, 320, 200);
                src = tmp_data + (i % 2) * 160 + (i / 2) * 100 * 320;
                dst = x_image_data;
                for (row = 0; row < 100; row++) {
                    for (col = 0; col < 160; col++)
                        *dst++ = (u_char)pixels[*src++];
                    src += 160;
                }
                XPutImage(x_display, pixmap[j * 4 + i], 
                          DefaultGC(x_display, DefaultScreen(x_display)), 
                          x_image, 0, 0, fx, fy, 160, 100);
            }
            break;
        }
    }
    
    for (i = 0; i < ctl_len; i++) {
        if (version == 2) {
            j = fgetc(fp);
            j += fgetc(fp) << 8;
            cmd[i] = j;
        } else {
            j = fgetc(fp);
            cmd[i] = (j % 10) - 1 + ((j / 10) - 1) * 4;
        }
    }
    
    labelpos = 0;
    if (isneg16(cmd[ctl_len - 1])) {
        labelpos = neg16(cmd[ctl_len - 1]) + 1; /* Correct? Why add 1 ?? */
        ctl_len--;	/* ignore that last command */
    }
    
    /*
     * Now for the animation. I use setitimer() and getitimer()
     * to try to keep 25 frames/sec. (Not that all DL-files are
     * adjusted to that but it seem natural)
     */
    signal(SIGALRM, SIG_IGN);
    i = 0;
    cmdnum = 0;
    label = -1;
    while (1) {
        for (; i < ctl_len; i++, cmdnum++) {
            if (cmdnum == labelpos && label == -1) {
                label = i;
            }
            if (isneg16(cmd[i])) {
                i++;              /* Skip argument to waitkey, see above */
                wait_for_key();
            } else {
                timer.it_interval.tv_sec = 0;
                timer.it_interval.tv_usec = 40000;
                timer.it_value.tv_sec = 0;
                timer.it_value.tv_usec = 40000;
                setitimer(ITIMER_REAL, &timer, NULL);
                XCopyArea(x_display, pixmap[cmd[i]], x_window, 
                          DefaultGC(x_display, DefaultScreen(x_display)), 
                          0, 0, 320, 200, 0, 0);
                XSync(x_display, False);
                getitimer(ITIMER_REAL, &timer);
                usleep(timer.it_value.tv_usec);
            }
        }
        i = cmdnum = label;
    }
}

-- 
------------------------------------------------------------------------------
 J o n a s   Y n g v e s s o n          email: jonas-y@isy.liu.se
Dept. of Electrical Engineering	        voice:  +46-(0)13-282162          
University of Linkoping, Sweden         fax  :  +46-(0)13-139282
Newsgroups: alt.graphics.pixutils