initial repo - lel - Farbfeld image viewer | |
git clone git://git.codemadness.org/lel | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 0bcb47ab3451309490d3f4a856b755ea43ff282e | |
Author: Hiltjo Posthuma <[email protected]> | |
Date: Fri, 1 Aug 2014 22:33:45 +0000 | |
initial repo | |
Signed-off-by: Hiltjo Posthuma <[email protected]> | |
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(-) | |
--- | |
diff --git a/.gitignore b/.gitignore | |
@@ -0,0 +1,2 @@ | |
+lel | |
+*.o | |
diff --git a/LICENSE b/LICENSE | |
@@ -0,0 +1,21 @@ | |
+MIT/X Consortium License | |
+ | |
+(c) 2014 Hiltjo Posthuma <[email protected]> | |
+ | |
+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. | |
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 | |
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. | |
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?). | |
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 | |
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 | |
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 | |
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! | |
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) | (h… | |
+ imgheight = ntohl((hdr[13] << 0) | (hdr[14] << 8) | (hdr[15] << 16) | … | |
+ 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… | |
+ NULL, newwidth, newheight, 32, 0); | |
+ ximg->data = malloc(ximg->bytes_per_line * ximg->height); | |
+ scale(ximg->width, ximg->height, ximg->bytes_per_line, ximg->d… | |
+ 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, xi… | |
+ 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… | |
+ winwidth = attr.width; | |
+ winheight = attr.height; | |
+ } | |
+ break; | |
+ case ConfigureNotify: | |
+ if(winwidth != ev->xconfigure.width || winheight != ev->xconfi… | |
+ 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,… | |
+ DefaultDepth(dpy, screen), InputOutput, | |
+ CopyFromParent, 0, NULL); | |
+ gc = XCreateGC(dpy, win, 0, NULL); | |
+ | |
+ XStoreName(dpy, win, wintitle); | |
+ XSelectInput(dpy, win, StructureNotifyMask | ExposureMask | KeyPressMa… | |
+ 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; | |
+} |