initial repo - lel - Farbfeld image viewer
 (HTM) git clone git://git.codemadness.org/lel
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 0bcb47ab3451309490d3f4a856b755ea43ff282e
 (HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org>
       Date:   Fri,  1 Aug 2014 22:33:45 +0000
       
       initial repo
       
       Signed-off-by: Hiltjo Posthuma <hiltjo@codemadness.org>
       
       Diffstat:
         A .gitignore                          |       2 ++
         A LICENSE                             |      21 +++++++++++++++++++++
         A Makefile                            |      45 +++++++++++++++++++++++++++++++
         A README                              |      41 +++++++++++++++++++++++++++++++
         A TODO                                |      25 +++++++++++++++++++++++++
         A arg.h                               |      63 +++++++++++++++++++++++++++++++
         A config.mk                           |      27 +++++++++++++++++++++++++++
         A lel-open                            |      20 ++++++++++++++++++++
         A lel.1                               |       9 +++++++++
         A lel.c                               |     509 +++++++++++++++++++++++++++++++
       
       10 files changed, 762 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/.gitignore b/.gitignore
       @@ -0,0 +1,2 @@
       +lel
       +*.o
 (DIR) diff --git a/LICENSE b/LICENSE
       @@ -0,0 +1,21 @@
       +MIT/X Consortium License
       +
       +(c) 2014 Hiltjo Posthuma <hiltjo@codemadness.org>
       +
       +Permission is hereby granted, free of charge, to any person obtaining a
       +copy of this software and associated documentation files (the "Software"),
       +to deal in the Software without restriction, including without limitation
       +the rights to use, copy, modify, merge, publish, distribute, sublicense,
       +and/or sell copies of the Software, and to permit persons to whom the
       +Software is furnished to do so, subject to the following conditions:
       +
       +The above copyright notice and this permission notice shall be included in
       +all copies or substantial portions of the Software.
       +
       +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
       +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
       +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
       +DEALINGS IN THE SOFTWARE.
 (DIR) diff --git a/Makefile b/Makefile
       @@ -0,0 +1,45 @@
       +include config.mk
       +
       +NAME = lel
       +SRC = lel.c
       +OBJ = ${SRC:.c=.o}
       +
       +all: lel
       +
       +options:
       +        @echo ${NAME} build options:
       +        @echo "CFLAGS   = ${CFLAGS}"
       +        @echo "LDFLAGS  = ${LDFLAGS}"
       +        @echo "CC       = ${CC}"
       +
       +.c.o:
       +        ${CC} -c ${CFLAGS} $<
       +
       +${OBJ}: config.mk
       +
       +lel: lel.o
       +        ${CC} -o $@ lel.o ${LDFLAGS}
       +
       +clean:
       +        rm -f lel ${OBJ}
       +
       +install: all
       +        @echo installing executable file to ${DESTDIR}${PREFIX}/bin
       +        @mkdir -p ${DESTDIR}${PREFIX}/bin
       +        @cp -f lel ${DESTDIR}${PREFIX}/bin
       +        @cp -f lel-open ${DESTDIR}${PREFIX}/bin
       +        @chmod 755 ${DESTDIR}${PREFIX}/bin/lel
       +        @chmod 755 ${DESTDIR}${PREFIX}/bin/lel-open
       +        @echo installing manual pages to ${DESTDIR}${MANPREFIX}/man1
       +        @mkdir -p ${DESTDIR}${MANPREFIX}/man1
       +        @cp -f lel.1 ${DESTDIR}${MANPREFIX}/man1
       +        @chmod 644 ${DESTDIR}${MANPREFIX}/man1/lel.1
       +
       +uninstall:
       +        @echo removing executable file from ${DESTDIR}${PREFIX}/bin
       +        @rm -f ${DESTDIR}${PREFIX}/bin/lel
       +        @rm -f ${DESTDIR}${PREFIX}/bin/lel-open
       +        @echo removing manual pages from ${DESTDIR}${MANPREFIX}/man1
       +        @rm -f ${DESTDIR}${MANPREFIX}/man1/lel.1
       +
       +.PHONY: all options clean dist install uninstall
 (DIR) diff --git a/README b/README
       @@ -0,0 +1,41 @@
       +lel - simple X11 image viewer
       +=============================
       +
       +lel is a simple X11 image viewer. It reads image data (in the "imagefile"
       +format from stdout) and displays it in a X11 window.
       +
       +
       +Features
       +--------
       +
       +- view modes:
       +  - original image size.
       +  - stretch image to window, keep aspect ratio.
       +  - stretch image to window, don't keep aspect ratio.
       +- keybinds
       +- zooming
       +- panning
       +
       +
       +Dependencies
       +------------
       +
       +- libX11
       +
       +
       +Optional (run-time) dependencies
       +--------------------------------
       +- imagefile: http://git.2f30.org/imagefile/
       +
       +
       +Compile:
       +--------
       +
       +$ make
       +# make install
       +
       +
       +Known issues:
       +-------------
       +
       +See TODO file.
 (DIR) diff --git a/TODO b/TODO
       @@ -0,0 +1,25 @@
       +[ ] bugs:
       +        [ ] alpha mask with png2if | lel is always black?
       +        [ ] resizing slow (might be a dwm issue).
       +[ ] use XSHM again?
       +[ ] enter key or some other key should print the current filename to stdout.
       +[ ] for pictures which use an alpha mask use a checked pattern, similar to:
       +    http://www.modejong.com/blog/Media/Ghost_TransparentBG_400x300.jpg
       +[ ] support multiple filenames as arguments, use hotkeys to switch image.
       +[ ] zooming
       +        [ ] improve performance (cull only the visible part?), use XGetImage() ?
       +        [ ] improve zoomfact "steps".
       +[ ] mouse support.
       +        [ ] mouse move panning.
       +        [x] mouse scroll zooming.
       +[ ] scale
       +        [ ] use XPutPixel for byte-order safety ?
       +[ ] improve README.
       +[ ] write man page.
       +        [ ] lel.1
       +        [ ] lel-open.1
       +[?] improve key lookup code.
       +[?] project name change.
       +[?] add support for more color depths (< 24).
       +[?] add command mode or shell support for viewing
       +    images in a directory (output key to stdout might be enough?).
 (DIR) diff --git a/arg.h b/arg.h
       @@ -0,0 +1,63 @@
       +/*
       + * Copy me if you can.
       + * by 20h
       + */
       +
       +#ifndef ARG_H__
       +#define ARG_H__
       +
       +extern char *argv0;
       +
       +/* use main(int argc, char *argv[]) */
       +#define ARGBEGIN        for (argv0 = *argv, argv++, argc--;\
       +                                        argv[0] && argv[0][1]\
       +                                        && argv[0][0] == '-';\
       +                                        argc--, argv++) {\
       +                                char argc_;\
       +                                char **argv_;\
       +                                int brk_;\
       +                                if (argv[0][1] == '-' && argv[0][2] == '\0') {\
       +                                        argv++;\
       +                                        argc--;\
       +                                        break;\
       +                                }\
       +                                for (brk_ = 0, argv[0]++, argv_ = argv;\
       +                                                argv[0][0] && !brk_;\
       +                                                argv[0]++) {\
       +                                        if (argv_ != argv)\
       +                                                break;\
       +                                        argc_ = argv[0][0];\
       +                                        switch (argc_)
       +
       +/* Handles obsolete -NUM syntax */
       +#define ARGNUM                                case '0':\
       +                                        case '1':\
       +                                        case '2':\
       +                                        case '3':\
       +                                        case '4':\
       +                                        case '5':\
       +                                        case '6':\
       +                                        case '7':\
       +                                        case '8':\
       +                                        case '9'
       +
       +#define ARGEND                        }\
       +                        }
       +
       +#define ARGC()                argc_
       +
       +#define ARGNUMF(base)        (brk_ = 1, estrtol(argv[0], (base)))
       +
       +#define EARGF(x)        ((argv[0][1] == '\0' && argv[1] == NULL)?\
       +                                ((x), abort(), (char *)0) :\
       +                                (brk_ = 1, (argv[0][1] != '\0')?\
       +                                        (&argv[0][1]) :\
       +                                        (argc--, argv++, argv[0])))
       +
       +#define ARGF()                ((argv[0][1] == '\0' && argv[1] == NULL)?\
       +                                (char *)0 :\
       +                                (brk_ = 1, (argv[0][1] != '\0')?\
       +                                        (&argv[0][1]) :\
       +                                        (argc--, argv++, argv[0])))
       +
       +#endif
 (DIR) diff --git a/config.mk b/config.mk
       @@ -0,0 +1,27 @@
       +VERSION = 0.1
       +
       +# customize below to fit your system
       +
       +# paths
       +PREFIX = /usr/local
       +MANPREFIX = ${PREFIX}/share/man
       +
       +# includes and libs
       +INCS =
       +LIBS = -lc -lX11
       +
       +# debug
       +CFLAGS = -O0 -g -std=c99 -Wall -pedantic -DVERSION=\"${VERSION}\"
       +LDFLAGS = ${LIBS}
       +
       +# optimized
       +#CFLAGS = -O2 -std=c99 -DVERSION=\"${VERSION}\"
       +#LDFLAGS = -s ${LIBS}
       +
       +# tcc
       +#CC = tcc
       +#CFLAGS = -DVERSION=\"${VERSION}\"
       +#LDFLAGS = -s ${LIBS}
       +
       +# compiler and linker
       +CC = cc
 (DIR) diff --git a/lel-open b/lel-open
       @@ -0,0 +1,20 @@
       +#!/bin/sh
       +filename="$1"
       +shift
       +ext=$(basename "$filename" | grep -o '\..[^\.]*$')
       +
       +if test x"$ext" = x".jpg"; then
       +        convert="jpg2if"
       +elif test x"$ext" = x".png"; then
       +        convert="png2if"
       +elif test x"$ext" = x".gif"; then
       +        convert="gif2if"
       +elif test x"$ext" = x".if"; then
       +        convert="cat"
       +else
       +        echo "unknown extension \"$ext\"" >&2
       +fi
       +
       +if test x"$convert" != x""; then
       +        "$convert" < "$filename" | lel -t "$filename" "$@"
       +fi
 (DIR) diff --git a/lel.1 b/lel.1
       @@ -0,0 +1,9 @@
       +.TH LEL 1 lel\-0.1
       +.SH NAME
       +lel \- simple X11 image viewer
       +.SH SYNOPSIS
       +.B lel
       +.SH DESCRIPTION
       +View an "imagefile" image.
       +.SH BUGS
       +Please report them!
 (DIR) diff --git a/lel.c b/lel.c
       @@ -0,0 +1,509 @@
       +#include <unistd.h>
       +#include <stdarg.h>
       +#include <stdint.h>
       +#include <stdlib.h>
       +#include <stdio.h>
       +#include <string.h>
       +#include <errno.h>
       +#include <signal.h>
       +#include <time.h>
       +#include <arpa/inet.h>
       +
       +#include <X11/Xlib.h>
       +#include <X11/Xutil.h>
       +#include <X11/keysym.h>
       +
       +#include "arg.h"
       +char *argv0;
       +
       +#define APP_NAME "lel"
       +#define HEADER_FORMAT "imagefile########"
       +
       +/* Image status flags. */
       +enum { NONE = 0, LOADED = 1, SCALED = 2, DRAWN = 4 };
       +/* View mode. */
       +enum { ASPECT = 0, FULL_ASPECT, FULL_STRETCH };
       +
       +static int viewmode = ASPECT;
       +static char *wintitle = APP_NAME;
       +static XImage *ximg = NULL;
       +static Drawable xpix = 0;
       +static Display *dpy = NULL;
       +static Window win;
       +static GC gc;
       +static int screen, xfd;
       +static int running = 1;
       +static int imgstate = NONE;
       +static int imgwidth, imgheight;
       +static uint8_t *imgbuf;
       +static int winx, winy, winwidth = 320, winheight = 240;
       +static int panxoffset = 0, panyoffset = 0;
       +static float zoomfact = 1.0, zoominc = 0.25;
       +
       +void
       +die(const char *fmt, ...)
       +{
       +        va_list ap;
       +
       +        va_start(ap, fmt);
       +        vfprintf(stderr, fmt, ap);
       +        va_end(ap);
       +
       +        if(fmt[0] && fmt[strlen(fmt)-1] == ':') {
       +                fputc(' ', stderr);
       +                perror(NULL);
       +        }
       +        exit(EXIT_FAILURE);
       +}
       +
       +void
       +usage(void)
       +{
       +        die("%s", APP_NAME " " VERSION " - (c) 2014 " APP_NAME " engineers\n\n"
       +              "usage: " APP_NAME "[OPTIONS...] [FILE]\n"
       +              "    -a            Full window, keep aspect ratio\n"
       +              "    -f            Full window, stretch (no aspect)\n"
       +              "    -w <w>        Window width\n"
       +              "    -h <h>        Window height\n"
       +              "    -x <x>        Window x position\n"
       +              "    -y <y>        Window y position\n"
       +              "    -t <title>    Use title\n"
       +              "    -v            Print version and exit\n");
       +}
       +
       +int
       +if_open(FILE *f)
       +{
       +        uint8_t hdr[17];
       +
       +        if (fread(hdr, 1, strlen(HEADER_FORMAT), f) != strlen(HEADER_FORMAT))
       +                return -1;
       +
       +        if(memcmp(hdr, "imagefile", 9))
       +                return -1;
       +
       +        imgwidth = ntohl((hdr[9] << 0) | (hdr[10] << 8) | (hdr[11] << 16) | (hdr[12] << 24));
       +        imgheight = ntohl((hdr[13] << 0) | (hdr[14] << 8) | (hdr[15] << 16) | (hdr[16] << 24));
       +        if(imgwidth <= 0 || imgheight <= 0)
       +                return -1;
       +
       +        return 0;
       +}
       +
       +int
       +if_read(FILE *f)
       +{
       +        int i, j, off, row_len;
       +        uint8_t *row;
       +
       +        row_len = imgwidth * strlen("RGBA");
       +        if(!(row = malloc(row_len)))
       +                return 1;
       +
       +        for(off = 0, i = 0; i < imgheight; ++i) {
       +                if(fread(row, 1, (size_t)row_len, f) != (size_t)row_len) {
       +                        free(row);
       +                        die("unexpected EOF or row-skew at %d\n", i);
       +                }
       +                for(j = 0; j < row_len; j += 4, off += 4) {
       +                        imgbuf[off] = row[j];
       +                        imgbuf[off + 1] = row[j + 1];
       +                        imgbuf[off + 2] = row[j + 2];
       +                        imgbuf[off + 3] = row[j + 3];
       +                }
       +        }
       +        free(row);
       +
       +        imgstate |= LOADED;
       +
       +        return 0;
       +}
       +
       +/* NOTE: will be removed later, for debugging alpha mask */
       +#if 0
       +void
       +normalsize(char *newbuf)
       +{
       +        unsigned int x, y, soff = 0, doff = 0;
       +
       +        for(y = 0; y < imgheight; y++) {
       +                for(x = 0; x < imgwidth; x++, soff += 4, doff += 4) {
       +                        newbuf[doff+0] = imgbuf[soff+2];
       +                        newbuf[doff+1] = imgbuf[soff+1];
       +                        newbuf[doff+2] = imgbuf[soff+0];
       +                        newbuf[doff+3] = imgbuf[soff+3];
       +                }
       +        }
       +}
       +#endif
       +
       +/* scales imgbuf data to newbuf (ximg->data), nearest neighbour. */
       +void
       +scale(unsigned int width, unsigned int height, unsigned int bytesperline,
       +        char *newbuf)
       +{
       +        unsigned char *ibuf;
       +        unsigned int jdy, dx, bufx, x, y;
       +
       +        jdy = bytesperline / 4 - width;
       +        dx = (imgwidth << 10) / width;
       +        for(y = 0; y < height; y++) {
       +                bufx = imgwidth / width;
       +                ibuf = &imgbuf[y * imgheight / height * imgwidth * 4];
       +
       +                for(x = 0; x < width; x++) {
       +                        *newbuf++ = (ibuf[(bufx >> 10)*4+2]);
       +                        *newbuf++ = (ibuf[(bufx >> 10)*4+1]);
       +                        *newbuf++ = (ibuf[(bufx >> 10)*4+0]);
       +                        newbuf++;
       +                        bufx += dx;
       +                }
       +                newbuf += jdy;
       +        }
       +}
       +
       +void
       +ximage(unsigned int newwidth, unsigned int newheight)
       +{
       +        int depth;
       +
       +        /* destroy previous image */
       +        if(ximg) {
       +                XDestroyImage(ximg);
       +                ximg = NULL;
       +        }
       +        depth = DefaultDepth(dpy, screen);
       +        if(depth >= 24) {
       +                if(xpix)
       +                        XFreePixmap(dpy, xpix);
       +                xpix = XCreatePixmap(dpy, win, winwidth, winheight, depth);
       +                ximg = XCreateImage(dpy, CopyFromParent, depth,        ZPixmap, 0,
       +                                    NULL, newwidth, newheight, 32, 0);
       +                ximg->data = malloc(ximg->bytes_per_line * ximg->height);
       +                scale(ximg->width, ximg->height, ximg->bytes_per_line, ximg->data);
       +                XInitImage(ximg);
       +        } else {
       +                die("This program does not yet support display depths < 24.\n");
       +        }
       +}
       +
       +void
       +scaleview(void)
       +{
       +        switch(viewmode) {
       +        case FULL_STRETCH:
       +                ximage(winwidth, winheight);
       +                break;
       +        case FULL_ASPECT:
       +                if(winwidth * imgheight > winheight * imgwidth)
       +                        ximage(imgwidth * winheight / imgheight, winheight);
       +                else
       +                        ximage(winwidth, imgheight * winwidth / imgwidth);
       +                break;
       +        case ASPECT:
       +        default:
       +                ximage(imgwidth * zoomfact, imgheight * zoomfact);
       +                break;
       +        }
       +        imgstate |= SCALED;
       +}
       +
       +void
       +draw(void)
       +{
       +        int xoffset = 0, yoffset = 0;
       +
       +        if(viewmode != FULL_STRETCH) {
       +                /* center vertical, horizontal */
       +                xoffset = (winwidth - ximg->width) / 2;
       +                yoffset = (winheight - ximg->height) / 2;
       +                /* pan offset */
       +                xoffset -= panxoffset;
       +                yoffset -= panyoffset;
       +        }
       +        XSetForeground(dpy, gc, BlackPixel(dpy, 0));
       +        XFillRectangle(dpy, xpix, gc, 0, 0, winwidth, winheight);
       +        XPutImage(dpy, xpix, gc, ximg, 0, 0, xoffset, yoffset, ximg->width, ximg->height);
       +        XCopyArea(dpy, xpix, win, gc, 0, 0, winwidth, winheight, 0, 0);
       +
       +        XFlush(dpy);
       +        imgstate |= DRAWN;
       +}
       +
       +void
       +update(void)
       +{
       +        if(!(imgstate & LOADED))
       +                return;
       +        if(!(imgstate & SCALED))
       +                scaleview();
       +        if(!(imgstate & DRAWN))
       +                draw();
       +}
       +
       +void
       +setview(int mode)
       +{
       +        if(viewmode == mode)
       +                return;
       +        viewmode = mode;
       +        imgstate &= ~(DRAWN | SCALED);
       +        update();
       +}
       +
       +void
       +pan(int x, int y)
       +{
       +        panxoffset -= x;
       +        panyoffset -= y;
       +        imgstate &= ~(DRAWN | SCALED);
       +        update();
       +}
       +
       +void
       +inczoom(float f)
       +{
       +        if((zoomfact + f) <= 0)
       +                return;
       +        zoomfact += f;
       +        imgstate &= ~(DRAWN | SCALED);
       +        update();
       +}
       +
       +void
       +zoom(float f)
       +{
       +        if(f == zoomfact)
       +                return;
       +        zoomfact = f;
       +        imgstate &= ~(DRAWN | SCALED);
       +        update();
       +}
       +
       +void
       +buttonpress(XEvent *ev)
       +{
       +        switch(ev->xbutton.button) {
       +        case Button4:
       +                inczoom(zoominc);
       +                break;
       +        case Button5:
       +                inczoom(-zoominc);
       +                break;
       +        }
       +}
       +
       +void
       +keypress(XEvent *ev)
       +{
       +        KeySym key;
       +
       +        key = XLookupKeysym(&ev->xkey, 0);
       +        switch(key) {
       +        case XK_Escape:
       +        case XK_q:
       +                running = 0;
       +                break;
       +        case XK_Left:
       +        case XK_h:
       +                pan(winwidth / 20, 0);
       +                break;
       +        case XK_Down:
       +        case XK_j:
       +                pan(0, -(winheight / 20));
       +                break;
       +        case XK_Up:
       +        case XK_k:
       +                pan(0, winheight / 20);
       +                break;
       +        case XK_Right:
       +        case XK_l:
       +                pan(-(winwidth / 20), 0);
       +                break;
       +        case XK_a:
       +                setview(FULL_ASPECT);
       +                break;
       +        case XK_o:
       +                setview(ASPECT);
       +                break;
       +        case XK_f:
       +                setview(FULL_STRETCH);
       +                break;
       +        case XK_KP_Add:
       +        case XK_equal:
       +        case XK_plus:
       +                inczoom(zoominc);
       +                break;
       +        case XK_KP_Subtract:
       +        case XK_underscore:
       +        case XK_minus:
       +                inczoom(-zoominc);
       +                break;
       +        case XK_3:
       +                zoom(4.0);
       +                break;
       +        case XK_2:
       +                zoom(2.0);
       +                break;
       +        case XK_1:
       +                zoom(1.0);
       +                break;
       +        case XK_0:
       +                zoom(1.0);
       +                setview(ASPECT); /* fallthrough */
       +        case XK_r:
       +                panxoffset = 0;
       +                panyoffset = 0;
       +                imgstate &= ~(DRAWN | SCALED);
       +                update();
       +                break;
       +        }
       +}
       +
       +void
       +handleevent(XEvent *ev)
       +{
       +        XWindowAttributes attr;
       +
       +        switch(ev->type) {
       +        case MapNotify:
       +                if (!winwidth || !winheight) {
       +                        XGetWindowAttributes(ev->xmap.display, ev->xmap.window, &attr);
       +                        winwidth = attr.width;
       +                        winheight = attr.height;
       +                }
       +                break;
       +        case ConfigureNotify:
       +                if(winwidth != ev->xconfigure.width || winheight != ev->xconfigure.height) {
       +                        winwidth = ev->xconfigure.width;
       +                        winheight = ev->xconfigure.height;
       +                        imgstate &= ~(SCALED);
       +                }
       +                break;
       +        case Expose:
       +                imgstate &= ~(DRAWN);
       +                update();
       +                break;
       +        case KeyPress:
       +                keypress(ev);
       +                break;
       +        case ButtonPress:
       +                buttonpress(ev);
       +                break;
       +        }
       +}
       +
       +void
       +setup(void)
       +{
       +        XClassHint class = { APP_NAME, APP_NAME };
       +
       +        if(!(dpy = XOpenDisplay(NULL)))
       +                die("Can't open X display.\n");
       +        xfd = ConnectionNumber(dpy);
       +        screen = DefaultScreen(dpy);
       +
       +        win = XCreateWindow(dpy, DefaultRootWindow(dpy), winx, winy, winwidth, winheight, 0,
       +                            DefaultDepth(dpy, screen), InputOutput,
       +                            CopyFromParent, 0, NULL);
       +        gc = XCreateGC(dpy, win, 0, NULL);
       +
       +        XStoreName(dpy, win, wintitle);
       +        XSelectInput(dpy, win, StructureNotifyMask | ExposureMask | KeyPressMask |
       +                               ButtonPressMask);
       +        XMapRaised(dpy, win);
       +        XSetWMProperties(dpy, win, NULL, NULL, NULL, 0, NULL, NULL, &class);
       +        XFlush(dpy);
       +}
       +
       +void
       +run(void)
       +{
       +        XEvent ev;
       +
       +        while(running && !XNextEvent(dpy, &ev)) {
       +                handleevent(&ev);
       +        }
       +}
       +
       +int
       +main(int argc, char *argv[]) {
       +        char *filename = "";
       +        FILE *fp = NULL;
       +        int tflag = 0;
       +        int wflag = 0;
       +        int hflag = 0;
       +
       +        ARGBEGIN {
       +        case 'a':
       +                viewmode = FULL_ASPECT;
       +                break;
       +        case 'f':
       +                viewmode = FULL_STRETCH;
       +                break;
       +        case 'h':
       +                hflag = 1;
       +                if(!(winheight = atoi(EARGF(usage()))))
       +                        usage();
       +                break;
       +        case 't':
       +                wintitle = EARGF(usage());
       +                tflag = 1;
       +                break;
       +        case 'w':
       +                wflag = 1;
       +                if(!(winwidth = atoi(EARGF(usage()))))
       +                        usage();
       +                break;
       +        case 'x':
       +                winx = atoi(EARGF(usage()));
       +                break;
       +        case 'y':
       +                winy = atoi(EARGF(usage()));
       +                break;
       +        default:
       +                usage();
       +                break;
       +        } ARGEND;
       +
       +        if(argc >= 1) {
       +                filename = argv[0];
       +                if(!(fp = fopen(filename, "rb"))) {
       +                        die("can't read %s:", filename);
       +                        return EXIT_FAILURE;
       +                }
       +        } else {
       +                filename = "<stdin>";
       +                fp = stdin;
       +        }
       +        if(!tflag)
       +                wintitle = filename;
       +
       +        if(if_open(fp))
       +                die("can't open image (invalid format?)\n");
       +        if(!(imgbuf = malloc((imgwidth) * (imgheight) * 4)))
       +                die("can't malloc\n");
       +        if_read(fp);
       +
       +        if(!wflag)
       +                winwidth = imgwidth;
       +        if(!hflag)
       +                winheight = imgheight;
       +
       +        setup();
       +        run();
       +
       +        if(fp && fp != stdin)
       +                fclose(fp);
       +
       +        free(imgbuf);
       +
       +        if(ximg)
       +                XDestroyImage(ximg);
       +        if(xpix)
       +                XFreePixmap(dpy, xpix);
       +        if(dpy)
       +                XCloseDisplay(dpy);
       +
       +        return EXIT_SUCCESS;
       +}