#include <xsimple.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>
#include "imgview.h"
#include "reader/common.h"
VARS v;
static void sighandler(int sig)
{
v.timeout++;
}
static void mycls(void)
{
if (! v.curimg) {
settitle(WINNAME);
XFillRectangle(xv.d, xv.w, xv.gc, 0, 0, v.win_w, v.win_h);
XSync(xv.d, False);
return;
}
if (v.ulx > 0 && v.ulx > v.oulx)
XFillRectangle(xv.d, xv.w, xv.gc, v.oulx, 0, v.ulx - v.oulx, v.win_h);
if (v.uly > 0 && v.uly > v.ouly)
XFillRectangle(xv.d, xv.w, xv.gc, 0, v.ouly, v.win_w, v.uly - v.ouly);
if (v.lrx < (v.win_w - 1) && v.lrx < v.olrx)
XFillRectangle(xv.d, xv.w, xv.gc, v.lrx + 1, 0, v.olrx - v.lrx,
v.win_h);
if (v.lry < (v.win_h - 1) && v.lry < v.olry)
XFillRectangle(xv.d, xv.w, xv.gc, 0, v.lry + 1, v.win_w,
v.olry - v.lry);
}
static void putimg(void)
{
static int first = 1;
if (! v.curimg) { if (first) first--; return; }
v.ulx = v.img_x - v.curimg->width/2;
v.uly = v.img_y - v.curimg->height/2;
v.lrx = v.ulx + v.curimg->width - 1;
v.lry = v.uly + v.curimg->height - 1;
if (! xv.pmap) {
GC putgc;
xv.pmap = mkpixmap(v.curimg->width, v.curimg->height, v.curimg->depth);
if (v.curimg->depth == 1) {
static GC mgc = NULL;
if (! mgc) mgc = XCreateGC(xv.d, xv.pmap, 0, NULL);
if (v.black == 1) {
XSetBackground(xv.d, mgc, 0); XSetForeground(xv.d, mgc, 1);
} else {
XSetBackground(xv.d, mgc, 1); XSetForeground(xv.d, mgc, 0);
}
putgc = mgc;
} else
putgc = xv.gc;
XPutImage(xv.d, xv.pmap, putgc, v.curimg, 0, 0, 0, 0, v.curimg->width,
v.curimg->height);
}
if (! first) {
mycls();
if (v.curimg->depth == 1)
XCopyPlane(xv.d, xv.pmap, xv.w, v.monogc, 0, 0, v.curimg->width,
v.curimg->height, v.ulx, v.uly, 1);
else
XCopyArea(xv.d, xv.pmap, xv.w, xv.gc, 0, 0, v.curimg->width,
v.curimg->height, v.ulx, v.uly);
} else
first--;
v.oulx = v.ulx; v.ouly = v.uly; v.olrx = v.lrx; v.olry = v.lry;
}
static void zoomimg(void)
{
int w, h;
unsigned char *dp1, *dp2;
unsigned short **sdpp1 = (unsigned short **)&dp1,
**sdpp2 = (unsigned short **)&dp2;
unsigned long **ldpp1 = (unsigned long **)&dp1,
**ldpp2 = (unsigned long **)&dp2;
int x, y, y2, sbit, sbit2, dbit;
int pad;
int subsmp = 0;
if (v.zimg) { rmximage(v.zimg); v.zimg = NULL; }
if (xv.pmap) { rmpixmap(xv.pmap); xv.pmap = 0; }
if (v.zoom == 1.) { v.curimg = v.img; putimg(); return; }
if (v.zoom > 1.) {
w = v.img->width * v.zoom; h = v.img->height * v.zoom;
} else {
subsmp = 1 / v.zoom;
w = (v.img->width + subsmp - 1) / subsmp;
h = (v.img->height + subsmp - 1) / subsmp;
}
pad = 0;
v.zimg = mkximage(w, h, v.img->depth, &pad);
if (v.img->depth == 1) memset(v.zimg->data, 0, v.zimg->bytes_per_line * h);
dp1 = v.img->data; dp2 = v.zimg->data;
if (v.zoom > 1.) {
int i, j;
for (y = 0; y < v.img->height; y++) {
unsigned char *tp = dp1;
for (i = 0; i < v.zoom; i++) {
dp1 = tp;
if (v.img->depth == 24) {
for (x = 0; x < v.img->width; x++) {
for (j = 0; j < v.zoom; j++)
*(*ldpp2)++ = *(*ldpp1);
(*ldpp1)++;
}
} else if (v.img->depth == 16) {
for (x = 0; x < v.img->width; x++) {
for (j = 0; j < v.zoom; j++)
*(*sdpp2)++ = *(*sdpp1);
(*sdpp1)++;
}
} else {
for (x = 0, sbit = dbit = 7; x < v.img->width; x++,
sbit--) {
if (sbit < 0) { dp1++; sbit = 7; }
sbit2 = (*dp1 >> sbit) & 1;
for (j = 0; j < v.zoom; j++, dbit--) {
if (dbit < 0) { dp2++; dbit = 7; }
*dp2 |= sbit2 << dbit;
}
}
dp1++; dp2++;
}
if (pad) dp2 += pad;
}
if (v.pad) dp1 += v.pad;
}
} else {
for (y = 0, y2 = 0; y < v.img->height; y += subsmp, y2++) {
dp1 = v.img->data + y * v.img->bytes_per_line;
if (v.img->depth == 24) {
for (x = 0; x < v.img->width; x += subsmp)
*(*ldpp2)++ = *(*ldpp1 + x);
} else if (v.img->depth == 16) {
for (x = 0; x < v.img->width; x += subsmp)
*(*sdpp2)++ = *(*sdpp1 + x);
} else {
for (x = 0, sbit = dbit = 7; x < v.img->width; x += subsmp,
sbit -= subsmp, dbit--) {
if (sbit < 0) { dp1 += (abs(sbit) + 7) / 8; sbit = 7; }
if (dbit < 0) { dp2++; dbit = 7; }
*dp2 |= ((*dp1 >> sbit) & 1) << dbit;
}
dp2++;
}
if (pad) dp2 += pad;
}
}
v.curimg = v.zimg;
putimg();
}
static int getfmt(int fd)
{
unsigned char magic[4];
if (read(fd, magic, 4) < 4)
return -1;
lseek(fd, 0, SEEK_SET);
if (magic[0] == 'P' && (magic[1] >= '1' && magic[1] <= '6'))
return FMT_PPM;
else if (*(unsigned int *)magic == 0x38464947)
return FMT_GIF;
else if (*(unsigned short *)magic == 0xd8ff)
return FMT_JPEG;
else if (*(unsigned int *)magic == 0x2a004d4d ||
*(unsigned int *)magic == 0x002a4949)
return FMT_TIFF;
else if (*(unsigned int *)magic == 0x474e5089)
return FMT_PNG;
else if (magic[0] == 0x0a)
return FMT_PCX;
else if (magic[2] >= 1 && magic[2] <= 8)
return FMT_JBIG;
else
return -1;
}
static void readfile(char *fname)
{
char *tail = NULL, tmp[512];
char *tmpname = NULL;
if (v.img) { rmximage(v.img); v.img = NULL; }
if (v.zimg) { rmximage(v.zimg); v.zimg = NULL; }
if (xv.pmap) { rmpixmap(xv.pmap); xv.pmap = 0; }
v.curimg = NULL;
v.black = 1; v.white = 0;
mycls();
if (fname) {
int fd;
if ((tail = strrchr(fname, '/'))) tail++; else tail = fname;
if (! strncmp(fname, "http://", 7)) {
int r;
tmpname = (char *)malloc(128);
sprintf(tmpname, "%s/.imgview.XXXXXX", getenv("HOME"));
if ((fd = mkstemp(tmpname)) < 0) {
perror("mkstemp");
goto err;
} else
close(fd);
sprintf(tmp, "wget -q -O %s %s", tmpname, fname);
if ((r = system(tmp))) {
if (r < 0)
perror("system");
else
fprintf(stderr, "wget failed with exit status %d\n",
WEXITSTATUS(r));
goto err;
}
if ((fd = open(tmpname, O_RDONLY)) < 0) {
perror("open");
goto err;
}
} else {
if ((fd = open(fname, O_RDONLY)) < 0) {
perror("open");
goto err;
}
}
switch (getfmt(fd)) {
case FMT_PPM:
v.img = readppm(fd); break;
case FMT_GIF:
v.img = readgif(fd); break;
case FMT_JPEG:
v.img = readjpeg(fd); break;
case FMT_TIFF:
v.img = readtiff(fd, fname); break;
case FMT_PNG:
v.img = readpng(fd); break;
case FMT_PCX:
v.img = readpcx(fd); break;
case FMT_JBIG:
v.img = readjbig(fd); break;
default:
close(fd); v.img = readxbm(fname); break;
}
} else
v.img = readppm(0);
if (! v.img) goto err;
if (fname) {
sprintf(tmp, "%s - %s [%dx%d]", WINNAME, tail, v.img->width,
v.img->height);
} else {
sprintf(tmp, "%s - stdin [%dx%d]", WINNAME, v.img->width,
v.img->height);
}
settitle(tmp);
v.curimg = v.img;
v.img_x = v.win_w/2; v.img_y = v.win_h/2;
if (v.keepzoom)
zoomimg();
else {
v.zoom = 1.0;
putimg();
}
if (tmpname) { unlink(tmpname); free(tmpname); }
return;
err:
if (fname)
sprintf(tmp, "%s - %s [ERROR]", WINNAME, tail);
else
sprintf(tmp, "%s - stdin [ERROR]", WINNAME);
settitle(tmp);
putimg();
if (fname)
fprintf(stderr, "%s: Error while reading `%s'\n", APPNAME, tail);
else
fprintf(stderr, "%s: Error while reading stdin\n", APPNAME);
if (tmpname) { unlink(tmpname); free(tmpname); }
}
static void usage(void)
{
fprintf(stderr, "usage: iv [-s] [-v] [file ...]\n");
exit(1);
}
static void init(int argc, char **argv)
{
int c, s_flag = 0;
char rcname[512];
FILE *f;
v.win_x = WIN_X; v.win_y = WIN_Y; v.win_w = WIN_W; v.win_h = WIN_H;
strcpy(v.bgcolor_s, BGCOLOR); strcpy(v.tbgcolor_s, TBGCOLOR);
strcpy(v.bwbgcolor_s, BWBGCOLOR); strcpy(v.bwfgcolor_s, BWFGCOLOR);
v.xymove = XYMOVE;
v.delay = DELAY;
v.jbigw = JBIGW;
v.jbigh = JBIGH;
v.zoom = 1.0;
v.keepzoom = v.slide = v.timeout = 0;
v.verbose = 0;
while ((c = getopt(argc, argv, ":sv")) != -1) {
switch (c) {
case 's':
s_flag++;
break;
case 'v':
v.verbose++;
break;
case '?':
case ':':
default:
usage();
}
}
if ((argc - optind) && s_flag) {
signal(SIGALRM, sighandler);
v.slide++;
}
atexit(writeconfig);
sprintf(rcname, "%s/.imgviewrc", getenv("HOME"));
if ((f = fopen(rcname, "r")))
readconfig(f);
xinit(APPNAME, CLASSNAME, WINNAME, v.win_x, v.win_y, v.win_w, v.win_h, 1,
v.bgcolor_s, v.bgcolor_s, 0);
v.win_w = xv.width; v.win_h = xv.height;
v.bgcolor = getnamedcolor(v.bgcolor_s, &v.bgcolor_rgb);
v.tbgcolor = getnamedcolor(v.tbgcolor_s, &v.tbgcolor_rgb);
v.bwbgcolor = getnamedcolor(v.bwbgcolor_s, NULL);
v.bwfgcolor = getnamedcolor(v.bwfgcolor_s, NULL);
XSetForeground(xv.d, xv.gc, v.bgcolor);
v.monogc = XCreateGC(xv.d, xv.w, 0, NULL);
XSetBackground(xv.d, v.monogc, v.bwbgcolor);
XSetForeground(xv.d, v.monogc, v.bwfgcolor);
}
int main(int argc, char **argv)
{
struct timeval t;
int fnum;
init(argc, argv);
fnum = optind;
readfile(argc - fnum ? argv[fnum] : NULL);
if (v.slide) alarm(v.delay);
for (;;) {
XEvent e;
if (v.slide && v.timeout) {
if (++fnum == argc) exit(0);
readfile(argv[fnum]);
v.timeout = 0; alarm(v.delay);
}
while (getevent(&e)) {
static int shift = 0, ctrl = 0;
int k;
switch (e.type) {
case ConfigureNotify:
{
v.win_x = xv.x; v.win_y = xv.y;
if (xv.width != v.win_w || xv.height != v.win_h) {
v.win_w = xv.width; v.win_h = xv.height;
v.img_x = v.win_w/2; v.img_y = v.win_h/2;
putimg();
}
}
break;
case Expose:
if (v.curimg) {
for (;;) {
XExposeEvent *ee = (XExposeEvent *)&e;
if (v.curimg->depth == 1)
XCopyPlane(xv.d, xv.pmap, xv.w, v.monogc,
ee->x - v.ulx, ee->y - v.uly,
ee->width, ee->height, ee->x, ee->y, 1);
else
XCopyArea(xv.d, xv.pmap, xv.w, xv.gc,
ee->x - v.ulx, ee->y - v.uly,
ee->width, ee->height, ee->x, ee->y);
if (! ee->count)
break;
XNextEvent(xv.d, &e);
}
}
break;
case KeyPress:
k = XLookupKeysym((XKeyEvent *)&e, 0);
switch (k) {
case XK_Shift_L:
case XK_Shift_R:
shift++; break;
case XK_Control_L:
case XK_Control_R:
ctrl++; break;
case XK_Left:
v.img_x += v.xymove * (ctrl ? 5 : 1);
putimg();
break;
case XK_Right:
v.img_x -= v.xymove * (ctrl ? 5 : 1);
putimg();
break;
case XK_Up:
v.img_y += v.xymove * (ctrl ? 5 : 1);
putimg();
break;
case XK_Down:
v.img_y -= v.xymove * (ctrl ? 5 : 1);
putimg();
break;
case XK_Prior:
v.zoom *= 2;
zoomimg();
break;
case XK_Next:
v.zoom /= 2;
zoomimg();
break;
case XK_Home:
v.img_x = v.win_w/2;
v.img_y = v.win_h/2;
if (ctrl) {
v.zoom = 1.0; zoomimg();
} else {
putimg();
}
break;
case XK_p:
if (argc > 1) {
int ofnum = fnum;
fnum = (shift ? 1 :
(fnum > 1 ? fnum - 1 : fnum));
if (fnum != ofnum) {
readfile(argv[fnum]);
if (v.slide) gettimeofday(&t, NULL);
}
}
break;
case XK_n:
if (argc > 1) {
int ofnum = fnum;
fnum = (shift ? argc - 1 :
(fnum < argc - 1 ? fnum + 1 : fnum));
if (fnum != ofnum) {
readfile(argv[fnum]);
if (v.slide) gettimeofday(&t, NULL);
}
}
break;
case XK_z:
v.keepzoom ^= 1; break;
case XK_q:
exit(0);
}
if (v.slide && k != XK_Shift_L && k != XK_Shift_R &&
k != XK_Control_L && k != XK_Control_R) {
v.timeout = 0; alarm(v.delay);
}
break;
case KeyRelease:
switch (XLookupKeysym((XKeyEvent *)&e, 0)) {
case XK_Shift_L:
case XK_Shift_R:
shift--; break;
case XK_Control_L:
case XK_Control_R:
ctrl--; break;
}
break;
}
}
usleep(1000);
}
}