- Configurable key and mouse mappings in config.h - Put event handling code from main.c into events.[ch]master
@@ -1,6 +1,6 @@ | |||||
all: sxiv | all: sxiv | ||||
VERSION = git-20110722 | |||||
VERSION = git-20110726 | |||||
CC = gcc | CC = gcc | ||||
DESTDIR = | DESTDIR = | ||||
@@ -9,7 +9,7 @@ CFLAGS = -Wall -pedantic -DVERSION=\"$(VERSION)\" | |||||
LDFLAGS = | LDFLAGS = | ||||
LIBS = -lX11 -lImlib2 | LIBS = -lX11 -lImlib2 | ||||
SRC = image.c main.c options.c thumbs.c util.c window.c | |||||
SRC = events.o image.c main.c options.c thumbs.c util.c window.c | |||||
OBJ = $(SRC:.c=.o) | OBJ = $(SRC:.c=.o) | ||||
sxiv: $(OBJ) | sxiv: $(OBJ) | ||||
@@ -1,36 +1,120 @@ | |||||
/* default window dimensions (overwritten via -g option): */ | |||||
#ifdef _GENERAL_CONFIG | |||||
/* enable external commands (defined below)? 0 = off, 1 = on: */ | |||||
enum { EXT_COMMANDS = 0 }; | |||||
#endif | |||||
#ifdef _WINDOW_CONFIG | |||||
/* default window dimensions (overwritten via -g option): */ | |||||
enum { WIN_WIDTH = 800, WIN_HEIGHT = 600 }; | enum { WIN_WIDTH = 800, WIN_HEIGHT = 600 }; | ||||
/* default color for window background: * | |||||
* (see X(7) "COLOR NAMES" section for valid values) */ | |||||
/* default color for window background: * | |||||
* (see X(7) "COLOR NAMES" section for valid values) */ | |||||
static const char * const BG_COLOR = "#999999"; | static const char * const BG_COLOR = "#999999"; | ||||
/* default color for thumbnail selection: */ | |||||
/* default color for thumbnail selection: */ | |||||
static const char * const SEL_COLOR = "#0066FF"; | static const char * const SEL_COLOR = "#0066FF"; | ||||
/* how should images be scaled when they are loaded?: * | |||||
* (also controllable via -d/-s/-Z/-z options) * | |||||
* SCALE_DOWN: 100%, but fit large images into window, * | |||||
* SCALE_FIT: fit all images into window, * | |||||
* SCALE_ZOOM: use current zoom level, 100% at startup */ | |||||
#endif | |||||
#ifdef _IMAGE_CONFIG | |||||
/* how should images be scaled when they are loaded?: * | |||||
* (also controllable via -d/-s/-Z/-z options) * | |||||
* SCALE_DOWN: 100%, but fit large images into window, * | |||||
* SCALE_FIT: fit all images into window, * | |||||
* SCALE_ZOOM: use current zoom level, 100% at startup */ | |||||
static const scalemode_t SCALE_MODE = SCALE_DOWN; | static const scalemode_t SCALE_MODE = SCALE_DOWN; | ||||
/* levels (percent) to use when zooming via '-' and '+': */ | |||||
/* levels (percent) to use when zooming via '-' and '+': */ | |||||
static const float zoom_levels[] = { | static const float zoom_levels[] = { | ||||
12.5, 25.0, 50.0, 75.0, | 12.5, 25.0, 50.0, 75.0, | ||||
100.0, 150.0, 200.0, 400.0, 800.0 | 100.0, 150.0, 200.0, 400.0, 800.0 | ||||
}; | }; | ||||
/* default dimension of thumbnails (width == height): */ | |||||
#endif | |||||
#ifdef _THUMBS_CONFIG | |||||
/* default dimension of thumbnails (width == height): */ | |||||
enum { THUMB_SIZE = 60 }; | enum { THUMB_SIZE = 60 }; | ||||
/* enable external commands (defined below)? 0=off, 1=on: */ | |||||
enum { EXT_COMMANDS = 0 }; | |||||
#endif | |||||
#ifdef _MAPPINGS_CONFIG | |||||
/* keyboard mappings for image and thumbnail mode: */ | |||||
static const keymap_t keys[] = { | |||||
/* key function argument */ | |||||
{ XK_q, quit, None }, | |||||
{ XK_r, reload, None }, | |||||
{ XK_f, toggle_fullscreen, None }, | |||||
{ XK_a, toggle_antialias, None }, | |||||
{ XK_A, toggle_alpha, None }, | |||||
{ XK_Return, switch_mode, None }, | |||||
{ XK_g, first, None }, | |||||
{ XK_G, last, None }, | |||||
{ XK_n, navigate, +1 }, | |||||
{ XK_space, navigate, +1 }, | |||||
{ XK_p, navigate, -1 }, | |||||
{ XK_BackSpace, navigate, -1 }, | |||||
{ XK_bracketright, navigate, +10 }, | |||||
{ XK_bracketleft, navigate, -10 }, | |||||
{ XK_D, remove_image, None }, | |||||
/* external commands and corresponding key mappings: */ | |||||
{ XK_h, move, DIR_LEFT }, | |||||
{ XK_Left, move, DIR_LEFT }, | |||||
{ XK_j, move, DIR_DOWN }, | |||||
{ XK_Down, move, DIR_DOWN }, | |||||
{ XK_k, move, DIR_UP }, | |||||
{ XK_Up, move, DIR_UP }, | |||||
{ XK_l, move, DIR_RIGHT }, | |||||
{ XK_Right, move, DIR_RIGHT }, | |||||
{ XK_braceleft, scroll, DIR_LEFT }, | |||||
{ XK_Next, scroll, DIR_DOWN }, | |||||
{ XK_Prior, scroll, DIR_UP }, | |||||
{ XK_braceright, scroll, DIR_RIGHT }, | |||||
{ XK_H, pan_edge, DIR_LEFT }, | |||||
{ XK_J, pan_edge, DIR_DOWN }, | |||||
{ XK_K, pan_edge, DIR_UP }, | |||||
{ XK_L, pan_edge, DIR_RIGHT }, | |||||
{ XK_plus, zoom, +1 }, | |||||
{ XK_equal, zoom, +1 }, | |||||
{ XK_KP_Add, zoom, +1 }, | |||||
{ XK_minus, zoom, -1 }, | |||||
{ XK_KP_Subtract, zoom, -1 }, | |||||
{ XK_0, zoom, 0 }, | |||||
{ XK_KP_0, zoom, 0 }, | |||||
{ XK_w, fit_to_win, None }, | |||||
{ XK_W, fit_to_img, None }, | |||||
{ XK_less, rotate, DIR_LEFT }, | |||||
{ XK_greater, rotate, DIR_RIGHT }, | |||||
}; | |||||
/* external commands and corresponding key mappings: */ | |||||
static const command_t commands[] = { | static const command_t commands[] = { | ||||
/* ctrl-... reload? command, '#' is replaced by filename */ | |||||
{ ',', 1, "jpegtran -rotate 270 -copy all -outfile # #" }, | |||||
{ '.', 1, "jpegtran -rotate 90 -copy all -outfile # #" }, | |||||
{ '<', 1, "mogrify -rotate -90 #" }, | |||||
{ '>', 1, "mogrify -rotate +90 #" } | |||||
/* ctrl-... reload? command, '#' is replaced by filename */ | |||||
{ XK_comma, True, "jpegtran -rotate 270 -copy all -outfile # #" }, | |||||
{ XK_period, True, "jpegtran -rotate 90 -copy all -outfile # #" }, | |||||
{ XK_less, True, "mogrify -rotate -90 #" }, | |||||
{ XK_greater, True, "mogrify -rotate +90 #" } | |||||
}; | |||||
/* mouse button mappings for image mode: */ | |||||
static const button_t buttons[] = { | |||||
/* modifier button function argument */ | |||||
{ None, Button1, navigate, +1 }, | |||||
{ None, Button3, navigate, -1 }, | |||||
{ None, Button2, drag, None }, | |||||
{ None, Button4, move, DIR_UP }, | |||||
{ None, Button5, move, DIR_DOWN }, | |||||
{ ShiftMask, Button4, move, DIR_LEFT }, | |||||
{ ShiftMask, Button5, move, DIR_RIGHT }, | |||||
{ ControlMask, Button4, zoom, +1 }, | |||||
{ ControlMask, Button5, zoom, -1 }, | |||||
}; | }; | ||||
#endif |
@@ -0,0 +1,562 @@ | |||||
/* sxiv: events.c | |||||
* Copyright (c) 2011 Bert Muennich <muennich at informatik.hu-berlin.de> | |||||
* | |||||
* This program is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this program; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
*/ | |||||
#define _GENERAL_CONFIG | |||||
#define _MAPPINGS_CONFIG | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <unistd.h> | |||||
#include <sys/time.h> | |||||
#include <sys/wait.h> | |||||
#include <X11/keysym.h> | |||||
#include <X11/Xutil.h> | |||||
#include "events.h" | |||||
#include "image.h" | |||||
#include "thumbs.h" | |||||
#include "types.h" | |||||
#include "util.h" | |||||
#include "window.h" | |||||
#include "config.h" | |||||
/* timeouts in milliseconds: */ | |||||
enum { | |||||
TO_WIN_RESIZE = 75, | |||||
TO_IMAGE_DRAG = 1, | |||||
TO_CURSOR_HIDE = 1500, | |||||
TO_THUMBS_LOAD = 200 | |||||
}; | |||||
void cleanup(); | |||||
void remove_file(int, unsigned char); | |||||
void load_image(int); | |||||
void update_title(); | |||||
extern appmode_t mode; | |||||
extern img_t img; | |||||
extern tns_t tns; | |||||
extern win_t win; | |||||
extern char **filenames; | |||||
extern int filecnt, fileidx; | |||||
int timo_cursor; | |||||
int timo_redraw; | |||||
unsigned char dragging; | |||||
int mox, moy; | |||||
int run_command(const char *cline, Bool reload) { | |||||
int fncnt, fnlen; | |||||
char *cn, *cmdline; | |||||
const char *co, *fname; | |||||
pid_t pid; | |||||
int ret, status; | |||||
if (!cline || !*cline) | |||||
return 0; | |||||
fncnt = 0; | |||||
co = cline - 1; | |||||
while ((co = strchr(co + 1, '#'))) | |||||
fncnt++; | |||||
if (!fncnt) | |||||
return 0; | |||||
ret = 0; | |||||
fname = filenames[mode == MODE_NORMAL ? fileidx : tns.sel]; | |||||
fnlen = strlen(fname); | |||||
cn = cmdline = (char*) s_malloc((strlen(cline) + fncnt * (fnlen + 2)) * | |||||
sizeof(char)); | |||||
/* replace all '#' with filename */ | |||||
for (co = cline; *co; co++) { | |||||
if (*co == '#') { | |||||
*cn++ = '"'; | |||||
strcpy(cn, fname); | |||||
cn += fnlen; | |||||
*cn++ = '"'; | |||||
} else { | |||||
*cn++ = *co; | |||||
} | |||||
} | |||||
*cn = '\0'; | |||||
if ((pid = fork()) == 0) { | |||||
execlp("/bin/sh", "/bin/sh", "-c", cmdline, NULL); | |||||
warn("could not exec: /bin/sh"); | |||||
exit(1); | |||||
} else if (pid < 0) { | |||||
warn("could not fork. command line was: %s", cmdline); | |||||
} else if (reload) { | |||||
waitpid(pid, &status, 0); | |||||
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) | |||||
ret = 1; | |||||
else | |||||
warn("child exited with non-zero return value: %d. command line was: %s", | |||||
WEXITSTATUS(status), cmdline); | |||||
} | |||||
free(cmdline); | |||||
return ret; | |||||
} | |||||
void redraw() { | |||||
if (mode == MODE_NORMAL) { | |||||
img_render(&img, &win); | |||||
if (timo_cursor) | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
else if (!dragging) | |||||
win_set_cursor(&win, CURSOR_NONE); | |||||
} else { | |||||
tns_render(&tns, &win); | |||||
} | |||||
update_title(); | |||||
timo_redraw = 0; | |||||
} | |||||
void on_keypress(XEvent *ev) { | |||||
int i; | |||||
XKeyEvent *kev; | |||||
KeySym ksym; | |||||
char key; | |||||
if (!ev || ev->type != KeyPress) | |||||
return; | |||||
kev = &ev->xkey; | |||||
XLookupString(kev, &key, 1, &ksym, NULL); | |||||
if (EXT_COMMANDS && (CLEANMASK(kev->state) & ControlMask)) { | |||||
for (i = 0; i < LEN(commands); i++) { | |||||
if (commands[i].ksym == ksym) { | |||||
win_set_cursor(&win, CURSOR_WATCH); | |||||
if (run_command(commands[i].cmdline, commands[i].reload)) { | |||||
if (mode == MODE_NORMAL) { | |||||
if (fileidx < tns.cnt) | |||||
tns_load(&tns, fileidx, filenames[fileidx], 1); | |||||
img_close(&img, 1); | |||||
load_image(fileidx); | |||||
} else { | |||||
if (!tns_load(&tns, tns.sel, filenames[tns.sel], 0)) { | |||||
remove_file(tns.sel, 0); | |||||
tns.dirty = 1; | |||||
if (tns.sel >= tns.cnt) | |||||
tns.sel = tns.cnt - 1; | |||||
} | |||||
} | |||||
redraw(); | |||||
} | |||||
if (mode == MODE_THUMBS) | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
else if (!timo_cursor) | |||||
win_set_cursor(&win, CURSOR_NONE); | |||||
return; | |||||
} | |||||
} | |||||
} | |||||
for (i = 0; i < LEN(keys); i++) { | |||||
if (ksym == keys[i].ksym && keys[i].handler) { | |||||
if (keys[i].handler(ev, keys[i].arg)) | |||||
redraw(); | |||||
return; | |||||
} | |||||
} | |||||
} | |||||
void on_buttonpress(XEvent *ev) { | |||||
int i, sel; | |||||
XButtonEvent *bev; | |||||
if (!ev || ev->type != ButtonPress) | |||||
return; | |||||
bev = &ev->xbutton; | |||||
if (mode == MODE_NORMAL) { | |||||
if (!dragging) { | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
timo_cursor = TO_CURSOR_HIDE; | |||||
} | |||||
for (i = 0; i < LEN(buttons); i++) { | |||||
if (CLEANMASK(bev->state) == CLEANMASK(buttons[i].mod) && | |||||
bev->button == buttons[i].button && buttons[i].handler) | |||||
{ | |||||
if (buttons[i].handler(ev, buttons[i].arg)) | |||||
redraw(); | |||||
return; | |||||
} | |||||
} | |||||
} else { | |||||
/* thumbnail mode */ | |||||
switch (bev->button) { | |||||
case Button1: | |||||
if ((sel = tns_translate(&tns, bev->x, bev->y)) >= 0) { | |||||
if (sel == tns.sel) { | |||||
load_image(tns.sel); | |||||
mode = MODE_NORMAL; | |||||
timo_cursor = TO_CURSOR_HIDE; | |||||
} else { | |||||
tns_highlight(&tns, &win, tns.sel, False); | |||||
tns_highlight(&tns, &win, sel, True); | |||||
tns.sel = sel; | |||||
} | |||||
redraw(); | |||||
break; | |||||
} | |||||
break; | |||||
case Button4: | |||||
case Button5: | |||||
if (tns_scroll(&tns, bev->button == Button4 ? DIR_UP : DIR_DOWN)) | |||||
redraw(); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
void on_motionnotify(XEvent *ev) { | |||||
XMotionEvent *mev; | |||||
if (!ev || ev->type != MotionNotify) | |||||
return; | |||||
mev = &ev->xmotion; | |||||
if (mev->x >= 0 && mev->x <= win.w && mev->y >= 0 && mev->y <= win.h) { | |||||
if (img_move(&img, &win, mev->x - mox, mev->y - moy)) | |||||
timo_redraw = TO_IMAGE_DRAG; | |||||
mox = mev->x; | |||||
moy = mev->y; | |||||
} | |||||
} | |||||
void run() { | |||||
int xfd, timeout; | |||||
fd_set fds; | |||||
struct timeval tt, t0, t1; | |||||
XEvent ev; | |||||
dragging = 0; | |||||
timo_cursor = mode == MODE_NORMAL ? TO_CURSOR_HIDE : 0; | |||||
redraw(); | |||||
while (1) { | |||||
if (mode == MODE_THUMBS && tns.cnt < filecnt) { | |||||
/* load thumbnails */ | |||||
win_set_cursor(&win, CURSOR_WATCH); | |||||
gettimeofday(&t0, 0); | |||||
while (tns.cnt < filecnt && !XPending(win.env.dpy)) { | |||||
if (tns_load(&tns, tns.cnt, filenames[tns.cnt], 0)) | |||||
tns.cnt++; | |||||
else | |||||
remove_file(tns.cnt, 0); | |||||
gettimeofday(&t1, 0); | |||||
if (TIMEDIFF(&t1, &t0) >= TO_THUMBS_LOAD) | |||||
break; | |||||
} | |||||
if (tns.cnt == filecnt) | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
if (!XPending(win.env.dpy)) { | |||||
redraw(); | |||||
continue; | |||||
} else { | |||||
timo_redraw = TO_THUMBS_LOAD; | |||||
} | |||||
} else if (timo_cursor || timo_redraw) { | |||||
/* check active timeouts */ | |||||
gettimeofday(&t0, 0); | |||||
timeout = MIN(timo_cursor + 1, timo_redraw + 1); | |||||
MSEC_TO_TIMEVAL(timeout, &tt); | |||||
xfd = ConnectionNumber(win.env.dpy); | |||||
FD_ZERO(&fds); | |||||
FD_SET(xfd, &fds); | |||||
if (!XPending(win.env.dpy)) | |||||
select(xfd + 1, &fds, 0, 0, &tt); | |||||
gettimeofday(&t1, 0); | |||||
timeout = MIN(TIMEDIFF(&t1, &t0), timeout); | |||||
/* timeouts fired? */ | |||||
if (timo_cursor) { | |||||
timo_cursor = MAX(0, timo_cursor - timeout); | |||||
if (!timo_cursor) | |||||
win_set_cursor(&win, CURSOR_NONE); | |||||
} | |||||
if (timo_redraw) { | |||||
timo_redraw = MAX(0, timo_redraw - timeout); | |||||
if (!timo_redraw) | |||||
redraw(); | |||||
} | |||||
if ((timo_cursor || timo_redraw) && !XPending(win.env.dpy)) | |||||
continue; | |||||
} | |||||
if (!XNextEvent(win.env.dpy, &ev)) { | |||||
switch (ev.type) { | |||||
case ButtonPress: | |||||
on_buttonpress(&ev); | |||||
break; | |||||
case ButtonRelease: | |||||
if (dragging) { | |||||
dragging = 0; | |||||
if (mode == MODE_NORMAL) { | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
timo_cursor = TO_CURSOR_HIDE; | |||||
} | |||||
} | |||||
break; | |||||
case ClientMessage: | |||||
if ((Atom) ev.xclient.data.l[0] == wm_delete_win) | |||||
return; | |||||
break; | |||||
case ConfigureNotify: | |||||
if (win_configure(&win, &ev.xconfigure)) { | |||||
timo_redraw = TO_WIN_RESIZE; | |||||
if (mode == MODE_NORMAL) | |||||
img.checkpan = 1; | |||||
else | |||||
tns.dirty = 1; | |||||
} | |||||
break; | |||||
case KeyPress: | |||||
on_keypress(&ev); | |||||
break; | |||||
case MotionNotify: | |||||
if (dragging) { | |||||
on_motionnotify(&ev); | |||||
} else if (mode == MODE_NORMAL) { | |||||
if (!timo_cursor) | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
timo_cursor = TO_CURSOR_HIDE; | |||||
} | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
/* handler functions for key and button mappings: */ | |||||
int quit(XEvent *e, arg_t a) { | |||||
cleanup(); | |||||
exit(0); | |||||
} | |||||
int reload(XEvent *e, arg_t a) { | |||||
if (mode == MODE_NORMAL) { | |||||
load_image(fileidx); | |||||
return 1; | |||||
} else { | |||||
return 0; | |||||
} | |||||
} | |||||
int toggle_fullscreen(XEvent *e, arg_t a) { | |||||
win_toggle_fullscreen(&win); | |||||
if (mode == MODE_NORMAL) | |||||
img.checkpan = 1; | |||||
else | |||||
tns.dirty = 1; | |||||
timo_redraw = TO_WIN_RESIZE; | |||||
return 0; | |||||
} | |||||
int toggle_antialias(XEvent *e, arg_t a) { | |||||
if (mode == MODE_NORMAL) { | |||||
img_toggle_antialias(&img); | |||||
return 1; | |||||
} else { | |||||
return 0; | |||||
} | |||||
} | |||||
int toggle_alpha(XEvent *e, arg_t a) { | |||||
if (mode == MODE_NORMAL) { | |||||
img.alpha ^= 1; | |||||
return 1; | |||||
} else { | |||||
return 0; | |||||
} | |||||
} | |||||
int switch_mode(XEvent *e, arg_t a) { | |||||
if (mode == MODE_NORMAL) { | |||||
if (!tns.thumbs) | |||||
tns_init(&tns, filecnt); | |||||
img_close(&img, 0); | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
timo_cursor = 0; | |||||
tns.sel = fileidx; | |||||
tns.dirty = 1; | |||||
mode = MODE_THUMBS; | |||||
} else { | |||||
timo_cursor = TO_CURSOR_HIDE; | |||||
load_image(tns.sel); | |||||
mode = MODE_NORMAL; | |||||
} | |||||
return 1; | |||||
} | |||||
int navigate(XEvent *e, arg_t n) { | |||||
if (mode == MODE_NORMAL) { | |||||
n += fileidx; | |||||
if (n < 0) | |||||
n = 0; | |||||
if (n >= filecnt) | |||||
n = filecnt - 1; | |||||
if (n != fileidx) { | |||||
load_image(n); | |||||
return 1; | |||||
} | |||||
} | |||||
return 0; | |||||
} | |||||
int first(XEvent *e, arg_t a) { | |||||
if (mode == MODE_NORMAL && fileidx != 0) { | |||||
load_image(0); | |||||
return 1; | |||||
} else if (mode == MODE_THUMBS && tns.sel != 0) { | |||||
tns.sel = 0; | |||||
tns.dirty = 1; | |||||
return 1; | |||||
} else { | |||||
return 0; | |||||
} | |||||
} | |||||
int last(XEvent *e, arg_t a) { | |||||
if (mode == MODE_NORMAL && fileidx != filecnt - 1) { | |||||
load_image(filecnt - 1); | |||||
return 1; | |||||
} else if (mode == MODE_THUMBS && tns.sel != tns.cnt - 1) { | |||||
tns.sel = tns.cnt - 1; | |||||
tns.dirty = 1; | |||||
return 1; | |||||
} else { | |||||
return 0; | |||||
} | |||||
} | |||||
int remove_image(XEvent *e, arg_t a) { | |||||
if (mode == MODE_NORMAL) { | |||||
remove_file(fileidx, 1); | |||||
load_image(fileidx >= filecnt ? filecnt - 1 : fileidx); | |||||
return 1; | |||||
} else if (tns.sel < tns.cnt) { | |||||
remove_file(tns.sel, 1); | |||||
tns.dirty = 1; | |||||
if (tns.sel >= tns.cnt) | |||||
tns.sel = tns.cnt - 1; | |||||
return 1; | |||||
} else { | |||||
return 0; | |||||
} | |||||
} | |||||
int move(XEvent *e, arg_t dir) { | |||||
if (mode == MODE_NORMAL) | |||||
return img_pan(&img, &win, dir, 0); | |||||
else | |||||
return tns_move_selection(&tns, &win, dir); | |||||
} | |||||
int scroll(XEvent *e, arg_t dir) { | |||||
if (mode == MODE_NORMAL) | |||||
return img_pan(&img, &win, dir, 1); | |||||
else | |||||
return 0; | |||||
} | |||||
int pan_edge(XEvent *e, arg_t dir) { | |||||
if (mode == MODE_NORMAL) | |||||
return img_pan_edge(&img, &win, dir); | |||||
else | |||||
return 0; | |||||
} | |||||
int drag(XEvent *e, arg_t a) { | |||||
if (mode == MODE_NORMAL) { | |||||
mox = e->xbutton.x; | |||||
moy = e->xbutton.y; | |||||
win_set_cursor(&win, CURSOR_HAND); | |||||
timo_cursor = 0; | |||||
dragging = 1; | |||||
} | |||||
return 0; | |||||
} | |||||
int rotate(XEvent *e, arg_t dir) { | |||||
if (mode == MODE_NORMAL) { | |||||
if (dir == DIR_LEFT) { | |||||
img_rotate_left(&img, &win); | |||||
return 1; | |||||
} else if (dir == DIR_RIGHT) { | |||||
img_rotate_right(&img, &win); | |||||
return 1; | |||||
} | |||||
} | |||||
return 0; | |||||
} | |||||
int zoom(XEvent *e, arg_t scale) { | |||||
if (mode != MODE_NORMAL) | |||||
return 0; | |||||
if (scale > 0) | |||||
return img_zoom_in(&img, &win); | |||||
else if (scale < 0) | |||||
return img_zoom_out(&img, &win); | |||||
else | |||||
return img_zoom(&img, &win, 1.0); | |||||
} | |||||
int fit_to_win(XEvent *e, arg_t ret) { | |||||
if (mode == MODE_NORMAL) { | |||||
if ((ret = img_fit_win(&img, &win))) | |||||
img_center(&img, &win); | |||||
return ret; | |||||
} else { | |||||
return 0; | |||||
} | |||||
} | |||||
int fit_to_img(XEvent *e, arg_t ret) { | |||||
int x, y; | |||||
unsigned int w, h; | |||||
if (mode == MODE_NORMAL) { | |||||
x = MAX(0, win.x + img.x); | |||||
y = MAX(0, win.y + img.y); | |||||
w = img.w * img.zoom; | |||||
h = img.h * img.zoom; | |||||
if ((ret = win_moveresize(&win, x, y, w, h))) { | |||||
img.x = x - win.x; | |||||
img.y = y - win.y; | |||||
} | |||||
return ret; | |||||
} else { | |||||
return 0; | |||||
} | |||||
} |
@@ -0,0 +1,67 @@ | |||||
/* sxiv: events.h | |||||
* Copyright (c) 2011 Bert Muennich <muennich at informatik.hu-berlin.de> | |||||
* | |||||
* This program is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this program; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
*/ | |||||
#ifndef EVENTS_H | |||||
#define EVENTS_H | |||||
#include <X11/Xlib.h> | |||||
typedef struct { | |||||
KeySym ksym; | |||||
Bool reload; | |||||
const char *cmdline; | |||||
} command_t; | |||||
typedef int arg_t; | |||||
typedef struct { | |||||
KeySym ksym; | |||||
int (*handler)(XEvent*, arg_t); | |||||
arg_t arg; | |||||
} keymap_t; | |||||
typedef struct { | |||||
unsigned int mod; | |||||
unsigned int button; | |||||
int (*handler)(XEvent*, arg_t); | |||||
arg_t arg; | |||||
} button_t; | |||||
void run(); | |||||
/* handler functions for key and button mappings: */ | |||||
int quit(XEvent*, arg_t); | |||||
int reload(XEvent*, arg_t); | |||||
int toggle_fullscreen(XEvent*, arg_t); | |||||
int toggle_antialias(XEvent*, arg_t); | |||||
int toggle_alpha(XEvent*, arg_t); | |||||
int switch_mode(XEvent*, arg_t); | |||||
int navigate(XEvent*, arg_t); | |||||
int first(XEvent*, arg_t); | |||||
int last(XEvent*, arg_t); | |||||
int remove_image(XEvent*, arg_t); | |||||
int move(XEvent*, arg_t); | |||||
int scroll(XEvent*, arg_t); | |||||
int pan_edge(XEvent*, arg_t); | |||||
int drag(XEvent*, arg_t); | |||||
int rotate(XEvent*, arg_t); | |||||
int zoom(XEvent*, arg_t); | |||||
int fit_to_win(XEvent*, arg_t); | |||||
int fit_to_img(XEvent*, arg_t); | |||||
#endif /* EVENTS_H */ |
@@ -16,6 +16,8 @@ | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||||
*/ | */ | ||||
#define _IMAGE_CONFIG | |||||
#include <unistd.h> | #include <unistd.h> | ||||
#include "image.h" | #include "image.h" | ||||
@@ -238,7 +240,7 @@ int img_zoom_in(img_t *img, win_t *win) { | |||||
if (!img || !img->im || !win) | if (!img || !img->im || !win) | ||||
return 0; | return 0; | ||||
for (i = 1; i < zl_cnt; ++i) { | |||||
for (i = 1; i < zl_cnt; i++) { | |||||
if (zoom_levels[i] > img->zoom * 100.0) | if (zoom_levels[i] > img->zoom * 100.0) | ||||
return img_zoom(img, win, zoom_levels[i] / 100.0); | return img_zoom(img, win, zoom_levels[i] / 100.0); | ||||
} | } | ||||
@@ -251,7 +253,7 @@ int img_zoom_out(img_t *img, win_t *win) { | |||||
if (!img || !img->im || !win) | if (!img || !img->im || !win) | ||||
return 0; | return 0; | ||||
for (i = zl_cnt - 2; i >= 0; --i) { | |||||
for (i = zl_cnt - 2; i >= 0; i--) { | |||||
if (zoom_levels[i] < img->zoom * 100.0) | if (zoom_levels[i] < img->zoom * 100.0) | ||||
return img_zoom(img, win, zoom_levels[i] / 100.0); | return img_zoom(img, win, zoom_levels[i] / 100.0); | ||||
} | } | ||||
@@ -16,32 +16,22 @@ | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||||
*/ | */ | ||||
#define _XOPEN_SOURCE 700 | |||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <stdio.h> | |||||
#include <string.h> | #include <string.h> | ||||
#include <sys/select.h> | |||||
#include <sys/stat.h> | |||||
#include <sys/time.h> | |||||
#include <sys/wait.h> | |||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <sys/stat.h> | |||||
#include <X11/Xlib.h> | |||||
#include <X11/Xutil.h> | |||||
#include <X11/keysym.h> | |||||
#include "events.h" | |||||
#include "image.h" | #include "image.h" | ||||
#include "options.h" | #include "options.h" | ||||
#include "thumbs.h" | #include "thumbs.h" | ||||
#include "types.h" | |||||
#include "util.h" | #include "util.h" | ||||
#include "window.h" | #include "window.h" | ||||
#include "config.h" | |||||
enum { TITLE_LEN = 256, FNAME_CNT = 1024 }; | |||||
void run(); | |||||
enum { | |||||
TITLE_LEN = 256, | |||||
FNAME_CNT = 1024 | |||||
}; | |||||
appmode_t mode; | appmode_t mode; | ||||
img_t img; | img_t img; | ||||
@@ -64,6 +54,23 @@ void cleanup() { | |||||
} | } | ||||
} | } | ||||
int check_add_file(char *filename) { | |||||
if (!filename) | |||||
return 0; | |||||
if (access(filename, R_OK)) { | |||||
warn("could not open file: %s", filename); | |||||
return 0; | |||||
} else { | |||||
if (fileidx == filecnt) { | |||||
filecnt *= 2; | |||||
filenames = (char**) s_realloc(filenames, filecnt * sizeof(char*)); | |||||
} | |||||
filenames[fileidx++] = filename; | |||||
return 1; | |||||
} | |||||
} | |||||
void remove_file(int n, unsigned char silent) { | void remove_file(int n, unsigned char silent) { | ||||
if (n < 0 || n >= filecnt) | if (n < 0 || n >= filecnt) | ||||
return; | return; | ||||
@@ -84,32 +91,32 @@ void remove_file(int n, unsigned char silent) { | |||||
memset(tns.thumbs + tns.cnt - 1, 0, sizeof(thumb_t)); | memset(tns.thumbs + tns.cnt - 1, 0, sizeof(thumb_t)); | ||||
} | } | ||||
--filecnt; | |||||
filecnt--; | |||||
if (n < tns.cnt) | if (n < tns.cnt) | ||||
--tns.cnt; | |||||
tns.cnt--; | |||||
} | } | ||||
int load_image(int new) { | |||||
void load_image(int new) { | |||||
struct stat fstats; | struct stat fstats; | ||||
if (new >= 0 && new < filecnt) { | |||||
win_set_cursor(&win, CURSOR_WATCH); | |||||
img_close(&img, 0); | |||||
while (!img_load(&img, filenames[new])) { | |||||
remove_file(new, 0); | |||||
if (new >= filecnt) | |||||
new = filecnt - 1; | |||||
} | |||||
fileidx = new; | |||||
if (!stat(filenames[new], &fstats)) | |||||
filesize = fstats.st_size; | |||||
else | |||||
filesize = 0; | |||||
if (new < 0 || new >= filecnt) | |||||
return; | |||||
/* cursor is reset in redraw() */ | |||||
/* cursor is reset in redraw() */ | |||||
win_set_cursor(&win, CURSOR_WATCH); | |||||
img_close(&img, 0); | |||||
while (!img_load(&img, filenames[new])) { | |||||
remove_file(new, 0); | |||||
if (new >= filecnt) | |||||
new = filecnt - 1; | |||||
} | } | ||||
return 1; | |||||
fileidx = new; | |||||
if (!stat(filenames[new], &fstats)) | |||||
filesize = fstats.st_size; | |||||
else | |||||
filesize = 0; | |||||
} | } | ||||
void update_title() { | void update_title() { | ||||
@@ -138,23 +145,6 @@ void update_title() { | |||||
win_set_title(&win, win_title); | win_set_title(&win, win_title); | ||||
} | } | ||||
int check_append(char *filename) { | |||||
if (!filename) | |||||
return 0; | |||||
if (access(filename, R_OK)) { | |||||
warn("could not open file: %s", filename); | |||||
return 0; | |||||
} else { | |||||
if (fileidx == filecnt) { | |||||
filecnt *= 2; | |||||
filenames = (char**) s_realloc(filenames, filecnt * sizeof(char*)); | |||||
} | |||||
filenames[fileidx++] = filename; | |||||
return 1; | |||||
} | |||||
} | |||||
int fncmp(const void *a, const void *b) { | int fncmp(const void *a, const void *b) { | ||||
return strcoll(*((char* const*) a), *((char* const*) b)); | return strcoll(*((char* const*) a), *((char* const*) b)); | ||||
} | } | ||||
@@ -187,20 +177,21 @@ int main(int argc, char **argv) { | |||||
filenames = (char**) s_malloc(filecnt * sizeof(char*)); | filenames = (char**) s_malloc(filecnt * sizeof(char*)); | ||||
fileidx = 0; | fileidx = 0; | ||||
/* build file list: */ | |||||
if (options->from_stdin) { | if (options->from_stdin) { | ||||
while ((len = getline(&filename, &n, stdin)) > 0) { | while ((len = getline(&filename, &n, stdin)) > 0) { | ||||
if (filename[len-1] == '\n') | if (filename[len-1] == '\n') | ||||
filename[len-1] = '\0'; | filename[len-1] = '\0'; | ||||
if (!*filename || !check_append(filename)) | |||||
if (!*filename || !check_add_file(filename)) | |||||
free(filename); | free(filename); | ||||
filename = NULL; | filename = NULL; | ||||
} | } | ||||
} else { | } else { | ||||
for (i = 0; i < options->filecnt; ++i) { | |||||
for (i = 0; i < options->filecnt; i++) { | |||||
filename = options->filenames[i]; | filename = options->filenames[i]; | ||||
if (stat(filename, &fstats) || !S_ISDIR(fstats.st_mode)) { | if (stat(filename, &fstats) || !S_ISDIR(fstats.st_mode)) { | ||||
check_append(filename); | |||||
check_add_file(filename); | |||||
} else { | } else { | ||||
if (!options->recursive) { | if (!options->recursive) { | ||||
warn("ignoring directory: %s", filename); | warn("ignoring directory: %s", filename); | ||||
@@ -212,7 +203,7 @@ int main(int argc, char **argv) { | |||||
} | } | ||||
start = fileidx; | start = fileidx; | ||||
while ((filename = r_readdir(&dir))) { | while ((filename = r_readdir(&dir))) { | ||||
if (!check_append(filename)) | |||||
if (!check_add_file(filename)) | |||||
free((void*) filename); | free((void*) filename); | ||||
} | } | ||||
r_closedir(&dir); | r_closedir(&dir); | ||||
@@ -252,550 +243,3 @@ int main(int argc, char **argv) { | |||||
return 0; | return 0; | ||||
} | } | ||||
int run_command(const char *cline, Bool reload) { | |||||
int fncnt, fnlen; | |||||
char *cn, *cmdline; | |||||
const char *co, *fname; | |||||
pid_t pid; | |||||
int ret, status; | |||||
if (!cline || !*cline) | |||||
return 0; | |||||
fncnt = 0; | |||||
co = cline - 1; | |||||
while ((co = strchr(co + 1, '#'))) | |||||
++fncnt; | |||||
if (!fncnt) | |||||
return 0; | |||||
ret = 0; | |||||
fname = filenames[mode == MODE_NORMAL ? fileidx : tns.sel]; | |||||
fnlen = strlen(fname); | |||||
cn = cmdline = (char*) s_malloc((strlen(cline) + fncnt * (fnlen + 2)) * | |||||
sizeof(char)); | |||||
/* replace all '#' with filename */ | |||||
for (co = cline; *co; ++co) { | |||||
if (*co == '#') { | |||||
*cn++ = '"'; | |||||
strcpy(cn, fname); | |||||
cn += fnlen; | |||||
*cn++ = '"'; | |||||
} else { | |||||
*cn++ = *co; | |||||
} | |||||
} | |||||
*cn = '\0'; | |||||
if ((pid = fork()) == 0) { | |||||
execlp("/bin/sh", "/bin/sh", "-c", cmdline, NULL); | |||||
warn("could not exec: /bin/sh"); | |||||
exit(1); | |||||
} else if (pid < 0) { | |||||
warn("could not fork. command line was: %s", cmdline); | |||||
} else if (reload) { | |||||
waitpid(pid, &status, 0); | |||||
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) | |||||
ret = 1; | |||||
else | |||||
warn("child exited with non-zero return value: %d. command line was: %s", | |||||
WEXITSTATUS(status), cmdline); | |||||
} | |||||
free(cmdline); | |||||
return ret; | |||||
} | |||||
/* event handling */ | |||||
/* timeouts in milliseconds: */ | |||||
enum { | |||||
TO_WIN_RESIZE = 75, | |||||
TO_IMAGE_DRAG = 1, | |||||
TO_CURSOR_HIDE = 1500, | |||||
TO_THUMBS_LOAD = 200 | |||||
}; | |||||
int timo_cursor; | |||||
int timo_redraw; | |||||
unsigned char drag; | |||||
int mox, moy; | |||||
void redraw() { | |||||
if (mode == MODE_NORMAL) { | |||||
img_render(&img, &win); | |||||
if (timo_cursor) | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
else if (!drag) | |||||
win_set_cursor(&win, CURSOR_NONE); | |||||
} else { | |||||
tns_render(&tns, &win); | |||||
} | |||||
update_title(); | |||||
timo_redraw = 0; | |||||
} | |||||
void on_keypress(XKeyEvent *kev) { | |||||
int x, y; | |||||
unsigned int w, h; | |||||
char key; | |||||
KeySym ksym; | |||||
int changed, ctrl; | |||||
if (!kev) | |||||
return; | |||||
XLookupString(kev, &key, 1, &ksym, NULL); | |||||
changed = 0; | |||||
ctrl = CLEANMASK(kev->state) & ControlMask; | |||||
/* external commands from commands.h */ | |||||
if (EXT_COMMANDS && ctrl) { | |||||
for (x = 0; x < LEN(commands); ++x) { | |||||
if (commands[x].key == key) { | |||||
win_set_cursor(&win, CURSOR_WATCH); | |||||
if (run_command(commands[x].cmdline, commands[x].reload)) { | |||||
if (mode == MODE_NORMAL) { | |||||
if (fileidx < tns.cnt) | |||||
tns_load(&tns, fileidx, filenames[fileidx], 1); | |||||
img_close(&img, 1); | |||||
load_image(fileidx); | |||||
} else { | |||||
if (!tns_load(&tns, tns.sel, filenames[tns.sel], 0)) { | |||||
remove_file(tns.sel, 0); | |||||
tns.dirty = 1; | |||||
if (tns.sel >= tns.cnt) | |||||
tns.sel = tns.cnt - 1; | |||||
} | |||||
} | |||||
redraw(); | |||||
} | |||||
if (mode == MODE_THUMBS) | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
else if (!timo_cursor) | |||||
win_set_cursor(&win, CURSOR_NONE); | |||||
return; | |||||
} | |||||
} | |||||
} | |||||
if (mode == MODE_NORMAL) { | |||||
switch (ksym) { | |||||
/* navigate image list */ | |||||
case XK_n: | |||||
case XK_space: | |||||
if (fileidx + 1 < filecnt) | |||||
changed = load_image(fileidx + 1); | |||||
break; | |||||
case XK_p: | |||||
case XK_BackSpace: | |||||
if (fileidx > 0) | |||||
changed = load_image(fileidx - 1); | |||||
break; | |||||
case XK_bracketleft: | |||||
if (fileidx != 0) | |||||
changed = load_image(MAX(0, fileidx - 10)); | |||||
break; | |||||
case XK_bracketright: | |||||
if (fileidx != filecnt - 1) | |||||
changed = load_image(MIN(fileidx + 10, filecnt - 1)); | |||||
break; | |||||
case XK_g: | |||||
if (fileidx != 0) | |||||
changed = load_image(0); | |||||
break; | |||||
case XK_G: | |||||
if (fileidx != filecnt - 1) | |||||
changed = load_image(filecnt - 1); | |||||
break; | |||||
/* zooming */ | |||||
case XK_plus: | |||||
case XK_equal: | |||||
case XK_KP_Add: | |||||
changed = img_zoom_in(&img, &win); | |||||
break; | |||||
case XK_minus: | |||||
case XK_KP_Subtract: | |||||
changed = img_zoom_out(&img, &win); | |||||
break; | |||||
case XK_0: | |||||
case XK_KP_0: | |||||
changed = img_zoom(&img, &win, 1.0); | |||||
break; | |||||
case XK_w: | |||||
if ((changed = img_fit_win(&img, &win))) | |||||
img_center(&img, &win); | |||||
break; | |||||
/* panning */ | |||||
case XK_h: | |||||
case XK_Left: | |||||
changed = img_pan(&img, &win, DIR_LEFT, ctrl); | |||||
break; | |||||
case XK_j: | |||||
case XK_Down: | |||||
changed = img_pan(&img, &win, DIR_DOWN, ctrl); | |||||
break; | |||||
case XK_k: | |||||
case XK_Up: | |||||
changed = img_pan(&img, &win, DIR_UP, ctrl); | |||||
break; | |||||
case XK_l: | |||||
case XK_Right: | |||||
changed = img_pan(&img, &win, DIR_RIGHT, ctrl); | |||||
break; | |||||
case XK_Prior: | |||||
changed = img_pan(&img, &win, DIR_UP, 1); | |||||
break; | |||||
case XK_Next: | |||||
changed = img_pan(&img, &win, DIR_DOWN, 1); | |||||
break; | |||||
case XK_H: | |||||
changed = img_pan_edge(&img, &win, DIR_LEFT); | |||||
break; | |||||
case XK_J: | |||||
changed = img_pan_edge(&img, &win, DIR_DOWN); | |||||
break; | |||||
case XK_K: | |||||
changed = img_pan_edge(&img, &win, DIR_UP); | |||||
break; | |||||
case XK_L: | |||||
changed = img_pan_edge(&img, &win, DIR_RIGHT); | |||||
break; | |||||
/* rotation */ | |||||
case XK_less: | |||||
img_rotate_left(&img, &win); | |||||
changed = 1; | |||||
break; | |||||
case XK_greater: | |||||
img_rotate_right(&img, &win); | |||||
changed = 1; | |||||
break; | |||||
/* control window */ | |||||
case XK_W: | |||||
x = MAX(0, win.x + img.x); | |||||
y = MAX(0, win.y + img.y); | |||||
w = img.w * img.zoom; | |||||
h = img.h * img.zoom; | |||||
if ((changed = win_moveresize(&win, x, y, w, h))) { | |||||
img.x = x - win.x; | |||||
img.y = y - win.y; | |||||
} | |||||
break; | |||||
/* switch to thumbnail mode */ | |||||
case XK_Return: | |||||
if (!tns.thumbs) | |||||
tns_init(&tns, filecnt); | |||||
img_close(&img, 0); | |||||
mode = MODE_THUMBS; | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
timo_cursor = 0; | |||||
tns.sel = fileidx; | |||||
changed = tns.dirty = 1; | |||||
break; | |||||
/* miscellaneous */ | |||||
case XK_a: | |||||
img_toggle_antialias(&img); | |||||
changed = 1; | |||||
break; | |||||
case XK_A: | |||||
img.alpha ^= 1; | |||||
changed = 1; | |||||
break; | |||||
case XK_D: | |||||
remove_file(fileidx, 1); | |||||
changed = load_image(fileidx >= filecnt ? filecnt - 1 : fileidx); | |||||
break; | |||||
case XK_r: | |||||
changed = load_image(fileidx); | |||||
break; | |||||
} | |||||
} else { | |||||
/* thumbnail mode */ | |||||
switch (ksym) { | |||||
/* open selected image */ | |||||
case XK_Return: | |||||
load_image(tns.sel); | |||||
mode = MODE_NORMAL; | |||||
changed = 1; | |||||
break; | |||||
/* move selection */ | |||||
case XK_h: | |||||
case XK_Left: | |||||
changed = tns_move_selection(&tns, &win, DIR_LEFT); | |||||
break; | |||||
case XK_j: | |||||
case XK_Down: | |||||
changed = tns_move_selection(&tns, &win, DIR_DOWN); | |||||
break; | |||||
case XK_k: | |||||
case XK_Up: | |||||
changed = tns_move_selection(&tns, &win, DIR_UP); | |||||
break; | |||||
case XK_l: | |||||
case XK_Right: | |||||
changed = tns_move_selection(&tns, &win, DIR_RIGHT); | |||||
break; | |||||
case XK_g: | |||||
if (tns.sel != 0) { | |||||
tns.sel = 0; | |||||
changed = tns.dirty = 1; | |||||
} | |||||
break; | |||||
case XK_G: | |||||
if (tns.sel != tns.cnt - 1) { | |||||
tns.sel = tns.cnt - 1; | |||||
changed = tns.dirty = 1; | |||||
} | |||||
break; | |||||
/* miscellaneous */ | |||||
case XK_D: | |||||
if (tns.sel < tns.cnt) { | |||||
remove_file(tns.sel, 1); | |||||
changed = tns.dirty = 1; | |||||
if (tns.sel >= tns.cnt) | |||||
tns.sel = tns.cnt - 1; | |||||
} | |||||
break; | |||||
} | |||||
} | |||||
/* common key mappings */ | |||||
switch (ksym) { | |||||
case XK_q: | |||||
cleanup(); | |||||
exit(0); | |||||
case XK_f: | |||||
win_toggle_fullscreen(&win); | |||||
if (mode == MODE_NORMAL) | |||||
img.checkpan = 1; | |||||
else | |||||
tns.dirty = 1; | |||||
timo_redraw = TO_WIN_RESIZE; | |||||
break; | |||||
} | |||||
if (changed) | |||||
redraw(); | |||||
} | |||||
void on_buttonpress(XButtonEvent *bev) { | |||||
int changed, sel; | |||||
unsigned int mask; | |||||
if (!bev) | |||||
return; | |||||
mask = CLEANMASK(bev->state); | |||||
changed = 0; | |||||
if (mode == MODE_NORMAL) { | |||||
if (!drag) { | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
timo_cursor = TO_CURSOR_HIDE; | |||||
} | |||||
switch (bev->button) { | |||||
case Button1: | |||||
if (fileidx + 1 < filecnt) | |||||
changed = load_image(fileidx + 1); | |||||
break; | |||||
case Button2: | |||||
mox = bev->x; | |||||
moy = bev->y; | |||||
win_set_cursor(&win, CURSOR_HAND); | |||||
timo_cursor = 0; | |||||
drag = 1; | |||||
break; | |||||
case Button3: | |||||
if (fileidx > 0) | |||||
changed = load_image(fileidx - 1); | |||||
break; | |||||
case Button4: | |||||
if (mask == ControlMask) | |||||
changed = img_zoom_in(&img, &win); | |||||
else if (mask == ShiftMask) | |||||
changed = img_pan(&img, &win, DIR_LEFT, 0); | |||||
else | |||||
changed = img_pan(&img, &win, DIR_UP, 0); | |||||
break; | |||||
case Button5: | |||||
if (mask == ControlMask) | |||||
changed = img_zoom_out(&img, &win); | |||||
else if (mask == ShiftMask) | |||||
changed = img_pan(&img, &win, DIR_RIGHT, 0); | |||||
else | |||||
changed = img_pan(&img, &win, DIR_DOWN, 0); | |||||
break; | |||||
case 6: | |||||
changed = img_pan(&img, &win, DIR_LEFT, 0); | |||||
break; | |||||
case 7: | |||||
changed = img_pan(&img, &win, DIR_RIGHT, 0); | |||||
break; | |||||
} | |||||
} else { | |||||
/* thumbnail mode */ | |||||
switch (bev->button) { | |||||
case Button1: | |||||
if ((sel = tns_translate(&tns, bev->x, bev->y)) >= 0) { | |||||
if (sel == tns.sel) { | |||||
load_image(tns.sel); | |||||
mode = MODE_NORMAL; | |||||
timo_cursor = TO_CURSOR_HIDE; | |||||
} else { | |||||
tns_highlight(&tns, &win, tns.sel, False); | |||||
tns_highlight(&tns, &win, sel, True); | |||||
tns.sel = sel; | |||||
} | |||||
changed = 1; | |||||
break; | |||||
} | |||||
break; | |||||
case Button4: | |||||
changed = tns_scroll(&tns, DIR_UP); | |||||
break; | |||||
case Button5: | |||||
changed = tns_scroll(&tns, DIR_DOWN); | |||||
break; | |||||
} | |||||
} | |||||
if (changed) | |||||
redraw(); | |||||
} | |||||
void on_motionnotify(XMotionEvent *mev) { | |||||
if (!mev) | |||||
return; | |||||
if (mev->x >= 0 && mev->x <= win.w && mev->y >= 0 && mev->y <= win.h) { | |||||
if (img_move(&img, &win, mev->x - mox, mev->y - moy)) | |||||
timo_redraw = TO_IMAGE_DRAG; | |||||
mox = mev->x; | |||||
moy = mev->y; | |||||
} | |||||
} | |||||
void run() { | |||||
int xfd, timeout; | |||||
fd_set fds; | |||||
struct timeval tt, t0, t1; | |||||
XEvent ev; | |||||
drag = 0; | |||||
timo_cursor = mode == MODE_NORMAL ? TO_CURSOR_HIDE : 0; | |||||
redraw(); | |||||
while (1) { | |||||
if (mode == MODE_THUMBS && tns.cnt < filecnt) { | |||||
win_set_cursor(&win, CURSOR_WATCH); | |||||
gettimeofday(&t0, 0); | |||||
while (tns.cnt < filecnt && !XPending(win.env.dpy)) { | |||||
if (tns_load(&tns, tns.cnt, filenames[tns.cnt], 0)) | |||||
++tns.cnt; | |||||
else | |||||
remove_file(tns.cnt, 0); | |||||
gettimeofday(&t1, 0); | |||||
if (TIMEDIFF(&t1, &t0) >= TO_THUMBS_LOAD) | |||||
break; | |||||
} | |||||
if (tns.cnt == filecnt) | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
if (!XPending(win.env.dpy)) { | |||||
redraw(); | |||||
continue; | |||||
} else { | |||||
timo_redraw = TO_THUMBS_LOAD; | |||||
} | |||||
} else if (timo_cursor || timo_redraw) { | |||||
gettimeofday(&t0, 0); | |||||
if (timo_cursor && timo_redraw) | |||||
timeout = MIN(timo_cursor, timo_redraw); | |||||
else if (timo_cursor) | |||||
timeout = timo_cursor; | |||||
else | |||||
timeout = timo_redraw; | |||||
MSEC_TO_TIMEVAL(timeout, &tt); | |||||
xfd = ConnectionNumber(win.env.dpy); | |||||
FD_ZERO(&fds); | |||||
FD_SET(xfd, &fds); | |||||
if (!XPending(win.env.dpy)) | |||||
select(xfd + 1, &fds, 0, 0, &tt); | |||||
gettimeofday(&t1, 0); | |||||
timeout = MIN(TIMEDIFF(&t1, &t0), timeout); | |||||
/* timeouts fired? */ | |||||
if (timo_cursor) { | |||||
timo_cursor = MAX(0, timo_cursor - timeout); | |||||
if (!timo_cursor) | |||||
win_set_cursor(&win, CURSOR_NONE); | |||||
} | |||||
if (timo_redraw) { | |||||
timo_redraw = MAX(0, timo_redraw - timeout); | |||||
if (!timo_redraw) | |||||
redraw(); | |||||
} | |||||
if (!XPending(win.env.dpy) && (timo_cursor || timo_redraw)) | |||||
continue; | |||||
} | |||||
if (!XNextEvent(win.env.dpy, &ev)) { | |||||
switch (ev.type) { | |||||
case KeyPress: | |||||
on_keypress(&ev.xkey); | |||||
break; | |||||
case ButtonPress: | |||||
on_buttonpress(&ev.xbutton); | |||||
break; | |||||
case ButtonRelease: | |||||
if (ev.xbutton.button == Button2) { | |||||
drag = 0; | |||||
if (mode == MODE_NORMAL) { | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
timo_cursor = TO_CURSOR_HIDE; | |||||
} | |||||
} | |||||
break; | |||||
case MotionNotify: | |||||
if (drag) { | |||||
on_motionnotify(&ev.xmotion); | |||||
} else if (mode == MODE_NORMAL) { | |||||
if (!timo_cursor) | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
timo_cursor = TO_CURSOR_HIDE; | |||||
} | |||||
break; | |||||
case ConfigureNotify: | |||||
if (win_configure(&win, &ev.xconfigure)) { | |||||
timo_redraw = TO_WIN_RESIZE; | |||||
if (mode == MODE_NORMAL) | |||||
img.checkpan = 1; | |||||
else | |||||
tns.dirty = 1; | |||||
} | |||||
break; | |||||
case ClientMessage: | |||||
if ((Atom) ev.xclient.data.l[0] == wm_delete_win) | |||||
return; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -17,6 +17,7 @@ | |||||
*/ | */ | ||||
#define _XOPEN_SOURCE | #define _XOPEN_SOURCE | ||||
#define _IMAGE_CONFIG | |||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
@@ -150,16 +150,16 @@ Pan to top image edge. | |||||
.B L | .B L | ||||
Pan to right image edge. | Pan to right image edge. | ||||
.TP | .TP | ||||
.BR Ctrl-h ", " Ctrl-Left | |||||
.BR { | |||||
Pan image one window width left. | Pan image one window width left. | ||||
.TP | .TP | ||||
.BR Ctrl-j ", " Ctrl-Down ", " PageDn | |||||
.BR PageDn | |||||
Pan image one window height down. | Pan image one window height down. | ||||
.TP | .TP | ||||
.BR Ctrl-k ", " Ctrl-Up ", " PageUp | |||||
.BR PageUp | |||||
Pan image one window height up. | Pan image one window height up. | ||||
.TP | .TP | ||||
.BR Ctrl-l ", " Ctrl-Right | |||||
.BR } | |||||
Pan image one window width right. | Pan image one window width right. | ||||
.SS Rotation | .SS Rotation | ||||
.TP | .TP | ||||
@@ -16,6 +16,8 @@ | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||||
*/ | */ | ||||
#define _THUMBS_CONFIG | |||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <sys/time.h> | #include <sys/time.h> | ||||
@@ -205,7 +207,7 @@ void tns_free(tns_t *tns) { | |||||
return; | return; | ||||
if (tns->thumbs) { | if (tns->thumbs) { | ||||
for (i = 0; i < tns->cnt; ++i) { | |||||
for (i = 0; i < tns->cnt; i++) { | |||||
if (tns->thumbs[i].im) { | if (tns->thumbs[i].im) { | ||||
imlib_context_set_image(tns->thumbs[i].im); | imlib_context_set_image(tns->thumbs[i].im); | ||||
imlib_free_image(); | imlib_free_image(); | ||||
@@ -337,7 +339,7 @@ void tns_render(tns_t *tns, win_t *win) { | |||||
tns->x = x = (win->w - MIN(cnt, tns->cols) * thumb_dim) / 2 + 5; | tns->x = x = (win->w - MIN(cnt, tns->cols) * thumb_dim) / 2 + 5; | ||||
tns->y = y = (win->h - (cnt / tns->cols + r) * thumb_dim) / 2 + 5; | tns->y = y = (win->h - (cnt / tns->cols + r) * thumb_dim) / 2 + 5; | ||||
for (i = 0; i < cnt; ++i) { | |||||
for (i = 0; i < cnt; i++) { | |||||
t = &tns->thumbs[tns->first + i]; | t = &tns->thumbs[tns->first + i]; | ||||
t->x = x + (THUMB_SIZE - t->w) / 2; | t->x = x + (THUMB_SIZE - t->w) / 2; | ||||
t->y = y + (THUMB_SIZE - t->h) / 2; | t->y = y + (THUMB_SIZE - t->h) / 2; | ||||
@@ -391,11 +393,11 @@ int tns_move_selection(tns_t *tns, win_t *win, direction_t dir) { | |||||
switch (dir) { | switch (dir) { | ||||
case DIR_LEFT: | case DIR_LEFT: | ||||
if (tns->sel > 0) | if (tns->sel > 0) | ||||
--tns->sel; | |||||
tns->sel--; | |||||
break; | break; | ||||
case DIR_RIGHT: | case DIR_RIGHT: | ||||
if (tns->sel < tns->cnt - 1) | if (tns->sel < tns->cnt - 1) | ||||
++tns->sel; | |||||
tns->sel++; | |||||
break; | break; | ||||
case DIR_UP: | case DIR_UP: | ||||
if (tns->sel >= tns->cols) | if (tns->sel >= tns->cols) | ||||
@@ -2,31 +2,25 @@ | |||||
#define TYPES_H | #define TYPES_H | ||||
typedef enum { | typedef enum { | ||||
MODE_NORMAL = 0, | |||||
MODE_NORMAL, | |||||
MODE_THUMBS | MODE_THUMBS | ||||
} appmode_t; | } appmode_t; | ||||
typedef struct { | |||||
char key; | |||||
int reload; | |||||
const char *cmdline; | |||||
} command_t; | |||||
typedef enum { | typedef enum { | ||||
DIR_LEFT = 0, | |||||
DIR_LEFT, | |||||
DIR_RIGHT, | DIR_RIGHT, | ||||
DIR_UP, | DIR_UP, | ||||
DIR_DOWN | DIR_DOWN | ||||
} direction_t; | } direction_t; | ||||
typedef enum { | typedef enum { | ||||
SCALE_DOWN = 0, | |||||
SCALE_DOWN, | |||||
SCALE_FIT, | SCALE_FIT, | ||||
SCALE_ZOOM | SCALE_ZOOM | ||||
} scalemode_t; | } scalemode_t; | ||||
typedef enum { | typedef enum { | ||||
CURSOR_ARROW = 0, | |||||
CURSOR_ARROW, | |||||
CURSOR_NONE, | CURSOR_NONE, | ||||
CURSOR_HAND, | CURSOR_HAND, | ||||
CURSOR_WATCH | CURSOR_WATCH | ||||
@@ -26,8 +26,10 @@ | |||||
#include "options.h" | #include "options.h" | ||||
#include "util.h" | #include "util.h" | ||||
#define DNAME_CNT 512 | |||||
#define FNAME_LEN 1024 | |||||
enum { | |||||
DNAME_CNT = 512, | |||||
FNAME_LEN = 1024 | |||||
}; | |||||
void cleanup(); | void cleanup(); | ||||
@@ -78,7 +80,7 @@ void size_readable(float *size, const char **unit) { | |||||
const char *units[] = { "", "K", "M", "G" }; | const char *units[] = { "", "K", "M", "G" }; | ||||
int i; | int i; | ||||
for (i = 0; i < LEN(units) && *size > 1024; ++i) | |||||
for (i = 0; i < LEN(units) && *size > 1024; i++) | |||||
*size /= 1024; | *size /= 1024; | ||||
*unit = units[MIN(i, LEN(units) - 1)]; | *unit = units[MIN(i, LEN(units) - 1)]; | ||||
} | } | ||||
@@ -16,6 +16,8 @@ | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||||
*/ | */ | ||||
#define _WINDOW_CONFIG | |||||
#include <string.h> | #include <string.h> | ||||
#include <X11/Xutil.h> | #include <X11/Xutil.h> | ||||