@@ -9,7 +9,7 @@ CFLAGS = -Wall -pedantic -O2 -DVERSION=\"$(VERSION)\" | |||||
LDFLAGS = | LDFLAGS = | ||||
LIBS = -lX11 -lImlib2 | LIBS = -lX11 -lImlib2 | ||||
SRC = events.o image.c main.c options.c thumbs.c util.c window.c | |||||
SRC = commands.c 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,4 +1,4 @@ | |||||
/* sxiv: events.c | |||||
/* sxiv: commands.c | |||||
* Copyright (c) 2011 Bert Muennich <muennich at informatik.hu-berlin.de> | * Copyright (c) 2011 Bert Muennich <muennich at informatik.hu-berlin.de> | ||||
* | * | ||||
* This program is free software; you can redistribute it and/or | * This program is free software; you can redistribute it and/or | ||||
@@ -16,36 +16,20 @@ | |||||
* Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301, USA. | * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
#define _GENERAL_CONFIG | |||||
#define _MAPPINGS_CONFIG | |||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <sys/time.h> | |||||
#include <sys/wait.h> | #include <sys/wait.h> | ||||
#include <X11/keysym.h> | |||||
#include <X11/Xutil.h> | |||||
#include "events.h" | |||||
#include "commands.h" | |||||
#include "image.h" | #include "image.h" | ||||
#include "thumbs.h" | #include "thumbs.h" | ||||
#include "types.h" | #include "types.h" | ||||
#include "util.h" | #include "util.h" | ||||
#include "window.h" | |||||
#include "config.h" | |||||
/* timeouts in milliseconds: */ | |||||
enum { | |||||
TO_WIN_RESIZE = 75, | |||||
TO_CURSOR_HIDE = 1500, | |||||
TO_THUMBS_LOAD = 200 | |||||
}; | |||||
void cleanup(); | void cleanup(); | ||||
void remove_file(int, unsigned char); | void remove_file(int, unsigned char); | ||||
void load_image(int); | void load_image(int); | ||||
void update_title(); | |||||
extern appmode_t mode; | extern appmode_t mode; | ||||
extern img_t img; | extern img_t img; | ||||
@@ -55,193 +39,8 @@ extern win_t win; | |||||
extern fileinfo_t *files; | extern fileinfo_t *files; | ||||
extern int filecnt, fileidx; | extern int filecnt, fileidx; | ||||
int timo_cursor; | |||||
int timo_redraw; | |||||
void redraw() { | |||||
if (mode == MODE_IMAGE) { | |||||
img_render(&img, &win); | |||||
if (timo_cursor) | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
else | |||||
win_set_cursor(&win, CURSOR_NONE); | |||||
} else { | |||||
tns_render(&tns, &win); | |||||
} | |||||
update_title(); | |||||
timo_redraw = 0; | |||||
} | |||||
Bool keymask(const keymap_t *k, unsigned int state) { | |||||
return (k->ctrl ? ControlMask : 0) == (state & ControlMask); | |||||
} | |||||
Bool buttonmask(const button_t *b, unsigned int state) { | |||||
return ((b->ctrl ? ControlMask : 0) | (b->shift ? ShiftMask : 0)) == | |||||
(state & (ControlMask | ShiftMask)); | |||||
} | |||||
void on_keypress(XKeyEvent *kev) { | |||||
int i; | |||||
KeySym ksym; | |||||
char key; | |||||
if (!kev) | |||||
return; | |||||
XLookupString(kev, &key, 1, &ksym, NULL); | |||||
for (i = 0; i < LEN(keys); i++) { | |||||
if (keys[i].ksym == ksym && keymask(&keys[i], kev->state)) { | |||||
if (keys[i].cmd && keys[i].cmd(keys[i].arg)) | |||||
redraw(); | |||||
return; | |||||
} | |||||
} | |||||
} | |||||
void on_buttonpress(XButtonEvent *bev) { | |||||
int i, sel; | |||||
if (!bev) | |||||
return; | |||||
if (mode == MODE_IMAGE) { | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
timo_cursor = TO_CURSOR_HIDE; | |||||
for (i = 0; i < LEN(buttons); i++) { | |||||
if (buttons[i].button == bev->button && | |||||
buttonmask(&buttons[i], bev->state)) | |||||
{ | |||||
if (buttons[i].cmd && buttons[i].cmd(buttons[i].arg)) | |||||
redraw(); | |||||
return; | |||||
} | |||||
} | |||||
} else { | |||||
/* thumbnail mode (hard-coded) */ | |||||
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_IMAGE; | |||||
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 run() { | |||||
int xfd, timeout; | |||||
fd_set fds; | |||||
struct timeval tt, t0, t1; | |||||
XEvent ev; | |||||
timo_cursor = mode == MODE_IMAGE ? TO_CURSOR_HIDE : 0; | |||||
redraw(); | |||||
while (1) { | |||||
if (mode == MODE_THUMB && 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, &files[tns.cnt], False, False)) | |||||
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)) { | |||||
/* handle events */ | |||||
switch (ev.type) { | |||||
case ButtonPress: | |||||
on_buttonpress(&ev.xbutton); | |||||
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_IMAGE) | |||||
img.checkpan = 1; | |||||
else | |||||
tns.dirty = 1; | |||||
} | |||||
break; | |||||
case KeyPress: | |||||
on_keypress(&ev.xkey); | |||||
break; | |||||
case MotionNotify: | |||||
if (!timo_cursor) | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
timo_cursor = TO_CURSOR_HIDE; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
/* command functions for key and button mappings: */ | |||||
extern int timo_cursor; | |||||
extern int timo_redraw; | |||||
int it_quit(arg_t a) { | int it_quit(arg_t a) { | ||||
cleanup(); | cleanup(); | ||||
@@ -593,7 +392,7 @@ int it_shell_cmd(arg_t a) { | |||||
end: | end: | ||||
if (mode == MODE_THUMB) | if (mode == MODE_THUMB) | ||||
win_set_cursor(&win, CURSOR_ARROW); | win_set_cursor(&win, CURSOR_ARROW); | ||||
/* else: cursor is reset in redraw() */ | |||||
/* else: cursor gets reset in redraw() */ | |||||
free(cmdline); | free(cmdline); | ||||
@@ -1,4 +1,4 @@ | |||||
/* sxiv: events.h | |||||
/* sxiv: commands.h | |||||
* Copyright (c) 2011 Bert Muennich <muennich at informatik.hu-berlin.de> | * Copyright (c) 2011 Bert Muennich <muennich at informatik.hu-berlin.de> | ||||
* | * | ||||
* This program is free software; you can redistribute it and/or | * This program is free software; you can redistribute it and/or | ||||
@@ -16,8 +16,8 @@ | |||||
* Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301, USA. | * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
#ifndef EVENTS_H | |||||
#define EVENTS_H | |||||
#ifndef COMMANDS_H | |||||
#define COMMANDS_H | |||||
#include <X11/Xlib.h> | #include <X11/Xlib.h> | ||||
@@ -39,9 +39,6 @@ typedef struct { | |||||
arg_t arg; | arg_t arg; | ||||
} button_t; | } button_t; | ||||
void run(); | |||||
/* command functions for key and button mappings: */ | |||||
int it_quit(arg_t); | int it_quit(arg_t); | ||||
int it_switch_mode(arg_t); | int it_switch_mode(arg_t); | ||||
int it_toggle_fullscreen(arg_t); | int it_toggle_fullscreen(arg_t); | ||||
@@ -63,4 +60,4 @@ int i_toggle_alpha(arg_t); | |||||
int it_open_with(arg_t); | int it_open_with(arg_t); | ||||
int it_shell_cmd(arg_t); | int it_shell_cmd(arg_t); | ||||
#endif /* EVENTS_H */ | |||||
#endif /* COMMANDS_H */ |
@@ -17,11 +17,15 @@ | |||||
*/ | */ | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <stdio.h> | |||||
#include <string.h> | #include <string.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <sys/stat.h> | #include <sys/stat.h> | ||||
#include <sys/time.h> | |||||
#include <X11/Xutil.h> | |||||
#include <X11/keysym.h> | |||||
#include "events.h" | |||||
#include "commands.h" | |||||
#include "image.h" | #include "image.h" | ||||
#include "options.h" | #include "options.h" | ||||
#include "thumbs.h" | #include "thumbs.h" | ||||
@@ -29,6 +33,9 @@ | |||||
#include "util.h" | #include "util.h" | ||||
#include "window.h" | #include "window.h" | ||||
#define _MAPPINGS_CONFIG | |||||
#include "config.h" | |||||
enum { | enum { | ||||
TITLE_LEN = 256, | TITLE_LEN = 256, | ||||
FNAME_CNT = 1024 | FNAME_CNT = 1024 | ||||
@@ -45,6 +52,9 @@ size_t filesize; | |||||
char win_title[TITLE_LEN]; | char win_title[TITLE_LEN]; | ||||
int timo_cursor; | |||||
int timo_redraw; | |||||
void cleanup() { | void cleanup() { | ||||
static int in = 0; | static int in = 0; | ||||
@@ -115,7 +125,7 @@ void load_image(int new) { | |||||
if (new < 0 || new >= filecnt) | if (new < 0 || new >= filecnt) | ||||
return; | return; | ||||
/* cursor is reset in redraw() */ | |||||
/* cursor gets reset in redraw() */ | |||||
win_set_cursor(&win, CURSOR_WATCH); | win_set_cursor(&win, CURSOR_WATCH); | ||||
img_close(&img, 0); | img_close(&img, 0); | ||||
@@ -158,6 +168,188 @@ void update_title() { | |||||
win_set_title(&win, win_title); | win_set_title(&win, win_title); | ||||
} | } | ||||
void redraw() { | |||||
if (mode == MODE_IMAGE) { | |||||
img_render(&img, &win); | |||||
if (timo_cursor) | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
else | |||||
win_set_cursor(&win, CURSOR_NONE); | |||||
} else { | |||||
tns_render(&tns, &win); | |||||
} | |||||
update_title(); | |||||
timo_redraw = 0; | |||||
} | |||||
Bool keymask(const keymap_t *k, unsigned int state) { | |||||
return (k->ctrl ? ControlMask : 0) == (state & ControlMask); | |||||
} | |||||
Bool buttonmask(const button_t *b, unsigned int state) { | |||||
return ((b->ctrl ? ControlMask : 0) | (b->shift ? ShiftMask : 0)) == | |||||
(state & (ControlMask | ShiftMask)); | |||||
} | |||||
void on_keypress(XKeyEvent *kev) { | |||||
int i; | |||||
KeySym ksym; | |||||
char key; | |||||
if (!kev) | |||||
return; | |||||
XLookupString(kev, &key, 1, &ksym, NULL); | |||||
for (i = 0; i < LEN(keys); i++) { | |||||
if (keys[i].ksym == ksym && keymask(&keys[i], kev->state)) { | |||||
if (keys[i].cmd && keys[i].cmd(keys[i].arg)) | |||||
redraw(); | |||||
return; | |||||
} | |||||
} | |||||
} | |||||
void on_buttonpress(XButtonEvent *bev) { | |||||
int i, sel; | |||||
if (!bev) | |||||
return; | |||||
if (mode == MODE_IMAGE) { | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
timo_cursor = TO_CURSOR_HIDE; | |||||
for (i = 0; i < LEN(buttons); i++) { | |||||
if (buttons[i].button == bev->button && | |||||
buttonmask(&buttons[i], bev->state)) | |||||
{ | |||||
if (buttons[i].cmd && buttons[i].cmd(buttons[i].arg)) | |||||
redraw(); | |||||
return; | |||||
} | |||||
} | |||||
} else { | |||||
/* thumbnail mode (hard-coded) */ | |||||
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_IMAGE; | |||||
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 run() { | |||||
int xfd, timeout; | |||||
fd_set fds; | |||||
struct timeval tt, t0, t1; | |||||
XEvent ev; | |||||
timo_cursor = mode == MODE_IMAGE ? TO_CURSOR_HIDE : 0; | |||||
redraw(); | |||||
while (1) { | |||||
if (mode == MODE_THUMB && 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, &files[tns.cnt], False, False)) | |||||
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)) { | |||||
/* handle events */ | |||||
switch (ev.type) { | |||||
case ButtonPress: | |||||
on_buttonpress(&ev.xbutton); | |||||
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_IMAGE) | |||||
img.checkpan = 1; | |||||
else | |||||
tns.dirty = 1; | |||||
} | |||||
break; | |||||
case KeyPress: | |||||
on_keypress(&ev.xkey); | |||||
break; | |||||
case MotionNotify: | |||||
if (!timo_cursor) | |||||
win_set_cursor(&win, CURSOR_ARROW); | |||||
timo_cursor = TO_CURSOR_HIDE; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
int fncmp(const void *a, const void *b) { | int fncmp(const void *a, const void *b) { | ||||
return strcoll(((fileinfo_t*) a)->name, ((fileinfo_t*) b)->name); | return strcoll(((fileinfo_t*) a)->name, ((fileinfo_t*) b)->name); | ||||
} | } | ||||
@@ -31,4 +31,11 @@ typedef struct { | |||||
const char *path; /* always absolute */ | const char *path; /* always absolute */ | ||||
} fileinfo_t; | } fileinfo_t; | ||||
/* timeouts in milliseconds: */ | |||||
enum { | |||||
TO_WIN_RESIZE = 75, | |||||
TO_CURSOR_HIDE = 1500, | |||||
TO_THUMBS_LOAD = 200 | |||||
}; | |||||
#endif /* TYPES_H */ | #endif /* TYPES_H */ |