nsxivbac610d
Merge branch 'tcache'
+506 −99
10 files changed
- M Makefile +1 −1
- M README.md +1 −0
- M main.c +31 −79
- M options.c +6 −2
- M options.h +1 −0
- M sxiv.1 +24 −1
- M thumbs.c +195 −15
- M thumbs.h +3 −0
- M util.c +221 −1
- M util.h +23 −0
| @@ -1,6 +1,6 @@ | |||
| 1 | 1 | all: sxiv | |
| 2 | 2 | ||
| 3 | − | VERSION=git-20110407 | |
| 3 | + | VERSION=git-20110408 | |
| 4 | 4 | ||
| 5 | 5 | CC?=gcc | |
| 6 | 6 | PREFIX?=/usr/local | |
| @@ -36,6 +36,7 @@ sxiv supports the following command-line options: | |||
| 36 | 36 | ||
| 37 | 37 | -a Display all given files, do not filter out unsupported files | |
| 38 | 38 | (shorter startup time for long file list or slow file types) | |
| 39 | + | -C Remove all orphaned cache files from thumbnail cache and exit | |
| 39 | 40 | -d Scale all images to 100%, but fit large images into window | |
| 40 | 41 | -F Use size-hints to make the window fixed/floating | |
| 41 | 42 | -f Start in fullscreen mode | |
| @@ -19,7 +19,6 @@ | |||
| 19 | 19 | #include <stdlib.h> | |
| 20 | 20 | #include <stdio.h> | |
| 21 | 21 | #include <string.h> | |
| 22 | − | #include <dirent.h> | |
| 23 | 22 | #include <sys/select.h> | |
| 24 | 23 | #include <sys/stat.h> | |
| 25 | 24 | #include <sys/time.h> | |
| @@ -48,7 +47,6 @@ typedef enum { | |||
| 48 | 47 | ||
| 49 | 48 | void update_title(); | |
| 50 | 49 | int check_append(const char*); | |
| 51 | − | void read_dir_rec(const char*); | |
| 52 | 50 | void run(); | |
| 53 | 51 | ||
| 54 | 52 | appmode_t mode; | |
| @@ -56,7 +54,6 @@ img_t img; | |||
| 56 | 54 | tns_t tns; | |
| 57 | 55 | win_t win; | |
| 58 | 56 | ||
| 59 | − | #define DNAME_CNT 512 | |
| 60 | 57 | #define FNAME_CNT 1024 | |
| 61 | 58 | const char **filenames; | |
| 62 | 59 | int filecnt, fileidx; | |
| @@ -95,13 +92,24 @@ int load_image(int new) { | |||
| 95 | 92 | return ret; | |
| 96 | 93 | } | |
| 97 | 94 | ||
| 95 | + | int fncmp(const void *a, const void *b) { | |
| 96 | + | return strcoll(*((char* const*) a), *((char* const*) b)); | |
| 97 | + | } | |
| 98 | + | ||
| 98 | 99 | int main(int argc, char **argv) { | |
| 99 | − | int i; | |
| 100 | + | int i, start; | |
| 100 | 101 | const char *filename; | |
| 101 | 102 | struct stat fstats; | |
| 103 | + | r_dir_t dir; | |
| 102 | 104 | ||
| 103 | 105 | parse_options(argc, argv); | |
| 104 | 106 | ||
| 107 | + | if (options->clean_cache) { | |
| 108 | + | tns_init(&tns, 0); | |
| 109 | + | tns_clear_cache(&tns); | |
| 110 | + | exit(0); | |
| 111 | + | } | |
| 112 | + | ||
| 105 | 113 | if (!options->filecnt) { | |
| 106 | 114 | print_usage(); | |
| 107 | 115 | exit(1); | |
| @@ -123,13 +131,26 @@ int main(int argc, char **argv) { | |||
| 123 | 131 | } else { | |
| 124 | 132 | for (i = 0; i < options->filecnt; ++i) { | |
| 125 | 133 | filename = options->filenames[i]; | |
| 126 | − | if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) { | |
| 127 | − | if (options->recursive) | |
| 128 | − | read_dir_rec(filename); | |
| 129 | − | else | |
| 130 | − | warn("ignoring directory: %s", filename); | |
| 131 | − | } else { | |
| 134 | + | ||
| 135 | + | if (stat(filename, &fstats) || !S_ISDIR(fstats.st_mode)) { | |
| 132 | 136 | check_append(filename); | |
| 137 | + | } else { | |
| 138 | + | if (!options->recursive) { | |
| 139 | + | warn("ignoring directory: %s", filename); | |
| 140 | + | continue; | |
| 141 | + | } | |
| 142 | + | if (r_opendir(&dir, filename)) { | |
| 143 | + | warn("could not open directory: %s", filename); | |
| 144 | + | continue; | |
| 145 | + | } | |
| 146 | + | start = fileidx; | |
| 147 | + | while ((filename = r_readdir(&dir))) { | |
| 148 | + | if (!check_append(filename)) | |
| 149 | + | free((void*) filename); | |
| 150 | + | } | |
| 151 | + | r_closedir(&dir); | |
| 152 | + | if (fileidx - start > 1) | |
| 153 | + | qsort(filenames + start, fileidx - start, sizeof(char*), fncmp); | |
| 133 | 154 | } | |
| 134 | 155 | } | |
| 135 | 156 | } | |
| @@ -215,75 +236,6 @@ int check_append(const char *filename) { | |||
| 215 | 236 | } | |
| 216 | 237 | } | |
| 217 | 238 | ||
| 218 | − | int fncmp(const void *a, const void *b) { | |
| 219 | − | return strcoll(*((char* const*) a), *((char* const*) b)); | |
| 220 | − | } | |
| 221 | − | ||
| 222 | − | void read_dir_rec(const char *dirname) { | |
| 223 | − | char *filename; | |
| 224 | − | const char **dirnames; | |
| 225 | − | int dircnt, diridx; | |
| 226 | − | int fcnt, fstart; | |
| 227 | − | unsigned char first; | |
| 228 | − | size_t len; | |
| 229 | − | DIR *dir; | |
| 230 | − | struct dirent *dentry; | |
| 231 | − | struct stat fstats; | |
| 232 | − | ||
| 233 | − | if (!dirname) | |
| 234 | − | return; | |
| 235 | − | ||
| 236 | − | dircnt = DNAME_CNT; | |
| 237 | − | diridx = first = 1; | |
| 238 | − | dirnames = (const char**) s_malloc(dircnt * sizeof(const char*)); | |
| 239 | − | dirnames[0] = dirname; | |
| 240 | − | ||
| 241 | − | fcnt = 0; | |
| 242 | − | fstart = fileidx; | |
| 243 | − | ||
| 244 | − | while (diridx > 0) { | |
| 245 | − | dirname = dirnames[--diridx]; | |
| 246 | − | if (!(dir = opendir(dirname))) { | |
| 247 | − | warn("could not open directory: %s", dirname); | |
| 248 | − | } else { | |
| 249 | − | while ((dentry = readdir(dir))) { | |
| 250 | − | if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) | |
| 251 | − | continue; | |
| 252 | − | ||
| 253 | − | len = strlen(dirname) + strlen(dentry->d_name) + 2; | |
| 254 | − | filename = (char*) s_malloc(len * sizeof(char)); | |
| 255 | − | snprintf(filename, len, "%s%s%s", dirname, | |
| 256 | − | dirname[strlen(dirname)-1] == '/' ? "" : "/", dentry->d_name); | |
| 257 | − | ||
| 258 | − | if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) { | |
| 259 | − | if (diridx == dircnt) { | |
| 260 | − | dircnt *= 2; | |
| 261 | − | dirnames = (const char**) s_realloc(dirnames, | |
| 262 | − | dircnt * sizeof(const char*)); | |
| 263 | − | } | |
| 264 | − | dirnames[diridx++] = filename; | |
| 265 | − | } else { | |
| 266 | − | if (check_append(filename)) | |
| 267 | − | ++fcnt; | |
| 268 | − | else | |
| 269 | − | free(filename); | |
| 270 | − | } | |
| 271 | − | } | |
| 272 | − | closedir(dir); | |
| 273 | − | } | |
| 274 | − | ||
| 275 | − | if (!first) | |
| 276 | − | free((void*) dirname); | |
| 277 | − | else | |
| 278 | − | first = 0; | |
| 279 | − | } | |
| 280 | − | ||
| 281 | − | if (fcnt > 1) | |
| 282 | − | qsort(filenames + fstart, fcnt, sizeof(char*), fncmp); | |
| 283 | − | ||
| 284 | − | free(dirnames); | |
| 285 | − | } | |
| 286 | − | ||
| 287 | 239 | #if EXT_COMMANDS | |
| 288 | 240 | int run_command(const char *cline, Bool reload) { | |
| 289 | 241 | int fncnt, fnlen; | |
| @@ -31,7 +31,7 @@ options_t _options; | |||
| 31 | 31 | const options_t *options = (const options_t*) &_options; | |
| 32 | 32 | ||
| 33 | 33 | void print_usage() { | |
| 34 | − | printf("usage: sxiv [-adFfhpqrstvZ] [-g GEOMETRY] [-z ZOOM] FILES...\n"); | |
| 34 | + | printf("usage: sxiv [-aCdFfhpqrstvZ] [-g GEOMETRY] [-z ZOOM] FILES...\n"); | |
| 35 | 35 | } | |
| 36 | 36 | ||
| 37 | 37 | void print_version() { | |
| @@ -53,9 +53,10 @@ void parse_options(int argc, char **argv) { | |||
| 53 | 53 | ||
| 54 | 54 | _options.all = 0; | |
| 55 | 55 | _options.quiet = 0; | |
| 56 | + | _options.clean_cache = 0; | |
| 56 | 57 | _options.recursive = 0; | |
| 57 | 58 | ||
| 58 | − | while ((opt = getopt(argc, argv, "adFfg:hpqrstvZz:")) != -1) { | |
| 59 | + | while ((opt = getopt(argc, argv, "aCdFfg:hpqrstvZz:")) != -1) { | |
| 59 | 60 | switch (opt) { | |
| 60 | 61 | case '?': | |
| 61 | 62 | print_usage(); | |
| @@ -63,6 +64,9 @@ void parse_options(int argc, char **argv) { | |||
| 63 | 64 | case 'a': | |
| 64 | 65 | _options.all = 1; | |
| 65 | 66 | break; | |
| 67 | + | case 'C': | |
| 68 | + | _options.clean_cache = 1; | |
| 69 | + | break; | |
| 66 | 70 | case 'd': | |
| 67 | 71 | _options.scalemode = SCALE_DOWN; | |
| 68 | 72 | break; | |
| @@ -37,6 +37,7 @@ typedef struct { | |||
| 37 | 37 | ||
| 38 | 38 | unsigned char all; | |
| 39 | 39 | unsigned char quiet; | |
| 40 | + | unsigned char clean_cache; | |
| 40 | 41 | unsigned char recursive; | |
| 41 | 42 | } options_t; | |
| 42 | 43 | ||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | sxiv \- Simple (or small or suckless) X Image Viewer | |
| 4 | 4 | .SH SYNOPSIS | |
| 5 | 5 | .B sxiv | |
| 6 | − | .RB [ \-adFfhpqrstvZ ] | |
| 6 | + | .RB [ \-aCdFfhpqrstvZ ] | |
| 7 | 7 | .RB [ \-g | |
| 8 | 8 | .IR GEOMETRY ] | |
| 9 | 9 | .RB [ \-z | |
| @@ -24,6 +24,9 @@ sxiv has two modes of operation: image and thumbnail mode. The default is image | |||
| 24 | 24 | mode, in which only the current image is shown. In thumbnail mode a grid of | |
| 25 | 25 | small previews is displayed, making it easy to choose an image to open. | |
| 26 | 26 | .P | |
| 27 | + | sxiv can also cache its thumbnails. Please see the section THUMBNAIL CACHING | |
| 28 | + | for information on how to enable this feature. | |
| 29 | + | .P | |
| 27 | 30 | Please note, that the fullscreen mode requires an EWMH/NetWM compliant window | |
| 28 | 31 | manager. | |
| 29 | 32 | .SH OPTIONS | |
| @@ -33,6 +36,9 @@ Display all given files, do not filter out unsupported files. This might result | |||
| 33 | 36 | in a much shorter startup time, when the file list is very long or contains | |
| 34 | 37 | large files of slow loadable types, e.g. gif and progressive jpg. | |
| 35 | 38 | .TP | |
| 39 | + | .B \-C | |
| 40 | + | Remove all orphaned cache files from the thumbnail cache directory and exit. | |
| 41 | + | .TP | |
| 36 | 42 | .B \-d | |
| 37 | 43 | Scale all images to 100%, but fit large images into window. | |
| 38 | 44 | .TP | |
| @@ -191,6 +197,23 @@ Pan image left. | |||
| 191 | 197 | .TP | |
| 192 | 198 | .B Shift+ScrollDown | |
| 193 | 199 | Pan image right. | |
| 200 | + | .SH THUMBNAIL CACHING | |
| 201 | + | To enable thumbnail caching, please make sure to create the directory | |
| 202 | + | .I ~/.sxiv/ | |
| 203 | + | with write permissions. sxiv will then store all thumbnails inside this | |
| 204 | + | directory, but it will not create this directory by itself. It rather uses the | |
| 205 | + | existance of this directory as an affirmation, that the user wants thumbnails | |
| 206 | + | to be cached. | |
| 207 | + | .P | |
| 208 | + | Use the command line option | |
| 209 | + | .I \-C | |
| 210 | + | to keep the cache directory clean by removing all orphaned cache files. | |
| 211 | + | Additionally, run the following command afterwards inside the cache directory | |
| 212 | + | to remove empty subdirectories: | |
| 213 | + | .P | |
| 214 | + | .RS | |
| 215 | + | find -type d -empty -delete | |
| 216 | + | .RE | |
| 194 | 217 | .SH AUTHORS | |
| 195 | 218 | .EX | |
| 196 | 219 | Bert Muennich <ber.t at gmx.com> | |
| @@ -18,49 +18,214 @@ | |||
| 18 | 18 | ||
| 19 | 19 | #include <stdlib.h> | |
| 20 | 20 | #include <string.h> | |
| 21 | + | #include <sys/time.h> | |
| 22 | + | #include <sys/types.h> | |
| 23 | + | #include <sys/stat.h> | |
| 24 | + | #include <unistd.h> | |
| 21 | 25 | ||
| 22 | 26 | #include "config.h" | |
| 23 | 27 | #include "thumbs.h" | |
| 24 | 28 | #include "util.h" | |
| 25 | 29 | ||
| 26 | 30 | extern Imlib_Image *im_invalid; | |
| 31 | + | ||
| 27 | 32 | const int thumb_dim = THUMB_SIZE + 10; | |
| 33 | + | char *cache_dir = NULL; | |
| 34 | + | ||
| 35 | + | int tns_cache_enabled() { | |
| 36 | + | struct stat stats; | |
| 37 | + | ||
| 38 | + | return cache_dir && !stat(cache_dir, &stats) && S_ISDIR(stats.st_mode) && | |
| 39 | + | !access(cache_dir, W_OK); | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | char* tns_cache_filename(const char *filename) { | |
| 43 | + | size_t len; | |
| 44 | + | char *cfile = NULL; | |
| 45 | + | const char *abspath; | |
| 46 | + | ||
| 47 | + | if (!cache_dir || !filename) | |
| 48 | + | return NULL; | |
| 49 | + | ||
| 50 | + | if (*filename != '/') { | |
| 51 | + | if (!(abspath = absolute_path(filename))) | |
| 52 | + | return NULL; | |
| 53 | + | } else { | |
| 54 | + | abspath = filename; | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | if (strncmp(abspath, cache_dir, strlen(cache_dir))) { | |
| 58 | + | len = strlen(cache_dir) + strlen(abspath) + 6; | |
| 59 | + | cfile = (char*) s_malloc(len); | |
| 60 | + | snprintf(cfile, len, "%s/%s.png", cache_dir, abspath + 1); | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | if (abspath != filename) | |
| 64 | + | free((void*) abspath); | |
| 65 | + | ||
| 66 | + | return cfile; | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | Imlib_Image* tns_cache_load(const char *filename) { | |
| 70 | + | char *cfile; | |
| 71 | + | struct stat cstats, fstats; | |
| 72 | + | Imlib_Image *im = NULL; | |
| 73 | + | ||
| 74 | + | if (!filename) | |
| 75 | + | return NULL; | |
| 76 | + | ||
| 77 | + | if (stat(filename, &fstats)) | |
| 78 | + | return NULL; | |
| 79 | + | ||
| 80 | + | if ((cfile = tns_cache_filename(filename))) { | |
| 81 | + | if (!stat(cfile, &cstats) && | |
| 82 | + | cstats.st_mtim.tv_sec == fstats.st_mtim.tv_sec && | |
| 83 | + | cstats.st_mtim.tv_nsec == fstats.st_mtim.tv_nsec) | |
| 84 | + | { | |
| 85 | + | im = imlib_load_image(cfile); | |
| 86 | + | } | |
| 87 | + | free(cfile); | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | return im; | |
| 91 | + | } | |
| 92 | + | ||
| 93 | + | void tns_cache_write(thumb_t *t, Bool force) { | |
| 94 | + | char *cfile, *dirend; | |
| 95 | + | struct stat cstats, fstats; | |
| 96 | + | struct timeval times[2]; | |
| 97 | + | Imlib_Load_Error err = 0; | |
| 98 | + | ||
| 99 | + | if (!t || !t->im || !t->filename) | |
| 100 | + | return; | |
| 101 | + | ||
| 102 | + | if (stat(t->filename, &fstats)) | |
| 103 | + | return; | |
| 104 | + | ||
| 105 | + | if ((cfile = tns_cache_filename(t->filename))) { | |
| 106 | + | if (force || stat(cfile, &cstats) || | |
| 107 | + | cstats.st_mtim.tv_sec != fstats.st_mtim.tv_sec || | |
| 108 | + | cstats.st_mtim.tv_nsec != fstats.st_mtim.tv_nsec) | |
| 109 | + | { | |
| 110 | + | if ((dirend = strrchr(cfile, '/'))) { | |
| 111 | + | *dirend = '\0'; | |
| 112 | + | err = r_mkdir(cfile); | |
| 113 | + | *dirend = '/'; | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | if (!err) { | |
| 117 | + | imlib_context_set_image(t->im); | |
| 118 | + | imlib_image_set_format("png"); | |
| 119 | + | imlib_save_image_with_error_return(cfile, &err); | |
| 120 | + | } | |
| 121 | + | ||
| 122 | + | if (err) { | |
| 123 | + | warn("could not cache thumbnail: %s", t->filename); | |
| 124 | + | } else { | |
| 125 | + | TIMESPEC_TO_TIMEVAL(×[0], &fstats.st_atim); | |
| 126 | + | TIMESPEC_TO_TIMEVAL(×[1], &fstats.st_mtim); | |
| 127 | + | utimes(cfile, times); | |
| 128 | + | } | |
| 129 | + | } | |
| 130 | + | free(cfile); | |
| 131 | + | } | |
| 132 | + | } | |
| 133 | + | ||
| 134 | + | void tns_clear_cache(tns_t *tns) { | |
| 135 | + | int dirlen, delete; | |
| 136 | + | char *cfile, *filename, *tpos; | |
| 137 | + | r_dir_t dir; | |
| 138 | + | ||
| 139 | + | if (!cache_dir) | |
| 140 | + | return; | |
| 141 | + | ||
| 142 | + | if (r_opendir(&dir, cache_dir)) { | |
| 143 | + | warn("could not open thumbnail cache directory: %s", cache_dir); | |
| 144 | + | return; | |
| 145 | + | } | |
| 146 | + | ||
| 147 | + | dirlen = strlen(cache_dir); | |
| 148 | + | ||
| 149 | + | while ((cfile = r_readdir(&dir))) { | |
| 150 | + | filename = cfile + dirlen; | |
| 151 | + | delete = 0; | |
| 152 | + | ||
| 153 | + | if ((tpos = strrchr(filename, '.'))) { | |
| 154 | + | *tpos = '\0'; | |
| 155 | + | delete = access(filename, F_OK); | |
| 156 | + | *tpos = '.'; | |
| 157 | + | } | |
| 158 | + | ||
| 159 | + | if (delete && unlink(cfile)) | |
| 160 | + | warn("could not delete cache file: %s", cfile); | |
| 161 | + | ||
| 162 | + | free(cfile); | |
| 163 | + | } | |
| 164 | + | ||
| 165 | + | r_closedir(&dir); | |
| 166 | + | } | |
| 167 | + | ||
| 28 | 168 | ||
| 29 | 169 | void tns_init(tns_t *tns, int cnt) { | |
| 170 | + | int len; | |
| 171 | + | char *homedir; | |
| 172 | + | ||
| 30 | 173 | if (!tns) | |
| 31 | 174 | return; | |
| 32 | 175 | ||
| 176 | + | if (cnt) { | |
| 177 | + | tns->thumbs = (thumb_t*) s_malloc(cnt * sizeof(thumb_t)); | |
| 178 | + | memset(tns->thumbs, 0, cnt * sizeof(thumb_t)); | |
| 179 | + | } else { | |
| 180 | + | tns->thumbs = NULL; | |
| 181 | + | } | |
| 182 | + | ||
| 33 | 183 | tns->cnt = tns->first = tns->sel = 0; | |
| 34 | − | tns->thumbs = (thumb_t*) s_malloc(cnt * sizeof(thumb_t)); | |
| 35 | − | memset(tns->thumbs, 0, cnt * sizeof(thumb_t)); | |
| 36 | 184 | tns->cap = cnt; | |
| 37 | 185 | tns->dirty = 0; | |
| 186 | + | ||
| 187 | + | if ((homedir = getenv("HOME"))) { | |
| 188 | + | if (cache_dir) | |
| 189 | + | free(cache_dir); | |
| 190 | + | len = strlen(homedir) + 10; | |
| 191 | + | cache_dir = (char*) s_malloc(len * sizeof(char)); | |
| 192 | + | snprintf(cache_dir, len, "%s/.sxiv", homedir); | |
| 193 | + | } else { | |
| 194 | + | warn("could not locate thumbnail cache directory"); | |
| 195 | + | } | |
| 38 | 196 | } | |
| 39 | 197 | ||
| 40 | 198 | void tns_free(tns_t *tns, win_t *win) { | |
| 41 | 199 | int i; | |
| 42 | 200 | ||
| 43 | − | if (!tns || !tns->thumbs) | |
| 201 | + | if (!tns) | |
| 44 | 202 | return; | |
| 45 | 203 | ||
| 46 | − | for (i = 0; i < tns->cnt; ++i) { | |
| 47 | − | if (tns->thumbs[i].im) { | |
| 48 | − | imlib_context_set_image(tns->thumbs[i].im); | |
| 49 | − | imlib_free_image(); | |
| 204 | + | if (tns->thumbs) { | |
| 205 | + | for (i = 0; i < tns->cnt; ++i) { | |
| 206 | + | if (tns->thumbs[i].im) { | |
| 207 | + | imlib_context_set_image(tns->thumbs[i].im); | |
| 208 | + | imlib_free_image(); | |
| 209 | + | } | |
| 50 | 210 | } | |
| 211 | + | free(tns->thumbs); | |
| 212 | + | tns->thumbs = NULL; | |
| 51 | 213 | } | |
| 52 | 214 | ||
| 53 | − | free(tns->thumbs); | |
| 54 | − | tns->thumbs = NULL; | |
| 215 | + | if (cache_dir) { | |
| 216 | + | free(cache_dir); | |
| 217 | + | cache_dir = NULL; | |
| 218 | + | } | |
| 55 | 219 | } | |
| 56 | 220 | ||
| 57 | 221 | void tns_load(tns_t *tns, win_t *win, int n, const char *filename) { | |
| 58 | 222 | int w, h; | |
| 223 | + | int use_cache, cached = 0; | |
| 59 | 224 | float z, zw, zh; | |
| 60 | 225 | thumb_t *t; | |
| 61 | 226 | Imlib_Image *im; | |
| 62 | 227 | ||
| 63 | − | if (!tns || !win || !filename) | |
| 228 | + | if (!tns || !tns->thumbs || !win || !filename) | |
| 64 | 229 | return; | |
| 65 | 230 | ||
| 66 | 231 | if (n >= tns->cap) | |
| @@ -75,7 +240,12 @@ void tns_load(tns_t *tns, win_t *win, int n, const char *filename) { | |||
| 75 | 240 | imlib_free_image(); | |
| 76 | 241 | } | |
| 77 | 242 | ||
| 78 | − | if ((im = imlib_load_image(filename))) | |
| 243 | + | if ((use_cache = tns_cache_enabled())) { | |
| 244 | + | if ((im = tns_cache_load(filename))) | |
| 245 | + | cached = 1; | |
| 246 | + | } | |
| 247 | + | ||
| 248 | + | if (cached || (im = imlib_load_image(filename))) | |
| 79 | 249 | imlib_context_set_image(im); | |
| 80 | 250 | else | |
| 81 | 251 | imlib_context_set_image(im_invalid); | |
| @@ -84,10 +254,12 @@ void tns_load(tns_t *tns, win_t *win, int n, const char *filename) { | |||
| 84 | 254 | h = imlib_image_get_height(); | |
| 85 | 255 | ||
| 86 | 256 | if (im) { | |
| 257 | + | t->filename = filename; | |
| 87 | 258 | zw = (float) THUMB_SIZE / (float) w; | |
| 88 | 259 | zh = (float) THUMB_SIZE / (float) h; | |
| 89 | 260 | z = MIN(zw, zh); | |
| 90 | 261 | } else { | |
| 262 | + | t->filename = NULL; | |
| 91 | 263 | z = 1.0; | |
| 92 | 264 | } | |
| 93 | 265 | ||
| @@ -99,6 +271,8 @@ void tns_load(tns_t *tns, win_t *win, int n, const char *filename) { | |||
| 99 | 271 | die("could not allocate memory"); | |
| 100 | 272 | if (im) | |
| 101 | 273 | imlib_free_image_and_decache(); | |
| 274 | + | if (use_cache && !cached) | |
| 275 | + | tns_cache_write(t, False); | |
| 102 | 276 | ||
| 103 | 277 | tns->dirty = 1; | |
| 104 | 278 | } | |
| @@ -134,7 +308,10 @@ void tns_render(tns_t *tns, win_t *win) { | |||
| 134 | 308 | int i, cnt, r, x, y; | |
| 135 | 309 | thumb_t *t; | |
| 136 | 310 | ||
| 137 | − | if (!tns || !tns->dirty || !win) | |
| 311 | + | if (!tns || !tns->thumbs || !win) | |
| 312 | + | return; | |
| 313 | + | ||
| 314 | + | if (!tns->dirty) | |
| 138 | 315 | return; | |
| 139 | 316 | ||
| 140 | 317 | win_clear(win); | |
| @@ -182,7 +359,7 @@ void tns_highlight(tns_t *tns, win_t *win, int n, Bool hl) { | |||
| 182 | 359 | thumb_t *t; | |
| 183 | 360 | unsigned long col; | |
| 184 | 361 | ||
| 185 | − | if (!tns || !win) | |
| 362 | + | if (!tns || !tns->thumbs || !win) | |
| 186 | 363 | return; | |
| 187 | 364 | ||
| 188 | 365 | if (n >= 0 && n < tns->cnt) { | |
| @@ -205,7 +382,7 @@ void tns_highlight(tns_t *tns, win_t *win, int n, Bool hl) { | |||
| 205 | 382 | int tns_move_selection(tns_t *tns, win_t *win, tnsdir_t dir) { | |
| 206 | 383 | int old; | |
| 207 | 384 | ||
| 208 | − | if (!tns || !win) | |
| 385 | + | if (!tns || !tns->thumbs || !win) | |
| 209 | 386 | return 0; | |
| 210 | 387 | ||
| 211 | 388 | old = tns->sel; | |
| @@ -264,7 +441,10 @@ int tns_translate(tns_t *tns, int x, int y) { | |||
| 264 | 441 | int n; | |
| 265 | 442 | thumb_t *t; | |
| 266 | 443 | ||
| 267 | − | if (!tns || x < tns->x || y < tns->y) | |
| 444 | + | if (!tns || !tns->thumbs) | |
| 445 | + | return -1; | |
| 446 | + | ||
| 447 | + | if (x < tns->x || y < tns->y) | |
| 268 | 448 | return -1; | |
| 269 | 449 | ||
| 270 | 450 | n = tns->first + (y - tns->y) / thumb_dim * tns->cols + | |
| @@ -32,6 +32,7 @@ typedef enum { | |||
| 32 | 32 | ||
| 33 | 33 | typedef struct { | |
| 34 | 34 | Imlib_Image *im; | |
| 35 | + | const char *filename; | |
| 35 | 36 | int x; | |
| 36 | 37 | int y; | |
| 37 | 38 | int w; | |
| @@ -51,6 +52,8 @@ typedef struct { | |||
| 51 | 52 | unsigned char dirty; | |
| 52 | 53 | } tns_t; | |
| 53 | 54 | ||
| 55 | + | void tns_clear_cache(tns_t*); | |
| 56 | + | ||
| 54 | 57 | void tns_init(tns_t*, int); | |
| 55 | 58 | void tns_free(tns_t*, win_t*); | |
| 56 | 59 | ||
| @@ -18,11 +18,16 @@ | |||
| 18 | 18 | ||
| 19 | 19 | #include <stdlib.h> | |
| 20 | 20 | #include <string.h> | |
| 21 | + | #include <sys/types.h> | |
| 22 | + | #include <sys/stat.h> | |
| 23 | + | #include <unistd.h> | |
| 24 | + | #include <errno.h> | |
| 21 | 25 | ||
| 22 | 26 | #include "options.h" | |
| 23 | 27 | #include "util.h" | |
| 24 | 28 | ||
| 25 | − | #define FNAME_LEN 512 | |
| 29 | + | #define DNAME_CNT 512 | |
| 30 | + | #define FNAME_LEN 1024 | |
| 26 | 31 | ||
| 27 | 32 | void cleanup(); | |
| 28 | 33 | ||
| @@ -78,6 +83,221 @@ void size_readable(float *size, const char **unit) { | |||
| 78 | 83 | *unit = units[MIN(i, LEN(units) - 1)]; | |
| 79 | 84 | } | |
| 80 | 85 | ||
| 86 | + | char* absolute_path(const char *filename) { | |
| 87 | + | size_t len; | |
| 88 | + | char *path = NULL; | |
| 89 | + | const char *basename; | |
| 90 | + | char *dirname = NULL; | |
| 91 | + | char *cwd = NULL; | |
| 92 | + | char *twd = NULL; | |
| 93 | + | char *dir; | |
| 94 | + | char *s; | |
| 95 | + | ||
| 96 | + | if (!filename || *filename == '\0' || *filename == '/') | |
| 97 | + | return NULL; | |
| 98 | + | ||
| 99 | + | len = FNAME_LEN; | |
| 100 | + | cwd = (char*) s_malloc(len); | |
| 101 | + | while (!(s = getcwd(cwd, len)) && errno == ERANGE) { | |
| 102 | + | len *= 2; | |
| 103 | + | cwd = (char*) s_realloc(cwd, len); | |
| 104 | + | } | |
| 105 | + | if (!s) | |
| 106 | + | goto error; | |
| 107 | + | ||
| 108 | + | s = strrchr(filename, '/'); | |
| 109 | + | if (s) { | |
| 110 | + | len = s - filename; | |
| 111 | + | dirname = (char*) s_malloc(len + 1); | |
| 112 | + | strncpy(dirname, filename, len); | |
| 113 | + | dirname[len] = '\0'; | |
| 114 | + | basename = s + 1; | |
| 115 | + | ||
| 116 | + | if (chdir(cwd)) | |
| 117 | + | /* we're not able to come back afterwards */ | |
| 118 | + | goto error; | |
| 119 | + | if (chdir(dirname)) | |
| 120 | + | goto error; | |
| 121 | + | ||
| 122 | + | len = FNAME_LEN; | |
| 123 | + | twd = (char*) s_malloc(len); | |
| 124 | + | while (!(s = getcwd(twd, len)) && errno == ERANGE) { | |
| 125 | + | len *= 2; | |
| 126 | + | twd = (char*) s_realloc(twd, len); | |
| 127 | + | } | |
| 128 | + | if (chdir(cwd)) | |
| 129 | + | die("could not revert to prior working directory"); | |
| 130 | + | if (!s) | |
| 131 | + | goto error; | |
| 132 | + | dir = twd; | |
| 133 | + | } else { | |
| 134 | + | /* only a single filename given */ | |
| 135 | + | basename = filename; | |
| 136 | + | dir = cwd; | |
| 137 | + | } | |
| 138 | + | ||
| 139 | + | len = strlen(dir) + strlen(basename) + 2; | |
| 140 | + | path = (char*) s_malloc(len); | |
| 141 | + | snprintf(path, len, "%s/%s", dir, basename); | |
| 142 | + | ||
| 143 | + | goto end; | |
| 144 | + | ||
| 145 | + | error: | |
| 146 | + | if (path) { | |
| 147 | + | free(path); | |
| 148 | + | path = NULL; | |
| 149 | + | } | |
| 150 | + | ||
| 151 | + | end: | |
| 152 | + | if (dirname) | |
| 153 | + | free(dirname); | |
| 154 | + | if (cwd) | |
| 155 | + | free(cwd); | |
| 156 | + | if (twd) | |
| 157 | + | free(twd); | |
| 158 | + | ||
| 159 | + | return path; | |
| 160 | + | } | |
| 161 | + | ||
| 162 | + | int r_opendir(r_dir_t *rdir, const char *dirname) { | |
| 163 | + | if (!rdir || !dirname || !*dirname) | |
| 164 | + | return -1; | |
| 165 | + | ||
| 166 | + | if (!(rdir->dir = opendir(dirname))) { | |
| 167 | + | rdir->name = NULL; | |
| 168 | + | rdir->stack = NULL; | |
| 169 | + | return -1; | |
| 170 | + | } | |
| 171 | + | ||
| 172 | + | rdir->stcap = DNAME_CNT; | |
| 173 | + | rdir->stack = (char**) s_malloc(rdir->stcap * sizeof(char*)); | |
| 174 | + | rdir->stlen = 0; | |
| 175 | + | ||
| 176 | + | rdir->name = (char*) dirname; | |
| 177 | + | rdir->d = 0; | |
| 178 | + | ||
| 179 | + | return 0; | |
| 180 | + | } | |
| 181 | + | ||
| 182 | + | int r_closedir(r_dir_t *rdir) { | |
| 183 | + | int ret = 0; | |
| 184 | + | ||
| 185 | + | if (!rdir) | |
| 186 | + | return -1; | |
| 187 | + | ||
| 188 | + | if (rdir->stack) { | |
| 189 | + | while (rdir->stlen > 0) | |
| 190 | + | free(rdir->stack[--rdir->stlen]); | |
| 191 | + | free(rdir->stack); | |
| 192 | + | rdir->stack = NULL; | |
| 193 | + | } | |
| 194 | + | ||
| 195 | + | if (rdir->dir) { | |
| 196 | + | if (!(ret = closedir(rdir->dir))) | |
| 197 | + | rdir->dir = NULL; | |
| 198 | + | } | |
| 199 | + | ||
| 200 | + | if (rdir->d && rdir->name) { | |
| 201 | + | free(rdir->name); | |
| 202 | + | rdir->name = NULL; | |
| 203 | + | } | |
| 204 | + | ||
| 205 | + | return ret; | |
| 206 | + | } | |
| 207 | + | ||
| 208 | + | char* r_readdir(r_dir_t *rdir) { | |
| 209 | + | size_t len; | |
| 210 | + | char *filename; | |
| 211 | + | struct dirent *dentry; | |
| 212 | + | struct stat fstats; | |
| 213 | + | ||
| 214 | + | if (!rdir || !rdir->dir || !rdir->name) | |
| 215 | + | return NULL; | |
| 216 | + | ||
| 217 | + | while (1) { | |
| 218 | + | if (rdir->dir && (dentry = readdir(rdir->dir))) { | |
| 219 | + | if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) | |
| 220 | + | continue; | |
| 221 | + | ||
| 222 | + | len = strlen(rdir->name) + strlen(dentry->d_name) + 2; | |
| 223 | + | filename = (char*) s_malloc(len); | |
| 224 | + | snprintf(filename, len, "%s%s%s", rdir->name, | |
| 225 | + | rdir->name[strlen(rdir->name)-1] == '/' ? "" : "/", | |
| 226 | + | dentry->d_name); | |
| 227 | + | ||
| 228 | + | if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) { | |
| 229 | + | /* put subdirectory on the stack */ | |
| 230 | + | if (rdir->stlen == rdir->stcap) { | |
| 231 | + | rdir->stcap *= 2; | |
| 232 | + | rdir->stack = (char**) s_realloc(rdir->stack, | |
| 233 | + | rdir->stcap * sizeof(char*)); | |
| 234 | + | } | |
| 235 | + | rdir->stack[rdir->stlen++] = filename; | |
| 236 | + | continue; | |
| 237 | + | } | |
| 238 | + | return filename; | |
| 239 | + | } | |
| 240 | + | ||
| 241 | + | if (rdir->stlen > 0) { | |
| 242 | + | /* open next subdirectory */ | |
| 243 | + | closedir(rdir->dir); | |
| 244 | + | if (rdir->d) | |
| 245 | + | free(rdir->name); | |
| 246 | + | rdir->name = rdir->stack[--rdir->stlen]; | |
| 247 | + | rdir->d = 1; | |
| 248 | + | if (!(rdir->dir = opendir(rdir->name))) | |
| 249 | + | warn("could not open directory: %s", rdir->name); | |
| 250 | + | continue; | |
| 251 | + | } | |
| 252 | + | ||
| 253 | + | /* no more entries */ | |
| 254 | + | break; | |
| 255 | + | } | |
| 256 | + | ||
| 257 | + | return NULL; | |
| 258 | + | } | |
| 259 | + | ||
| 260 | + | int r_mkdir(const char *path) { | |
| 261 | + | char *dir, *d; | |
| 262 | + | struct stat stats; | |
| 263 | + | int err = 0; | |
| 264 | + | ||
| 265 | + | if (!path || !*path) | |
| 266 | + | return -1; | |
| 267 | + | ||
| 268 | + | if (!stat(path, &stats)) { | |
| 269 | + | if (S_ISDIR(stats.st_mode)) { | |
| 270 | + | return 0; | |
| 271 | + | } else { | |
| 272 | + | warn("not a directory: %s", path); | |
| 273 | + | return -1; | |
| 274 | + | } | |
| 275 | + | } | |
| 276 | + | ||
| 277 | + | d = dir = (char*) s_malloc(strlen(path) + 1); | |
| 278 | + | strcpy(dir, path); | |
| 279 | + | ||
| 280 | + | while (d != NULL && !err) { | |
| 281 | + | d = strchr(d + 1, '/'); | |
| 282 | + | if (d != NULL) | |
| 283 | + | *d = '\0'; | |
| 284 | + | if (access(dir, F_OK) && errno == ENOENT) { | |
| 285 | + | if (mkdir(dir, 0755)) { | |
| 286 | + | warn("could not create directory: %s", dir); | |
| 287 | + | err = -1; | |
| 288 | + | } | |
| 289 | + | } else if (stat(dir, &stats) || !S_ISDIR(stats.st_mode)) { | |
| 290 | + | warn("not a directory: %s", dir); | |
| 291 | + | err = -1; | |
| 292 | + | } | |
| 293 | + | if (d != NULL) | |
| 294 | + | *d = '/'; | |
| 295 | + | } | |
| 296 | + | free(dir); | |
| 297 | + | ||
| 298 | + | return err; | |
| 299 | + | } | |
| 300 | + | ||
| 81 | 301 | char* readline(FILE *stream) { | |
| 82 | 302 | size_t len; | |
| 83 | 303 | char *buf, *s, *end; | |
| @@ -21,6 +21,7 @@ | |||
| 21 | 21 | ||
| 22 | 22 | #include <stdio.h> | |
| 23 | 23 | #include <stdarg.h> | |
| 24 | + | #include <dirent.h> | |
| 24 | 25 | ||
| 25 | 26 | #define ABS(a) ((a) < 0 ? (-(a)) : (a)) | |
| 26 | 27 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) | |
| @@ -30,6 +31,21 @@ | |||
| 30 | 31 | #define TV_TO_DOUBLE(x) ((double) ((x).tv_sec) + 0.000001 * \ | |
| 31 | 32 | (double) ((x).tv_usec)) | |
| 32 | 33 | ||
| 34 | + | #define TIMESPEC_TO_TIMEVAL(tv, ts) { \ | |
| 35 | + | (tv)->tv_sec = (ts)->tv_sec; \ | |
| 36 | + | (tv)->tv_usec = (ts)->tv_nsec / 1000; \ | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | typedef struct { | |
| 40 | + | DIR *dir; | |
| 41 | + | char *name; | |
| 42 | + | int d; | |
| 43 | + | ||
| 44 | + | char **stack; | |
| 45 | + | int stcap; | |
| 46 | + | int stlen; | |
| 47 | + | } r_dir_t; | |
| 48 | + | ||
| 33 | 49 | void* s_malloc(size_t); | |
| 34 | 50 | void* s_realloc(void*, size_t); | |
| 35 | 51 | ||
| @@ -38,6 +54,13 @@ void die(const char*, ...); | |||
| 38 | 54 | ||
| 39 | 55 | void size_readable(float*, const char**); | |
| 40 | 56 | ||
| 57 | + | char* absolute_path(const char*); | |
| 58 | + | ||
| 59 | + | int r_opendir(r_dir_t*, const char*); | |
| 60 | + | int r_closedir(r_dir_t*); | |
| 61 | + | char* r_readdir(r_dir_t*); | |
| 62 | + | int r_mkdir(const char *); | |
| 63 | + | ||
| 41 | 64 | char* readline(FILE*); | |
| 42 | 65 | ||
| 43 | 66 | #endif /* UTIL_H */ | |