Merge branch 'tcache'
Conflicts: main.c
This commit is contained in:
		
						commit
						bac610ddc4
					
				
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | |||||||
| all: sxiv | all: sxiv | ||||||
| 
 | 
 | ||||||
| VERSION=git-20110407 | VERSION=git-20110408 | ||||||
| 
 | 
 | ||||||
| CC?=gcc | CC?=gcc | ||||||
| PREFIX?=/usr/local | PREFIX?=/usr/local | ||||||
|  | |||||||
| @ -36,6 +36,7 @@ sxiv supports the following command-line options: | |||||||
| 
 | 
 | ||||||
|     -a           Display all given files, do not filter out unsupported files |     -a           Display all given files, do not filter out unsupported files | ||||||
|                  (shorter startup time for long file list or slow file types) |                  (shorter startup time for long file list or slow file types) | ||||||
|  |     -C           Remove all orphaned cache files from thumbnail cache and exit | ||||||
|     -d           Scale all images to 100%, but fit large images into window |     -d           Scale all images to 100%, but fit large images into window | ||||||
|     -F           Use size-hints to make the window fixed/floating |     -F           Use size-hints to make the window fixed/floating | ||||||
|     -f           Start in fullscreen mode |     -f           Start in fullscreen mode | ||||||
|  | |||||||
							
								
								
									
										110
									
								
								main.c
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								main.c
									
									
									
									
									
								
							| @ -19,7 +19,6 @@ | |||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <dirent.h> |  | ||||||
| #include <sys/select.h> | #include <sys/select.h> | ||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
| #include <sys/time.h> | #include <sys/time.h> | ||||||
| @ -48,7 +47,6 @@ typedef enum { | |||||||
| 
 | 
 | ||||||
| void update_title(); | void update_title(); | ||||||
| int check_append(const char*); | int check_append(const char*); | ||||||
| void read_dir_rec(const char*); |  | ||||||
| void run(); | void run(); | ||||||
| 
 | 
 | ||||||
| appmode_t mode; | appmode_t mode; | ||||||
| @ -56,7 +54,6 @@ img_t img; | |||||||
| tns_t tns; | tns_t tns; | ||||||
| win_t win; | win_t win; | ||||||
| 
 | 
 | ||||||
| #define DNAME_CNT 512 |  | ||||||
| #define FNAME_CNT 1024 | #define FNAME_CNT 1024 | ||||||
| const char **filenames; | const char **filenames; | ||||||
| int filecnt, fileidx; | int filecnt, fileidx; | ||||||
| @ -95,13 +92,24 @@ int load_image(int new) { | |||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | int fncmp(const void *a, const void *b) { | ||||||
|  | 	return strcoll(*((char* const*) a), *((char* const*) b)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int main(int argc, char **argv) { | int main(int argc, char **argv) { | ||||||
| 	int i; | 	int i, start; | ||||||
| 	const char *filename; | 	const char *filename; | ||||||
| 	struct stat fstats; | 	struct stat fstats; | ||||||
|  | 	r_dir_t dir; | ||||||
| 
 | 
 | ||||||
| 	parse_options(argc, argv); | 	parse_options(argc, argv); | ||||||
| 
 | 
 | ||||||
|  | 	if (options->clean_cache) { | ||||||
|  | 		tns_init(&tns, 0); | ||||||
|  | 		tns_clear_cache(&tns); | ||||||
|  | 		exit(0); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if (!options->filecnt) { | 	if (!options->filecnt) { | ||||||
| 		print_usage(); | 		print_usage(); | ||||||
| 		exit(1); | 		exit(1); | ||||||
| @ -123,13 +131,26 @@ int main(int argc, char **argv) { | |||||||
| 	} 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 (options->recursive) | 			if (stat(filename, &fstats) || !S_ISDIR(fstats.st_mode)) { | ||||||
| 					read_dir_rec(filename); |  | ||||||
| 				else |  | ||||||
| 					warn("ignoring directory: %s", filename); |  | ||||||
| 			} else { |  | ||||||
| 				check_append(filename); | 				check_append(filename); | ||||||
|  | 			} else { | ||||||
|  | 				if (!options->recursive) { | ||||||
|  | 					warn("ignoring directory: %s", filename); | ||||||
|  | 					continue; | ||||||
|  | 				} | ||||||
|  | 				if (r_opendir(&dir, filename)) { | ||||||
|  | 					warn("could not open directory: %s", filename); | ||||||
|  | 					continue; | ||||||
|  | 				} | ||||||
|  | 				start = fileidx; | ||||||
|  | 				while ((filename = r_readdir(&dir))) { | ||||||
|  | 					if (!check_append(filename)) | ||||||
|  | 						free((void*) filename); | ||||||
|  | 				} | ||||||
|  | 				r_closedir(&dir); | ||||||
|  | 				if (fileidx - start > 1) | ||||||
|  | 					qsort(filenames + start, fileidx - start, sizeof(char*), fncmp); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @ -215,75 +236,6 @@ int check_append(const char *filename) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int fncmp(const void *a, const void *b) { |  | ||||||
| 	return strcoll(*((char* const*) a), *((char* const*) b)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void read_dir_rec(const char *dirname) { |  | ||||||
| 	char *filename; |  | ||||||
| 	const char **dirnames; |  | ||||||
| 	int dircnt, diridx; |  | ||||||
| 	int fcnt, fstart; |  | ||||||
| 	unsigned char first; |  | ||||||
| 	size_t len; |  | ||||||
| 	DIR *dir; |  | ||||||
| 	struct dirent *dentry; |  | ||||||
| 	struct stat fstats; |  | ||||||
| 
 |  | ||||||
| 	if (!dirname) |  | ||||||
| 		return; |  | ||||||
| 
 |  | ||||||
| 	dircnt = DNAME_CNT; |  | ||||||
| 	diridx = first = 1; |  | ||||||
| 	dirnames = (const char**) s_malloc(dircnt * sizeof(const char*)); |  | ||||||
| 	dirnames[0] = dirname; |  | ||||||
| 
 |  | ||||||
| 	fcnt = 0; |  | ||||||
| 	fstart = fileidx; |  | ||||||
| 
 |  | ||||||
| 	while (diridx > 0) { |  | ||||||
| 		dirname = dirnames[--diridx]; |  | ||||||
| 		if (!(dir = opendir(dirname))) { |  | ||||||
| 			warn("could not open directory: %s", dirname); |  | ||||||
| 		} else { |  | ||||||
| 			while ((dentry = readdir(dir))) { |  | ||||||
| 				if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) |  | ||||||
| 					continue; |  | ||||||
| 
 |  | ||||||
| 				len = strlen(dirname) + strlen(dentry->d_name) + 2; |  | ||||||
| 				filename = (char*) s_malloc(len * sizeof(char)); |  | ||||||
| 				snprintf(filename, len, "%s%s%s", dirname, |  | ||||||
| 				         dirname[strlen(dirname)-1] == '/' ? "" : "/", dentry->d_name); |  | ||||||
| 
 |  | ||||||
| 				if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) { |  | ||||||
| 					if (diridx == dircnt) { |  | ||||||
| 						dircnt *= 2; |  | ||||||
| 						dirnames = (const char**) s_realloc(dirnames, |  | ||||||
| 						                                    dircnt * sizeof(const char*)); |  | ||||||
| 					} |  | ||||||
| 					dirnames[diridx++] = filename; |  | ||||||
| 				} else { |  | ||||||
| 					if (check_append(filename)) |  | ||||||
| 						++fcnt; |  | ||||||
| 					else |  | ||||||
| 						free(filename); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			closedir(dir); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (!first) |  | ||||||
| 			free((void*) dirname); |  | ||||||
| 		else |  | ||||||
| 			first = 0; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (fcnt > 1) |  | ||||||
| 		qsort(filenames + fstart, fcnt, sizeof(char*), fncmp); |  | ||||||
| 
 |  | ||||||
| 	free(dirnames); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #if EXT_COMMANDS | #if EXT_COMMANDS | ||||||
| int run_command(const char *cline, Bool reload) { | int run_command(const char *cline, Bool reload) { | ||||||
| 	int fncnt, fnlen; | 	int fncnt, fnlen; | ||||||
|  | |||||||
| @ -31,7 +31,7 @@ options_t _options; | |||||||
| const options_t *options = (const options_t*) &_options; | const options_t *options = (const options_t*) &_options; | ||||||
| 
 | 
 | ||||||
| void print_usage() { | void print_usage() { | ||||||
| 	printf("usage: sxiv [-adFfhpqrstvZ] [-g GEOMETRY] [-z ZOOM] FILES...\n"); | 	printf("usage: sxiv [-aCdFfhpqrstvZ] [-g GEOMETRY] [-z ZOOM] FILES...\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void print_version() { | void print_version() { | ||||||
| @ -53,9 +53,10 @@ void parse_options(int argc, char **argv) { | |||||||
| 
 | 
 | ||||||
| 	_options.all = 0; | 	_options.all = 0; | ||||||
| 	_options.quiet = 0; | 	_options.quiet = 0; | ||||||
|  | 	_options.clean_cache = 0; | ||||||
| 	_options.recursive = 0; | 	_options.recursive = 0; | ||||||
| 
 | 
 | ||||||
| 	while ((opt = getopt(argc, argv, "adFfg:hpqrstvZz:")) != -1) { | 	while ((opt = getopt(argc, argv, "aCdFfg:hpqrstvZz:")) != -1) { | ||||||
| 		switch (opt) { | 		switch (opt) { | ||||||
| 			case '?': | 			case '?': | ||||||
| 				print_usage(); | 				print_usage(); | ||||||
| @ -63,6 +64,9 @@ void parse_options(int argc, char **argv) { | |||||||
| 			case 'a': | 			case 'a': | ||||||
| 				_options.all = 1; | 				_options.all = 1; | ||||||
| 				break; | 				break; | ||||||
|  | 			case 'C': | ||||||
|  | 				_options.clean_cache = 1; | ||||||
|  | 				break; | ||||||
| 			case 'd': | 			case 'd': | ||||||
| 				_options.scalemode = SCALE_DOWN; | 				_options.scalemode = SCALE_DOWN; | ||||||
| 				break; | 				break; | ||||||
|  | |||||||
| @ -37,6 +37,7 @@ typedef struct { | |||||||
| 
 | 
 | ||||||
| 	unsigned char all; | 	unsigned char all; | ||||||
| 	unsigned char quiet; | 	unsigned char quiet; | ||||||
|  | 	unsigned char clean_cache; | ||||||
| 	unsigned char recursive; | 	unsigned char recursive; | ||||||
| } options_t; | } options_t; | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										25
									
								
								sxiv.1
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								sxiv.1
									
									
									
									
									
								
							| @ -3,7 +3,7 @@ | |||||||
| sxiv \- Simple (or small or suckless) X Image Viewer | sxiv \- Simple (or small or suckless) X Image Viewer | ||||||
| .SH SYNOPSIS | .SH SYNOPSIS | ||||||
| .B sxiv | .B sxiv | ||||||
| .RB [ \-adFfhpqrstvZ ] | .RB [ \-aCdFfhpqrstvZ ] | ||||||
| .RB [ \-g | .RB [ \-g | ||||||
| .IR GEOMETRY ] | .IR GEOMETRY ] | ||||||
| .RB [ \-z | .RB [ \-z | ||||||
| @ -24,6 +24,9 @@ sxiv has two modes of operation: image and thumbnail mode. The default is image | |||||||
| mode, in which only the current image is shown. In thumbnail mode a grid of  | mode, in which only the current image is shown. In thumbnail mode a grid of  | ||||||
| small previews is displayed, making it easy to choose an image to open. | small previews is displayed, making it easy to choose an image to open. | ||||||
| .P | .P | ||||||
|  | sxiv can also cache its thumbnails. Please see the section THUMBNAIL CACHING | ||||||
|  | for information on how to enable this feature. | ||||||
|  | .P | ||||||
| Please note, that the fullscreen mode requires an EWMH/NetWM compliant window | Please note, that the fullscreen mode requires an EWMH/NetWM compliant window | ||||||
| manager. | manager. | ||||||
| .SH OPTIONS | .SH OPTIONS | ||||||
| @ -33,6 +36,9 @@ Display all given files, do not filter out unsupported files. This might result | |||||||
| in a much shorter startup time, when the file list is very long or contains | in a much shorter startup time, when the file list is very long or contains | ||||||
| large files of slow loadable types, e.g. gif and progressive jpg. | large files of slow loadable types, e.g. gif and progressive jpg. | ||||||
| .TP | .TP | ||||||
|  | .B \-C | ||||||
|  | Remove all orphaned cache files from the thumbnail cache directory and exit. | ||||||
|  | .TP | ||||||
| .B \-d | .B \-d | ||||||
| Scale all images to 100%, but fit large images into window. | Scale all images to 100%, but fit large images into window. | ||||||
| .TP | .TP | ||||||
| @ -191,6 +197,23 @@ Pan image left. | |||||||
| .TP | .TP | ||||||
| .B Shift+ScrollDown | .B Shift+ScrollDown | ||||||
| Pan image right. | Pan image right. | ||||||
|  | .SH THUMBNAIL CACHING | ||||||
|  | To enable thumbnail caching, please make sure to create the directory | ||||||
|  | .I ~/.sxiv/ | ||||||
|  | with write permissions. sxiv will then store all thumbnails inside this | ||||||
|  | directory, but it will not create this directory by itself. It rather uses the | ||||||
|  | existance of this directory as an affirmation, that the user wants thumbnails | ||||||
|  | to be cached. | ||||||
|  | .P | ||||||
|  | Use the command line option | ||||||
|  | .I \-C | ||||||
|  | to keep the cache directory clean by removing all orphaned cache files. | ||||||
|  | Additionally, run the following command afterwards inside the cache directory | ||||||
|  | to remove empty subdirectories: | ||||||
|  | .P | ||||||
|  | .RS | ||||||
|  | find -type d -empty -delete | ||||||
|  | .RE | ||||||
| .SH AUTHORS | .SH AUTHORS | ||||||
| .EX | .EX | ||||||
| Bert Muennich <ber.t at gmx.com> | Bert Muennich <ber.t at gmx.com> | ||||||
|  | |||||||
							
								
								
									
										198
									
								
								thumbs.c
									
									
									
									
									
								
							
							
						
						
									
										198
									
								
								thumbs.c
									
									
									
									
									
								
							| @ -18,49 +18,214 @@ | |||||||
| 
 | 
 | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  | #include <sys/time.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <unistd.h> | ||||||
| 
 | 
 | ||||||
| #include "config.h" | #include "config.h" | ||||||
| #include "thumbs.h" | #include "thumbs.h" | ||||||
| #include "util.h" | #include "util.h" | ||||||
| 
 | 
 | ||||||
| extern Imlib_Image *im_invalid; | extern Imlib_Image *im_invalid; | ||||||
|  | 
 | ||||||
| const int thumb_dim = THUMB_SIZE + 10; | const int thumb_dim = THUMB_SIZE + 10; | ||||||
|  | char *cache_dir = NULL; | ||||||
|  | 
 | ||||||
|  | int tns_cache_enabled() { | ||||||
|  | 	struct stat stats; | ||||||
|  | 
 | ||||||
|  | 	return cache_dir && !stat(cache_dir, &stats) && S_ISDIR(stats.st_mode) && | ||||||
|  | 	       !access(cache_dir, W_OK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | char* tns_cache_filename(const char *filename) { | ||||||
|  | 	size_t len; | ||||||
|  | 	char *cfile = NULL; | ||||||
|  | 	const char *abspath; | ||||||
|  | 
 | ||||||
|  | 	if (!cache_dir || !filename) | ||||||
|  | 		return NULL; | ||||||
|  | 	 | ||||||
|  | 	if (*filename != '/') { | ||||||
|  | 		if (!(abspath = absolute_path(filename))) | ||||||
|  | 			return NULL; | ||||||
|  | 	} else { | ||||||
|  | 		abspath = filename; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (strncmp(abspath, cache_dir, strlen(cache_dir))) { | ||||||
|  | 		len = strlen(cache_dir) + strlen(abspath) + 6; | ||||||
|  | 		cfile = (char*) s_malloc(len); | ||||||
|  | 		snprintf(cfile, len, "%s/%s.png", cache_dir, abspath + 1); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	if (abspath != filename) | ||||||
|  | 		free((void*) abspath); | ||||||
|  | 
 | ||||||
|  | 	return cfile; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Imlib_Image* tns_cache_load(const char *filename) { | ||||||
|  | 	char *cfile; | ||||||
|  | 	struct stat cstats, fstats; | ||||||
|  | 	Imlib_Image *im = NULL; | ||||||
|  | 
 | ||||||
|  | 	if (!filename) | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | 	if (stat(filename, &fstats)) | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | 	if ((cfile = tns_cache_filename(filename))) { | ||||||
|  | 		if (!stat(cfile, &cstats) && | ||||||
|  | 		    cstats.st_mtim.tv_sec == fstats.st_mtim.tv_sec && | ||||||
|  | 				cstats.st_mtim.tv_nsec == fstats.st_mtim.tv_nsec) | ||||||
|  | 		{ | ||||||
|  | 			im = imlib_load_image(cfile); | ||||||
|  | 		} | ||||||
|  | 		free(cfile); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return im; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void tns_cache_write(thumb_t *t, Bool force) { | ||||||
|  | 	char *cfile, *dirend; | ||||||
|  | 	struct stat cstats, fstats; | ||||||
|  | 	struct timeval times[2]; | ||||||
|  | 	Imlib_Load_Error err = 0; | ||||||
|  | 
 | ||||||
|  | 	if (!t || !t->im || !t->filename) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	if (stat(t->filename, &fstats)) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	if ((cfile = tns_cache_filename(t->filename))) { | ||||||
|  | 		if (force || stat(cfile, &cstats) || | ||||||
|  | 		    cstats.st_mtim.tv_sec != fstats.st_mtim.tv_sec || | ||||||
|  | 		    cstats.st_mtim.tv_nsec != fstats.st_mtim.tv_nsec) | ||||||
|  | 		{ | ||||||
|  | 			if ((dirend = strrchr(cfile, '/'))) { | ||||||
|  | 				*dirend = '\0'; | ||||||
|  | 				err = r_mkdir(cfile); | ||||||
|  | 				*dirend = '/'; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!err) { | ||||||
|  | 				imlib_context_set_image(t->im); | ||||||
|  | 				imlib_image_set_format("png"); | ||||||
|  | 				imlib_save_image_with_error_return(cfile, &err); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (err) { | ||||||
|  | 				warn("could not cache thumbnail: %s", t->filename); | ||||||
|  | 			} else { | ||||||
|  | 				TIMESPEC_TO_TIMEVAL(×[0], &fstats.st_atim); | ||||||
|  | 				TIMESPEC_TO_TIMEVAL(×[1], &fstats.st_mtim); | ||||||
|  | 				utimes(cfile, times); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		free(cfile); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void tns_clear_cache(tns_t *tns) { | ||||||
|  | 	int dirlen, delete; | ||||||
|  | 	char *cfile, *filename, *tpos; | ||||||
|  | 	r_dir_t dir; | ||||||
|  | 
 | ||||||
|  | 	if (!cache_dir) | ||||||
|  | 		return; | ||||||
|  | 	 | ||||||
|  | 	if (r_opendir(&dir, cache_dir)) { | ||||||
|  | 		warn("could not open thumbnail cache directory: %s", cache_dir); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	dirlen = strlen(cache_dir); | ||||||
|  | 
 | ||||||
|  | 	while ((cfile = r_readdir(&dir))) { | ||||||
|  | 		filename = cfile + dirlen; | ||||||
|  | 		delete = 0; | ||||||
|  | 
 | ||||||
|  | 		if ((tpos = strrchr(filename, '.'))) { | ||||||
|  | 			*tpos = '\0'; | ||||||
|  | 			delete = access(filename, F_OK); | ||||||
|  | 			*tpos = '.'; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (delete && unlink(cfile)) | ||||||
|  | 			warn("could not delete cache file: %s", cfile); | ||||||
|  | 
 | ||||||
|  | 		free(cfile); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	r_closedir(&dir); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| void tns_init(tns_t *tns, int cnt) { | void tns_init(tns_t *tns, int cnt) { | ||||||
|  | 	int len; | ||||||
|  | 	char *homedir; | ||||||
|  | 
 | ||||||
| 	if (!tns) | 	if (!tns) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	tns->cnt = tns->first = tns->sel = 0; | 	if (cnt) { | ||||||
| 		tns->thumbs = (thumb_t*) s_malloc(cnt * sizeof(thumb_t)); | 		tns->thumbs = (thumb_t*) s_malloc(cnt * sizeof(thumb_t)); | ||||||
| 		memset(tns->thumbs, 0, cnt * sizeof(thumb_t)); | 		memset(tns->thumbs, 0, cnt * sizeof(thumb_t)); | ||||||
|  | 	} else { | ||||||
|  | 		tns->thumbs = NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	tns->cnt = tns->first = tns->sel = 0; | ||||||
| 	tns->cap = cnt; | 	tns->cap = cnt; | ||||||
| 	tns->dirty = 0; | 	tns->dirty = 0; | ||||||
|  | 
 | ||||||
|  | 	if ((homedir = getenv("HOME"))) { | ||||||
|  | 		if (cache_dir) | ||||||
|  | 			free(cache_dir); | ||||||
|  | 		len = strlen(homedir) + 10; | ||||||
|  | 		cache_dir = (char*) s_malloc(len * sizeof(char)); | ||||||
|  | 		snprintf(cache_dir, len, "%s/.sxiv", homedir); | ||||||
|  | 	} else { | ||||||
|  | 		warn("could not locate thumbnail cache directory"); | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void tns_free(tns_t *tns, win_t *win) { | void tns_free(tns_t *tns, win_t *win) { | ||||||
| 	int i; | 	int i; | ||||||
| 
 | 
 | ||||||
| 	if (!tns || !tns->thumbs) | 	if (!tns) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
|  | 	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(); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 |  | ||||||
| 		free(tns->thumbs); | 		free(tns->thumbs); | ||||||
| 		tns->thumbs = NULL; | 		tns->thumbs = NULL; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if (cache_dir) { | ||||||
|  | 		free(cache_dir); | ||||||
|  | 		cache_dir = NULL; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void tns_load(tns_t *tns, win_t *win, int n, const char *filename) { | void tns_load(tns_t *tns, win_t *win, int n, const char *filename) { | ||||||
| 	int w, h; | 	int w, h; | ||||||
|  | 	int use_cache, cached = 0; | ||||||
| 	float z, zw, zh; | 	float z, zw, zh; | ||||||
| 	thumb_t *t; | 	thumb_t *t; | ||||||
| 	Imlib_Image *im; | 	Imlib_Image *im; | ||||||
| 
 | 
 | ||||||
| 	if (!tns || !win || !filename) | 	if (!tns || !tns->thumbs || !win || !filename) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	if (n >= tns->cap) | 	if (n >= tns->cap) | ||||||
| @ -75,7 +240,12 @@ void tns_load(tns_t *tns, win_t *win, int n, const char *filename) { | |||||||
| 		imlib_free_image(); | 		imlib_free_image(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if ((im = imlib_load_image(filename))) | 	if ((use_cache = tns_cache_enabled())) { | ||||||
|  | 		if ((im = tns_cache_load(filename))) | ||||||
|  | 			cached = 1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (cached || (im = imlib_load_image(filename))) | ||||||
| 		imlib_context_set_image(im); | 		imlib_context_set_image(im); | ||||||
| 	else | 	else | ||||||
| 		imlib_context_set_image(im_invalid); | 		imlib_context_set_image(im_invalid); | ||||||
| @ -84,10 +254,12 @@ void tns_load(tns_t *tns, win_t *win, int n, const char *filename) { | |||||||
| 	h = imlib_image_get_height(); | 	h = imlib_image_get_height(); | ||||||
| 
 | 
 | ||||||
| 	if (im) { | 	if (im) { | ||||||
|  | 		t->filename = filename; | ||||||
| 		zw = (float) THUMB_SIZE / (float) w; | 		zw = (float) THUMB_SIZE / (float) w; | ||||||
| 		zh = (float) THUMB_SIZE / (float) h; | 		zh = (float) THUMB_SIZE / (float) h; | ||||||
| 		z = MIN(zw, zh); | 		z = MIN(zw, zh); | ||||||
| 	} else { | 	} else { | ||||||
|  | 		t->filename = NULL; | ||||||
| 		z = 1.0; | 		z = 1.0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -99,6 +271,8 @@ void tns_load(tns_t *tns, win_t *win, int n, const char *filename) { | |||||||
| 		die("could not allocate memory"); | 		die("could not allocate memory"); | ||||||
| 	if (im) | 	if (im) | ||||||
| 		imlib_free_image_and_decache(); | 		imlib_free_image_and_decache(); | ||||||
|  | 	if (use_cache && !cached) | ||||||
|  | 		tns_cache_write(t, False); | ||||||
| 
 | 
 | ||||||
| 	tns->dirty = 1; | 	tns->dirty = 1; | ||||||
| } | } | ||||||
| @ -134,7 +308,10 @@ void tns_render(tns_t *tns, win_t *win) { | |||||||
| 	int i, cnt, r, x, y; | 	int i, cnt, r, x, y; | ||||||
| 	thumb_t *t; | 	thumb_t *t; | ||||||
| 
 | 
 | ||||||
| 	if (!tns || !tns->dirty || !win) | 	if (!tns || !tns->thumbs || !win) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	if (!tns->dirty) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	win_clear(win); | 	win_clear(win); | ||||||
| @ -182,7 +359,7 @@ void tns_highlight(tns_t *tns, win_t *win, int n, Bool hl) { | |||||||
| 	thumb_t *t; | 	thumb_t *t; | ||||||
| 	unsigned long col; | 	unsigned long col; | ||||||
| 
 | 
 | ||||||
| 	if (!tns || !win) | 	if (!tns || !tns->thumbs || !win) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	if (n >= 0 && n < tns->cnt) { | 	if (n >= 0 && n < tns->cnt) { | ||||||
| @ -205,7 +382,7 @@ void tns_highlight(tns_t *tns, win_t *win, int n, Bool hl) { | |||||||
| int tns_move_selection(tns_t *tns, win_t *win, tnsdir_t dir) { | int tns_move_selection(tns_t *tns, win_t *win, tnsdir_t dir) { | ||||||
| 	int old; | 	int old; | ||||||
| 
 | 
 | ||||||
| 	if (!tns || !win) | 	if (!tns || !tns->thumbs || !win) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	old = tns->sel; | 	old = tns->sel; | ||||||
| @ -264,7 +441,10 @@ int tns_translate(tns_t *tns, int x, int y) { | |||||||
| 	int n; | 	int n; | ||||||
| 	thumb_t *t; | 	thumb_t *t; | ||||||
| 
 | 
 | ||||||
| 	if (!tns || x < tns->x || y < tns->y) | 	if (!tns || !tns->thumbs) | ||||||
|  | 		return -1; | ||||||
|  | 
 | ||||||
|  | 	if (x < tns->x || y < tns->y) | ||||||
| 		return -1; | 		return -1; | ||||||
| 
 | 
 | ||||||
| 	n = tns->first + (y - tns->y) / thumb_dim * tns->cols + | 	n = tns->first + (y - tns->y) / thumb_dim * tns->cols + | ||||||
|  | |||||||
							
								
								
									
										3
									
								
								thumbs.h
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								thumbs.h
									
									
									
									
									
								
							| @ -32,6 +32,7 @@ typedef enum { | |||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| 	Imlib_Image *im; | 	Imlib_Image *im; | ||||||
|  | 	const char *filename; | ||||||
| 	int x; | 	int x; | ||||||
| 	int y; | 	int y; | ||||||
| 	int w; | 	int w; | ||||||
| @ -51,6 +52,8 @@ typedef struct { | |||||||
| 	unsigned char dirty; | 	unsigned char dirty; | ||||||
| } tns_t; | } tns_t; | ||||||
| 
 | 
 | ||||||
|  | void tns_clear_cache(tns_t*); | ||||||
|  | 
 | ||||||
| void tns_init(tns_t*, int); | void tns_init(tns_t*, int); | ||||||
| void tns_free(tns_t*, win_t*); | void tns_free(tns_t*, win_t*); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										222
									
								
								util.c
									
									
									
									
									
								
							
							
						
						
									
										222
									
								
								util.c
									
									
									
									
									
								
							| @ -18,11 +18,16 @@ | |||||||
| 
 | 
 | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <errno.h> | ||||||
| 
 | 
 | ||||||
| #include "options.h" | #include "options.h" | ||||||
| #include "util.h" | #include "util.h" | ||||||
| 
 | 
 | ||||||
| #define FNAME_LEN 512 | #define DNAME_CNT 512 | ||||||
|  | #define FNAME_LEN 1024 | ||||||
| 
 | 
 | ||||||
| void cleanup(); | void cleanup(); | ||||||
| 
 | 
 | ||||||
| @ -78,6 +83,221 @@ void size_readable(float *size, const char **unit) { | |||||||
| 	*unit = units[MIN(i, LEN(units) - 1)]; | 	*unit = units[MIN(i, LEN(units) - 1)]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | char* absolute_path(const char *filename) { | ||||||
|  | 	size_t len; | ||||||
|  | 	char *path = NULL; | ||||||
|  | 	const char *basename; | ||||||
|  | 	char *dirname = NULL; | ||||||
|  | 	char *cwd = NULL; | ||||||
|  | 	char *twd = NULL; | ||||||
|  | 	char *dir; | ||||||
|  | 	char *s; | ||||||
|  | 
 | ||||||
|  | 	if (!filename || *filename == '\0' || *filename == '/') | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | 	len = FNAME_LEN; | ||||||
|  | 	cwd = (char*) s_malloc(len); | ||||||
|  | 	while (!(s = getcwd(cwd, len)) && errno == ERANGE) { | ||||||
|  | 		len *= 2; | ||||||
|  | 		cwd = (char*) s_realloc(cwd, len); | ||||||
|  | 	} | ||||||
|  | 	if (!s) | ||||||
|  | 		goto error; | ||||||
|  | 
 | ||||||
|  | 	s = strrchr(filename, '/'); | ||||||
|  | 	if (s) { | ||||||
|  | 		len = s - filename; | ||||||
|  | 		dirname = (char*) s_malloc(len + 1); | ||||||
|  | 		strncpy(dirname, filename, len); | ||||||
|  | 		dirname[len] = '\0'; | ||||||
|  | 		basename = s + 1; | ||||||
|  | 
 | ||||||
|  | 		if (chdir(cwd)) | ||||||
|  | 			/* we're not able to come back afterwards */ | ||||||
|  | 			goto error; | ||||||
|  | 		if (chdir(dirname)) | ||||||
|  | 			goto error; | ||||||
|  | 
 | ||||||
|  | 		len = FNAME_LEN; | ||||||
|  | 		twd = (char*) s_malloc(len); | ||||||
|  | 		while (!(s = getcwd(twd, len)) && errno == ERANGE) { | ||||||
|  | 			len *= 2; | ||||||
|  | 			twd = (char*) s_realloc(twd, len); | ||||||
|  | 		} | ||||||
|  | 		if (chdir(cwd)) | ||||||
|  | 			die("could not revert to prior working directory"); | ||||||
|  | 		if (!s) | ||||||
|  | 			goto error; | ||||||
|  | 		dir = twd; | ||||||
|  | 	} else { | ||||||
|  | 		/* only a single filename given */ | ||||||
|  | 		basename = filename; | ||||||
|  | 		dir = cwd; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	len = strlen(dir) + strlen(basename) + 2; | ||||||
|  | 	path = (char*) s_malloc(len); | ||||||
|  | 	snprintf(path, len, "%s/%s", dir, basename); | ||||||
|  | 
 | ||||||
|  | goto end; | ||||||
|  | 
 | ||||||
|  | error: | ||||||
|  | 	if (path) { | ||||||
|  | 		free(path); | ||||||
|  | 		path = NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | end: | ||||||
|  | 	if (dirname) | ||||||
|  | 		free(dirname); | ||||||
|  | 	if (cwd) | ||||||
|  | 		free(cwd); | ||||||
|  | 	if (twd) | ||||||
|  | 		free(twd); | ||||||
|  | 
 | ||||||
|  | 	return path; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int r_opendir(r_dir_t *rdir, const char *dirname) { | ||||||
|  | 	if (!rdir || !dirname || !*dirname) | ||||||
|  | 		return -1; | ||||||
|  | 
 | ||||||
|  | 	if (!(rdir->dir = opendir(dirname))) { | ||||||
|  | 		rdir->name = NULL; | ||||||
|  | 		rdir->stack = NULL; | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	rdir->stcap = DNAME_CNT; | ||||||
|  | 	rdir->stack = (char**) s_malloc(rdir->stcap * sizeof(char*)); | ||||||
|  | 	rdir->stlen = 0; | ||||||
|  | 
 | ||||||
|  | 	rdir->name = (char*) dirname; | ||||||
|  | 	rdir->d = 0; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int r_closedir(r_dir_t *rdir) { | ||||||
|  | 	int ret = 0; | ||||||
|  | 
 | ||||||
|  | 	if (!rdir) | ||||||
|  | 		return -1; | ||||||
|  | 	 | ||||||
|  | 	if (rdir->stack) { | ||||||
|  | 		while (rdir->stlen > 0) | ||||||
|  | 			free(rdir->stack[--rdir->stlen]); | ||||||
|  | 		free(rdir->stack); | ||||||
|  | 		rdir->stack = NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (rdir->dir) { | ||||||
|  | 		if (!(ret = closedir(rdir->dir))) | ||||||
|  | 			rdir->dir = NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (rdir->d && rdir->name) { | ||||||
|  | 		free(rdir->name); | ||||||
|  | 		rdir->name = NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | char* r_readdir(r_dir_t *rdir) { | ||||||
|  | 	size_t len; | ||||||
|  | 	char *filename; | ||||||
|  | 	struct dirent *dentry; | ||||||
|  | 	struct stat fstats; | ||||||
|  | 
 | ||||||
|  | 	if (!rdir || !rdir->dir || !rdir->name) | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | 	while (1) { | ||||||
|  | 		if (rdir->dir && (dentry = readdir(rdir->dir))) { | ||||||
|  | 			if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) | ||||||
|  | 				continue; | ||||||
|  | 
 | ||||||
|  | 			len = strlen(rdir->name) + strlen(dentry->d_name) + 2; | ||||||
|  | 			filename = (char*) s_malloc(len); | ||||||
|  | 			snprintf(filename, len, "%s%s%s", rdir->name, | ||||||
|  | 			         rdir->name[strlen(rdir->name)-1] == '/' ? "" : "/", | ||||||
|  | 			         dentry->d_name); | ||||||
|  | 
 | ||||||
|  | 			if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) { | ||||||
|  | 				/* put subdirectory on the stack */ | ||||||
|  | 				if (rdir->stlen == rdir->stcap) { | ||||||
|  | 					rdir->stcap *= 2; | ||||||
|  | 					rdir->stack = (char**) s_realloc(rdir->stack, | ||||||
|  | 					                                 rdir->stcap * sizeof(char*)); | ||||||
|  | 				} | ||||||
|  | 				rdir->stack[rdir->stlen++] = filename; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			return filename; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		if (rdir->stlen > 0) { | ||||||
|  | 			/* open next subdirectory */ | ||||||
|  | 			closedir(rdir->dir); | ||||||
|  | 			if (rdir->d) | ||||||
|  | 				free(rdir->name); | ||||||
|  | 			rdir->name = rdir->stack[--rdir->stlen]; | ||||||
|  | 			rdir->d = 1; | ||||||
|  | 			if (!(rdir->dir = opendir(rdir->name))) | ||||||
|  | 				warn("could not open directory: %s", rdir->name); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/* no more entries */ | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int r_mkdir(const char *path) { | ||||||
|  | 	char *dir, *d; | ||||||
|  | 	struct stat stats; | ||||||
|  | 	int err = 0; | ||||||
|  | 
 | ||||||
|  | 	if (!path || !*path) | ||||||
|  | 		return -1; | ||||||
|  | 
 | ||||||
|  | 	if (!stat(path, &stats)) { | ||||||
|  | 		if (S_ISDIR(stats.st_mode)) { | ||||||
|  | 			return 0; | ||||||
|  | 		} else { | ||||||
|  | 			warn("not a directory: %s", path); | ||||||
|  | 			return -1; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	d = dir = (char*) s_malloc(strlen(path) + 1); | ||||||
|  | 	strcpy(dir, path); | ||||||
|  | 
 | ||||||
|  | 	while (d != NULL && !err) { | ||||||
|  | 		d = strchr(d + 1, '/'); | ||||||
|  | 		if (d != NULL) | ||||||
|  | 			*d = '\0'; | ||||||
|  | 		if (access(dir, F_OK) && errno == ENOENT) { | ||||||
|  | 			if (mkdir(dir, 0755)) { | ||||||
|  | 				warn("could not create directory: %s", dir); | ||||||
|  | 				err = -1; | ||||||
|  | 			} | ||||||
|  | 		} else if (stat(dir, &stats) || !S_ISDIR(stats.st_mode)) { | ||||||
|  | 			warn("not a directory: %s", dir); | ||||||
|  | 			err = -1; | ||||||
|  | 		} | ||||||
|  | 		if (d != NULL) | ||||||
|  | 			*d = '/'; | ||||||
|  | 	} | ||||||
|  | 	free(dir); | ||||||
|  | 
 | ||||||
|  | 	return err; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| char* readline(FILE *stream) { | char* readline(FILE *stream) { | ||||||
| 	size_t len; | 	size_t len; | ||||||
| 	char *buf, *s, *end; | 	char *buf, *s, *end; | ||||||
|  | |||||||
							
								
								
									
										23
									
								
								util.h
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								util.h
									
									
									
									
									
								
							| @ -21,6 +21,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
|  | #include <dirent.h> | ||||||
| 
 | 
 | ||||||
| #define ABS(a)   ((a) < 0 ? (-(a)) : (a)) | #define ABS(a)   ((a) < 0 ? (-(a)) : (a)) | ||||||
| #define MIN(a,b) ((a) < (b) ? (a) : (b)) | #define MIN(a,b) ((a) < (b) ? (a) : (b)) | ||||||
| @ -30,6 +31,21 @@ | |||||||
| #define TV_TO_DOUBLE(x) ((double) ((x).tv_sec) + 0.000001 * \ | #define TV_TO_DOUBLE(x) ((double) ((x).tv_sec) + 0.000001 * \ | ||||||
|                          (double) ((x).tv_usec)) |                          (double) ((x).tv_usec)) | ||||||
| 
 | 
 | ||||||
|  | #define TIMESPEC_TO_TIMEVAL(tv, ts) {      \ | ||||||
|  | 		(tv)->tv_sec = (ts)->tv_sec;           \ | ||||||
|  | 		(tv)->tv_usec = (ts)->tv_nsec / 1000;  \ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  | 	DIR *dir; | ||||||
|  | 	char *name; | ||||||
|  | 	int d; | ||||||
|  | 
 | ||||||
|  | 	char **stack; | ||||||
|  | 	int stcap; | ||||||
|  | 	int stlen; | ||||||
|  | } r_dir_t; | ||||||
|  | 
 | ||||||
| void* s_malloc(size_t); | void* s_malloc(size_t); | ||||||
| void* s_realloc(void*, size_t); | void* s_realloc(void*, size_t); | ||||||
| 
 | 
 | ||||||
| @ -38,6 +54,13 @@ void die(const char*, ...); | |||||||
| 
 | 
 | ||||||
| void size_readable(float*, const char**); | void size_readable(float*, const char**); | ||||||
| 
 | 
 | ||||||
|  | char* absolute_path(const char*); | ||||||
|  | 
 | ||||||
|  | int r_opendir(r_dir_t*, const char*); | ||||||
|  | int r_closedir(r_dir_t*); | ||||||
|  | char* r_readdir(r_dir_t*); | ||||||
|  | int r_mkdir(const char *); | ||||||
|  | 
 | ||||||
| char* readline(FILE*); | char* readline(FILE*); | ||||||
| 
 | 
 | ||||||
| #endif /* UTIL_H */ | #endif /* UTIL_H */ | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Bert
						Bert