Major code refactoring
- Configurable key and mouse mappings in config.h - Put event handling code from main.c into events.[ch]
This commit is contained in:
		
							parent
							
								
									a271e16744
								
							
						
					
					
						commit
						b8ff1677b1
					
				
							
								
								
									
										4
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
									
									
									
									
								
							| @ -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) | ||||||
|  | |||||||
							
								
								
									
										124
									
								
								config.h
									
									
									
									
									
								
							
							
						
						
									
										124
									
								
								config.h
									
									
									
									
									
								
							| @ -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:                   *
 | /* default color for window background:                        *
 | ||||||
|  * (see X(7) "COLOR NAMES" section for valid values)      */ |  * (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?:     *
 | #endif | ||||||
|  * (also controllable via -d/-s/-Z/-z options)            * | #ifdef _IMAGE_CONFIG | ||||||
|  *   SCALE_DOWN: 100%, but fit large images into window,  * | 
 | ||||||
|  *   SCALE_FIT:  fit all images into window,              * | /* how should images be scaled when they are loaded?:          *
 | ||||||
|  *   SCALE_ZOOM: use current zoom level, 100% at startup  */ |  * (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: */ | #endif | ||||||
| enum { EXT_COMMANDS = 0 }; | #ifdef _MAPPINGS_CONFIG | ||||||
| 
 | 
 | ||||||
| /* external commands and corresponding key mappings:      */ | /* keyboard mappings for image and thumbnail mode:             */ | ||||||
| static const command_t commands[] = { | static const keymap_t keys[] = { | ||||||
| 	/* ctrl-...  reload?  command, '#' is replaced by filename */ | 	/* key              function            argument */ | ||||||
| 	{  ',',      1,       "jpegtran -rotate 270 -copy all -outfile # #" }, | 	{ XK_q,             quit,               None }, | ||||||
| 	{  '.',      1,       "jpegtran -rotate 90 -copy all -outfile # #" }, | 	{ XK_r,             reload,             None }, | ||||||
| 	{  '<',      1,       "mogrify -rotate -90 #" }, | 	{ XK_f,             toggle_fullscreen,  None }, | ||||||
| 	{  '>',      1,       "mogrify -rotate +90 #" } | 	{ 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 }, | ||||||
|  | 
 | ||||||
|  | 	{ 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[] = { | ||||||
|  | 	/* 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 | ||||||
|  | |||||||
							
								
								
									
										562
									
								
								events.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										562
									
								
								events.c
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										67
									
								
								events.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								events.h
									
									
									
									
									
										Normal file
									
								
							| @ -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 */ | ||||||
							
								
								
									
										6
									
								
								image.c
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								image.c
									
									
									
									
									
								
							| @ -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); | ||||||
| 	} | 	} | ||||||
|  | |||||||
							
								
								
									
										648
									
								
								main.c
									
									
									
									
									
								
							
							
						
						
									
										648
									
								
								main.c
									
									
									
									
									
								
							| @ -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 "events.h" | ||||||
| #include <X11/Xutil.h> |  | ||||||
| #include <X11/keysym.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 }; | enum { | ||||||
| 
 | 	TITLE_LEN = 256, | ||||||
| void run(); | 	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) { | 	if (new < 0 || new >= filecnt) | ||||||
| 		win_set_cursor(&win, CURSOR_WATCH); | 		return; | ||||||
| 		img_close(&img, 0); |  | ||||||
| 
 | 
 | ||||||
| 		while (!img_load(&img, filenames[new])) { | 	/* cursor is reset in redraw() */ | ||||||
| 			remove_file(new, 0); | 	win_set_cursor(&win, CURSOR_WATCH); | ||||||
| 			if (new >= filecnt) | 	img_close(&img, 0); | ||||||
| 				new = filecnt - 1; |  | ||||||
| 		} |  | ||||||
| 		fileidx = new; |  | ||||||
| 		if (!stat(filenames[new], &fstats)) |  | ||||||
| 			filesize = fstats.st_size; |  | ||||||
| 		else |  | ||||||
| 			filesize = 0; |  | ||||||
| 		 | 		 | ||||||
| 		/* cursor is reset in redraw() */ | 	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> | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								sxiv.1
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								sxiv.1
									
									
									
									
									
								
							| @ -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 | ||||||
|  | |||||||
							
								
								
									
										10
									
								
								thumbs.c
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								thumbs.c
									
									
									
									
									
								
							| @ -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) | ||||||
|  | |||||||
							
								
								
									
										14
									
								
								types.h
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								types.h
									
									
									
									
									
								
							| @ -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 | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								util.c
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								util.c
									
									
									
									
									
								
							| @ -26,8 +26,10 @@ | |||||||
| #include "options.h" | #include "options.h" | ||||||
| #include "util.h" | #include "util.h" | ||||||
| 
 | 
 | ||||||
| #define DNAME_CNT 512 | enum { | ||||||
| #define FNAME_LEN 1024 | 	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)]; | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								window.c
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								window.c
									
									
									
									
									
								
							| @ -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> | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Bert
						Bert