diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 4 | ||||
-rw-r--r-- | src/feh.h | 15 | ||||
-rw-r--r-- | src/filelist.c | 43 | ||||
-rw-r--r-- | src/filelist.h | 1 | ||||
-rw-r--r-- | src/help.raw | 6 | ||||
-rw-r--r-- | src/imlib.c | 200 | ||||
-rw-r--r-- | src/index.c | 2 | ||||
-rw-r--r-- | src/keyevents.c | 4 | ||||
-rw-r--r-- | src/menu.c | 17 | ||||
-rw-r--r-- | src/options.c | 29 | ||||
-rw-r--r-- | src/options.h | 1 | ||||
-rw-r--r-- | src/slideshow.c | 103 | ||||
-rw-r--r-- | src/strverscmp.c | 57 | ||||
-rw-r--r-- | src/thumbnail.c | 3 | ||||
-rw-r--r-- | src/timers.c | 33 | ||||
-rw-r--r-- | src/timers.h | 2 | ||||
-rw-r--r-- | src/wallpaper.c | 185 | ||||
-rw-r--r-- | src/wallpaper.h | 3 | ||||
-rw-r--r-- | src/winwidget.c | 38 |
19 files changed, 571 insertions, 175 deletions
diff --git a/src/Makefile b/src/Makefile index ac4d2fe..10e33eb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -34,6 +34,10 @@ ifeq (${exif},1) exif_nikon.c endif +ifneq (${verscmp},1) + TARGETS += strverscmp.c +endif + OBJECTS = ${TARGETS:.c=.o} I_SRCS = ${shell echo *.raw} @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * strverscmp(3) is a GNU extension. In most supporting C libraries it * requires _GNU_SOURCE to be defined. */ -#ifdef HAVE_VERSCMP +#ifdef HAVE_STRVERSCMP #define _GNU_SOURCE #endif @@ -115,6 +115,14 @@ enum slide_change { SLIDE_NEXT, SLIDE_PREV, SLIDE_RAND, SLIDE_FIRST, SLIDE_LAST, SLIDE_JUMP_PREV_DIR }; +enum feh_load_error { + LOAD_ERROR_IMLIB = 0, + LOAD_ERROR_IMAGEMAGICK, + LOAD_ERROR_CURL, + LOAD_ERROR_DCRAW, + LOAD_ERROR_MAGICBYTES +}; + #define INPLACE_EDIT_FLIP -1 #define INPLACE_EDIT_MIRROR -2 @@ -172,7 +180,7 @@ void feh_display_status(char stat); void real_loadables_mode(int loadable); void feh_reload_image(winwidget w, int resize, int force_new); void feh_filelist_image_remove(winwidget winwid, char do_delete); -void feh_imlib_print_load_error(char *file, winwidget w, Imlib_Load_Error err); +void feh_print_load_error(char *file, winwidget w, Imlib_Load_Error err, enum feh_load_error feh_err); void slideshow_save_image(winwidget win); void feh_edit_inplace(winwidget w, int orientation); void feh_edit_inplace_lossless(winwidget w, int orientation); @@ -182,6 +190,9 @@ gib_list *feh_list_jump(gib_list * root, gib_list * l, int direction, int num); #ifdef HAVE_INOTIFY void feh_event_handle_inotify(void); #endif +#ifndef HAVE_STRVERSCMP +int strverscmp(const char *l0, const char *r0); +#endif /* Imlib stuff */ extern Display *disp; diff --git a/src/filelist.c b/src/filelist.c index d5b0750..49355c4 100644 --- a/src/filelist.c +++ b/src/filelist.c @@ -28,6 +28,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include <libexif/exif-data.h> #endif +#ifdef HAVE_LIBCURL +#include <curl/curl.h> +#endif + #include "feh.h" #include "filelist.h" #include "signals.h" @@ -402,7 +406,6 @@ void feh_file_dirname(char *dst, feh_file * f, int maxlen) dst[n] = '\0'; } -#ifdef HAVE_VERSCMP static inline int strcmp_or_strverscmp(const char *s1, const char *s2) { if (!opt.version_sort) @@ -410,9 +413,6 @@ static inline int strcmp_or_strverscmp(const char *s1, const char *s2) else return(strverscmp(s1, s2)); } -#else -#define strcmp_or_strverscmp strcmp -#endif int feh_cmp_filename(void *file1, void *file2) { @@ -664,8 +664,17 @@ char *feh_absolute_path(char *path) void feh_save_filelist() { char *tmpname; + char *base_dir = ""; + + if (opt.output_dir) { + base_dir = estrjoin("", opt.output_dir, "/", NULL); + } + + tmpname = feh_unique_filename(base_dir, "filelist"); - tmpname = feh_unique_filename("", "filelist"); + if (opt.output_dir) { + free(base_dir); + } if (opt.verbose) fprintf(stderr, "saving filelist to filename '%s'\n", tmpname); @@ -674,3 +683,27 @@ void feh_save_filelist() free(tmpname); return; } + +#ifdef HAVE_LIBCURL + +char *feh_http_unescape(char *url) +{ + CURL *curl = curl_easy_init(); + if (!curl) { + return NULL; + } + char *tmp_url = curl_easy_unescape(curl, url, 0, NULL); + char *new_url = estrdup(tmp_url); + curl_free(tmp_url); + curl_easy_cleanup(curl); + return new_url; +} + +#else + +char *feh_http_unescape(char *url) +{ + return NULL; +} + +#endif diff --git a/src/filelist.h b/src/filelist.h index e24a6a6..ff0645e 100644 --- a/src/filelist.h +++ b/src/filelist.h @@ -97,6 +97,7 @@ gib_list *feh_read_filelist(char *filename); char *feh_absolute_path(char *path); gib_list *feh_file_remove_from_list(gib_list * list, gib_list * l); void feh_save_filelist(); +char *feh_http_unescape(char * url); int feh_cmp_name(void *file1, void *file2); int feh_cmp_dirname(void *file1, void *file2); diff --git a/src/help.raw b/src/help.raw index a9cf9da..1846908 100644 --- a/src/help.raw +++ b/src/help.raw @@ -100,6 +100,7 @@ OPTIONS --scroll-step COUNT scroll COUNT pixels when movement key is pressed --cache-size NUM imlib cache size in mebibytes (0 .. 2048) --auto-reload automatically reload shown image if file was changed + --window-id Draw to an existing X11 window by its ID MONTAGE MODE OPTIONS -X, --ignore-aspect Set thumbnail to specified width/height without @@ -124,6 +125,7 @@ INDEX MODE OPTIONS font is specified, a title will not be printed FORMAT SPECIFIERS + %a information about slideshow state (playing/paused) %f image path/filename %F image path/filename (shell-escaped) %g window dimensions (\"width,height\") in pixels @@ -193,8 +195,8 @@ DEFAULT KEYS <KEYPAD UP> Move the image up <KEYPAD DOWN> Move the image down <KEYPAD BEGIN> Antialias the image - <KEYPAD +> Zoom in - <KEYPAD -> Zoom out + <KEYPAD +>, <UP> Zoom in + <KEYPAD ->, <DOWN> Zoom out <KEYPAD *> Zoom to 100% <KEYPAD /> Zoom to fit the window diff --git a/src/imlib.c b/src/imlib.c index 62d88c6..fc81bdd 100644 --- a/src/imlib.c +++ b/src/imlib.c @@ -164,11 +164,31 @@ int feh_load_image_char(Imlib_Image * im, char *filename) return(i); } -void feh_imlib_print_load_error(char *file, winwidget w, Imlib_Load_Error err) +void feh_print_load_error(char *file, winwidget w, Imlib_Load_Error err, enum feh_load_error feh_err) { if (err == IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS) eprintf("%s - Out of file descriptors while loading", file); else if (!opt.quiet || w) { + switch (feh_err) { + case LOAD_ERROR_IMLIB: + // handled in the next switch/case statement + break; + case LOAD_ERROR_IMAGEMAGICK: + im_weprintf(w, "%s - No ImageMagick loader for that file format", file); + break; + case LOAD_ERROR_CURL: + im_weprintf(w, "%s - libcurl was unable to retrieve the file", file); + break; + case LOAD_ERROR_DCRAW: + im_weprintf(w, "%s - Unable to open preview via dcraw", file); + break; + case LOAD_ERROR_MAGICBYTES: + im_weprintf(w, "%s - Does not look like an image (magic bytes missing)", file); + break; + } + if (feh_err != LOAD_ERROR_IMLIB) { + return; + } switch (err) { case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST: im_weprintf(w, "%s - File does not exist", file); @@ -215,9 +235,104 @@ void feh_imlib_print_load_error(char *file, winwidget w, Imlib_Load_Error err) } } +/* + * This is a workaround for an Imlib2 regression, causing unloadable image + * detection to be excessively slow (and, thus, causing feh to hang for a while + * when encountering an unloadable image). We use magic byte detection to + * avoid calling Imlib2 for files it probably cannot handle. See + * <https://phab.enlightenment.org/T8739> and + * <https://github.com/derf/feh/issues/505>. + * + * Note that this drops support for bz2-compressed files, unless + * FEH_SKIP_MAGIC is set + */ +int feh_is_image(feh_file * file) +{ + unsigned char buf[16]; + FILE *fh = fopen(file->filename, "r"); + if (!fh) { + return 0; + } + if (fread(buf, 1, 16, fh) != 16) { + fclose(fh); + return 0; + } + fclose(fh); + + if (buf[0] == 0xff && buf[1] == 0xd8) { + // JPEG + return 1; + } + if (!memcmp(buf, "\x89PNG\x0d\x0a\x1a\x0a", 8)) { + // PNG + return 1; + } + if (buf[0] == 'A' && buf[1] == 'R' && buf[2] == 'G' && buf[3] == 'B') { + // ARGB + return 1; + } + if (buf[0] == 'B' && buf[1] == 'M') { + // BMP + return 1; + } + if (!memcmp(buf, "farbfeld", 8)) { + // farbfeld + return 1; + } + if (buf[0] == 'G' && buf[1] == 'I' && buf[2] == 'F') { + // GIF + return 1; + } + if (buf[0] == 0x00 && buf[1] == 0x00 && buf[2] <= 0x02 && buf[3] == 0x00) { + // ICO + return 1; + } + if (!memcmp(buf, "FORM", 4)) { + // Amiga IFF ILBM + return 1; + } + if (buf[0] == 'P' && buf[1] >= '1' && buf[1] <= '7') { + // PNM et al. + return 1; + } + if (strstr(file->filename, ".tga")) { + // TGA + return 1; + } + if (!memcmp(buf, "II\x2a\x00", 4) || !memcmp(buf, "MM\x00\x2a", 4)) { + // TIFF + return 1; + } + if (!memcmp(buf, "RIFF", 4)) { + // might be webp + return 1; + } + if (!memcmp(buf + 4, "ftyphei", 7) || !memcmp(buf + 4, "ftypmif1", 8)) { + // HEIC/HEIF - note that this is only supported in imlib2-heic. Ordinary + // imlib2 releases do not support heic/heif images as of 2021-01. + return 1; + } + buf[15] = 0; + if (strstr((char *)buf, "XPM")) { + // XPM + return 1; + } + if (strstr(file->filename, ".bz2") || strstr(file->filename, ".gz")) { + // Imlib2 supports compressed images. It relies on the filename to + // determine the appropriate loader and does not use magic bytes here. + return 1; + } + // moved to the end as this variable won't be set in most cases + if (getenv("FEH_SKIP_MAGIC")) { + return 1; + } + return 0; +} + int feh_load_image(Imlib_Image * im, feh_file * file) { Imlib_Load_Error err = IMLIB_LOAD_ERROR_NONE; + enum feh_load_error feh_err = LOAD_ERROR_IMLIB; enum { SRC_IMLIB, SRC_HTTP, SRC_MAGICK, SRC_DCRAW } image_source = SRC_IMLIB; char *tmpname = NULL; char *real_filename = NULL; @@ -230,23 +345,37 @@ int feh_load_image(Imlib_Image * im, feh_file * file) if (path_is_url(file->filename)) { image_source = SRC_HTTP; - if ((tmpname = feh_http_load_image(file->filename)) == NULL) + if ((tmpname = feh_http_load_image(file->filename)) == NULL) { + feh_err = LOAD_ERROR_CURL; err = IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST; + } } - else if (opt.conversion_timeout >= 0 && feh_file_is_raw(file->filename)) { - image_source = SRC_DCRAW; - tmpname = feh_dcraw_load_image(file->filename); - if (!tmpname) + else { + if (feh_is_image(file)) { + *im = imlib_load_image_with_error_return(file->filename, &err); + } else { + feh_err = LOAD_ERROR_MAGICBYTES; err = IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT; + } } - else - *im = imlib_load_image_with_error_return(file->filename, &err); if (opt.conversion_timeout >= 0 && ( (err == IMLIB_LOAD_ERROR_UNKNOWN) || (err == IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT))) { - image_source = SRC_MAGICK; - tmpname = feh_magick_load_image(file->filename); + if (feh_file_is_raw(file->filename)) { + image_source = SRC_DCRAW; + tmpname = feh_dcraw_load_image(file->filename); + if (!tmpname) { + feh_err = LOAD_ERROR_DCRAW; + } + } else { + image_source = SRC_MAGICK; + feh_err = LOAD_ERROR_IMLIB; + tmpname = feh_magick_load_image(file->filename); + if (!tmpname) { + feh_err = LOAD_ERROR_IMAGEMAGICK; + } + } } if (tmpname) { @@ -269,7 +398,14 @@ int feh_load_image(Imlib_Image * im, feh_file * file) file->filename = real_filename; #ifdef HAVE_LIBEXIF - file->ed = exif_get_data(tmpname); + /* + * if we're called from within feh_reload_image, file->ed is already + * populated. + */ + if (file->ed) { + exif_data_unref(file->ed); + } + file->ed = exif_data_new_from_file(tmpname); #endif } if (!opt.use_conversion_cache && ((image_source != SRC_HTTP) || !opt.keep_http)) @@ -279,8 +415,19 @@ int feh_load_image(Imlib_Image * im, feh_file * file) // add_file_to_rm_filelist duplicates tmpname add_file_to_rm_filelist(tmpname); - if (image_source != SRC_HTTP && !opt.use_conversion_cache) + if (!opt.use_conversion_cache) free(tmpname); + } else if (im) { +#ifdef HAVE_LIBEXIF + /* + * if we're called from within feh_reload_image, file->ed is already + * populated. + */ + if (file->ed) { + exif_data_unref(file->ed); + } + file->ed = exif_data_new_from_file(file->filename); +#endif } if ((err) || (!im)) { @@ -288,7 +435,7 @@ int feh_load_image(Imlib_Image * im, feh_file * file) fputs("\n", stderr); reset_output = 1; } - feh_imlib_print_load_error(file->filename, NULL, err); + feh_print_load_error(file->filename, NULL, err, feh_err); D(("Load *failed*\n")); return(0); } @@ -305,14 +452,12 @@ int feh_load_image(Imlib_Image * im, feh_file * file) #ifdef HAVE_LIBEXIF int orientation = 0; - ExifData *exifData = exif_data_new_from_file(file->filename); - if (exifData) { - ExifByteOrder byteOrder = exif_data_get_byte_order(exifData); - ExifEntry *exifEntry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION); + if (file->ed) { + ExifByteOrder byteOrder = exif_data_get_byte_order(file->ed); + ExifEntry *exifEntry = exif_data_get_entry(file->ed, EXIF_TAG_ORIENTATION); if (exifEntry && opt.auto_rotate) orientation = exif_get_short(exifEntry->data, byteOrder); } - file->ed = exifData; if (orientation == 2) gib_imlib_image_flip_horizontal(*im); @@ -430,11 +575,9 @@ static int feh_file_is_raw(char *filename) } if (childpid == 0) { - if (opt.quiet) { - int devnull = open("/dev/null", O_WRONLY); - dup2(devnull, 1); - dup2(devnull, 2); - } + int devnull = open("/dev/null", O_WRONLY); + dup2(devnull, 1); + dup2(devnull, 2); execlp("dcraw", "dcraw", "-i", filename, NULL); _exit(1); } else { @@ -1085,6 +1228,7 @@ void feh_draw_exif(winwidget w) info_buf[i], IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255); gib_imlib_text_draw(im, fn, NULL, 1, (i * line_height) + 1, info_buf[i], IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); + free(info_buf[i]); } @@ -1392,8 +1536,10 @@ void feh_edit_inplace(winwidget w, int op) return; } - if (!strcmp(gib_imlib_image_format(w->im), "jpeg") && - !path_is_url(FEH_FILE(w->file->data)->filename)) { + // Imlib2 <= 1.5 returns "jpeg", Imlib2 >= 1.6 uses "jpg" + if ((!strcmp(gib_imlib_image_format(w->im), "jpeg") + || !strcmp(gib_imlib_image_format(w->im), "jpg")) + && !path_is_url(FEH_FILE(w->file->data)->filename)) { feh_edit_inplace_lossless(w, op); feh_reload_image(w, 1, 1); return; @@ -1413,8 +1559,8 @@ void feh_edit_inplace(winwidget w, int op) FEH_FILE(w->file->data)->filename, &err); gib_imlib_free_image(old); if (err) - feh_imlib_print_load_error(FEH_FILE(w->file->data)->filename, - w, err); + feh_print_load_error(FEH_FILE(w->file->data)->filename, + w, err, LOAD_ERROR_IMLIB); feh_reload_image(w, 1, 1); } else { /* diff --git a/src/index.c b/src/index.c index 3633c96..85a3ee8 100644 --- a/src/index.c +++ b/src/index.c @@ -332,7 +332,7 @@ void init_index_mode(void) gib_imlib_save_image_with_error_return(im_main, output_buf, &err); if (err) { - feh_imlib_print_load_error(output_buf, im_main, err); + feh_print_load_error(output_buf, im_main, err, LOAD_ERROR_IMLIB); } else if (opt.verbose) { int tw, th; diff --git a/src/keyevents.c b/src/keyevents.c index 0da07d3..0ca489a 100644 --- a/src/keyevents.c +++ b/src/keyevents.c @@ -319,7 +319,7 @@ void feh_event_handle_stdin() char stdin_buf[2]; static char is_esc = 0; KeySym keysym = NoSymbol; - if (read(STDIN_FILENO, &stdin_buf, 1) == -1) { + if (read(STDIN_FILENO, &stdin_buf, 1) <= 0) { control_via_stdin = 0; if (isatty(STDIN_FILENO) && getpgrp() == (tcgetpgrp(STDIN_FILENO))) { weprintf("reading a command from stdin failed - disabling control via stdin"); @@ -710,6 +710,8 @@ void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysy } else if (feh_is_kp(EVENT_toggle_pause, state, keysym, button)) { slideshow_pause_toggle(winwid); + /* We need to re-render the image to update the info string immediately. */ + winwidget_render_image(winwid, 0, 0); } else if (feh_is_kp(EVENT_save_image, state, keysym, button)) { slideshow_save_image(winwid); @@ -80,6 +80,8 @@ enum { CB_OPT_FREEZE_WINDOW, CB_OPT_FULLSCREEN, CB_EDIT_ROTATE, + CB_EDIT_MIRROR, + CB_EDIT_FLIP, CB_OPT_AUTO_ZOOM, CB_OPT_KEEP_ZOOM_VP }; @@ -920,7 +922,12 @@ void feh_menu_init_main(void) feh_menu_add_entry(m, "Reload", NULL, CB_RELOAD, 0, NULL); feh_menu_add_entry(m, "Save Image", NULL, CB_SAVE_IMAGE, 0, NULL); feh_menu_add_entry(m, "Save List", NULL, CB_SAVE_FILELIST, 0, NULL); - feh_menu_add_entry(m, "Edit in Place", "EDIT", 0, 0, NULL); + if (opt.edit) { + feh_menu_add_entry(m, "Edit in Place", "EDIT", 0, 0, NULL); + } + else { + feh_menu_add_entry(m, "Change View", "EDIT", 0, 0, NULL); + } feh_menu_add_entry(m, "Background", "BACKGROUND", 0, 0, NULL); feh_menu_add_entry(m, NULL, NULL, 0, 0, NULL); feh_menu_add_entry(m, "Hide", NULL, CB_REMOVE, 0, NULL); @@ -963,6 +970,8 @@ void feh_menu_init_common() feh_menu_add_entry(m, "Rotate 90 CW", NULL, CB_EDIT_ROTATE, 1, NULL); feh_menu_add_entry(m, "Rotate 180", NULL, CB_EDIT_ROTATE, 2, NULL); feh_menu_add_entry(m, "Rotate 90 CCW", NULL, CB_EDIT_ROTATE, 3, NULL); + feh_menu_add_entry(m, "Mirror", NULL, CB_EDIT_MIRROR, 0, NULL); + feh_menu_add_entry(m, "Flip", NULL, CB_EDIT_FLIP, 0, NULL); menu_bg = feh_menu_new(); menu_bg->name = estrdup("BACKGROUND"); @@ -1309,6 +1318,12 @@ void feh_menu_cb(feh_menu * m, feh_menu_item * i, int action, unsigned short dat case CB_EDIT_ROTATE: feh_edit_inplace(m->fehwin, data); break; + case CB_EDIT_MIRROR: + feh_edit_inplace(m->fehwin, INPLACE_EDIT_MIRROR); + break; + case CB_EDIT_FLIP: + feh_edit_inplace(m->fehwin, INPLACE_EDIT_FLIP); + break; case CB_SAVE_IMAGE: slideshow_save_image(m->fehwin); break; diff --git a/src/options.c b/src/options.c index 2cd7a9e..4744bb0 100644 --- a/src/options.c +++ b/src/options.c @@ -433,6 +433,7 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun) #endif {"class" , 1, 0, 249}, {"no-conversion-cache", 0, 0, 250}, + {"window-id", 1, 0, 251}, {0, 0, 0, 0} }; int optch = 0, cmdx = 0; @@ -833,6 +834,9 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun) case 250: opt.use_conversion_cache = 0; break; + case 251: + opt.x11_windowid = atol(optarg); + break; default: break; } @@ -855,11 +859,26 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun) */ if (opt.start_list_at && path_is_url(opt.start_list_at) && (strlen(opt.start_list_at) <= 8 || strncmp(opt.start_list_at, "file:///", 8) != 0)) { add_file_to_filelist_recursively(opt.start_list_at, FILELIST_FIRST); + /* + * Otherwise, make "feh --start-at dir/file.jpg" behave like + * "feh --start-at dir/file.jpg dir". + */ } else if (opt.start_list_at && strrchr(opt.start_list_at, '/')) { + /* + * feh can't candle urlencoded path components ("some%20dir" etc). + * Use libcurl to unescape them if --start-at is file://... + */ if (strlen(opt.start_list_at) > 8 && strncmp(opt.start_list_at, "file:///", 8) == 0) { - char *start_at_path = estrdup(opt.start_list_at + 7); - free(opt.start_list_at); - opt.start_list_at = start_at_path; + char *unescaped_path = feh_http_unescape(opt.start_list_at); + if (unescaped_path != NULL) { + free(opt.start_list_at); + opt.start_list_at = estrdup(unescaped_path + 7); + free(unescaped_path); + } else { + char *new_path = estrdup(opt.start_list_at + 7); + free(opt.start_list_at); + opt.start_list_at = new_path; + } } char *target_directory = estrdup(opt.start_list_at); char *filename_start = strrchr(target_directory, '/'); @@ -943,8 +962,8 @@ static void show_version(void) "stat64 " #endif -#ifdef HAVE_VERSCMP - "verscmp " +#ifdef HAVE_STRVERSCMP + "verscmp " #endif #ifdef HAVE_LIBXINERAMA diff --git a/src/options.h b/src/options.h index 4906004..7ca699b 100644 --- a/src/options.h +++ b/src/options.h @@ -130,6 +130,7 @@ struct __fehoptions { unsigned char adjust_reload; int xinerama_index; char *x11_class; + unsigned long int x11_windowid; /* signed in case someone wants to invert scrolling real quick */ int scroll_step; diff --git a/src/slideshow.c b/src/slideshow.c index 591848c..87ec4cb 100644 --- a/src/slideshow.c +++ b/src/slideshow.c @@ -57,6 +57,7 @@ void init_slideshow_mode(void) // Try finding an exact filename match first for (; l && opt.start_list_at; l = l->next) { if (!strcmp(opt.start_list_at, FEH_FILE(l->data)->filename)) { + free(opt.start_list_at); opt.start_list_at = NULL; break; } @@ -83,6 +84,7 @@ void init_slideshow_mode(void) current_filename = FEH_FILE(l->data)->filename; } if (!strcmp(start_at_filename, current_filename)) { + free(opt.start_list_at); opt.start_list_at = NULL; break; } @@ -135,47 +137,54 @@ void cb_reload_timer(void *data) winwidget w = (winwidget) data; - /* save the current filename for refinding it in new list */ - current_filename = estrdup(FEH_FILE(current_file->data)->filename); + /* + * multi-window mode has no concept of a "current file" and + * dynamically adding/removing windows is not implemented at the moment. + * So don't reload filelists in multi-window mode. + */ + if (current_file != NULL) { + /* save the current filename for refinding it in new list */ + current_filename = estrdup(FEH_FILE(current_file->data)->filename); - for (l = filelist; l; l = l->next) { - feh_file_free(l->data); - l->data = NULL; - } - gib_list_free_and_data(filelist); - filelist = NULL; - filelist_len = 0; - current_file = NULL; - - /* rebuild filelist from original_file_items */ - if (gib_list_length(original_file_items) > 0) - for (l = gib_list_last(original_file_items); l; l = l->prev) - add_file_to_filelist_recursively(l->data, FILELIST_FIRST); - else if (!opt.filelistfile && !opt.bgmode) - add_file_to_filelist_recursively(".", FILELIST_FIRST); - - if (opt.filelistfile) { - filelist = gib_list_cat(filelist, feh_read_filelist(opt.filelistfile)); - } - - if (!(filelist_len = gib_list_length(filelist))) { - eprintf("No files found to reload."); - } + for (l = filelist; l; l = l->next) { + feh_file_free(l->data); + l->data = NULL; + } + gib_list_free_and_data(filelist); + filelist = NULL; + filelist_len = 0; + current_file = NULL; + + /* rebuild filelist from original_file_items */ + if (gib_list_length(original_file_items) > 0) + for (l = gib_list_last(original_file_items); l; l = l->prev) + add_file_to_filelist_recursively(l->data, FILELIST_FIRST); + else if (!opt.filelistfile && !opt.bgmode) + add_file_to_filelist_recursively(".", FILELIST_FIRST); + + if (opt.filelistfile) { + filelist = gib_list_cat(filelist, feh_read_filelist(opt.filelistfile)); + } + + if (!(filelist_len = gib_list_length(filelist))) { + eprintf("No files found to reload."); + } - feh_prepare_filelist(); + feh_prepare_filelist(); - /* find the previously current file */ - for (l = filelist; l; l = l->next) - if (strcmp(FEH_FILE(l->data)->filename, current_filename) == 0) { - current_file = l; - break; - } + /* find the previously current file */ + for (l = filelist; l; l = l->next) + if (strcmp(FEH_FILE(l->data)->filename, current_filename) == 0) { + current_file = l; + break; + } - free(current_filename); + free(current_filename); - if (!current_file) - current_file = filelist; - w->file = current_file; + if (!current_file) + current_file = filelist; + w->file = current_file; + } feh_reload_image(w, 1, 0); feh_add_unique_timer(cb_reload_timer, w, opt.reload); @@ -218,6 +227,18 @@ void slideshow_change_image(winwidget winwid, int change, int render) /* The for loop prevents us looping infinitely */ for (i = 0; i < our_filelist_len; i++) { winwidget_free_image(winwid); +#ifdef HAVE_LIBEXIF + /* + * An EXIF data chunk requires up to 50 kB of space. For large and + * long-running slideshows, this would acculumate gigabytes of + * EXIF data after a few days. We therefore do not cache EXIF data + * in slideshows. + */ + if (FEH_FILE(winwid->file->data)->ed) { + exif_data_unref(FEH_FILE(winwid->file->data)->ed); + FEH_FILE(winwid->file->data)->ed = NULL; + } +#endif switch (change) { case SLIDE_NEXT: current_file = feh_list_jump(filelist, current_file, FORWARD, 1); @@ -398,6 +419,14 @@ char *feh_printf(char *str, feh_file * file, winwidget winwid) if ((*c == '%') && (*(c+1) != '\0')) { c++; switch (*c) { + case 'a': + if (opt.paused == 1) { + strncat(ret, "paused", sizeof(ret) - strlen(ret) - 1); + } + else { + strncat(ret, "playing", sizeof(ret) - strlen(ret) - 1); + } + break; case 'f': if (file) strncat(ret, file->filename, sizeof(ret) - strlen(ret) - 1); @@ -612,7 +641,7 @@ void slideshow_save_image(winwidget win) gib_imlib_save_image_with_error_return(win->im, tmpname, &err); if (err) - feh_imlib_print_load_error(tmpname, win, err); + feh_print_load_error(tmpname, win, err, LOAD_ERROR_IMLIB); free(tmpname); return; diff --git a/src/strverscmp.c b/src/strverscmp.c new file mode 100644 index 0000000..92cd877 --- /dev/null +++ b/src/strverscmp.c @@ -0,0 +1,57 @@ +/* + * Copyright © 2005-2020 Rich Felker, et al. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#define _GNU_SOURCE +#include <ctype.h> +#include <string.h> + +int strverscmp(const char *l0, const char *r0) +{ + const unsigned char *l = (const void *)l0; + const unsigned char *r = (const void *)r0; + size_t i, dp, j; + int z = 1; + + /* Find maximal matching prefix and track its maximal digit + * suffix and whether those digits are all zeros. */ + for (dp=i=0; l[i]==r[i]; i++) { + int c = l[i]; + if (!c) return 0; + if (!isdigit(c)) dp=i+1, z=1; + else if (c!='0') z=0; + } + + if (l[dp]!='0' && r[dp]!='0') { + /* If we're not looking at a digit sequence that began + * with a zero, longest digit string is greater. */ + for (j=i; isdigit(l[j]); j++) + if (!isdigit(r[j])) return 1; + if (isdigit(r[j])) return -1; + } else if (z && dp<i && (isdigit(l[i]) || isdigit(r[i]))) { + /* Otherwise, if common prefix of digit sequence is + * all zeros, digits order less than non-digits. */ + return (unsigned char)(l[i]-'0') - (unsigned char)(r[i]-'0'); + } + + return l[i] - r[i]; +} diff --git a/src/thumbnail.c b/src/thumbnail.c index 41bd1a0..9781027 100644 --- a/src/thumbnail.c +++ b/src/thumbnail.c @@ -392,7 +392,7 @@ void init_thumbnail_mode(void) } gib_imlib_save_image_with_error_return(td.im_main, output_buf, &err); if (err) { - feh_imlib_print_load_error(output_buf, td.im_main, err); + feh_print_load_error(output_buf, td.im_main, err, LOAD_ERROR_IMLIB); } else if (opt.verbose) { int tw, th; @@ -411,6 +411,7 @@ void init_thumbnail_mode(void) else if (opt.start_list_at) { for (l = thumbnails; l; l = l->next) { if (!strcmp(opt.start_list_at, FEH_THUMB(l->data)->file->filename)) { + free(opt.start_list_at); opt.start_list_at = NULL; feh_thumbnail_select(winwid, FEH_THUMB(l->data)); break; diff --git a/src/timers.c b/src/timers.c index 4bdd64e..b2cdbc7 100644 --- a/src/timers.c +++ b/src/timers.c @@ -58,7 +58,37 @@ double feh_get_time(void) return((double) timev.tv_sec + (((double) timev.tv_usec) / 1000000)); } -void feh_remove_timer(char *name) +void feh_remove_timer_by_data(void *data) +{ + fehtimer ft, ptr, pptr; + + D(("removing timer for %p\n", data)); + pptr = NULL; + ptr = first_timer; + while (ptr) { + D(("Stepping through event list\n")); + ft = ptr; + if (ft->data == data) { + D(("Found it. Removing\n")); + if (pptr) + pptr->next = ft->next; + else + first_timer = ft->next; + if (ft->next) + ft->next->in += ft->in; + if (ft->name) + free(ft->name); + if (ft) + free(ft); + return; + } + pptr = ptr; + ptr = ptr->next; + } + return; +} + +static void feh_remove_timer(char *name) { fehtimer ft, ptr, pptr; @@ -88,6 +118,7 @@ void feh_remove_timer(char *name) return; } + void feh_add_timer(void (*func) (void *data), void *data, double in, char *name) { fehtimer ft, ptr, pptr; diff --git a/src/timers.h b/src/timers.h index a4243ca..e95d9b5 100644 --- a/src/timers.h +++ b/src/timers.h @@ -37,7 +37,7 @@ struct __fehtimer { void feh_handle_timer(void); double feh_get_time(void); -void feh_remove_timer(char *name); +void feh_remove_timer_by_data(void *data); void feh_add_timer(void (*func) (void *data), void *data, double in, char *name); void feh_add_unique_timer(void (*func) (void *data), void *data, double in); diff --git a/src/wallpaper.c b/src/wallpaper.c index 62b36a4..5adb3ca 100644 --- a/src/wallpaper.c +++ b/src/wallpaper.c @@ -245,6 +245,106 @@ static void feh_wm_set_bg_maxed(Pixmap pmap, Imlib_Image im, int use_filelist, return; } +/* +** Creates a script that can be used to create the same background +** as the last time the program was called. +*/ +void feh_wm_gen_bg_script(char* fil, int centered, int scaled, int filled, int use_filelist) { + char * home = getenv("HOME"); + + if (!home) + return; + + FILE *fp; + int fd; + char *path; + char *exec_method; + char *absolute_path; + struct stat s; + gib_list *filelist_pos = filelist; + + if (strchr(cmdargv[0], '/')) + exec_method = feh_absolute_path(cmdargv[0]); + else + exec_method = cmdargv[0]; + + path = estrjoin("/", home, ".fehbg", NULL); + + if ((fp = fopen(path, "w")) == NULL) { + weprintf("Can't write to %s", path); + } else { + fputs("#!/bin/sh\n", fp); + fputs(exec_method, fp); + fputs(" --no-fehbg --bg-", fp); + if (centered) + fputs("center", fp); + else if (scaled) + fputs("scale", fp); + else if (filled == 1) + fputs("fill", fp); + else if (filled == 2) + fputs("max", fp); + else + fputs("tile", fp); + if (opt.image_bg) { + fputs(" --image-bg ", fp); + fputs(shell_escape(opt.image_bg), fp); + } +#ifdef HAVE_LIBXINERAMA + if (opt.xinerama) { + if (opt.xinerama_index >= 0) { + fprintf(fp, " --xinerama-index %d", opt.xinerama_index); + } + } + else { + fputs(" --no-xinerama", fp); + } +#endif /* HAVE_LIBXINERAMA */ + if (opt.geom_flags & XValue) { + fprintf(fp, " --geometry %c%d", + opt.geom_flags & XNegative ? '-' : '+', + opt.geom_flags & XNegative ? abs(opt.geom_x) : opt.geom_x); + if (opt.geom_flags & YValue) { + fprintf(fp, "%c%d", + opt.geom_flags & YNegative ? '-' : '+', + opt.geom_flags & YNegative ? abs(opt.geom_y) : opt.geom_y); + } + } + if (opt.force_aliasing) { + fputs(" --force-aliasing", fp); + } + fputc(' ', fp); + if (use_filelist) { +#ifdef HAVE_LIBXINERAMA + for (int i = 0; (i < opt.xinerama ? num_xinerama_screens : 1) && filelist_pos; i++) +#else + for (int i = 0; (i < 1 ) && filelist_pos; i++) +#endif + { + absolute_path = feh_absolute_path(FEH_FILE(filelist_pos->data)->filename); + fputs(shell_escape(absolute_path), fp); + filelist_pos = filelist_pos->next; + free(absolute_path); + fputc(' ', fp); + } + } else if (fil) { + absolute_path = feh_absolute_path(fil); + fputs(shell_escape(absolute_path), fp); + free(absolute_path); + } + fputc('\n', fp); + fd = fileno(fp); + if (fstat(fd, &s) != 0 || fchmod(fd, s.st_mode | S_IXUSR | S_IXGRP) != 0) { + weprintf("Can't set %s as executable", path); + } + fclose(fp); + } + free(path); + + if(exec_method != cmdargv[0]) + free(exec_method); +} + void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled, int filled, int desktop, int use_filelist) { @@ -325,8 +425,6 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled, unsigned char *data_root = NULL, *data_esetroot = NULL; Pixmap pmap_d1, pmap_d2; - char *home; - /* local display to set closedownmode on */ Display *disp2; Window root2; @@ -451,87 +549,8 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled, gib_imlib_render_image_on_drawable(pmap_d1, im, 0, 0, 1, 1, 0); } - if (!opt.no_fehbg) { - home = getenv("HOME"); - if (home) { - FILE *fp; - int fd; - char *path; - char *absolute_path; - struct stat s; - gib_list *filelist_pos = filelist; - path = estrjoin("/", home, ".fehbg", NULL); - if ((fp = fopen(path, "w")) == NULL) { - weprintf("Can't write to %s", path); - } else { - fputs("#!/bin/sh\n", fp); - fputs(cmdargv[0], fp); - fputs(" --no-fehbg --bg-", fp); - if (centered) - fputs("center", fp); - else if (scaled) - fputs("scale", fp); - else if (filled == 1) - fputs("fill", fp); - else if (filled == 2) - fputs("max", fp); - else - fputs("tile", fp); - if (opt.image_bg) { - fputs(" --image-bg ", fp); - fputs(shell_escape(opt.image_bg), fp); - } -#ifdef HAVE_LIBXINERAMA - if (opt.xinerama) { - if (opt.xinerama_index >= 0) { - fprintf(fp, " --xinerama-index %d", opt.xinerama_index); - } - } - else { - fputs(" --no-xinerama", fp); - } -#endif /* HAVE_LIBXINERAMA */ - if (opt.geom_flags & XValue) { - fprintf(fp, " --geometry %c%d", - opt.geom_flags & XNegative ? '-' : '+', - opt.geom_flags & XNegative ? abs(opt.geom_x) : opt.geom_x); - if (opt.geom_flags & YValue) { - fprintf(fp, "%c%d", - opt.geom_flags & YNegative ? '-' : '+', - opt.geom_flags & YNegative ? abs(opt.geom_y) : opt.geom_y); - } - } - if (opt.force_aliasing) { - fputs(" --force-aliasing", fp); - } - fputc(' ', fp); - if (use_filelist) { -#ifdef HAVE_LIBXINERAMA - for (int i = 0; (i < opt.xinerama ? num_xinerama_screens : 1) && filelist_pos; i++) { -#else - for (int i = 0; (i < 1 ) && filelist_pos; i++) { -#endif - absolute_path = feh_absolute_path(FEH_FILE(filelist_pos->data)->filename); - fputs(shell_escape(absolute_path), fp); - filelist_pos = filelist_pos->next; - free(absolute_path); - fputc(' ', fp); - } - } else if (fil) { - absolute_path = feh_absolute_path(fil); - fputs(shell_escape(absolute_path), fp); - free(absolute_path); - } - fputc('\n', fp); - fd = fileno(fp); - if (fstat(fd, &s) != 0 || fchmod(fd, s.st_mode | S_IXUSR | S_IXGRP) != 0) { - weprintf("Can't set %s as executable", path); - } - fclose(fp); - } - free(path); - } - } + if (!opt.no_fehbg) + feh_wm_gen_bg_script(fil, centered, scaled, filled, use_filelist); /* create new display, copy pixmap to new display */ disp2 = XOpenDisplay(NULL); diff --git a/src/wallpaper.h b/src/wallpaper.h index cf52d2d..77769f8 100644 --- a/src/wallpaper.h +++ b/src/wallpaper.h @@ -42,7 +42,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. extern Window ipc_win; extern Atom ipc_atom; -_XFUNCPROTOBEGIN extern Window enl_ipc_get_win(void); +extern Window enl_ipc_get_win(void); extern void enl_ipc_send(char *); extern char *enl_wait_for_reply(void); extern char *enl_ipc_get(const char *); @@ -53,5 +53,4 @@ extern int feh_wm_get_num_desks(void); extern signed char feh_wm_get_wm_is_e(void); void feh_wm_set_bg_filelist(unsigned char bgmode); -_XFUNCPROTOEND #endif diff --git a/src/winwidget.c b/src/winwidget.c index 6ff90ed..37031ec 100644 --- a/src/winwidget.c +++ b/src/winwidget.c @@ -29,6 +29,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "winwidget.h" #include "options.h" #include "events.h" +#include "timers.h" #ifdef HAVE_INOTIFY #include <sys/inotify.h> @@ -143,6 +144,7 @@ winwidget winwidget_create_from_file(gib_list * list, char type) void winwidget_create_window(winwidget ret, int w, int h) { + XWindowAttributes window_attr; XSetWindowAttributes attr; XEvent ev; XClassHint *xch; @@ -249,11 +251,31 @@ void winwidget_create_window(winwidget ret, int w, int h) } } - ret->win = - XCreateWindow(disp, DefaultRootWindow(disp), x, y, w, h, 0, - depth, InputOutput, vis, - CWOverrideRedirect | CWSaveUnder | CWBackingStore - | CWColormap | CWBackPixel | CWBorderPixel | CWEventMask, &attr); + if (opt.x11_windowid == 0) { + ret->win = + XCreateWindow(disp, DefaultRootWindow(disp), x, y, w, h, 0, + depth, InputOutput, vis, + CWOverrideRedirect | CWSaveUnder | CWBackingStore + | CWColormap | CWBackPixel | CWBorderPixel | CWEventMask, + &attr); + } else { + /* x11_windowid is a pointer to the window */ + ret->win = (Window) opt.x11_windowid; + + /* set our attributes on the window */ + XChangeWindowAttributes(disp, ret->win, + CWOverrideRedirect | CWSaveUnder | CWBackingStore + | CWColormap | CWBackPixel | CWBorderPixel + | CWEventMask, &attr); + + /* determine the size and visibility of the window */ + XGetWindowAttributes(disp, ret->win, &window_attr); + ret->visible = (window_attr.map_state == IsViewable); + ret->x = window_attr.x; + ret->y = window_attr.y; + ret->w = window_attr.width; + ret->h = window_attr.height; + } if (mwmhints.flags) { XChangeProperty(disp, ret->win, prop, prop, 32, @@ -673,6 +695,9 @@ void winwidget_destroy(winwidget winwid) #ifdef HAVE_INOTIFY winwidget_inotify_remove(winwid); #endif + if (opt.reload > 0 && opt.multiwindow) { + feh_remove_timer_by_data(winwid); + } winwidget_destroy_xwin(winwid); if (winwid->name) free(winwid->name); @@ -1010,8 +1035,9 @@ void winwidget_rename(winwidget winwid, char *newname) void winwidget_free_image(winwidget w) { - if (w->im) + if (w->im) { gib_imlib_free_image(w->im); + } w->im = NULL; w->im_w = 0; w->im_h = 0; |