diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 59 | ||||
-rw-r--r-- | src/about.png | bin | 61171 -> 0 bytes | |||
-rw-r--r-- | src/collage.c | 211 | ||||
-rw-r--r-- | src/debug.h | 90 | ||||
-rw-r--r-- | src/deps.mk | 41 | ||||
-rw-r--r-- | src/events.c | 695 | ||||
-rw-r--r-- | src/events.h | 2 | ||||
-rw-r--r-- | src/exif.c | 439 | ||||
-rw-r--r-- | src/exif.h | 47 | ||||
-rw-r--r-- | src/exif_canon.c | 61 | ||||
-rw-r--r-- | src/exif_canon.h (renamed from src/ipc.h) | 31 | ||||
-rw-r--r-- | src/exif_cfg.h | 71 | ||||
-rw-r--r-- | src/exif_nikon.c | 542 | ||||
-rw-r--r-- | src/exif_nikon.h | 34 | ||||
-rw-r--r-- | src/feh.h | 95 | ||||
-rw-r--r-- | src/feh_png.c | 40 | ||||
-rw-r--r-- | src/feh_png.h | 6 | ||||
-rw-r--r-- | src/filelist.c | 548 | ||||
-rw-r--r-- | src/filelist.h | 43 | ||||
-rw-r--r-- | src/getopt.c | 949 | ||||
-rw-r--r-- | src/getopt.h | 130 | ||||
-rw-r--r-- | src/getopt1.c | 173 | ||||
-rw-r--r-- | src/gib_hash.c | 149 | ||||
-rw-r--r-- | src/gib_hash.h | 75 | ||||
-rw-r--r-- | src/gib_imlib.c | 732 | ||||
-rw-r--r-- | src/gib_imlib.h | 194 | ||||
-rw-r--r-- | src/gib_list.c | 579 | ||||
-rw-r--r-- | src/gib_list.h | 105 | ||||
-rw-r--r-- | src/gib_style.c | 114 | ||||
-rw-r--r-- | src/gib_style.h | 69 | ||||
-rw-r--r-- | src/help.raw | 211 | ||||
-rw-r--r-- | src/imlib.c | 1814 | ||||
-rw-r--r-- | src/index.c | 462 | ||||
-rw-r--r-- | src/index.h | 35 | ||||
-rw-r--r-- | src/ipc.c | 78 | ||||
-rw-r--r-- | src/keyevents.c | 896 | ||||
-rw-r--r-- | src/list.c | 57 | ||||
-rw-r--r-- | src/main.c | 180 | ||||
-rw-r--r-- | src/menu.c | 1341 | ||||
-rw-r--r-- | src/menu.h | 31 | ||||
-rw-r--r-- | src/menubg_aluminium.png | bin | 1862 -> 0 bytes | |||
-rw-r--r-- | src/menubg_aqua.png | bin | 5325 -> 0 bytes | |||
-rw-r--r-- | src/menubg_black.png | bin | 1716 -> 0 bytes | |||
-rw-r--r-- | src/menubg_brushed.png | bin | 8649 -> 0 bytes | |||
-rw-r--r-- | src/menubg_chrome.png | bin | 10995 -> 0 bytes | |||
-rw-r--r-- | src/menubg_default.png | bin | 209 -> 0 bytes | |||
-rw-r--r-- | src/menubg_pastel.png | bin | 3676 -> 0 bytes | |||
-rw-r--r-- | src/menubg_sky.png | bin | 1928 -> 0 bytes | |||
-rw-r--r-- | src/menubg_wood.png | bin | 7873 -> 0 bytes | |||
-rw-r--r-- | src/multiwindow.c | 26 | ||||
-rw-r--r-- | src/options.c | 1566 | ||||
-rw-r--r-- | src/options.h | 282 | ||||
-rw-r--r-- | src/signals.c | 94 | ||||
-rw-r--r-- | src/signals.h | 32 | ||||
-rw-r--r-- | src/slideshow.c | 594 | ||||
-rw-r--r-- | src/structs.h | 3 | ||||
-rw-r--r-- | src/strverscmp.c | 57 | ||||
-rw-r--r-- | src/support.c | 610 | ||||
-rw-r--r-- | src/thumbnail.c | 775 | ||||
-rw-r--r-- | src/thumbnail.h | 38 | ||||
-rw-r--r-- | src/timers.c | 82 | ||||
-rw-r--r-- | src/timers.h | 2 | ||||
-rw-r--r-- | src/ttfonts/black.style | 4 | ||||
-rw-r--r-- | src/ttfonts/menu.style | 4 | ||||
-rw-r--r-- | src/ttfonts/yudit.ttf | bin | 57708 -> 0 bytes | |||
-rw-r--r-- | src/utils.c | 88 | ||||
-rw-r--r-- | src/utils.h | 12 | ||||
-rw-r--r-- | src/wallpaper.c | 918 | ||||
-rw-r--r-- | src/wallpaper.h (renamed from src/support.h) | 14 | ||||
-rw-r--r-- | src/winwidget.c | 900 | ||||
-rw-r--r-- | src/winwidget.h | 59 |
71 files changed, 10855 insertions, 6734 deletions
diff --git a/src/Makefile b/src/Makefile index a5ea9b0..2968671 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,17 +1,64 @@ include ../config.mk -TARGETS = ${shell echo *.c} +TARGETS = \ + events.c \ + feh_png.c \ + filelist.c \ + gib_hash.c \ + gib_imlib.c \ + gib_list.c \ + gib_style.c \ + imlib.c \ + index.c \ + keyevents.c \ + list.c \ + main.c \ + md5.c \ + menu.c \ + multiwindow.c \ + options.c \ + signals.c \ + slideshow.c \ + thumbnail.c \ + timers.c \ + utils.c \ + wallpaper.c \ + winwidget.c + +ifeq (${exif},1) + TARGETS += \ + exif.c \ + exif_canon.c \ + exif_nikon.c +endif + +ifneq (${verscmp},1) + TARGETS += strverscmp.c +endif + OBJECTS = ${TARGETS:.c=.o} -feh: deps.mk ${OBJECTS} - ${CC} ${CFLAGS} -o $@ ${OBJECTS} ${LDFLAGS} +I_SRCS = ${shell echo *.raw} +I_DSTS = ${I_SRCS:.raw=.inc} -deps.mk: ${TARGETS} - ${CC} -MM ${TARGETS} > $@ +feh: deps.mk ${OBJECTS} ${I_DSTS} + ${CC} ${LDFLAGS} ${CFLAGS} -o $@ ${OBJECTS} ${LDLIBS} include deps.mk +.raw.inc: + sed 's/^\(.*\)$$/"\1\\n"/' < ${@:.inc=.raw} > $@ + +fehrc.inc: fehrc.raw +help.inc: help.raw + +# CFLAGS might contain include paths needed to resolve includes in headers +deps.mk: ${TARGETS} ${I_DSTS} + ${CC} ${CFLAGS} -MM ${TARGETS} > $@ + clean: - rm -f feh *.o + rm -f feh *.o *.inc .PHONY: clean + +.SUFFIXES: .inc .raw diff --git a/src/about.png b/src/about.png Binary files differdeleted file mode 100644 index 5aaaf17..0000000 --- a/src/about.png +++ /dev/null diff --git a/src/collage.c b/src/collage.c deleted file mode 100644 index 3e03828..0000000 --- a/src/collage.c +++ /dev/null @@ -1,211 +0,0 @@ -/* collage.c - -Copyright (C) 1999-2003 Tom Gilbert. - -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 of the Software and its documentation and acknowledgment shall be -given in the documentation and software packages that this Software was -used. - -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 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. - -*/ - -#include "feh.h" -#include "winwidget.h" -#include "filelist.h" -#include "options.h" - -void init_collage_mode(void) -{ - Imlib_Image im_main; - Imlib_Image im_temp; - int ww, hh, www, hhh, xxx, yyy; - int w = 800, h = 600; - int bg_w = 0, bg_h = 0; - winwidget winwid = NULL; - Imlib_Image bg_im = NULL, im_thumb = NULL; - feh_file *file = NULL; - unsigned char trans_bg = 0; - gib_list *l, *last = NULL; - char *s; - - D_ENTER(4); - - mode = "collage"; - - /* Use bg image dimensions for default size */ - if (opt.bg && opt.bg_file) { - if (!strcmp(opt.bg_file, "trans")) - trans_bg = 1; - else { - - D(4, ("Time to apply a background to blend onto\n")); - if (feh_load_image_char(&bg_im, opt.bg_file) != 0) { - bg_w = gib_imlib_image_get_width(bg_im); - bg_h = gib_imlib_image_get_height(bg_im); - } - } - } - - if (!opt.limit_w || !opt.limit_h) { - if (bg_im) { - if (opt.verbose) - fprintf(stdout, - PACKAGE - " - No size restriction specified for collage.\n" - " You did specify a background however, so the\n" - " collage size has defaulted to the size of the image\n"); - opt.limit_w = bg_w; - opt.limit_h = bg_h; - } else { - if (opt.verbose) - fprintf(stdout, - PACKAGE - " - No size restriction specified for collage.\n" - " - For collage mode, you need to specify width and height.\n" - " Using defaults (width 800, height 600)\n"); - opt.limit_w = 800; - opt.limit_h = 600; - } - } - - w = opt.limit_w; - h = opt.limit_h; - D(4, ("Limiting width to %d and height to %d\n", w, h)); - - im_main = imlib_create_image(w, h); - - if (!im_main) - eprintf("Imlib error creating image"); - - if (bg_im) - gib_imlib_blend_image_onto_image(im_main, bg_im, - gib_imlib_image_has_alpha(bg_im), 0, 0, - bg_w, bg_h, 0, 0, w, h, 1, 0, 0); - else if (trans_bg) { - gib_imlib_image_fill_rectangle(im_main, 0, 0, w, h, 0, 0, 0, 0); - gib_imlib_image_set_has_alpha(im_main, 1); - } else { - /* Colour the background */ - gib_imlib_image_fill_rectangle(im_main, 0, 0, w, h, 0, 0, 0, 255); - } - - /* Create the title string */ - - if (!opt.title) - s = estrdup(PACKAGE " [collage mode]"); - else - s = estrdup(feh_printf(opt.title, NULL)); - - if (opt.display) { - winwid = winwidget_create_from_image(im_main, s, WIN_TYPE_SINGLE); - winwidget_show(winwid); - } - - for (l = filelist; l; l = l->next) { - file = FEH_FILE(l->data); - if (last) { - filelist = feh_file_remove_from_list(filelist, last); - last = NULL; - } - D(3, ("About to load image %s\n", file->filename)); - if (feh_load_image(&im_temp, file) != 0) { - D(3, ("Successfully loaded %s\n", file->filename)); - if (opt.verbose) - feh_display_status('.'); - www = opt.thumb_w; - hhh = opt.thumb_h; - ww = gib_imlib_image_get_width(im_temp); - hh = gib_imlib_image_get_height(im_temp); - - if (opt.aspect) { - double ratio = 0.0; - - /* Keep the aspect ratio for the thumbnail */ - ratio = ((double) ww / hh) / ((double) www / hhh); - - if (ratio > 1.0) - hhh = opt.thumb_h / ratio; - else if (ratio != 1.0) - www = opt.thumb_w * ratio; - } - - if ((!opt.stretch) && ((www > ww) || (hhh > hh))) { - /* Don't make the image larger unless stretch is specified */ - www = ww; - hhh = hh; - } - - /* pick random coords for thumbnail */ - xxx = ((w - www) * ((double) rand() / RAND_MAX)); - yyy = ((h - hhh) * ((double) rand() / RAND_MAX)); - D(5, ("image going on at x=%d, y=%d\n", xxx, yyy)); - - im_thumb = gib_imlib_create_cropped_scaled_image(im_temp, - 0, 0, ww, hh, www, hhh, 1); - gib_imlib_free_image_and_decache(im_temp); - - if (opt.alpha) { - DATA8 atab[256]; - - D(4, ("Applying alpha options\n")); - gib_imlib_image_set_has_alpha(im_thumb, 1); - memset(atab, opt.alpha_level, sizeof(atab)); - gib_imlib_apply_color_modifier_to_rectangle(im_thumb, - 0, 0, www, hhh, NULL, NULL, NULL, atab); - } - gib_imlib_blend_image_onto_image(im_main, im_thumb, - gib_imlib_image_has_alpha(im_thumb), 0, 0, www, hhh, xxx, - yyy,www, hhh, 1, gib_imlib_image_has_alpha(im_thumb), 0); - gib_imlib_free_image_and_decache(im_thumb); - } else { - last = l; - if (opt.verbose) - feh_display_status('x'); - } - if (opt.display) { - winwidget_render_image(winwid, 0, 0); - if (!feh_main_iteration(0)) - exit(0); - } - } - if (opt.verbose) - fprintf(stdout, "\n"); - - if (opt.output && opt.output_file) { - char output_buf[1024]; - if (opt.output_dir) - snprintf(output_buf, 1024, "%s/%s", opt.output_dir, opt.output_file); - else - strncpy(output_buf, opt.output_file, 1024); - gib_imlib_save_image(im_main, output_buf); - if (opt.verbose) { - int tw, th; - - tw = gib_imlib_image_get_width(im_main); - th = gib_imlib_image_get_height(im_main); - fprintf(stdout, PACKAGE " - File saved as %s\n", output_buf); - fprintf(stdout, - " - Image is %dx%d pixels and contains %d thumbnails\n", - tw, th, (tw / opt.thumb_w) * (th / opt.thumb_h)); - } - } - - if (!opt.display) - gib_imlib_free_image_and_decache(im_main); - free(s); - D_RETURN_(4); -} diff --git a/src/debug.h b/src/debug.h index 3585cd7..0ff1447 100644 --- a/src/debug.h +++ b/src/debug.h @@ -1,6 +1,7 @@ /* debug.h Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2020 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -26,8 +27,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef DEBUG_H #define DEBUG_H -/* #define DEBUG */ - #ifdef WITH_DMALLOC #include <dmalloc.h> #define emalloc(a) malloc(a) @@ -41,75 +40,26 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifdef DEBUG #ifdef __GNUC__ -#define D(i, a) \ -{ \ - if(i <= opt.debug_level) \ - { \ - printf("%s +%u %s() %s ",__FILE__,__LINE__,__FUNCTION__, stroflen(' ', call_level)); \ - printf a; \ - fflush(stdout); \ - } \ - } -#define D_ENTER(i) \ - { \ - call_level++; \ - if(i <= opt.debug_level) \ - { \ - printf("%s +%u %s() %s ENTER\n",__FILE__,__LINE__,__FUNCTION__, stroflen('>', call_level)); \ - fflush(stdout); \ - } \ - } -#define D_RETURN(i, a) \ +#define D(a) \ { \ - if(i <= opt.debug_level) \ - { \ - printf("%s +%u %s() %s LEAVE\n",__FILE__,__LINE__,__FUNCTION__, stroflen('<', call_level)); \ - fflush(stdout); \ - } \ - call_level--; \ - return (a); \ + if (opt.debug) { \ + printf("%-12s +%-4u %-20s : ",__FILE__,__LINE__,__FUNCTION__); \ + printf a; \ + fflush(stdout); \ + } \ } -#define D_RETURN_(i) \ +#else /* __GNUC__ */ +#define D(a) \ { \ - if(i <= opt.debug_level) \ - { \ - printf("%s +%u %s() %s LEAVE\n",__FILE__,__LINE__,__FUNCTION__, stroflen('<', call_level)); \ - fflush(stdout); \ - } \ - call_level--; \ - return; \ - } -#else -#define D(i, a) \ -{ \ - if(i <= opt.debug_level) \ - { \ - printf("%s +%u : ",__FILE__,__LINE__); \ - printf a; \ - } \ - fflush(stdout); \ - } -#define D_ENTER(a) -#define D_RETURN(i, a) \ - { \ - return(a); \ - } -#define D_RETURN_(i) \ - { \ - return; \ - } -#endif -#else -#define D(i,a) -#define D_ENTER(a) -#define D_RETURN(i, a) \ - { \ - return (a); \ - } -#define D_RETURN_(i) \ - { \ - return; \ - } -#endif + if (opt.debug) { \ + printf("%-12s +%-4u : ",__FILE__,__LINE__); \ + printf a; \ + fflush(stdout); \ + } \ +} +#endif /* __GNUC__ */ +#else /* DEBUG */ +#define D(a) +#endif /* DEBUG */ -#endif +#endif /* DEBUG_H */ diff --git a/src/deps.mk b/src/deps.mk deleted file mode 100644 index 5d0b475..0000000 --- a/src/deps.mk +++ /dev/null @@ -1,41 +0,0 @@ -collage.o: collage.c feh.h structs.h menu.h ipc.h utils.h getopt.h \ - debug.h winwidget.h filelist.h options.h -events.o: events.c feh.h structs.h menu.h ipc.h utils.h getopt.h debug.h \ - filelist.h winwidget.h timers.h options.h events.h thumbnail.h -feh_png.o: feh_png.c feh_png.h feh.h structs.h menu.h ipc.h utils.h \ - getopt.h debug.h -filelist.o: filelist.c feh.h structs.h menu.h ipc.h utils.h getopt.h \ - debug.h filelist.h options.h -getopt.o: getopt.c -getopt1.o: getopt1.c getopt.h -imlib.o: imlib.c feh.h structs.h menu.h ipc.h utils.h getopt.h debug.h \ - filelist.h winwidget.h options.h -index.o: index.c feh.h structs.h menu.h ipc.h utils.h getopt.h debug.h \ - filelist.h winwidget.h options.h -ipc.o: ipc.c feh.h structs.h menu.h ipc.h utils.h getopt.h debug.h \ - options.h -keyevents.o: keyevents.c feh.h structs.h menu.h ipc.h utils.h getopt.h \ - debug.h thumbnail.h filelist.h winwidget.h options.h -list.o: list.c feh.h structs.h menu.h ipc.h utils.h getopt.h debug.h \ - filelist.h options.h -main.o: main.c feh.h structs.h menu.h ipc.h utils.h getopt.h debug.h \ - filelist.h winwidget.h timers.h options.h events.h support.h -md5.o: md5.c md5.h -menu.o: menu.c feh.h structs.h menu.h ipc.h utils.h getopt.h debug.h \ - support.h thumbnail.h filelist.h winwidget.h options.h -multiwindow.o: multiwindow.c feh.h structs.h menu.h ipc.h utils.h \ - getopt.h debug.h winwidget.h timers.h filelist.h options.h -options.o: options.c feh.h structs.h menu.h ipc.h utils.h getopt.h \ - debug.h filelist.h options.h -slideshow.o: slideshow.c feh.h structs.h menu.h ipc.h utils.h getopt.h \ - debug.h filelist.h timers.h winwidget.h options.h -support.o: support.c feh.h structs.h menu.h ipc.h utils.h getopt.h \ - debug.h filelist.h options.h support.h -thumbnail.o: thumbnail.c feh.h structs.h menu.h ipc.h utils.h getopt.h \ - debug.h filelist.h winwidget.h options.h thumbnail.h md5.h feh_png.h -timers.o: timers.c feh.h structs.h menu.h ipc.h utils.h getopt.h debug.h \ - options.h timers.h -utils.o: utils.c feh.h structs.h menu.h ipc.h utils.h getopt.h debug.h \ - options.h -winwidget.o: winwidget.c feh.h structs.h menu.h ipc.h utils.h getopt.h \ - debug.h filelist.h winwidget.h options.h diff --git a/src/events.c b/src/events.c index 7db319d..bafc517 100644 --- a/src/events.c +++ b/src/events.c @@ -1,6 +1,7 @@ /* events.c Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2024 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -31,21 +32,151 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "events.h" #include "thumbnail.h" +#define FEH_JITTER_OFFSET 2 +#define FEH_JITTER_TIME 1 + +extern struct __fehkey keys[EVENT_LIST_END]; +fehkey *feh_str_to_kb(char *action); + feh_event_handler *ev_handler[LASTEvent]; static void feh_event_handle_ButtonPress(XEvent * ev); static void feh_event_handle_ButtonRelease(XEvent * ev); -static void feh_event_handle_ConfigureNotify(XEvent * ev); -static void feh_event_handle_EnterNotify(XEvent * ev); static void feh_event_handle_LeaveNotify(XEvent * ev); static void feh_event_handle_MotionNotify(XEvent * ev); static void feh_event_handle_ClientMessage(XEvent * ev); +static void feh_set_bb(unsigned int bb_index, int modifier, char button) +{ + keys[bb_index].state = modifier; + keys[bb_index].button = button; +} + +static void feh_set_parse_bb_partial(fehkey *button, char *binding) +{ + char *cur = binding; + int mod = 0; + + if (!*binding) { + button->button = 0; + return; + } + + while (cur[1] == '-') { + switch (cur[0]) { + case 'C': + mod |= ControlMask; + break; + case 'S': + mod |= ShiftMask; + break; + case '1': + mod |= Mod1Mask; + break; + case '4': + mod |= Mod4Mask; + break; + default: + weprintf("buttons: invalid modifier %c in \"%s\"", cur[0], binding); + break; + } + cur += 2; + } + + button->button = atoi(cur); + button->state = mod; + + if (button->button == 0) { + /* + * Mod3 is unused on today's keyboards. If Mod3 is unset and button==0, + * we are dealing with an uninitialized or unset binding. If Mod3 is set + * and button==0, it refers to mouse movement. + */ + button->state |= Mod3Mask; + } +} + +/* + * Called after init_keyevents in keyevents.c + * -> no need to memset + */ +void init_buttonbindings(void) +{ + char *home = NULL; + char *confhome = NULL; + char *confpath = NULL; + char line[128]; + char action[32], button[8]; + struct __fehkey *cur_bb = NULL; + FILE *conf = NULL; + int read = 0; + + feh_set_bb(EVENT_pan, 0, 1); + feh_set_bb(EVENT_zoom, 0, 2); + feh_set_bb(EVENT_toggle_menu, 0, 3); + feh_set_bb(EVENT_prev_img, 0, 4); + feh_set_bb(EVENT_next_img, 0, 5); + feh_set_bb(EVENT_blur, 4, 1); + feh_set_bb(EVENT_rotate, 4, 2); + + home = getenv("HOME"); + confhome = getenv("XDG_CONFIG_HOME"); + + if (confhome) + confpath = estrjoin("/", confhome, "feh/buttons", NULL); + else if (home) + confpath = estrjoin("/", home, ".config/feh/buttons", NULL); + else + return; + + conf = fopen(confpath, "r"); + + free(confpath); + + if (!conf && ((conf = fopen("/etc/feh/buttons", "r")) == NULL)) + return; + + while (fgets(line, sizeof(line), conf)) { + *action = '\0'; + *button = '\0'; + cur_bb = NULL; + + read = sscanf(line, "%31s %7s\n", (char *) &action, (char *) &button); + + if ((read == EOF) || (read == 0) || (line[0] == '#')) + continue; + + cur_bb = feh_str_to_kb(action); + if (cur_bb == NULL) { + if (!strcmp(action, "reload")) + cur_bb = &keys[EVENT_reload_image]; + else if (!strcmp(action, "menu")) + cur_bb = &keys[EVENT_toggle_menu]; + else if (!strcmp(action, "prev")) + cur_bb = &keys[EVENT_prev_img]; + else if (!strcmp(action, "next")) + cur_bb = &keys[EVENT_next_img]; + } + if (cur_bb) + feh_set_parse_bb_partial(cur_bb, button); + else + weprintf("buttons: Invalid action: %s", action); + } + fclose(conf); +} + +static short feh_is_bb(unsigned int key_index, unsigned int button, unsigned int mod) +{ + if ((keys[key_index].state == mod) && (keys[key_index].button == button)) + return 1; + return 0; +} + + void feh_event_init(void) { int i; - D_ENTER(4); for (i = 0; i < LASTEvent; i++) ev_handler[i] = NULL; @@ -53,144 +184,155 @@ void feh_event_init(void) ev_handler[ButtonPress] = feh_event_handle_ButtonPress; ev_handler[ButtonRelease] = feh_event_handle_ButtonRelease; ev_handler[ConfigureNotify] = feh_event_handle_ConfigureNotify; - ev_handler[EnterNotify] = feh_event_handle_EnterNotify; ev_handler[LeaveNotify] = feh_event_handle_LeaveNotify; ev_handler[MotionNotify] = feh_event_handle_MotionNotify; ev_handler[ClientMessage] = feh_event_handle_ClientMessage; - D_RETURN_(4); + return; } static void feh_event_handle_ButtonPress(XEvent * ev) { winwidget winwid = NULL; - int scr_width, scr_height; - - D_ENTER(4); + unsigned int state, button; /* get the heck out if it's a mouse-click on the cover, we'll hide the menus on release */ if (ev->xbutton.window == menu_cover) { - D_RETURN_(4); + return; } - scr_width = scr->width; - scr_height = scr->height; -#ifdef HAVE_LIBXINERAMA - if (opt.xinerama && xinerama_screens) { - scr_width = xinerama_screens[xinerama_screen].width; - scr_height = xinerama_screens[xinerama_screen].height; - } -#endif /* HAVE_LIBXINERAMA */ - winwid = winwidget_get_from_window(ev->xbutton.window); - if (winwid && winwid->caption_entry) { - D_RETURN_(4); + if (winwid == NULL || winwid->caption_entry) { + return; } - if (!opt.no_menus && EV_IS_MENU_BUTTON(ev)) { - D(3, ("Menu Button Press event\n")); - if (winwid != NULL) { - winwidget_show_menu(winwid); - } - } else if ((ev->xbutton.button == opt.rotate_button) - && ((opt.no_rotate_ctrl_mask) - || (ev->xbutton.state & ControlMask))) { - if (winwid != NULL) { - opt.mode = MODE_ROTATE; - winwid->mode = MODE_ROTATE; - D(3, ("rotate starting at %d, %d\n", ev->xbutton.x, ev->xbutton.y)); - } - } else if ((ev->xbutton.button == opt.blur_button) - && ((opt.no_blur_ctrl_mask) - || (ev->xbutton.state & ControlMask))) { - if (winwid != NULL) { - opt.mode = MODE_BLUR; - winwid->mode = MODE_BLUR; - D(3, ("blur starting at %d, %d\n", ev->xbutton.x, ev->xbutton.y)); - } - } else if (ev->xbutton.button == opt.pan_button) { - D(3, ("Pan Button Press event\n")); - if (winwid != NULL) { - D(3, ("Next button, but could be pan mode\n")); - opt.mode = MODE_NEXT; - winwid->mode = MODE_NEXT; - D(3, ("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y)); - winwid->click_offset_x = ev->xbutton.x - winwid->im_x; - winwid->click_offset_y = ev->xbutton.y - winwid->im_y; - } - } else if (ev->xbutton.button == opt.zoom_button) { - D(3, ("Zoom Button Press event\n")); - if (winwid != NULL) { - D(3, ("Zoom mode baby!\n")); - opt.mode = MODE_ZOOM; - winwid->mode = MODE_ZOOM; - D(3, ("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y)); - winwid->click_offset_x = ev->xbutton.x - winwid->im_x; - winwid->click_offset_y = ev->xbutton.y - winwid->im_y; - winwid->im_click_offset_x = winwid->click_offset_x / winwid->zoom; - winwid->im_click_offset_y = winwid->click_offset_y / winwid->zoom; - winwid->zoom = 1.0; - if (winwid->full_screen) { - winwid->im_x = (scr_width - winwid->im_w) >> 1; - winwid->im_y = (scr_height - winwid->im_h) >> 1; - } else { - if (opt.geom_flags & WidthValue) { - winwid->im_x = (opt.geom_w - winwid->im_w) >> 1; - } else { - winwid->im_x = 0; - } - if (opt.geom_flags & HeightValue) { - winwid->im_y = (opt.geom_h - winwid->im_h) >> 1; - } else { - winwid->im_y = 0; - } - } - if (winwid->im_click_offset_x < 30) - winwid->im_click_offset_x = 30; - if (winwid->im_click_offset_y < 0) - winwid->im_click_offset_y = 0; - if (winwid->im_click_offset_x > winwid->im_w) - winwid->im_click_offset_x = winwid->im_w; - if (winwid->im_click_offset_y > winwid->im_h) - winwid->im_click_offset_y = winwid->im_h; - - if (winwid->click_offset_x < 30) - winwid->click_offset_x = 30; - if (winwid->click_offset_y < 0) - winwid->click_offset_y = 0; - if (winwid->click_offset_x > winwid->w) - winwid->click_offset_x = winwid->w; - if (winwid->click_offset_y > winwid->h) - winwid->click_offset_y = winwid->h; + state = ev->xbutton.state & (ControlMask | ShiftMask | Mod1Mask | Mod4Mask); + button = ev->xbutton.button; + + if (!opt.no_menus && feh_is_bb(EVENT_toggle_menu, button, state)) { + D(("Menu Button Press event\n")); + winwidget_show_menu(winwid); + + } else if (feh_is_bb(EVENT_rotate, button, state) + && (winwid->type != WIN_TYPE_THUMBNAIL)) { + opt.mode = MODE_ROTATE; + winwid->mode = MODE_ROTATE; + D(("rotate starting at %d, %d\n", ev->xbutton.x, ev->xbutton.y)); + + } else if (feh_is_bb(EVENT_blur, button, state) + && (winwid->type != WIN_TYPE_THUMBNAIL)) { + opt.mode = MODE_BLUR; + winwid->mode = MODE_BLUR; + D(("blur starting at %d, %d\n", ev->xbutton.x, ev->xbutton.y)); + + } else if (feh_is_bb(EVENT_pan, button, state)) { + D(("Next button, but could be pan mode\n")); + opt.mode = MODE_NEXT; + winwid->mode = MODE_NEXT; + D(("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y)); + winwid->click_offset_x = ev->xbutton.x - winwid->im_x; + winwid->click_offset_y = ev->xbutton.y - winwid->im_y; + winwid->click_start_time = time(NULL); + + } else if (feh_is_bb(EVENT_zoom, button, state)) { + D(("Zoom Button Press event\n")); + opt.mode = MODE_ZOOM; + winwid->mode = MODE_ZOOM; + D(("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y)); + winwid->click_offset_x = ev->xbutton.x; + winwid->click_offset_y = ev->xbutton.y; + winwid->old_zoom = winwid->zoom; + + /* required to adjust the image position in zoom mode */ + winwid->im_click_offset_x = (winwid->click_offset_x + - winwid->im_x) / winwid->old_zoom; + winwid->im_click_offset_y = (winwid->click_offset_y + - winwid->im_y) / winwid->old_zoom; + + } else if (feh_is_bb(EVENT_zoom_in, button, state)) { + D(("Zoom_In Button Press event\n")); + D(("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y)); + winwid->click_offset_x = ev->xbutton.x; + winwid->click_offset_y = ev->xbutton.y; + winwid->old_zoom = winwid->zoom; + + /* required to adjust the image position in zoom mode */ + winwid->im_click_offset_x = (winwid->click_offset_x + - winwid->im_x) / winwid->old_zoom; + winwid->im_click_offset_y = (winwid->click_offset_y + - winwid->im_y) / winwid->old_zoom; + + /* copied from zoom_in, keyevents.c */ + winwid->zoom = winwid->zoom * opt.zoom_rate; + + if (winwid->zoom > ZOOM_MAX) + winwid->zoom = ZOOM_MAX; + + /* copied from below (ZOOM, feh_event_handle_MotionNotify) */ + winwid->im_x = winwid->click_offset_x + - (winwid->im_click_offset_x * winwid->zoom); + winwid->im_y = winwid->click_offset_y + - (winwid->im_click_offset_y * winwid->zoom); + + winwidget_sanitise_offsets(winwid); + winwidget_render_image(winwid, 0, 0); + + } else if (feh_is_bb(EVENT_zoom_out, button, state)) { + D(("Zoom_Out Button Press event\n")); + D(("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y)); + winwid->click_offset_x = ev->xbutton.x; + winwid->click_offset_y = ev->xbutton.y; + winwid->old_zoom = winwid->zoom; + + /* required to adjust the image position in zoom mode */ + winwid->im_click_offset_x = (winwid->click_offset_x + - winwid->im_x) / winwid->old_zoom; + winwid->im_click_offset_y = (winwid->click_offset_y + - winwid->im_y) / winwid->old_zoom; + + /* copied from zoom_out, keyevents.c */ + winwid->zoom = winwid->zoom / opt.zoom_rate; + + if (winwid->zoom < ZOOM_MIN) + winwid->zoom = ZOOM_MIN; + + /* copied from below (ZOOM, feh_event_handle_MotionNotify) */ + winwid->im_x = winwid->click_offset_x + - (winwid->im_click_offset_x * winwid->zoom); + winwid->im_y = winwid->click_offset_y + - (winwid->im_click_offset_y * winwid->zoom); + + winwidget_sanitise_offsets(winwid); + winwidget_render_image(winwid, 0, 0); + + } else if (feh_is_bb(EVENT_reload_image, button, state)) { + D(("Reload Button Press event\n")); + feh_reload_image(winwid, 0, 1); + + } else if (feh_is_bb(EVENT_prev_img, button, state)) { + D(("Prev Button Press event\n")); + if (winwid->type == WIN_TYPE_SLIDESHOW) + slideshow_change_image(winwid, SLIDE_PREV, 1); + + } else if (feh_is_bb(EVENT_next_img, button, state)) { + D(("Next Button Press event\n")); + if (winwid->type == WIN_TYPE_SLIDESHOW) + slideshow_change_image(winwid, SLIDE_NEXT, 1); - winwidget_render_image(winwid, 0, 0); - } - } else if (ev->xbutton.button == opt.reload_button) { - D(3, ("Reload Button Press event\n")); - if (winwid != NULL) - feh_reload_image(winwid, 0, 0); - } else if (ev->xbutton.button == opt.prev_button) { - D(3, ("Prev Button Press event\n")); - if ((winwid != NULL) - && (winwid->type == WIN_TYPE_SLIDESHOW)) - slideshow_change_image(winwid, SLIDE_PREV); - } else if (ev->xbutton.button == opt.next_button) { - D(3, ("Next Button 5 Press event\n")); - if ((winwid != NULL) - && (winwid->type == WIN_TYPE_SLIDESHOW)) - slideshow_change_image(winwid, SLIDE_NEXT); } else { - D(3, ("Received other ButtonPress event\n")); + D(("Received other ButtonPress event\n")); + feh_event_handle_generic(winwid, state, NoSymbol, button); } - D_RETURN_(4); + return; } static void feh_event_handle_ButtonRelease(XEvent * ev) { winwidget winwid = NULL; + unsigned int state = ev->xbutton.state & (ControlMask | ShiftMask | Mod1Mask | Mod4Mask); + unsigned int button = ev->xbutton.button; - D_ENTER(4); if (menu_root) { /* if menus are open, close them, and execute action if needed */ @@ -206,43 +348,32 @@ static void feh_event_handle_ButtonRelease(XEvent * ev) feh_menu_item_activate(m, i); } } - D_RETURN_(4); + return; } winwid = winwidget_get_from_window(ev->xbutton.window); - if (winwid && winwid->caption_entry) { - D_RETURN_(4); + if (winwid == NULL || winwid->caption_entry) { + return; } - if ((ev->xbutton.button == opt.menu_button) - && (((!opt.menu_ctrl_mask) - && ((!(ev->xbutton.state & ControlMask)) - || ((ev->xbutton.state & ControlMask) - && (opt.menu_ctrl_mask))))) - && (opt.no_menus)) - winwidget_destroy_all(); - else if (ev->xbutton.button == opt.pan_button) { + if (feh_is_bb(EVENT_pan, button, state)) { if (opt.mode == MODE_PAN) { - if (winwid != NULL) { - D(3, ("Disabling pan mode\n")); - opt.mode = MODE_NORMAL; - winwid->mode = MODE_NORMAL; - winwidget_sanitise_offsets(winwid); - winwidget_render_image(winwid, 0, 1); - } - } else { + D(("Disabling pan mode\n")); + opt.mode = MODE_NORMAL; + winwid->mode = MODE_NORMAL; + winwidget_sanitise_offsets(winwid); + winwidget_render_image(winwid, 0, 0); + } else if (opt.mode == MODE_NEXT) { opt.mode = MODE_NORMAL; - if (winwid != NULL) - winwid->mode = MODE_NORMAL; - if ((winwid != NULL) - && (winwid->type == WIN_TYPE_SLIDESHOW)) { - slideshow_change_image(winwid, SLIDE_NEXT); - } else if ((winwid != NULL) - && (winwid->type == WIN_TYPE_THUMBNAIL)) { + winwid->mode = MODE_NORMAL; + if (winwid->type == WIN_TYPE_SLIDESHOW) { + if (opt.tap_zones && ev->xbutton.x < winwid->w / 2) + slideshow_change_image(winwid, SLIDE_PREV, 1); + else + slideshow_change_image(winwid, SLIDE_NEXT, 1); + } else if (winwid->type == WIN_TYPE_THUMBNAIL) { feh_file *thumbfile; - winwidget thumbwin = NULL; int x, y; - char *s; x = ev->xbutton.x; y = ev->xbutton.y; @@ -252,60 +383,55 @@ static void feh_event_handle_ButtonRelease(XEvent * ev) y /= winwid->zoom; thumbfile = feh_thumbnail_get_file_from_coords(x, y); if (thumbfile) { - if (!opt.thumb_title) - s = thumbfile->name; - else - s = feh_printf(opt.thumb_title, thumbfile); - thumbwin = winwidget_get_first_window_of_type(WIN_TYPE_THUMBNAIL_VIEWER); - if (!thumbwin) { - thumbwin = winwidget_create_from_file( - gib_list_add_front(NULL, thumbfile), - s, WIN_TYPE_THUMBNAIL_VIEWER); - winwidget_show(thumbwin); - } else if (FEH_FILE(thumbwin->file->data) != thumbfile) { - free(thumbwin->file); - thumbwin->file = gib_list_add_front(NULL, thumbfile); - winwidget_rename(thumbwin, s); - feh_reload_image(thumbwin, 1, 0); + if (opt.actions[0]) { + feh_action_run(thumbfile, opt.actions[0], winwid); + if (!opt.hold_actions[0]) + feh_thumbnail_mark_removed(thumbfile, 0); + } else { + feh_thumbnail_show_fullsize(thumbfile); } } } - } - } else if ((ev->xbutton.button == opt.rotate_button) - || (ev->xbutton.button == opt.zoom_button)) { - D(3, ("Mode-based Button Release event\n")); - if (winwid != NULL) { - D(3, ("Disabling mode\n")); - opt.mode = MODE_NORMAL; - winwid->mode = MODE_NORMAL; - winwidget_sanitise_offsets(winwid); - winwidget_render_image(winwid, 0, 1); - } - } else if ((ev->xbutton.button == opt.blur_button) - && ((opt.no_blur_ctrl_mask) - || (ev->xbutton.state & ControlMask))) { - D(3, ("Blur Button Release event\n")); - if (winwid != NULL) { - D(3, ("Disabling Blur mode\n")); + } else { opt.mode = MODE_NORMAL; winwid->mode = MODE_NORMAL; } + + } else if (feh_is_bb(EVENT_rotate, button, state) + || feh_is_bb(EVENT_zoom, button, state)) { + D(("Disabling mode\n")); + opt.mode = MODE_NORMAL; + winwid->mode = MODE_NORMAL; + + if ((feh_is_bb(EVENT_zoom, button, state)) + && (ev->xbutton.x == winwid->click_offset_x) + && (ev->xbutton.y == winwid->click_offset_y)) { + winwid->zoom = 1.0; + winwidget_center_image(winwid); + } else + winwidget_sanitise_offsets(winwid); + + winwidget_render_image(winwid, 0, 0); + + } else if (feh_is_bb(EVENT_blur, button, state)) { + D(("Disabling Blur mode\n")); + opt.mode = MODE_NORMAL; + winwid->mode = MODE_NORMAL; } - D_RETURN_(4); + return; } -static void feh_event_handle_ConfigureNotify(XEvent * ev) +void feh_event_handle_ConfigureNotify(XEvent * ev) { - D_ENTER(4); while (XCheckTypedWindowEvent(disp, ev->xconfigure.window, ConfigureNotify, ev)); if (!menu_root) { winwidget w = winwidget_get_from_window(ev->xconfigure.window); if (w) { - D(3, ("configure size %dx%d\n", ev->xconfigure.width, ev->xconfigure.height)); + D(("configure size %dx%d\n", ev->xconfigure.width, ev->xconfigure.height)); if ((w->w != ev->xconfigure.width) || (w->h != ev->xconfigure.height)) { - D(3, ("assigning size and rerendering\n")); + D(("assigning size and rerendering\n")); w->w = ev->xconfigure.width; w->h = ev->xconfigure.height; w->had_resize = 1; @@ -313,31 +439,23 @@ static void feh_event_handle_ConfigureNotify(XEvent * ev) opt.geom_w = w->w; opt.geom_h = w->h; } - winwidget_render_image(w, 0, 1); + winwidget_render_image(w, 0, 0); } } } - D_RETURN_(4); -} - -static void feh_event_handle_EnterNotify(XEvent * ev) -{ - D_ENTER(4); - D_RETURN_(4); - ev = NULL; + return; } static void feh_event_handle_LeaveNotify(XEvent * ev) { - D_ENTER(4); if ((menu_root) && (ev->xcrossing.window == menu_root->win)) { feh_menu_item *ii; - D(4, ("It is for a menu\n")); + D(("It is for a menu\n")); for (ii = menu_root->items; ii; ii = ii->next) { if (MENU_ITEM_IS_SELECTED(ii)) { - D(4, ("Unselecting menu\n")); + D(("Unselecting menu\n")); MENU_ITEM_SET_NORMAL(ii); menu_root->updates = imlib_update_append_rect(menu_root->updates, ii->x, ii->y, ii->w, ii->h); @@ -347,7 +465,7 @@ static void feh_event_handle_LeaveNotify(XEvent * ev) feh_raise_all_menus(); } - D_RETURN_(4); + return; } static void feh_event_handle_MotionNotify(XEvent * ev) @@ -365,26 +483,25 @@ static void feh_event_handle_MotionNotify(XEvent * ev) } #endif /* HAVE_LIBXINERAMA */ - D_ENTER(5); if (menu_root) { feh_menu *m; feh_menu_item *selected_item, *mouseover_item; - D(3, ("motion notify with menus open\n")); + D(("motion notify with menus open\n")); while (XCheckTypedWindowEvent(disp, ev->xmotion.window, MotionNotify, ev)); if (ev->xmotion.window == menu_cover) { - D_RETURN_(5); + return; } else if ((m = feh_menu_get_from_window(ev->xmotion.window))) { selected_item = feh_menu_find_selected(m); mouseover_item = feh_menu_find_at_xy(m, ev->xmotion.x, ev->xmotion.y); if (selected_item != mouseover_item) { - D(4, ("selecting a menu item\n")); + D(("selecting a menu item\n")); if (selected_item) feh_menu_deselect_selected(m); if ((mouseover_item) - && ((mouseover_item->func) + && ((mouseover_item->action) || (mouseover_item->submenu) || (mouseover_item->func_gen_sub))) feh_menu_select(m, mouseover_item); @@ -404,6 +521,8 @@ static void feh_event_handle_MotionNotify(XEvent * ev) dy = scr_height - (m->y + m->h); dx = dx < 0 ? dx : 0; dy = dy < 0 ? dy : 0; + dx = m->x + dx < 0 ? -m->x : dx; + dy = m->y + dy < 0 ? -m->y : dy; if (dx || dy) feh_menu_slide_all_menus_relative(dx, dy); } @@ -416,6 +535,8 @@ static void feh_event_handle_MotionNotify(XEvent * ev) dy = scr->height - (m->next->y + m->next->h); dx = dx < 0 ? dx : 0; dy = dy < 0 ? dy : 0; + dx = m->x + dx < 0 ? -m->x : dx; + dy = m->y + dy < 0 ? -m->y : dy; if (dx || dy) feh_menu_slide_all_menus_relative(dx, dy); } @@ -425,23 +546,27 @@ static void feh_event_handle_MotionNotify(XEvent * ev) winwid = winwidget_get_from_window(ev->xmotion.window); if (winwid) { - winwid->zoom = ((double) ev->xmotion.x - (double) winwid->click_offset_x) / 64.0; - if (winwid->zoom < 0) - winwid->zoom = 1.0 + ((winwid->zoom * 64.0) / ((double) - (winwid->click_offset_x + 1))); + if (ev->xmotion.x > winwid->click_offset_x) + winwid->zoom = winwid->old_zoom + ( + ((double) ev->xmotion.x - (double) winwid->click_offset_x) + / 128.0); else - winwid->zoom += 1.0; + winwid->zoom = winwid->old_zoom - ( + ((double) winwid->click_offset_x - (double) ev->xmotion.x) + / 128.0); - if (winwid->zoom < 0.01) - winwid->zoom = 0.01; + if (winwid->zoom < ZOOM_MIN) + winwid->zoom = ZOOM_MIN; + else if (winwid->zoom > ZOOM_MAX) + winwid->zoom = ZOOM_MAX; - /* calculate change in zoom and move im_x and im_y respectively to - enable zooming to the clicked spot... */ - /* for now, center around im_click_offset_x and im_click_offset_y */ - winwid->im_x = (winwid->w / 2) - (winwid->im_click_offset_x * winwid->zoom); - winwid->im_y = (winwid->h / 2) - (winwid->im_click_offset_y * winwid->zoom); + /* center around click_offset */ + winwid->im_x = winwid->click_offset_x + - (winwid->im_click_offset_x * winwid->zoom); + winwid->im_y = winwid->click_offset_y + - (winwid->im_click_offset_y * winwid->zoom); - winwidget_render_image(winwid, 0, 0); + winwidget_render_image(winwid, 0, 1); } } else if ((opt.mode == MODE_PAN) || (opt.mode == MODE_NEXT)) { int orig_x, orig_y; @@ -450,10 +575,16 @@ static void feh_event_handle_MotionNotify(XEvent * ev) winwid = winwidget_get_from_window(ev->xmotion.window); if (winwid) { if (opt.mode == MODE_NEXT) { - opt.mode = MODE_PAN; - winwid->mode = MODE_PAN; + if ((abs(winwid->click_offset_x - (ev->xmotion.x - winwid->im_x)) > FEH_JITTER_OFFSET) + || (abs(winwid->click_offset_y - (ev->xmotion.y - winwid->im_y)) > FEH_JITTER_OFFSET) + || (time(NULL) - winwid->click_start_time > FEH_JITTER_TIME)) { + opt.mode = MODE_PAN; + winwid->mode = MODE_PAN; + } + else + return; } - D(5, ("Panning\n")); + D(("Panning\n")); orig_x = winwid->im_x; orig_y = winwid->im_y; @@ -462,29 +593,69 @@ static void feh_event_handle_MotionNotify(XEvent * ev) winwidget_sanitise_offsets(winwid); + D(("im_x %d, im_w %d, off %d, mx %d, my %d\n", winwid->im_x, + winwid->im_w, winwid->click_offset_x, ev->xmotion.x, + ev->xmotion.y)); + + /* XWarpPointer generates a MotionNotify event which we will + * parse. Since that event would undo the effect of the pointer + * warp, we need to change the click_offset to compensate this. + */ + if ((winwid->w - ev->xmotion.x <= 1) && (winwid->im_x < 0)) + { + XWarpPointer(disp, None, winwid->win, 0, 0, 0, 0, 3, + ev->xmotion.y); + winwid->click_offset_x -= winwid->w - 4; + } + // TODO needlessly warps for certain zoom levels + else if ((ev->xmotion.x <= 1) && (winwid->im_x > + (winwid->w - winwid->im_w * winwid->zoom))) + { + XWarpPointer(disp, None, winwid->win, 0, 0, 0, 0, + winwid->w - 4, ev->xmotion.y); + winwid->click_offset_x += winwid->w - 4; + } + else if ((winwid->h - ev->xmotion.y <= 1) + && (winwid->im_y < 0)) + { + XWarpPointer(disp, None, winwid->win, 0, 0, 0, 0, + ev->xmotion.x, 3); + winwid->click_offset_y -= winwid->h - 4; + } + // TODO needlessly warps for certain zoomlevels + else if ((ev->xmotion.y <= 1) && (winwid->im_y > + (winwid->h - winwid->im_h * winwid->zoom))) + { + XWarpPointer(disp, None, winwid->win, 0, 0, 0, 0, + ev->xmotion.x, winwid->h - 4); + winwid->click_offset_y += winwid->h - 4; + } + if ((winwid->im_x != orig_x) || (winwid->im_y != orig_y)) - winwidget_render_image(winwid, 0, 0); + winwidget_render_image(winwid, 0, 1); } } else if (opt.mode == MODE_ROTATE) { while (XCheckTypedWindowEvent(disp, ev->xmotion.window, MotionNotify, ev)); winwid = winwidget_get_from_window(ev->xmotion.window); if (winwid) { - D(5, ("Rotating\n")); + D(("Rotating\n")); if (!winwid->has_rotated) { Imlib_Image temp; temp = gib_imlib_create_rotated_image(winwid->im, 0.0); - winwid->im_w = gib_imlib_image_get_width(temp); - winwid->im_h = gib_imlib_image_get_height(temp); - gib_imlib_free_image_and_decache(temp); - if (!winwid->full_screen && !opt.geom_flags) - winwidget_resize(winwid, winwid->im_w, winwid->im_h); - winwid->has_rotated = 1; + if (temp != NULL) { + winwid->im_w = gib_imlib_image_get_width(temp); + winwid->im_h = gib_imlib_image_get_height(temp); + gib_imlib_free_image_and_decache(temp); + if (!winwid->full_screen && !opt.geom_flags) + winwidget_resize(winwid, winwid->im_w, winwid->im_h, 0); + winwid->has_rotated = 1; + } } winwid->im_angle = (ev->xmotion.x - winwid->w / 2) / ((double) winwid->w / 2) * 3.1415926535; - D(5, ("angle: %f\n", winwid->im_angle)); - winwidget_render_image(winwid, 0, 0); + D(("angle: %f\n", winwid->im_angle)); + winwidget_render_image(winwid, 0, 1); } } else if (opt.mode == MODE_BLUR) { while (XCheckTypedWindowEvent(disp, ev->xmotion.window, MotionNotify, ev)); @@ -493,90 +664,52 @@ static void feh_event_handle_MotionNotify(XEvent * ev) Imlib_Image temp, ptr; signed int blur_radius; - D(5, ("Blurring\n")); + D(("Blurring\n")); temp = gib_imlib_clone_image(winwid->im); - blur_radius = (((double) ev->xmotion.x / winwid->w) * 20) - 10; - D(5, ("angle: %d\n", blur_radius)); - if (blur_radius > 0) - gib_imlib_image_sharpen(temp, blur_radius); - else - gib_imlib_image_blur(temp, 0 - blur_radius); - ptr = winwid->im; - winwid->im = temp; - winwidget_render_image(winwid, 0, 0); - gib_imlib_free_image_and_decache(winwid->im); - winwid->im = ptr; + if (temp != NULL) { + blur_radius = (((double) ev->xmotion.x / winwid->w) * 20) - 10; + D(("angle: %d\n", blur_radius)); + if (blur_radius > 0) + gib_imlib_image_sharpen(temp, blur_radius); + else + gib_imlib_image_blur(temp, 0 - blur_radius); + ptr = winwid->im; + winwid->im = temp; + winwidget_render_image(winwid, 0, 1); + gib_imlib_free_image_and_decache(winwid->im); + winwid->im = ptr; + } } } else { while (XCheckTypedWindowEvent(disp, ev->xmotion.window, MotionNotify, ev)); winwid = winwidget_get_from_window(ev->xmotion.window); if (winwid != NULL) { - if (winwid->type == WIN_TYPE_ABOUT) { - Imlib_Image orig_im; - int x, y; - - x = ev->xmotion.x - winwid->im_x; - y = ev->xmotion.y - winwid->im_y; - orig_im = winwid->im; - winwid->im = gib_imlib_clone_image(orig_im); - imlib_context_set_image(winwid->im); - imlib_apply_filter("bump_map_point(x=[],y=[],map=" - PREFIX "/share/feh/images/about.png);", &x, &y); - winwidget_render_image(winwid, 0, 1); - gib_imlib_free_image_and_decache(winwid->im); - winwid->im = orig_im; - } else if (winwid->type == WIN_TYPE_THUMBNAIL) { - static feh_thumbnail *last_thumb = NULL; + if (winwid->type == WIN_TYPE_THUMBNAIL) { feh_thumbnail *thumbnail; int x, y; x = (ev->xbutton.x - winwid->im_x) / winwid->zoom; y = (ev->xbutton.y - winwid->im_y) / winwid->zoom; thumbnail = feh_thumbnail_get_thumbnail_from_coords(x, y); - if (thumbnail != last_thumb) { - if (thumbnail) { - Imlib_Image origwin; - - origwin = winwid->im; - winwid->im = gib_imlib_clone_image(origwin); - gib_imlib_image_fill_rectangle(winwid->im, - thumbnail->x, thumbnail->y, thumbnail->w, - thumbnail->h, 50, 50, 255, 100); - gib_imlib_image_draw_rectangle(winwid->im, - thumbnail->x, thumbnail->y, thumbnail->w, - thumbnail->h, 255, 255, 255, 255); - gib_imlib_image_draw_rectangle(winwid->im, - thumbnail->x + 1, thumbnail->y + 1, - thumbnail->w - 2, thumbnail->h - 2, - 0, 0, 0, 255); - gib_imlib_image_draw_rectangle(winwid->im, - thumbnail->x + 2, thumbnail->y + 2, - thumbnail->w - 4, thumbnail->h - 4, - 255, 255, 255, 255); - winwidget_render_image(winwid, 0, 1); - gib_imlib_free_image_and_decache(winwid->im); - winwid->im = origwin; - } else - winwidget_render_image(winwid, 0, 1); - } - last_thumb = thumbnail; + feh_thumbnail_select(winwid, thumbnail); + } else { + feh_event_handle_generic(winwid, ev->xmotion.state | Mod3Mask, NoSymbol, 0); } } } - D_RETURN_(5); + return; } static void feh_event_handle_ClientMessage(XEvent * ev) { winwidget winwid = NULL; - D_ENTER(4); if (ev->xclient.format == 32 && ev->xclient.data.l[0] == (signed) wmDeleteWindow) { winwid = winwidget_get_from_window(ev->xclient.window); if (winwid) winwidget_destroy(winwid); } - D_RETURN_(4); + return; } diff --git a/src/events.h b/src/events.h index 22abaf7..f334c58 100644 --- a/src/events.h +++ b/src/events.h @@ -32,6 +32,6 @@ extern feh_event_handler *ev_handler[]; void feh_event_init(void); -#define EV_IS_MENU_BUTTON(ev) ((((ev)->xbutton.button == opt.menu_button) || (opt.menu_button == 0)) && (((!opt.menu_ctrl_mask) && (!((ev)->xbutton.state & ControlMask))) || (((ev)->xbutton.state & ControlMask) && (opt.menu_ctrl_mask)))) +void feh_event_handle_ConfigureNotify(XEvent * ev); #endif diff --git a/src/exif.c b/src/exif.c new file mode 100644 index 0000000..6b0719d --- /dev/null +++ b/src/exif.c @@ -0,0 +1,439 @@ +/* exif.c + +Copyright (C) 2012 Dennis Real. +Copyright (C) 2021 Birte Kristina Friesel. + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + +#ifdef HAVE_LIBEXIF + +#include <stdio.h> +#include <string.h> +#include <libexif/exif-data.h> +#include <limits.h> + +#include "feh.h" +#include "options.h" +#include "debug.h" +#include "exif.h" +#include "exif_canon.h" +#include "exif_nikon.h" +#include "exif_cfg.h" + + +/* remove all spaces on the right end of a string */ +void exif_trim_spaces(char *str) +{ + char *end; + + for (end = str; *str != '\0'; str++) { + if (*str != ' ') { + end = str + 1; + } + } + *end = '\0'; +} + + + +/* show given exif tag content with tag name */ +void exif_get_tag(ExifData * d, ExifIfd ifd, ExifTag tag, char *buffer, + unsigned int maxsize) +{ + char s[EXIF_MAX_DATA]; + ExifEntry *entry = NULL; + + if ((d != NULL) && (buffer != NULL) && (maxsize > 0)) { + entry = exif_content_get_entry(d->ifd[ifd], tag); + if (entry != NULL) { + /* Get the contents of the tag in human-readable form */ + exif_entry_get_value(entry, s, EXIF_MAX_DATA); + + /* Don't bother printing it if it's entirely blank */ + exif_trim_spaces(s); + if (*s != '\0') { + D(("%s: %s\n", + exif_tag_get_name_in_ifd(tag, ifd), s)); + snprintf(buffer, (size_t) maxsize, + "%s: %s\n", + exif_tag_get_name_in_ifd(tag, + ifd), s); + } + } + } +} + + + +/* show given exif tag content without tag name */ +void exif_get_tag_content(ExifData * d, ExifIfd ifd, ExifTag tag, + char *buffer, unsigned int maxsize) +{ + char s[EXIF_MAX_DATA]; + ExifEntry *entry = NULL; + + if ((d != NULL) && (buffer != NULL) && (maxsize > 0)) { + entry = exif_content_get_entry(d->ifd[ifd], tag); + if (entry != NULL) { + /* Get the contents of the tag in human-readable form */ + exif_entry_get_value(entry, s, EXIF_MAX_DATA); + + /* Don't bother printing it if it's entirely blank */ + exif_trim_spaces(s); + if (*s != '\0') { + D(("%s - %s\n", + exif_tag_get_name_in_ifd(tag, ifd), s)); + snprintf(buffer, (size_t) maxsize, "%s", + s); + } + } + } + +} + + + +/* Show the given MakerNote tag if it exists */ +void exif_get_mnote_tag(ExifData * d, unsigned int tag, char *buffer, + unsigned int maxsize) +{ + ExifMnoteData *mn = NULL; + int i, num; + char buf[1024]; + + if ((d != NULL) && (buffer != NULL) && (maxsize > 0)) { + mn = exif_data_get_mnote_data(d); + } else { + return; + } + + if (mn != NULL) { + num = exif_mnote_data_count(mn); + + /* Loop through all MakerNote tags, searching for the desired one */ + for (i = 0; i < num; ++i) { + D(("%d/%d %d 0x%2x %s; %s\n", i, num, + exif_mnote_data_get_id(mn, i), + exif_mnote_data_get_id(mn, i), + exif_mnote_data_get_name(mn, i), + exif_mnote_data_get_title(mn, i))); + + if (exif_mnote_data_get_id(mn, i) == tag) { + if (exif_mnote_data_get_value + (mn, i, buf, sizeof(buf))) { + /* Don't bother printing it if it's entirely blank */ + exif_trim_spaces(buf); + if (*buf != '\0') { + D(("%s\n", buf)); + snprintf(buffer, + (size_t) maxsize, + "%s: %s\n", + exif_mnote_data_get_title + (mn, i), buf); + } + } + } + } + } +} + +void exif_get_make_model_lens(ExifData * ed, char *buffer, unsigned int maxsize) +{ + char make[EXIF_STD_BUF_LEN]; + char model[EXIF_STD_BUF_LEN]; + char lens[EXIF_STD_BUF_LEN]; + unsigned int offset = 0; + + make[0] = model[0] = lens[0] = '\0'; + + exif_get_tag_content(ed, EXIF_IFD_0, EXIF_TAG_MAKE, make, sizeof(make)); + exif_get_tag_content(ed, EXIF_IFD_0, EXIF_TAG_MODEL, model, sizeof(model)); + exif_get_tag_content(ed, EXIF_IFD_EXIF, 0xa434, lens, sizeof(lens)); + + if (make[0] && strncmp(make, model, strlen(make)) != 0) { + offset += snprintf(buffer, maxsize, "%s ", make); + } + if (model[0]) { + offset += snprintf(buffer + offset, maxsize - offset, "%s", model); + } + if (lens[0]) { + offset += snprintf(buffer + offset, maxsize - offset, " + %s", lens); + } + snprintf(buffer + offset, maxsize - offset, "\n"); +} + +void exif_get_exposure(ExifData * ed, char *buffer, unsigned int maxsize) +{ + char fnumber[EXIF_STD_BUF_LEN]; + char exposure[EXIF_STD_BUF_LEN]; + char iso[EXIF_STD_BUF_LEN]; + char focus[EXIF_STD_BUF_LEN]; + char focus35[EXIF_STD_BUF_LEN]; + unsigned int offset = 0; + + fnumber[0] = exposure[0] = iso[0] = '\0'; + focus[0] = focus35[0] = '\0'; + + exif_get_tag_content(ed, EXIF_IFD_EXIF, EXIF_TAG_FNUMBER, fnumber, sizeof(fnumber)); + exif_get_tag_content(ed, EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_TIME, exposure, sizeof(exposure)); + exif_get_tag_content(ed, EXIF_IFD_EXIF, EXIF_TAG_ISO_SPEED_RATINGS, iso, sizeof(iso)); + exif_get_tag_content(ed, EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH, focus, sizeof(focus)); + exif_get_tag_content(ed, EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH_IN_35MM_FILM, focus35, sizeof(focus35)); + + if (fnumber[0] || exposure[0]) { + offset += snprintf(buffer, maxsize, "%s %s ", fnumber, exposure); + } + if (iso[0]) { + offset += snprintf(buffer + offset, maxsize - offset, "ISO%s ", iso); + } + if (focus[0] && focus35[0]) { + snprintf(buffer + offset, maxsize - offset, "%s (%s mm)\n", focus, focus35); + } else if (focus[0]) { + snprintf(buffer + offset, maxsize - offset, "%s\n", focus); + } +} + +void exif_get_flash(ExifData * ed, char *buffer, unsigned int maxsize) +{ + char flash[EXIF_STD_BUF_LEN]; + + flash[0] = '\0'; + + exif_get_tag_content(ed, EXIF_IFD_EXIF, EXIF_TAG_FLASH, flash, sizeof(flash)); + + if (flash[0]) { + snprintf(buffer, maxsize, "%s\n", flash); + } +} + +void exif_get_mode(ExifData * ed, char *buffer, unsigned int maxsize) +{ + char mode[EXIF_STD_BUF_LEN]; + char program[EXIF_STD_BUF_LEN]; + + mode[0] = program[0] = '\0'; + + exif_get_tag_content(ed, EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_MODE, mode, sizeof(mode)); + exif_get_tag_content(ed, EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_PROGRAM, program, sizeof(program)); + + if (mode[0] || program[0]) { + snprintf(buffer, maxsize, "%s (%s)\n", mode, program); + } +} + +void exif_get_datetime(ExifData * ed, char *buffer, unsigned int maxsize) +{ + char datetime[EXIF_STD_BUF_LEN]; + + datetime[0] = '\0'; + + exif_get_tag_content(ed, EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_ORIGINAL, datetime, sizeof(datetime)); + + if (datetime[0]) { + snprintf(buffer, maxsize, "%s\n", datetime); + } +} + +void exif_get_description(ExifData * ed, char *buffer, unsigned int maxsize) +{ + char description[EXIF_STD_BUF_LEN]; + + description[0] = '\0'; + + exif_get_tag_content(ed, EXIF_IFD_0, EXIF_TAG_IMAGE_DESCRIPTION, description, sizeof(description)); + + if (description[0]) { + snprintf(buffer, maxsize, "\"%s\"\n", description); + } +} + + +/* get gps coordinates if available */ +void exif_get_gps_coords(ExifData * ed, char *buffer, unsigned int maxsize) +{ + char buf[EXIF_STD_BUF_LEN]; + + buf[0] = '\0'; + exif_get_tag_content(ed, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE_REF, + buf, sizeof(buf)); + if (buf[0] != '\0') { + snprintf(buffer + strlen(buffer), maxsize - strlen(buffer), + "GPS: %s ", buf); + } else { + return; + } + + buf[0] = '\0'; + exif_get_tag_content(ed, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE, buf, + sizeof(buf)); + if (buf[0] != '\0') { + snprintf(buffer + strlen(buffer), maxsize - strlen(buffer), + "%s ", buf); + } else { + return; + } + + buf[0] = '\0'; + exif_get_tag_content(ed, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE_REF, + buf, sizeof(buf)); + if (buf[0] != '\0') { + snprintf(buffer + strlen(buffer), maxsize - strlen(buffer), + ", %s ", buf); + } else { + return; + } + + buf[0] = '\0'; + exif_get_tag_content(ed, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE, buf, + sizeof(buf)); + if (buf[0] != '\0') { + snprintf(buffer + strlen(buffer), maxsize - strlen(buffer), + "%s ", buf); + } else { + return; + } + + buf[0] = '\0'; + exif_get_tag_content(ed, EXIF_IFD_GPS, EXIF_TAG_GPS_MAP_DATUM, buf, + sizeof(buf)); + if (buf[0] != '\0') { + snprintf(buffer + strlen(buffer), maxsize - strlen(buffer), + "(%s)\n", buf); + } else { + return; + } + +} + + + +/* return data structure with exif data if available */ +ExifData *exif_get_data(char *path) +{ + ExifData *ed = NULL; + + /* Load an ExifData object from an EXIF file */ + ed = exif_data_new_from_file(path); + if (ed == NULL) { + D(("File not readable or no Exif data present in %s\n", + path)); + } + + return (ed); +} + + + + +/* get all exif data in readable form */ +void exif_get_info(ExifData * ed, char *buffer, unsigned int maxsize) +{ + ExifEntry *entry = NULL; + char buf[EXIF_STD_BUF_LEN]; + unsigned short int i = 0; + + if ((buffer == NULL) || (maxsize == 0)) { + return; + } else if (ed == NULL) { + snprintf(buffer, (size_t) maxsize, "%s\n", + "No Exif data in file."); + return; + } + + exif_get_description(ed, buffer + strlen(buffer), + maxsize - strlen(buffer)); + exif_get_make_model_lens(ed, buffer + strlen(buffer), + maxsize - strlen(buffer)); + exif_get_exposure(ed, buffer + strlen(buffer), + maxsize - strlen(buffer)); + exif_get_mode(ed, buffer + strlen(buffer), + maxsize - strlen(buffer)); + exif_get_flash(ed, buffer + strlen(buffer), + maxsize - strlen(buffer)); + exif_get_datetime(ed, buffer + strlen(buffer), + maxsize - strlen(buffer)); + + /* show vendor specific makernote tags */ + entry = + exif_content_get_entry(ed->ifd[EXIF_IFD_0], + EXIF_TAG_MAKE); + if (entry != NULL) { + + if (exif_entry_get_value(entry, buf, sizeof(buf))) { + exif_trim_spaces(buf); + + if ((strcmp(buf, "NIKON CORPORATION") == 0) + || (strcmp(buf, "Nikon") == 0) + || (strcmp(buf, "NIKON") == 0) + ) { + /* show nikon makernote exif tags. list must be defined in exif_cfg.h */ + i = 0; + while ((i < USHRT_MAX) + && + (Exif_makernote_nikon_tag_list + [i] != + EXIF_NIKON_MAKERNOTE_END)) + { + exn_get_mnote_nikon_tags + (ed, + Exif_makernote_nikon_tag_list + [i], + buffer + + strlen(buffer), + maxsize - + strlen(buffer)); + i++; + } + + } else if ((strcmp(buf, "Canon") == 0)) { + /* show canon makernote exif tags. list must be defined in exif_cfg.h */ + i = 0; + while ((i < USHRT_MAX) + && + (Exif_makernote_canon_tag_list + [i] != + EXIF_CANON_MAKERNOTE_END)) + { + exc_get_mnote_canon_tags + (ed, + Exif_makernote_canon_tag_list + [i], + buffer + + strlen(buffer), + maxsize - + strlen(buffer)); + i++; + } + + } else { + } + } + + } + + /* show gps coordinates */ + exif_get_gps_coords(ed, buffer + strlen(buffer), + maxsize - strlen(buffer)); + +} + +#endif diff --git a/src/exif.h b/src/exif.h new file mode 100644 index 0000000..41769c0 --- /dev/null +++ b/src/exif.h @@ -0,0 +1,47 @@ +/* exif.h + +Copyright (C) 2012 Dennis Real. + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + +#ifndef EXIF_H +#define EXIF_H + +#include <libexif/exif-data.h> + +#define EXIF_MAX_DATA 1024 +#define EXIF_STD_BUF_LEN 128 + +extern void exif_trim_spaces(char *str); +extern void exif_get_tag(ExifData * d, ExifIfd ifd, ExifTag tag, + char *buffer, unsigned int maxsize); +extern void exif_get_tag_content(ExifData * d, ExifIfd ifd, ExifTag tag, + char *buffer, unsigned int maxsize); +extern void exif_get_mnote_tag(ExifData * d, unsigned int tag, + char *buffer, unsigned int maxsize); +extern void exif_get_gps_coords(ExifData * ed, char *buffer, + unsigned int maxsize); +extern ExifData *exif_get_data(char *path); +extern void exif_get_info(ExifData * ed, char *buffer, + unsigned int maxsize); + +#endif diff --git a/src/exif_canon.c b/src/exif_canon.c new file mode 100644 index 0000000..ee72164 --- /dev/null +++ b/src/exif_canon.c @@ -0,0 +1,61 @@ +/* exif_canon.c + +Copyright (C) 2012 Dennis Real. + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + +#ifdef HAVE_LIBEXIF + +#include <stdio.h> +#include <libexif/exif-data.h> + +#include "feh.h" +#include "debug.h" +#include "exif.h" +#include "exif_canon.h" + + + +/* get interesting canon maker note tags in readable form */ +void exc_get_mnote_canon_tags(ExifData * ed, unsigned int tag, + char *buffer, unsigned int maxsize) +{ + /* char buf[EXIF_STD_BUF_LEN]; + + buf[0] = '\0'; + exif_get_tag(ed, EXIF_IFD_EXIF, EXIF_TAG_FLASH, buf, sizeof(buf)); + exif_trim_spaces(buf); */ + + switch (tag) { + default: + { + /* normal makernote tags without special treatment */ + exif_get_mnote_tag(ed, tag, buffer, maxsize); + } + break; + } + + + return; +} + +#endif diff --git a/src/ipc.h b/src/exif_canon.h index ac1b814..58ecc0e 100644 --- a/src/ipc.h +++ b/src/exif_canon.h @@ -1,6 +1,6 @@ -/* ipc.h +/* exif_canon.h -Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2012 Dennis Real. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -23,29 +23,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef IPC_H -#define IPC_H +#ifndef EXIF_CANON_H +#define EXIF_CANON_H -#define FEH_IPC_VERSION 0x100; +#include <libexif/exif-data.h> -void feh_ipc_create_socket(void); -int feh_ipc_get_session_id(void); - -typedef struct { - unsigned int version; - unsigned int command; - unsigned int data_length; -} feh_ipc_client_header; - -typedef struct { - unsigned int version; - unsigned int data_length; -} feh_ipc_server_header; - -enum { - IPC_CMD_QUIT, - IPC_CMD_FILELIST_NEXT, - IPC_CMD_FILELIST_PREV -}; +extern void exc_get_mnote_canon_tags(ExifData * ed, unsigned int tag, + char *buffer, unsigned int maxsize); #endif diff --git a/src/exif_cfg.h b/src/exif_cfg.h new file mode 100644 index 0000000..0a13fa3 --- /dev/null +++ b/src/exif_cfg.h @@ -0,0 +1,71 @@ +/* exif_cfg.h + +Copyright (C) 2012 Dennis Real. + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + +#ifndef EXIF_CFG_H +#define EXIF_CFG_H + +#include <libexif/exif-data.h> + +/* Nikon */ + +#define EXIF_NIKON_MAKERNOTE_END 0 /* end marker: if 0 used as a tag we must find something else */ + +/* show these nikon makernote tags */ +const unsigned int Exif_makernote_nikon_tag_list[] = { + + 6, + 8, /* Flash Setting */ + 9, /* Flash Mode */ + 135, /* Flash used */ + 18, /* Flash Exposure Comp */ + 168, /* Flash info: control mode */ + + 2, /* ISO. Has some more info than EXIF_TAG_ISO_SPEED_RATINGS but also fails on Lo.1 */ + 5, /* White Balance */ + 132, /* Lens */ + 171, /* Digital Vari-Program */ + 34, /* Active D-Lighting */ + + 35, /* PictureControlData */ + 183, /* AFInfo2 */ + + EXIF_NIKON_MAKERNOTE_END /* end marker */ +}; + + + +/* Canon */ +#define EXIF_CANON_MAKERNOTE_END 0xFFFF /* end marker: if this is used as a tag we must find something else */ + +/* show these canon makernote tags */ +const unsigned int Exif_makernote_canon_tag_list[] = { + 8, /* Image Number */ + 9, /* Owner Name */ + + EXIF_CANON_MAKERNOTE_END /* end marker */ +}; + + +#endif diff --git a/src/exif_nikon.c b/src/exif_nikon.c new file mode 100644 index 0000000..a81f290 --- /dev/null +++ b/src/exif_nikon.c @@ -0,0 +1,542 @@ +/* exif_nikon.c + +Copyright (C) 2012 Dennis Real. + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + +#ifdef HAVE_LIBEXIF + +#include <stdio.h> +#include <libexif/exif-data.h> + +#include "feh.h" +#include "debug.h" +#include "exif.h" +#include "exif_nikon.h" + + +/* Flash control mode */ +/* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#FlashControlMode */ +#define EXN_FLASH_CONTROL_MODES_MAX 9 +char *EXN_NikonFlashControlModeValues[EXN_FLASH_CONTROL_MODES_MAX] = + { "Off", + "iTTL-BL", "iTTL", "Auto Aperture", + "Automatic", "GN (distance priority)", + "Manual", "Repeating Flash", + "N/A" /* "N/A" is not a nikon setting */ +}; + +#define EXN_FLASH_CONTROL_MODE_MASK 0x7F + + +/* AFInfo2 */ +/* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#AFInfo2 */ +#define EXN_CONTRAST_DETECT_AF_MAX 2 +char *EXN_NikonContrastDetectAF[EXN_CONTRAST_DETECT_AF_MAX] = + { "Off", "On" }; + +/* AFArea Mode for ContrastDetectAF Off */ +#define EXN_AF_AREA_MODE_P_MAX 13 +char *EXN_NikonAFAreaModePhase[EXN_AF_AREA_MODE_P_MAX] = { + "Single Area", "Dynamic Area", "Dynamic Area (closest subject)", + "Group Dynamic ", "Dynamic Area (9 points) ", + "Dynamic Area (21 points)", + "Dynamic Area (51 points) ", + "Dynamic Area (51 points, 3D-tracking)", + "Auto-area", "Dynamic Area (3D-tracking)", "Single Area (wide)", + "Dynamic Area (wide)", "Dynamic Area (wide, 3D-tracking)" +}; + +/* AFArea Mode for ContrastDetectAF On */ +#define EXN_AF_AREA_MODE_C_MAX 5 +char *EXN_NikonAFAreaModeContr[EXN_AF_AREA_MODE_C_MAX] = { + "Contrast-detect", + "Contrast-detect (normal area)", + "Contrast-detect (wide area)", + "Contrast-detect (face priority)", + "Contrast-detect (subject tracking)" +}; + +#define EXN_PHASE_DETECT_AF_MAX 4 +char *EXN_NikonPhaseDetectAF[EXN_PHASE_DETECT_AF_MAX] = + { "Off", "On (51-point)", + "On (11-point)", "On (39-point)" +}; + +/* PrimaryAFPoint and AFPointsUsed only valid with PhaseDetectAF == On */ + +#define EXN_PRIM_AF_PT_51_MAX 52 +char *EXN_Prim_AF_Pt_51[EXN_PRIM_AF_PT_51_MAX] = + { "(none)", "C6 (Center)", "B6", "A5", + "D6", "E5", "C7", "B7", "A6", "D7", "E6", "C5", "B5", "A4", "D5", + "E4", "C8", "B8", + "A7", "D8", "E7", "C9", "B9", "A8", "D9", "E8", "C10", "B10", "A9", + "D10", "E9", + "C11", "B11", "D11", "C4", "B4", "A3", "D4", "E3", "C3", "B3", + "A2", "D3", "E2", + "C2", "B2", "A1", "D2", "E1", "C1", "B1", "D1" +}; + +#define EXN_PRIM_AF_PT_11_MAX 12 +char *EXN_Prim_AF_Pt_11[EXN_PRIM_AF_PT_11_MAX] = + { "(none)", "Center", "Top", "Bottom", + "Mid-left", "Upper-left", "Lower-left", "Far Left", "Mid-right", + "Upper-right", + "Lower-right", "Far Right" +}; + +#define EXN_PRIM_AF_PT_39_MAX 40 +char *EXN_Prim_AF_Pt_39[EXN_PRIM_AF_PT_39_MAX] = + { "(none)", "C6 (Center)", "B6", "A2", + "D6", "E2", "C7", "B7", "A3", "D7", "E3", "C5", "B5", "A1", "D5", + "E1", "C8", "B8", + "D8", "C9", "B9", "D9", "C10", "B10", "D10", "C11", "B11", "D11", + "C4", "B4", "D4", + "C3", "B3", "D3", "C2", "B2", "D2", "C1", "B1", "D1" +}; + + +#define EXN_PIC_CTRL_ADJ_MAX 3 +char *EXN_Pic_Ctrl_Adj[EXN_PIC_CTRL_ADJ_MAX] = { "Default Settings", + "Quick Adjust", + "Full Control" +}; + + + +static void exn_get_prim_af_pt(unsigned int phasedetectaf, + unsigned int primafpt, + char *buffer, unsigned int maxsize); +static void exn_get_flash_output(unsigned int flashoutput, char *buffer, + unsigned int maxsize); +static void exn_get_mnote_nikon_18(ExifData * ed, char *buffer, + unsigned int maxsize); +static void exn_get_mnote_nikon_34(ExifData * ed, char *buffer, + unsigned int maxsize); +static void exn_get_mnote_nikon_35(ExifData * ed, char *buffer, + unsigned int maxsize); +static void exn_get_mnote_nikon_168(ExifData * ed, char *buffer, + unsigned int maxsize); +static void exn_get_mnote_nikon_183(ExifData * ed, char *buffer, + unsigned int maxsize); + + + +/* get primary AF point */ +static void exn_get_prim_af_pt(unsigned int phasedetectaf, + unsigned int primafpt, + char *buffer, unsigned int maxsize) +{ + + switch (phasedetectaf) { + case 0: + { + /* phasedetect not used. should not happen */ + snprintf(buffer, maxsize, "FAIL"); + return; + } + break; + case 1: + { + /* 51 pt */ + if (primafpt < EXN_PRIM_AF_PT_51_MAX) { + snprintf(buffer, maxsize, "%s", + EXN_Prim_AF_Pt_51[primafpt]); + } + return; + } + break; + case 2: + { + /* 11 pt */ + if (primafpt < EXN_PRIM_AF_PT_11_MAX) { + snprintf(buffer, maxsize, "%s", + EXN_Prim_AF_Pt_11[primafpt]); + } + return; + } + break; + case 3: + { + /* 39 pt */ + if (primafpt < EXN_PRIM_AF_PT_39_MAX) { + snprintf(buffer, maxsize, "%s", + EXN_Prim_AF_Pt_39[primafpt]); + } + return; + } + break; + default: + { + snprintf(buffer, maxsize, "?"); + return; + } + break; + + } + +} + + + +/* get flash output power (for FlashInfo010x) */ +static void exn_get_flash_output(unsigned int flashoutput, char *buffer, + unsigned int maxsize) +{ + + if (flashoutput == 0) { + /* full power */ + snprintf(buffer, maxsize, "Full"); + } else { + if ((flashoutput % 6) == 0) { + /* value is a power of 2 */ + snprintf(buffer, maxsize, "1/%d", + 1 << (flashoutput / 6)); + } else { + /* something uneven...ugly. maybe introduce pow() function from libm later */ + snprintf(buffer, maxsize, "1/2^(%f)", + ((float) flashoutput) / 6.0); + } + } +} + + + +/* get ActiveD-Lighting (18) info */ +static void exn_get_mnote_nikon_18(ExifData * ed, char *buffer, + unsigned int maxsize) +{ + + char buf[EXIF_STD_BUF_LEN]; + float data = 0; + + buf[0] = '\0'; + exif_get_mnote_tag(ed, 18, buf, sizeof(buf)); + + sscanf(buf, "Flash Exposure Compensation: %f", &data); /* libexif buggy here. fix conversion */ + + snprintf(buffer, maxsize, "FlashExposureCompensation: %+.1f EV\n", + ((float) ((signed char) round(data * 6.0))) / 6.0); +} + + + +/* get ActiveD-Lighting (34) info */ +static void exn_get_mnote_nikon_34(ExifData * ed, char *buffer, + unsigned int maxsize) +{ + char buf[EXIF_STD_BUF_LEN]; + unsigned int data = 0; + char *answer; + + buf[0] = '\0'; + exif_get_mnote_tag(ed, 34, buf, sizeof(buf)); + sscanf(buf, "(null): %u", &data); /* not directly supported by libexif yet */ + + switch (data) { + case 0: + { + answer = "Off"; + } + break; + case 1: + { + answer = "Low"; + } + break; + case 3: + { + answer = "Normal"; + } + break; + case 5: + { + answer = "High"; + } + break; + case 7: + { + answer = "Extra High"; + } + break; + case 65535: + { + answer = "Auto"; + } + break; + default: + { + answer = "N/A"; /* this is not a nikon value */ + } + + } + + snprintf(buffer, maxsize, "Active D-Lightning: %s\n", answer); + +} + + + +/* get nikon PictureControlData (35) info */ +static void exn_get_mnote_nikon_35(ExifData * ed, char *buffer, + unsigned int maxsize) +{ + char buf[EXIF_STD_BUF_LEN]; + char picturecontrolname[EXIF_STD_BUF_LEN]; + char picturecontrolbase[EXIF_STD_BUF_LEN]; + unsigned int version = 0; + unsigned int length = 0; + unsigned int piccontroladj = 0; + unsigned int piccontrolquickadj = 0; + unsigned int sharpness = 0; + unsigned int contrast = 0; + unsigned int brightness = 0; + unsigned int saturation = 0; + unsigned int hueadjustment = 0; + unsigned int i, j; + + /* libexif does not support PictureControlData 35 yet. so we have to parse the debug data :-( */ + buf[0] = '\0'; + exif_get_mnote_tag(ed, 35, buf, sizeof(buf)); + + sscanf(buf, + "(null): %u bytes unknown data: 303130%02X%40s%40s%*8s%02X%02X%02X%02X%02X%02X%02X", + &length, &version, &picturecontrolname[0], + &picturecontrolbase[0], &piccontroladj, &piccontrolquickadj, + &sharpness, &contrast, &brightness, &saturation, + &hueadjustment); + + /* printf("--%s %d-%d-\n", buf, version, piccontroladj); */ + + for (i = 0; i < 40; i++) { + sscanf(&picturecontrolname[2 * i], "%2X", &j); + picturecontrolname[i] = j; + sscanf(&picturecontrolbase[2 * i], "%2X", &j); + picturecontrolbase[i] = j; + + } + exif_trim_spaces(picturecontrolname); + exif_trim_spaces(picturecontrolbase); + + if (((length == 58) && (version == '0')) + && (piccontroladj < EXN_PIC_CTRL_ADJ_MAX) + ) { + snprintf(buffer, maxsize, + "PictCtrlData: Name: %s; Base: %s; CtrlAdj: %s; Quick: %d; Shrp: %d; Contr: %d; Brght: %d; Sat: %d; Hue: %d\n", + picturecontrolname, picturecontrolbase, + EXN_Pic_Ctrl_Adj[piccontroladj], + piccontrolquickadj, sharpness, contrast, + brightness, saturation, hueadjustment); + } + +} + + + + +/* get nikon Flash info: control mode (168) info */ +static void exn_get_mnote_nikon_168(ExifData * ed, char *buffer, + unsigned int maxsize) +{ + char buf[EXIF_STD_BUF_LEN]; + unsigned int version = 0; + unsigned int length = 0; + unsigned int exn_fcm = (EXN_FLASH_CONTROL_MODES_MAX - 1); /* default to N/A */ + unsigned int flashoutput = 0; + unsigned int externalflashflags = 0; + unsigned int flashcompensation = 0; + + /* libexif does not support flash info 168 yet. so we have to parse the debug data :-( */ + buf[0] = '\0'; + exif_get_mnote_tag(ed, 168, buf, sizeof(buf)); + sscanf(buf, + "(null): %u bytes unknown data: 303130%02X%*8s%02X%02X%02X%02X", + &length, &version, &externalflashflags, &exn_fcm, + &flashoutput, &flashcompensation); + exn_fcm = exn_fcm & EXN_FLASH_CONTROL_MODE_MASK; + + /* printf("%s - %d %d %d %d\n", buf, externalflashflags, exn_fcm, flashoutput, (signed char)flashcompensation); */ + + if ((exn_fcm < EXN_FLASH_CONTROL_MODES_MAX) + && (((length == 22) && (version == '3')) /* Nikon FlashInfo0103 */ + ||((length == 22) && (version == '4')) /* Nikon FlashInfo0104 */ + ||((length == 21) && (version == '2')) /* Nikon FlashInfo0102 */ + ||((length == 19) && (version == '0')) /* Nikon FlashInfo0100 */ + ) + ) { + + buf[0] = '\0'; + exn_get_flash_output(flashoutput, buf, EXIF_STD_BUF_LEN); + snprintf(buffer, maxsize, + "NikonFlashControlMode: %s (Power: %s)\n", + EXN_NikonFlashControlModeValues[exn_fcm], buf); + + /* External Flash Flags. Not as useful as expected. Not used (yet). */ + /* if ( (externalflashflags & (1<<2)) ) -> Bounce Flash */ + /* if ( (externalflashflags & (1<<4)) ) -> Wide Flash Adapter */ + /* if ( (externalflashflags & (1<<5)) ) -> Dome Diffusor */ + + } + +} + + + +/* get nikon AFInfo2 (183) info */ +static void exn_get_mnote_nikon_183(ExifData * ed, char *buffer, + unsigned int maxsize) +{ + char buf[EXIF_STD_BUF_LEN]; + unsigned int contrastdetectaf = 0; + unsigned int afareamode = 0; + unsigned int phasedetectaf = 0; + unsigned int primaryafpoint = 0; + unsigned int version = 0; + unsigned int length = 0; + + /* AFInfo2 */ + /* libexif does not support AFInfo2 183 yet. so we have to parse the debug data :-( */ + buf[0] = '\0'; + exif_get_mnote_tag(ed, 183, buf, sizeof(buf)); + sscanf(buf, + "(null): %u bytes unknown data: 303130%02X%02X%02X%02X%02X", + &length, &version, &contrastdetectaf, &afareamode, + &phasedetectaf, &primaryafpoint); + + + if (((length == 30) && (version == '0')) + && (contrastdetectaf < EXN_CONTRAST_DETECT_AF_MAX) + && (phasedetectaf < EXN_PHASE_DETECT_AF_MAX) + ) { + if ((contrastdetectaf != 0) + && (afareamode < EXN_AF_AREA_MODE_C_MAX)) { + /* Contrast AF (live view) */ + snprintf(buffer, maxsize, + "ContrastDetectAF: %s; AFAreaMode: %s\n", + EXN_NikonContrastDetectAF + [contrastdetectaf], + EXN_NikonAFAreaModeContr[afareamode]); + + } else if ((phasedetectaf != 0) + && (afareamode < EXN_AF_AREA_MODE_P_MAX)) { + /* Phase AF */ + buf[0] = '\0'; + exn_get_prim_af_pt(phasedetectaf, primaryafpoint, + buf, EXIF_STD_BUF_LEN); + + snprintf(buffer, maxsize, + "PhaseDetectAF: %s; AreaMode: %s; PrimaryAFPoint: %s\n", + EXN_NikonPhaseDetectAF[phasedetectaf], + EXN_NikonAFAreaModePhase[afareamode], + buf); + } + + } +} + + + +/* get interesting nikon maker note tags in readable form */ +void exn_get_mnote_nikon_tags(ExifData * ed, unsigned int tag, + char *buffer, unsigned int maxsize) +{ + char buf[EXIF_STD_BUF_LEN]; + + buf[0] = '\0'; + exif_get_tag(ed, EXIF_IFD_EXIF, EXIF_TAG_FLASH, buf, sizeof(buf)); + exif_trim_spaces(buf); + + switch (tag) { + /* show only if flash was used */ + case 8: /* Flash Setting */ + case 9: /* Flash Mode */ + case 135: /* Flash used */ + { + if (! + (strcmp("Flash: Flash did not fire\n", buf) == + 0)) { + /* show extended flash info only if flash was fired */ + exif_get_mnote_tag(ed, tag, buffer, + maxsize); + } + } + break; + + case 18: /* FlashExposureComp */ + { + if (! + (strcmp("Flash: Flash did not fire\n", buf) == + 0)) { + /* show only if flash was fired */ + exn_get_mnote_nikon_18(ed, buffer, + maxsize); + } + } + break; + + case 34: + { + /* ActiveD-Lighting */ + exn_get_mnote_nikon_34(ed, buffer, maxsize); + } + break; + + case 35: + { + /* PictureControlData */ + exn_get_mnote_nikon_35(ed, buffer, maxsize); + } + break; + + case 168: + { + /* Flash info: control mode */ + if (! + (strcmp("Flash: Flash did not fire\n", buf) == + 0)) { + /* show extended flash info only if flash was fired */ + exn_get_mnote_nikon_168(ed, buffer, + maxsize); + } + } + break; + + case 183: + { + /* AFInfo 2 */ + exn_get_mnote_nikon_183(ed, buffer, maxsize); + } + break; + + default: + { + /* normal makernote tags without special treatment */ + exif_get_mnote_tag(ed, tag, buffer, maxsize); + } + break; + } + + + return; +} + +#endif diff --git a/src/exif_nikon.h b/src/exif_nikon.h new file mode 100644 index 0000000..49d14b6 --- /dev/null +++ b/src/exif_nikon.h @@ -0,0 +1,34 @@ +/* exif_nikon.h + +Copyright (C) 2012 Dennis Real. + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + +#ifndef EXIF_NIKON_H +#define EXIF_NIKON_H + +#include <libexif/exif-data.h> + +extern void exn_get_mnote_nikon_tags(ExifData * ed, unsigned int tag, + char *buffer, unsigned int maxsize); + +#endif @@ -1,6 +1,7 @@ /* feh.h Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2020 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -26,6 +27,14 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef FEH_H #define FEH_H +/* + * strverscmp(3) is a GNU extension. In most supporting C libraries it + * requires _GNU_SOURCE to be defined. + */ +#ifdef HAVE_STRVERSCMP +#define _GNU_SOURCE +#endif + #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xatom.h> @@ -54,16 +63,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include <signal.h> #include <sys/wait.h> #include <math.h> +#include <getopt.h> #include <Imlib2.h> -#include <giblib/giblib.h> +#include "gib_hash.h" +#include "gib_imlib.h" +#include "gib_list.h" +#include "gib_style.h" #include "structs.h" #include "menu.h" -#include "ipc.h" #include "utils.h" -#include "getopt.h" #include "debug.h" @@ -90,18 +101,36 @@ enum mode_type { MODE_NORMAL = 0, MODE_PAN, MODE_ZOOM, MODE_ROTATE, MODE_BLUR, M }; enum bgmode_type { BG_MODE_NONE = 0, BG_MODE_TILE, BG_MODE_CENTER, - BG_MODE_SEAMLESS, BG_MODE_SCALE, BG_MODE_FILL + BG_MODE_SCALE, BG_MODE_FILL, BG_MODE_MAX }; +enum zoom_mode { ZOOM_MODE_FILL = 1, ZOOM_MODE_MAX }; + +enum text_bg { TEXT_BG_CLEAR = 0, TEXT_BG_TINTED }; + enum slide_change { SLIDE_NEXT, SLIDE_PREV, SLIDE_RAND, SLIDE_FIRST, SLIDE_LAST, SLIDE_JUMP_FWD, - SLIDE_JUMP_BACK + SLIDE_JUMP_BACK, + SLIDE_JUMP_NEXT_DIR, + 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 + +#define ZOOM_MIN 0.002 +#define ZOOM_MAX 2000 + typedef void (*sighandler_t) (int); -void show_usage(void); -void show_version(void); int feh_main_iteration(int block); void feh_handle_event(XEvent * ev); void init_imlib_fonts(void); @@ -111,43 +140,63 @@ void init_xinerama(void); #endif /* HAVE_LIBXINERAMA */ void init_multiwindow_mode(void); void init_thumbnail_mode(void); -void init_collage_mode(void); void init_index_mode(void); void init_slideshow_mode(void); void init_list_mode(void); void init_loadables_mode(void); void init_unloadables_mode(void); +#ifdef HAVE_LIBMAGIC +void uninit_magic(void); +void init_magic(void); +#endif void feh_clean_exit(void); +int feh_should_ignore_image(Imlib_Image * im); int feh_load_image(Imlib_Image * im, feh_file * file); void show_mini_usage(void); -void slideshow_change_image(winwidget winwid, int change); +void slideshow_change_image(winwidget winwid, int change, int render); void slideshow_pause_toggle(winwidget w); -char *slideshow_create_name(feh_file * file); -char *chop_file_from_full_path(char *str); +void init_keyevents(void); +void init_buttonbindings(void); +void setup_stdin(void); +void restore_stdin(void); void feh_event_handle_keypress(XEvent * ev); -void feh_action_run(feh_file * file, char *action); -char *feh_printf(char *str, feh_file * file); +void feh_event_handle_stdin(void); +void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysym, unsigned int button); +fehkey *feh_str_to_kb(char * action); +void feh_action_run(feh_file * file, char *action, winwidget winwid); +char *format_size(double size); +char *feh_printf(char *str, feh_file * file, winwidget winwid); +void im_weprintf(winwidget w, char *fmt, ...); void feh_draw_zoom(winwidget w); void feh_draw_checks(winwidget win); void cb_slide_timer(void *data); void cb_reload_timer(void *data); -char *feh_http_load_image(char *url); int feh_load_image_char(Imlib_Image * im, char *filename); void feh_draw_filename(winwidget w); +#ifdef HAVE_LIBEXIF +void feh_draw_exif(winwidget w); +#endif void feh_draw_actions(winwidget w); void feh_draw_caption(winwidget w); +void feh_draw_info(winwidget w); +void feh_draw_errstr(winwidget w); 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); -char *feh_strip_hostname(char *url); -struct hostent *feh_gethostbyname(const char *name); +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_orient(winwidget w, int orientation); -void feh_edit_inplace_lossless_rotate(winwidget w, int orientation); -gib_list *feh_wrap_string(char *text, int wrap_width, int max_height, Imlib_Font fn, gib_style * style); -char *build_caption_filename(feh_file * file); +void feh_edit_inplace(winwidget w, int orientation); +void feh_edit_inplace_lossless(winwidget w, int orientation); +gib_list *feh_wrap_string(char *text, int wrap_width, Imlib_Font fn, gib_style * style); +char *build_caption_filename(feh_file * file, short create_dir); 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; @@ -169,9 +218,13 @@ extern Window root; extern XContext xid_context; extern Screen *scr; extern unsigned char reset_output; -extern int call_level; extern feh_menu *menu_main; extern feh_menu *menu_close; extern char *mode; /* label for the current mode */ +/* to terminate long-running children with SIGALRM */ +extern int childpid; + +extern unsigned char control_via_stdin; + #endif diff --git a/src/feh_png.c b/src/feh_png.c index e2842f8..8f5b94d 100644 --- a/src/feh_png.c +++ b/src/feh_png.c @@ -23,20 +23,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "feh_png.h" - #include <png.h> #include <stdio.h> #include <stdarg.h> +#include "feh_png.h" + #define FEH_PNG_COMPRESSION 3 -#define FEH_PNG_NUM_COMMENTS 2 /* only Thumb::URI and Thumb::MTime for now */ +#define FEH_PNG_NUM_COMMENTS 4 gib_hash *feh_png_read_comments(char *file) { - gib_hash *hash = NULL; - FILE *fp; int i, sig_bytes, comments = 0; @@ -45,31 +43,31 @@ gib_hash *feh_png_read_comments(char *file) png_textp text_ptr; if (!(fp = fopen(file, "rb"))) - return hash; + return NULL; if (!(sig_bytes = feh_png_file_is_png(fp))) { fclose(fp); - return hash; + return NULL; } /* initialize data structures */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { fclose(fp); - return hash; + return NULL; } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL); fclose(fp); - return hash; + return NULL; } - if (setjmp(png_ptr->jmpbuf)) { + if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); fclose(fp); - return hash; + return NULL; } /* initialize reading */ @@ -78,6 +76,8 @@ gib_hash *feh_png_read_comments(char *file) png_read_info(png_ptr, info_ptr); + gib_hash *hash = NULL; + #ifdef PNG_TEXT_SUPPORTED png_get_text(png_ptr, info_ptr, &text_ptr, &comments); if (comments > 0) { @@ -94,7 +94,7 @@ gib_hash *feh_png_read_comments(char *file) } /* grab image data from image and write info file with comments ... */ -int feh_png_write_png(Imlib_Image image, char *file, ...) +int feh_png_write_png_fd(Imlib_Image image, int fd, ...) { FILE *fp; int i, w, h; @@ -111,20 +111,23 @@ int feh_png_write_png(Imlib_Image image, char *file, ...) char *pair_key, *pair_text; #endif /* PNG_TEXT_SUPPORTED */ - if (!(fp = fopen(file, "wb"))) + if (!(fp = fdopen(fd, "wb"))) return 0; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) + if (!png_ptr) { + fclose(fp); return 0; + } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp) NULL); + fclose(fp); return 0; } - if (setjmp(png_ptr->jmpbuf)) { + if (setjmp(png_jmpbuf(png_ptr))) { fclose(fp); png_destroy_write_struct(&png_ptr, &info_ptr); png_destroy_info_struct(png_ptr, &info_ptr); @@ -152,7 +155,7 @@ int feh_png_write_png(Imlib_Image image, char *file, ...) png_set_sBIT(png_ptr, info_ptr, &sig_bit); #ifdef PNG_TEXT_SUPPORTED - va_start(args, file); + va_start(args, fd); for (i = 0; i < FEH_PNG_NUM_COMMENTS; i++) { if ((pair_key = va_arg(args, char *)) && (pair_text = va_arg(args, char *))) { @@ -194,7 +197,10 @@ int feh_png_file_is_png(FILE * fp) { unsigned char buf[8]; - fread(buf, 1, 8, fp); + if (fread(buf, 1, 8, fp) != 8) { + return 0; + } + if (png_sig_cmp(buf, 0, 8)) { return 0; } diff --git a/src/feh_png.h b/src/feh_png.h index 6502978..035d36a 100644 --- a/src/feh_png.h +++ b/src/feh_png.h @@ -26,13 +26,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef FEH_PNG_H #define FEH_PNG_H -#include "feh.h" - #include <stdio.h> #include <stdarg.h> +#include "feh.h" + gib_hash *feh_png_read_comments(char *file); -int feh_png_write_png(Imlib_Image image, char *file, ...); +int feh_png_write_png_fd(Imlib_Image image, int fd, ...); int feh_png_file_is_png(FILE * fp); diff --git a/src/filelist.c b/src/filelist.c index 2eb43af..3d9bcef 100644 --- a/src/filelist.c +++ b/src/filelist.c @@ -1,6 +1,7 @@ /* filelist.c Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2024 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -25,12 +26,17 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "feh.h" #include "filelist.h" +#include "signals.h" #include "options.h" +#ifdef HAVE_LIBCURL +#include <curl/curl.h> +#endif + gib_list *filelist = NULL; +gib_list *original_file_items = NULL; /* original file items from argv */ int filelist_len = 0; gib_list *current_file = NULL; -extern int errno; static gib_list *rm_filelist = NULL; @@ -39,8 +45,6 @@ feh_file *feh_file_new(char *filename) feh_file *newfile; char *s; - D_ENTER(4); - newfile = (feh_file *) emalloc(sizeof(feh_file)); newfile->caption = NULL; newfile->filename = estrdup(filename); @@ -49,15 +53,19 @@ feh_file *feh_file_new(char *filename) newfile->name = estrdup(s + 1); else newfile->name = estrdup(filename); + newfile->size = -1; + newfile->mtime = 0; newfile->info = NULL; - D_RETURN(4, newfile); +#ifdef HAVE_LIBEXIF + newfile->ed = NULL; +#endif + return(newfile); } void feh_file_free(feh_file * file) { - D_ENTER(4); if (!file) - D_RETURN_(4); + return; if (file->filename) free(file->filename); if (file->name) @@ -66,70 +74,136 @@ void feh_file_free(feh_file * file) free(file->caption); if (file->info) feh_file_info_free(file->info); +#ifdef HAVE_LIBEXIF + if (file->ed) + exif_data_unref(file->ed); +#endif free(file); - D_RETURN_(4); + return; } feh_file_info *feh_file_info_new(void) { feh_file_info *info; - D_ENTER(4); info = (feh_file_info *) emalloc(sizeof(feh_file_info)); info->width = 0; info->height = 0; - info->size = 0; info->pixels = 0; info->has_alpha = 0; info->format = NULL; info->extension = NULL; - D_RETURN(4, info); + return(info); } void feh_file_info_free(feh_file_info * info) { - D_ENTER(4); if (!info) - D_RETURN_(4); + return; if (info->format) free(info->format); if (info->extension) free(info->extension); free(info); - D_RETURN_(4); + return; } gib_list *feh_file_rm_and_free(gib_list * list, gib_list * l) { - D_ENTER(4); unlink(FEH_FILE(l->data)->filename); - D_RETURN(4, feh_file_remove_from_list(list, l)); + return(feh_file_remove_from_list(list, l)); } gib_list *feh_file_remove_from_list(gib_list * list, gib_list * l) { - D_ENTER(4); feh_file_free(FEH_FILE(l->data)); - D(4, ("filelist_len %d -> %d\n", filelist_len, filelist_len - 1)); + D(("filelist_len %d -> %d\n", filelist_len, filelist_len - 1)); filelist_len--; - D_RETURN(4, gib_list_remove(list, l)); + return(gib_list_remove(list, l)); +} + +int file_selector_all(const struct dirent *unused __attribute__((unused))) +{ + return 1; +} + +static void feh_print_stat_error(char *path) +{ + if (opt.quiet) + return; + + switch (errno) { + case ENOENT: + case ENOTDIR: + weprintf("%s does not exist - skipping", path); + break; + case ELOOP: + weprintf("%s - too many levels of symbolic links - skipping", path); + break; + case EACCES: + weprintf("you don't have permission to open %s - skipping", path); + break; + case EOVERFLOW: + weprintf("Cannot open %s - EOVERFLOW.\n" + "Recompile with stat64=1 to fix this", path); + break; + default: + weprintf("couldn't open %s", path); + break; + } +} + +static void add_stdin_to_filelist(void) +{ + char buf[1024]; + size_t readsize; + char *sfn = estrjoin("_", "/tmp/feh_stdin", "XXXXXX", NULL); + int fd = mkstemp(sfn); + FILE *outfile; + + if (fd == -1) { + free(sfn); + weprintf("cannot read from stdin: mktemp:"); + return; + } + + outfile = fdopen(fd, "w"); + + if (outfile == NULL) { + free(sfn); + weprintf("cannot read from stdin: fdopen:"); + return; + } + + while ((readsize = fread(buf, sizeof(char), sizeof(buf), stdin)) > 0) { + if (fwrite(buf, sizeof(char), readsize, outfile) < readsize) { + free(sfn); + fclose(outfile); + return; + } + } + fclose(outfile); + + filelist = gib_list_add_front(filelist, feh_file_new(sfn)); + add_file_to_rm_filelist(sfn); + free(sfn); } + /* Recursive */ void add_file_to_filelist_recursively(char *origpath, unsigned char level) { struct stat st; char *path; - D_ENTER(5); - if (!origpath) - D_RETURN_(5); + if (!origpath || *origpath == '\0') + return; path = estrdup(origpath); - D(4, ("file is %s\n", path)); + D(("file is %s\n", path)); if (level == FILELIST_FIRST) { /* First time through, sort out pathname */ @@ -139,15 +213,17 @@ void add_file_to_filelist_recursively(char *origpath, unsigned char level) if (path[len - 1] == '/') path[len - 1] = '\0'; - if ((!strncmp(path, "http://", 7)) - || (!strncmp(path, "https://", 8)) - || (!strncmp(path, "ftp://", 6))) { - /* Its a url */ - D(3, ("Adding url %s to filelist\n", path)); + if (path_is_url(path)) { + D(("Adding url %s to filelist\n", path)); filelist = gib_list_add_front(filelist, feh_file_new(path)); /* We'll download it later... */ free(path); - D_RETURN_(5); + return; + } else if ((len == 1) && (path[0] == '-')) { + D(("Adding temporary file for stdin (-) to filelist\n")); + add_stdin_to_filelist(); + free(path); + return; } else if (opt.filelistfile) { char *newpath = feh_absolute_path(path); @@ -158,166 +234,162 @@ void add_file_to_filelist_recursively(char *origpath, unsigned char level) errno = 0; if (stat(path, &st)) { - /* Display useful error message */ - switch (errno) { - case ENOENT: - case ENOTDIR: - if (!opt.quiet) - weprintf("%s does not exist - skipping", path); - break; - case ELOOP: - if (!opt.quiet) - weprintf("%s - too many levels of symbolic links - skipping", path); - break; - case EACCES: - if (!opt.quiet) - weprintf("you don't have permission to open %s - skipping", path); - break; - default: - if (!opt.quiet) - weprintf("couldn't open %s", path); - break; - } + feh_print_stat_error(path); free(path); - D_RETURN_(5); + return; } if ((S_ISDIR(st.st_mode)) && (level != FILELIST_LAST)) { - struct dirent *de; + struct dirent **de; DIR *dir; + int cnt, n; - D(4, ("It is a directory\n")); + D(("It is a directory\n")); if ((dir = opendir(path)) == NULL) { if (!opt.quiet) weprintf("couldn't open directory %s:", path); free(path); - D_RETURN_(5); + return; } - de = readdir(dir); - while (de != NULL) { - if (strcmp(de->d_name, ".") - && strcmp(de->d_name, "..")) { - char *newfile; - - newfile = estrjoin("", path, "/", de->d_name, NULL); - - /* This ensures we go down one level even if not fully recursive - - this way "feh some_dir" expands to some_dir's contents */ - if (opt.recursive) - add_file_to_filelist_recursively(newfile, FILELIST_CONTINUE); - else - add_file_to_filelist_recursively(newfile, FILELIST_LAST); - - free(newfile); + n = scandir(path, &de, file_selector_all, alphasort); + if (n < 0) { + switch (errno) { + case ENOMEM: + weprintf("Insufficient memory to scan directory %s:", path); + break; + default: + weprintf("Failed to scan directory %s:", path); + } + } else { + for (cnt = 0; cnt < n; cnt++) { + if (strcmp(de[cnt]->d_name, ".") + && strcmp(de[cnt]->d_name, "..")) { + char *newfile; + + newfile = estrjoin("", path, "/", de[cnt]->d_name, NULL); + + /* This ensures we go down one level even if not fully recursive + - this way "feh some_dir" expands to some_dir's contents */ + if (opt.recursive) + add_file_to_filelist_recursively(newfile, FILELIST_CONTINUE); + else + add_file_to_filelist_recursively(newfile, FILELIST_LAST); + + free(newfile); + } + free(de[cnt]); } - de = readdir(dir); + free(de); } closedir(dir); } else if (S_ISREG(st.st_mode)) { - D(5, ("Adding regular file %s to filelist\n", path)); + D(("Adding regular file %s to filelist\n", path)); filelist = gib_list_add_front(filelist, feh_file_new(path)); } free(path); - D_RETURN_(5); + return; } void add_file_to_rm_filelist(char *file) { - D_ENTER(4); rm_filelist = gib_list_add_front(rm_filelist, feh_file_new(file)); - D_RETURN_(4); + return; } void delete_rm_files(void) { gib_list *l; - D_ENTER(4); for (l = rm_filelist; l; l = l->next) unlink(FEH_FILE(l->data)->filename); - D_RETURN_(4); + return; } -gib_list *feh_file_info_preload(gib_list * list) +gib_list *feh_file_info_preload(gib_list * list, int load_images) { gib_list *l; feh_file *file = NULL; gib_list *remove_list = NULL; - D_ENTER(4); - if (opt.verbose) - fprintf(stdout, PACKAGE " - preloading...\n"); - for (l = list; l; l = l->next) { file = FEH_FILE(l->data); - D(5, ("file %p, file->next %p, file->name %s\n", l, l->next, file->name)); - if (feh_file_info_load(file, NULL)) { - D(3, ("Failed to load file %p\n", file)); - remove_list = gib_list_add_front(remove_list, l); - if (opt.verbose) - feh_display_status('x'); - } else if (opt.verbose) - feh_display_status('.'); + D(("file %p, file->next %p, file->name %s\n", l, l->next, file->name)); + if (load_images) { + if (feh_file_info_load(file, NULL)) { + D(("Failed to load file %p\n", file)); + remove_list = gib_list_add_front(remove_list, l); + if (opt.verbose) + feh_display_status('x'); + } else if (((unsigned int)file->info->width < opt.min_width) + || ((unsigned int)file->info->width > opt.max_width) + || ((unsigned int)file->info->height < opt.min_height) + || ((unsigned int)file->info->height > opt.max_height)) { + remove_list = gib_list_add_front(remove_list, l); + if (opt.verbose) + feh_display_status('s'); + } else if (opt.verbose) + feh_display_status('.'); + } else { + if (feh_file_stat(file)) { + D(("Failed to stat file %p\n", file)); + remove_list = gib_list_add_front(remove_list, l); + } + } + if (sig_exit) { + feh_display_status(0); + exit(sig_exit); + } } if (opt.verbose) - fprintf(stdout, "\n"); + feh_display_status(0); if (remove_list) { - for (l = remove_list; l; l = l->next) + for (l = remove_list; l; l = l->next) { + feh_file_free(FEH_FILE(((gib_list *) l->data)->data)); filelist = list = gib_list_remove(list, (gib_list *) l->data); + } gib_list_free(remove_list); } - D_RETURN(4, list); + return(list); } -int feh_file_info_load(feh_file * file, Imlib_Image im) +int feh_file_stat(feh_file * file) { struct stat st; + + errno = 0; + if (stat(file->filename, &st)) { + feh_print_stat_error(file->filename); + return(1); + } + + file->mtime = st.st_mtime; + + file->size = st.st_size; + + return(0); +} + +int feh_file_info_load(feh_file * file, Imlib_Image im) +{ int need_free = 1; Imlib_Image im1; - D_ENTER(4); + if (feh_file_stat(file)) + return(1); - D(4, ("im is %p\n", im)); + D(("im is %p\n", im)); if (im) need_free = 0; - errno = 0; - if (stat(file->filename, &st)) { - /* Display useful error message */ - switch (errno) { - case ENOENT: - case ENOTDIR: - if (!opt.quiet) - weprintf("%s does not exist - skipping", file->filename); - break; - case ELOOP: - if (!opt.quiet) - weprintf("%s - too many levels of symbolic links - skipping", file->filename); - break; - case EACCES: - if (!opt.quiet) - weprintf("you don't have permission to open %s - skipping", file->filename); - break; - default: - if (!opt.quiet) - weprintf("couldn't open %s ", file->filename); - break; - } - D_RETURN(4, 1); - } - if (im) im1 = im; - else if (!feh_load_image(&im1, file)) - D_RETURN(4, 1); - - if (!im1) - D_RETURN(4, 1); + else if (!feh_load_image(&im1, file) || !im1) + return(1); file->info = feh_file_info_new(); @@ -330,67 +402,112 @@ int feh_file_info_load(feh_file * file, Imlib_Image im) file->info->format = estrdup(gib_imlib_image_format(im1)); - file->info->size = st.st_size; - - if (need_free && im1) + if (need_free) gib_imlib_free_image_and_decache(im1); - D_RETURN(4, 0); + return(0); +} + +void feh_file_dirname(char *dst, feh_file * f, int maxlen) +{ + int n = strlen(f->filename) - strlen(f->name); + + /* Give up on long dirnames */ + if (n <= 0 || n >= maxlen) { + dst[0] = '\0'; + return; + } + + memcpy(dst, f->filename, n); + dst[n] = '\0'; +} + +static inline int strcmp_or_strverscmp(const char *s1, const char *s2) +{ + if (!opt.version_sort) + return(strcmp(s1, s2)); + else + return(strverscmp(s1, s2)); } int feh_cmp_filename(void *file1, void *file2) { - D_ENTER(4); - D_RETURN(4, strcmp(FEH_FILE(file1)->filename, FEH_FILE(file2)->filename)); + return(strcmp_or_strverscmp(FEH_FILE(file1)->filename, FEH_FILE(file2)->filename)); } int feh_cmp_name(void *file1, void *file2) { - D_ENTER(4); - D_RETURN(4, strcmp(FEH_FILE(file1)->name, FEH_FILE(file2)->name)); + return(strcmp_or_strverscmp(FEH_FILE(file1)->name, FEH_FILE(file2)->name)); +} + +int feh_cmp_dirname(void *file1, void *file2) +{ + char dir1[PATH_MAX], dir2[PATH_MAX]; + int cmp; + feh_file_dirname(dir1, FEH_FILE(file1), PATH_MAX); + feh_file_dirname(dir2, FEH_FILE(file2), PATH_MAX); + if ((cmp = strcmp_or_strverscmp(dir1, dir2)) != 0) + return(cmp); + return(feh_cmp_name(file1, file2)); +} + +/* Return -1 if file1 is _newer_ than file2 */ +int feh_cmp_mtime(void *file1, void *file2) +{ + /* gib_list_sort is not stable, so explicitly return 0 as -1 */ + return(FEH_FILE(file1)->mtime >= FEH_FILE(file2)->mtime ? -1 : 1); } int feh_cmp_width(void *file1, void *file2) { - D_ENTER(4); - D_RETURN(4, (FEH_FILE(file1)->info->width - FEH_FILE(file2)->info->width)); + return((FEH_FILE(file1)->info->width - FEH_FILE(file2)->info->width)); } int feh_cmp_height(void *file1, void *file2) { - D_ENTER(4); - D_RETURN(4, (FEH_FILE(file1)->info->height - FEH_FILE(file2)->info->height)); + return((FEH_FILE(file1)->info->height - FEH_FILE(file2)->info->height)); } int feh_cmp_pixels(void *file1, void *file2) { - D_ENTER(4); - D_RETURN(4, (FEH_FILE(file1)->info->pixels - FEH_FILE(file2)->info->pixels)); + return((FEH_FILE(file1)->info->pixels - FEH_FILE(file2)->info->pixels)); } int feh_cmp_size(void *file1, void *file2) { - D_ENTER(4); - D_RETURN(4, (FEH_FILE(file1)->info->size - FEH_FILE(file2)->info->size)); + return((FEH_FILE(file1)->size - FEH_FILE(file2)->size)); } int feh_cmp_format(void *file1, void *file2) { - D_ENTER(4); - D_RETURN(4, strcmp(FEH_FILE(file1)->info->format, FEH_FILE(file2)->info->format)); + return(strcmp(FEH_FILE(file1)->info->format, FEH_FILE(file2)->info->format)); } void feh_prepare_filelist(void) { - D_ENTER(4); - if (opt.list || opt.customlist || (opt.sort > SORT_FILENAME) - || opt.preload) { + /* + * list and customlist mode as well as the somewhat more fancy sort modes + * need access to file infos. Preloading them is also useful for + * list/customlist as --min-dimension/--max-dimension may filter images + * which should not be processed. + * Finally, if --min-dimension/--max-dimension (-> opt.filter_by_dimensions) + * is set and we're in thumbnail mode, we need to filter images first so + * we can create a properly sized thumbnail list. + */ + if (opt.list || opt.preload || opt.customlist || (opt.sort >= SORT_WIDTH) + || (opt.filter_by_dimensions && (opt.index || opt.thumbs || opt.bgmode))) { /* For these sort options, we have to preload images */ - filelist = feh_file_info_preload(filelist); + filelist = feh_file_info_preload(filelist, TRUE); + if (!gib_list_length(filelist)) + show_mini_usage(); + } else if (opt.sort >= SORT_SIZE) { + /* For these sort options, we need stat(2) information on the files, + * but there is no need to load the images. */ + filelist = feh_file_info_preload(filelist, FALSE); if (!gib_list_length(filelist)) show_mini_usage(); } - D(3, ("sort mode requested is: %d\n", opt.sort)); + D(("sort mode requested is: %d\n", opt.sort)); switch (opt.sort) { case SORT_NONE: if (opt.randomize) { @@ -407,6 +524,12 @@ void feh_prepare_filelist(void) case SORT_FILENAME: filelist = gib_list_sort(filelist, feh_cmp_filename); break; + case SORT_DIRNAME: + filelist = gib_list_sort(filelist, feh_cmp_dirname); + break; + case SORT_MTIME: + filelist = gib_list_sort(filelist, feh_cmp_mtime); + break; case SORT_WIDTH: filelist = gib_list_sort(filelist, feh_cmp_width); break; @@ -428,11 +551,11 @@ void feh_prepare_filelist(void) /* no point reversing a random list */ if (opt.reverse && (opt.sort != SORT_NONE)) { - D(3, ("Reversing filelist as requested\n")); + D(("Reversing filelist as requested\n")); filelist = gib_list_reverse(filelist); } - D_RETURN_(4); + return; } int feh_write_filelist(gib_list * list, char *filename) @@ -440,15 +563,13 @@ int feh_write_filelist(gib_list * list, char *filename) FILE *fp; gib_list *l; - D_ENTER(4); - - if (!list || !filename) - D_RETURN(4, 0); + if (!list || !filename || !strcmp(filename, "/dev/stdin")) + return(0); errno = 0; if ((fp = fopen(filename, "w")) == NULL) { weprintf("can't write filelist %s:", filename); - D_RETURN(4, 0); + return(0); } for (l = list; l; l = l->next) @@ -456,7 +577,7 @@ int feh_write_filelist(gib_list * list, char *filename) fclose(fp); - D_RETURN(4, 1); + return(1); } gib_list *feh_read_filelist(char *filename) @@ -464,46 +585,58 @@ gib_list *feh_read_filelist(char *filename) FILE *fp; gib_list *list = NULL; char s[1024], s1[1024]; - Imlib_Image im1; - - D_ENTER(4); + Imlib_Load_Error err = IMLIB_LOAD_ERROR_NONE; + Imlib_Image tmp_im; + struct stat st; + signed short tmp_conversion_timeout; if (!filename) - D_RETURN(4, NULL); + return(NULL); - /* try and load the given filelist as an image, cowardly refuse to - * overwrite an image with a filelist. (requested by user who did feh -df * - * when he meant feh -dF *, as it overwrote the first image with the - * filelist). + /* + * feh_load_image will fail horribly if filename is not seekable */ - if (feh_load_image_char(&im1, filename)) { - weprintf( - "The file you specified as a filelist to read - %s - appears to be an image. Ignoring it (this is a common mistake).\n", - filename); - opt.filelistfile = NULL; - D_RETURN(4, NULL); + tmp_conversion_timeout = opt.conversion_timeout; + opt.conversion_timeout = -1; + if (!stat(filename, &st) && S_ISREG(st.st_mode)) { + tmp_im = imlib_load_image_with_error_return(filename, &err); + if (err == IMLIB_LOAD_ERROR_NONE) { + gib_imlib_free_image_and_decache(tmp_im); + weprintf("Filelist file %s is an image, refusing to use it.\n" + "Did you mix up -f and -F?", filename); + opt.filelistfile = NULL; + return NULL; + } } + opt.conversion_timeout = tmp_conversion_timeout; errno = 0; - if ((fp = fopen(filename, "r")) == NULL) { + + if (!strcmp(filename, "/dev/stdin")) + fp = stdin; + else + fp = fopen(filename, "r"); + + if (fp == NULL) { /* return quietly, as it's okay to specify a filelist file that doesn't exist. In that case we create it on exit. */ - D_RETURN(4, NULL); + return(NULL); } for (; fgets(s, sizeof(s), fp);) { - D(5, ("Got line '%s'\n", s)); + D(("Got line '%s'\n", s)); s1[0] = '\0'; sscanf(s, "%[^\n]", (char *) &s1); if (!(*s1) || (*s1 == '\n')) continue; - D(5, ("Got filename %s from filelist file\n", s1)); + D(("Got filename %s from filelist file\n", s1)); /* Add it to the new list */ list = gib_list_add_front(list, feh_file_new(s1)); } - fclose(fp); + if (strcmp(filename, "/dev/stdin")) + fclose(fp); - D_RETURN(4, list); + return(list); } char *feh_absolute_path(char *path) @@ -513,42 +646,73 @@ char *feh_absolute_path(char *path) char temp[PATH_MAX]; char *ret; - D_ENTER(4); - if (!path) - D_RETURN(4, NULL); - if (path[0] == '/') - D_RETURN(4, estrdup(path)); + return(NULL); + if (path[0] == '/' || path_is_url(path)) + return(estrdup(path)); /* This path is not relative. We're gonna convert it, so that a filelist file can be saved anywhere and feh will still find the images */ - D(4, ("Need to convert %s to an absolute form\n", path)); - /* I SHOULD be able to just use a simple realpath() here, but dumb * + D(("Need to convert %s to an absolute form\n", path)); + /* I SHOULD be able to just use a simple realpath() here, but dumb * old Solaris's realpath doesn't return an absolute path if the path you give it is relative. Linux and BSD get this right... */ - getcwd(cwd, sizeof(cwd)); - snprintf(temp, sizeof(temp), "%s/%s", cwd, path); + if (getcwd(cwd, sizeof(cwd)) == NULL) + eprintf("Cannot determine working directory:"); + if ((size_t) snprintf(temp, sizeof(temp), "%s/%s", cwd, path) >= sizeof(temp)) + eprintf("Absolute path for working directory was truncated"); if (realpath(temp, fullpath) != NULL) { ret = estrdup(fullpath); } else { ret = estrdup(temp); } - D(4, ("Converted path to %s\n", ret)); - D_RETURN(4, ret); + D(("Converted path to %s\n", ret)); + return(ret); } -void feh_save_filelist() +void feh_save_filelist(void) { char *tmpname; + char *base_dir = ""; - D_ENTER(4); + if (opt.output_dir) { + base_dir = estrjoin("", opt.output_dir, "/", NULL); + } - tmpname = feh_unique_filename("", "filelist"); + tmpname = feh_unique_filename(base_dir, "filelist"); - if (!opt.quiet) - printf("saving filelist to filename '%s'\n", tmpname); + if (opt.output_dir) { + free(base_dir); + } + + if (opt.verbose) + fprintf(stderr, "saving filelist to filename '%s'\n", tmpname); feh_write_filelist(filelist, tmpname); free(tmpname); - D_RETURN_(4); + 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 842e3af..4fc3930 100644 --- a/src/filelist.h +++ b/src/filelist.h @@ -1,6 +1,7 @@ /* filelist.h Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2024 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -26,19 +27,27 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef FILELIST_H #define FILELIST_H +#ifdef HAVE_LIBEXIF +#include <libexif/exif-data.h> +#endif + struct __feh_file { char *filename; char *caption; char *name; /* info stuff */ + time_t mtime; + int size; feh_file_info *info; /* only set when needed */ +#ifdef HAVE_LIBEXIF + ExifData *ed; +#endif }; struct __feh_file_info { int width; int height; - int size; int pixels; unsigned char has_alpha; char *format; @@ -47,12 +56,29 @@ struct __feh_file_info { #define FEH_FILE(l) ((feh_file *) l) +/* + * PATH_MAX may not be defined on all systems. Since we only use it in for a + * getcwd call in feh_absolute_path, it isn't really worth the effort to malloc + * ever-increasing buffers until it fits. Instead, we just set it to 4096 and + * have --filelist and --bg-* hiccup if the path is larger. + */ +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + enum filelist_recurse { FILELIST_FIRST, FILELIST_CONTINUE, FILELIST_LAST }; -enum sort_type { SORT_NONE, SORT_NAME, SORT_FILENAME, SORT_WIDTH, +enum sort_type { + SORT_NONE, + SORT_NAME, + SORT_FILENAME, + SORT_DIRNAME, + SORT_SIZE, // everything after SORT_SIZE requires stat(2) information on the filelist + SORT_MTIME, + SORT_WIDTH, // everything after SORT_WIDTH requires preloading the images in the filelist SORT_HEIGHT, SORT_PIXELS, - SORT_SIZE, SORT_FORMAT + SORT_FORMAT }; feh_file *feh_file_new(char *filename); @@ -60,20 +86,26 @@ void feh_file_free(feh_file * file); feh_file_info *feh_file_info_new(void); void feh_file_info_free(feh_file_info * info); gib_list *feh_file_rm_and_free(gib_list * list, gib_list * file); +int file_selector_all(const struct dirent *unused); void add_file_to_filelist_recursively(char *origpath, unsigned char level); void add_file_to_rm_filelist(char *file); void delete_rm_files(void); -gib_list *feh_file_info_preload(gib_list * list); +gib_list *feh_file_info_preload(gib_list * list, int load_images); +int feh_file_stat(feh_file * file); int feh_file_info_load(feh_file * file, Imlib_Image im); +void feh_file_dirname(char *dst, feh_file * f, int maxlen); void feh_prepare_filelist(void); int feh_write_filelist(gib_list * list, char *filename); 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(); +void feh_save_filelist(void); +char *feh_http_unescape(char * url); int feh_cmp_name(void *file1, void *file2); +int feh_cmp_dirname(void *file1, void *file2); int feh_cmp_filename(void *file1, void *file2); +int feh_cmp_mtime(void *file1, void *file2); int feh_cmp_width(void *file1, void *file2); int feh_cmp_height(void *file1, void *file2); int feh_cmp_pixels(void *file1, void *file2); @@ -81,6 +113,7 @@ int feh_cmp_size(void *file1, void *file2); int feh_cmp_format(void *file1, void *file2); extern gib_list *filelist; +extern gib_list *original_file_items; extern int filelist_len; extern gib_list *current_file; diff --git a/src/getopt.c b/src/getopt.c deleted file mode 100644 index d212b3a..0000000 --- a/src/getopt.c +++ /dev/null @@ -1,949 +0,0 @@ -/* Getopt for GNU. - NOTE: getopt is now part of the C library, so if you don't know what - "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu - before changing it! - - Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97 - Free Software Foundation, Inc. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. - Ditto for AIX 3.2 and <stdlib.h>. */ -#ifndef _NO_PROTO -#define _NO_PROTO -#endif - -#if !defined (__STDC__) || !__STDC__ -/* This is a separate conditional since some stdc systems - reject `defined (const)'. */ -#ifndef const -#define const -#endif -#endif - -#include <stdio.h> - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -#define GETOPT_INTERFACE_VERSION 2 -#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 -#include <gnu-versions.h> -#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION -#define ELIDE_CODE -#endif -#endif - -#ifndef ELIDE_CODE - -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -/* Don't include stdlib.h for non-GNU C libraries because some of them - contain conflicting prototypes for getopt. */ -#include <stdlib.h> -#include <unistd.h> -#endif /* GNU C library. */ - -#ifdef VMS -#include <unixlib.h> -#if HAVE_STRING_H - 0 -#include <string.h> -#endif -#endif - -#ifndef _ -/* This is for other GNU distributions with internationalized messages. - When compiling libc, the _ macro is predefined. */ -#ifdef HAVE_LIBINTL_H -# include <libintl.h> -# define _(msgid) gettext (msgid) -#else -# define _(msgid) (msgid) -#endif -#endif - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of ARGV so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. - - Setting the environment variable POSIXLY_CORRECT disables permutation. - Then the behavior is completely standard. - - GNU application programs can use a third alternative mode in which - they can distinguish the relative order of options and other arguments. */ - -#include "getopt.h" - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -char *optarg = NULL; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -/* 1003.2 says this must be 1 before any call. */ -int optind = 1; - -/* Formerly, initialization of getopt depended on optind==0, which - causes problems with re-calling getopt as programs generally don't - know that. */ - -int __getopt_initialized = 0; - -/* The next char to be scanned in the option-element - in which the last option character we returned was found. - This allows us to pick up the scan where we left off. - - If this is zero, or a null string, it means resume the scan - by advancing to the next ARGV-element. */ - -static char *nextchar; - -/* Callers store zero here to inhibit the error message - for unrecognized options. */ - -int opterr = 1; - -/* Set to an option character which was unrecognized. - This must be initialized on some systems to avoid linking in the - system's own getopt implementation. */ - -int optopt = '?'; - -/* Describe how to deal with options that follow non-option ARGV-elements. - - If the caller did not specify anything, - the default is REQUIRE_ORDER if the environment variable - POSIXLY_CORRECT is defined, PERMUTE otherwise. - - REQUIRE_ORDER means don't recognize them as options; - stop option processing when the first non-option is seen. - This is what Unix does. - This mode of operation is selected by either setting the environment - variable POSIXLY_CORRECT, or using `+' as the first character - of the list of option characters. - - PERMUTE is the default. We permute the contents of ARGV as we scan, - so that eventually all the non-options are at the end. This allows options - to be given in any order, even with programs that were not written to - expect this. - - RETURN_IN_ORDER is an option available to programs that were written - to expect options and other ARGV-elements in any order and that care about - the ordering of the two. We describe each non-option ARGV-element - as if it were the argument of an option with character code 1. - Using `-' as the first character of the list of option characters - selects this mode of operation. - - The special argument `--' forces an end of option-scanning regardless - of the value of `ordering'. In the case of RETURN_IN_ORDER, only - `--' can cause `getopt' to return -1 with `optind' != ARGC. */ - -static enum { - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER -} ordering; - -/* Value of POSIXLY_CORRECT environment variable. */ -static char *posixly_correct; - -#ifdef __GNU_LIBRARY__ -/* We want to avoid inclusion of string.h with non-GNU libraries - because there are many ways it can cause trouble. - On some systems, it contains special magic macros that don't work - in GCC. */ -#include <string.h> -#define my_index strchr -#else - -/* Avoid depending on library functions or files - whose names are inconsistent. */ - -char *getenv(); - -static char *my_index(str, chr) -const char *str; -int chr; -{ - while (*str) { - if (*str == chr) - return (char *) str; - str++; - } - return 0; -} - -/* If using GCC, we can safely declare strlen this way. - If not using GCC, it is ok not to declare it. */ -#ifdef __GNUC__ -/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. - That was relevant to code that was here before. */ -#if !defined (__STDC__) || !__STDC__ -/* gcc with -traditional declares the built-in strlen to return int, - and has done so at least since version 2.4.5. -- rms. */ -extern int strlen(const char *); -#endif /* not __STDC__ */ -#endif /* __GNUC__ */ - -#endif /* not __GNU_LIBRARY__ */ - -/* Handle permutation of arguments. */ - -/* Describe the part of ARGV that contains non-options that have - been skipped. `first_nonopt' is the index in ARGV of the first of them; - `last_nonopt' is the index after the last of them. */ - -static int first_nonopt; -static int last_nonopt; - -#ifdef _LIBC -/* Bash 2.0 gives us an environment variable containing flags - indicating ARGV elements that should not be considered arguments. */ - -/* Defined in getopt_init.c */ -extern char *__getopt_nonoption_flags; - -static int nonoption_flags_max_len; -static int nonoption_flags_len; - -static int original_argc; -static char *const *original_argv; - -/* Make sure the environment variable bash 2.0 puts in the environment - is valid for the getopt call we must make sure that the ARGV passed - to getopt is that one passed to the process. */ -static void - __attribute__ ((unused)) store_args_and_env(int argc, char *const *argv) -{ - /* XXX This is no good solution. We should rather copy the args so that - we can compare them later. But we must not use malloc(3). */ - original_argc = argc; - original_argv = argv; -} - -# ifdef text_set_element -text_set_element(__libc_subinit, store_args_and_env); -# endif /* text_set_element */ - -# define SWAP_FLAGS(ch1, ch2) \ - if (nonoption_flags_len > 0) \ - { \ - char __tmp = __getopt_nonoption_flags[ch1]; \ - __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ - __getopt_nonoption_flags[ch2] = __tmp; \ - } -#else /* !_LIBC */ -# define SWAP_FLAGS(ch1, ch2) -#endif /* _LIBC */ - -/* Exchange two adjacent subsequences of ARGV. - One subsequence is elements [first_nonopt,last_nonopt) - which contains all the non-options that have been skipped so far. - The other is elements [last_nonopt,optind), which contains all - the options processed since those non-options were skipped. - - `first_nonopt' and `last_nonopt' are relocated so that they describe - the new indices of the non-options in ARGV after they are moved. */ - -#if defined (__STDC__) && __STDC__ -static void exchange(char **); -#endif - -static void exchange(argv) -char **argv; -{ - int bottom = first_nonopt; - int middle = last_nonopt; - int top = optind; - char *tem; - - /* Exchange the shorter segment with the far end of the longer segment. - That puts the shorter segment into the right place. It leaves the - longer segment in the right place overall, but it consists of two parts - that need to be swapped next. */ - -#ifdef _LIBC - /* First make sure the handling of the `__getopt_nonoption_flags' string - can work normally. Our top argument must be in the range of the - string. */ - if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) { - /* We must extend the array. The user plays games with us and presents - new arguments. */ - char *new_str = malloc(top + 1); - - if (new_str == NULL) - nonoption_flags_len = nonoption_flags_max_len = 0; - else { - memset(__mempcpy - (new_str, __getopt_nonoption_flags, - nonoption_flags_max_len), '\0', top + 1 - nonoption_flags_max_len); - nonoption_flags_max_len = top + 1; - __getopt_nonoption_flags = new_str; - } - } -#endif - - while (top > middle && middle > bottom) { - if (top - middle > middle - bottom) { - /* Bottom segment is the short one. */ - int len = middle - bottom; - register int i; - - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) { - tem = argv[bottom + i]; - argv[bottom + i] = argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - SWAP_FLAGS(bottom + i, top - (middle - bottom) + i); - } - /* Exclude the moved bottom segment from further swapping. */ - top -= len; - } else { - /* Top segment is the short one. */ - int len = top - middle; - register int i; - - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - SWAP_FLAGS(bottom + i, middle + i); - } - /* Exclude the moved top segment from further swapping. */ - bottom += len; - } - } - - /* Update records for the slots the non-options now occupy. */ - - first_nonopt += (optind - last_nonopt); - last_nonopt = optind; -} - -/* Initialize the internal data when the first call is made. */ - -#if defined (__STDC__) && __STDC__ -static const char *_getopt_initialize(int, char *const *, const char *); -#endif -static const char *_getopt_initialize(argc, argv, optstring) -int argc; -char *const *argv; -const char *optstring; -{ - /* Start processing options with ARGV-element 1 (since ARGV-element 0 is - the program name); the sequence of previously skipped non-option - ARGV-elements is empty. */ - - first_nonopt = last_nonopt = optind; - - nextchar = NULL; - - posixly_correct = getenv("POSIXLY_CORRECT"); - - /* Determine how to handle the ordering of options and nonoptions. */ - - if (optstring[0] == '-') { - ordering = RETURN_IN_ORDER; - ++optstring; - } else if (optstring[0] == '+') { - ordering = REQUIRE_ORDER; - ++optstring; - } else if (posixly_correct != NULL) - ordering = REQUIRE_ORDER; - else - ordering = PERMUTE; - -#ifdef _LIBC - if (posixly_correct == NULL && argc == original_argc && argv == original_argv) { - if (nonoption_flags_max_len == 0) { - if (__getopt_nonoption_flags == NULL || __getopt_nonoption_flags[0] == '\0') - nonoption_flags_max_len = -1; - else { - const char *orig_str = __getopt_nonoption_flags; - int len = nonoption_flags_max_len = strlen(orig_str); - - if (nonoption_flags_max_len < argc) - nonoption_flags_max_len = argc; - __getopt_nonoption_flags = (char *) - malloc(nonoption_flags_max_len); - if (__getopt_nonoption_flags == NULL) - nonoption_flags_max_len = -1; - else - memset(__mempcpy - (__getopt_nonoption_flags, - orig_str, len), '\0', nonoption_flags_max_len - len); - } - } - nonoption_flags_len = nonoption_flags_max_len; - } else - nonoption_flags_len = 0; -#endif - - return optstring; -} - -/* Scan elements of ARGV (whose length is ARGC) for option characters - given in OPTSTRING. - - If an element of ARGV starts with '-', and is not exactly "-" or "--", - then it is an option element. The characters of this element - (aside from the initial '-') are option characters. If `getopt' - is called repeatedly, it returns successively each of the option characters - from each of the option elements. - - If `getopt' finds another option character, it returns that character, - updating `optind' and `nextchar' so that the next call to `getopt' can - resume the scan with the following option character or ARGV-element. - - If there are no more option characters, `getopt' returns -1. - Then `optind' is the index in ARGV of the first ARGV-element - that is not an option. (The ARGV-elements have been permuted - so that those that are not options now come last.) - - OPTSTRING is a string containing the legitimate option characters. - If an option character is seen that is not listed in OPTSTRING, - return '?' after printing an error message. If you set `opterr' to - zero, the error message is suppressed but we still return '?'. - - If a char in OPTSTRING is followed by a colon, that means it wants an arg, - so the following text in the same ARGV-element, or the text of the following - ARGV-element, is returned in `optarg'. Two colons mean an option that - wants an optional arg; if there is text in the current ARGV-element, - it is returned in `optarg', otherwise `optarg' is set to zero. - - If OPTSTRING starts with `-' or `+', it requests different methods of - handling the non-option ARGV-elements. - See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - - Long-named options begin with `--' instead of `-'. - Their names may be abbreviated as long as the abbreviation is unique - or is an exact match for some defined option. If they have an - argument, it follows the option name in the same ARGV-element, separated - from the option name by a `=', or else the in next ARGV-element. - When `getopt' finds a long-named option, it returns 0 if that option's - `flag' field is nonzero, the value of the option's `val' field - if the `flag' field is zero. - - The elements of ARGV aren't really const, because we permute them. - But we pretend they're const in the prototype to be compatible - with other systems. - - LONGOPTS is a vector of `struct option' terminated by an - element containing a name which is zero. - - LONGIND returns the index in LONGOPT of the long-named option found. - It is only valid when a long-named option has been found by the most - recent call. - - If LONG_ONLY is nonzero, '-' as well as '--' can introduce - long-named options. */ - -int _getopt_internal(argc, argv, optstring, longopts, longind, long_only) -int argc; -char *const *argv; -const char *optstring; -const struct option *longopts; -int *longind; -int long_only; -{ - optarg = NULL; - - if (optind == 0 || !__getopt_initialized) { - if (optind == 0) - optind = 1; /* Don't scan ARGV[0], the - program name. */ - optstring = _getopt_initialize(argc, argv, optstring); - __getopt_initialized = 1; - } - - /* Test whether ARGV[optind] points to a non-option argument. Either it - does not have option syntax, or there is an environment flag from the - shell indicating it is not an option. The later information is only - used when the used in the GNU libc. */ -#ifdef _LIBC -#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ - || (optind < nonoption_flags_len \ - && __getopt_nonoption_flags[optind] == '1')) -#else -#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') -#endif - - if (nextchar == NULL || *nextchar == '\0') { - /* Advance to the next ARGV-element. */ - - /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been - moved back by the user (who may also have changed the arguments). */ - if (last_nonopt > optind) - last_nonopt = optind; - if (first_nonopt > optind) - first_nonopt = optind; - - if (ordering == PERMUTE) { - /* If we have just processed some options following some - non-options, exchange them so that the options come first. */ - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange((char **) argv); - else if (last_nonopt != optind) - first_nonopt = optind; - - /* Skip any additional non-options and extend the range of - non-options previously skipped. */ - - while (optind < argc && NONOPTION_P) - optind++; - last_nonopt = optind; - } - - /* The special ARGV-element `--' means premature end of options. Skip - it like a null option, then exchange with previous non-options as if - it were an option, then skip everything else like a non-option. */ - - if (optind != argc && !strcmp(argv[optind], "--")) { - optind++; - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange((char **) argv); - else if (first_nonopt == last_nonopt) - first_nonopt = optind; - last_nonopt = argc; - - optind = argc; - } - - /* If we have done all the ARGV-elements, stop the scan and back over - any non-options that we skipped and permuted. */ - - if (optind == argc) { - /* Set the next-arg-index to point at the non-options that we - previously skipped, so the caller will digest them. */ - if (first_nonopt != last_nonopt) - optind = first_nonopt; - return -1; - } - - /* If we have come to a non-option and did not permute it, either stop - the scan or describe it to the caller and pass it by. */ - - if (NONOPTION_P) { - if (ordering == REQUIRE_ORDER) - return -1; - optarg = argv[optind++]; - return 1; - } - - /* We have found another option-ARGV-element. Skip the initial - punctuation. */ - - nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); - } - - /* Decode the current option-ARGV-element. */ - - /* Check whether the ARGV-element is a long option. - - If long_only and the ARGV-element has the form "-f", where f is a valid - short option, don't consider it an abbreviated form of a long option - that starts with f. Otherwise there would be no way to give the -f - short option. - - On the other hand, if there's a long option "fubar" and the - ARGV-element is "-fu", do consider that an abbreviation of the long - option, just like "--fu", and not "-f" with arg "u". - - This distinction seems to be the most useful approach. */ - - if (longopts != NULL && (argv[optind][1] == '-' || (long_only && (argv[optind][2] - || !my_index(optstring, argv[optind] - [1]))))) { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = -1; - int option_index; - - for (nameend = nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match or abbreviated matches. - */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp(p->name, nextchar, nameend - nextchar)) { - if ((unsigned int) (nameend - nextchar) == (unsigned int) strlen(p->name)) { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } else if (pfound == NULL) { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } else - /* Second or later nonexact match found. */ - ambig = 1; - } - - if (ambig && !exact) { - if (opterr) - fprintf(stderr, _("%s: option `%s' is ambiguous\n"), argv[0], argv[optind]); - nextchar += strlen(nextchar); - optind++; - optopt = 0; - return '?'; - } - - if (pfound != NULL) { - option_index = indfound; - optind++; - if (*nameend) { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = nameend + 1; - else { - if (opterr) { - if (argv[optind - 1][1] == '-') - /* --option */ - fprintf(stderr, - _ - ("%s: option `--%s' doesn't allow an argument\n"), - argv[0], pfound->name); - else - /* +option or -option */ - fprintf(stderr, - _ - ("%s: option `%c%s' doesn't allow an argument\n"), - argv[0], argv[optind - 1][0], pfound->name); - } - - nextchar += strlen(nextchar); - - optopt = pfound->val; - return '?'; - } - } else if (pfound->has_arg == 1) { - if (optind < argc) - optarg = argv[optind++]; - else { - if (opterr) - fprintf(stderr, - _ - ("%s: option `%s' requires an argument\n"), - argv[0], argv[optind - 1]); - nextchar += strlen(nextchar); - optopt = pfound->val; - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += strlen(nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - - /* Can't find it as a long option. If this is not getopt_long_only, or - the option starts with '--' or is not a valid short option, then - it's an error. Otherwise interpret it as a short option. */ - if (!long_only || argv[optind][1] == '-' || my_index(optstring, *nextchar) == NULL) { - if (opterr) { - if (argv[optind][1] == '-') - /* --option */ - fprintf(stderr, _("%s: unrecognized option `--%s'\n"), argv[0], nextchar); - else - /* +option or -option */ - fprintf(stderr, - _ - ("%s: unrecognized option `%c%s'\n"), - argv[0], argv[optind][0], nextchar); - } - nextchar = (char *) ""; - optind++; - optopt = 0; - return '?'; - } - } - - /* Look at and handle the next short option-character. */ - - { - char c = *nextchar++; - char *temp = my_index(optstring, c); - - /* Increment `optind' when we start to process its last character. */ - if (*nextchar == '\0') - ++optind; - - if (temp == NULL || c == ':') { - if (opterr) { - if (posixly_correct) - /* 1003.2 specifies the format of this message. */ - fprintf(stderr, _("%s: illegal option -- %c\n"), argv[0], c); - else - fprintf(stderr, _("%s: invalid option -- %c\n"), argv[0], c); - } - optopt = c; - return '?'; - } - /* Convenience. Treat POSIX -W foo same as long option --foo */ - if (temp[0] == 'W' && temp[1] == ';') { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = 0; - int option_index; - - /* This is an option that requires an argument. */ - if (*nextchar != '\0') { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, we - must advance to the next element now. */ - optind++; - } else if (optind == argc) { - if (opterr) { - /* 1003.2 specifies the format of this message. */ - fprintf(stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); - } - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - return c; - } else - /* We already incremented `optind' once; increment it again when - taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - - /* optarg is now the argument, see if it's in the table of longopts. - */ - - for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match or abbreviated - matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp(p->name, nextchar, nameend - nextchar)) { - if ((unsigned int) (nameend - nextchar) == strlen(p->name)) { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } else if (pfound == NULL) { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } else - /* Second or later nonexact match found. */ - ambig = 1; - } - if (ambig && !exact) { - if (opterr) - fprintf(stderr, _("%s: option `-W %s' is ambiguous\n"), argv[0], argv[optind]); - nextchar += strlen(nextchar); - optind++; - return '?'; - } - if (pfound != NULL) { - option_index = indfound; - if (*nameend) { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = nameend + 1; - else { - if (opterr) - fprintf(stderr, _("\ -%s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name); - - nextchar += strlen(nextchar); - return '?'; - } - } else if (pfound->has_arg == 1) { - if (optind < argc) - optarg = argv[optind++]; - else { - if (opterr) - fprintf(stderr, - _ - ("%s: option `%s' requires an argument\n"), - argv[0], argv[optind - 1]); - nextchar += strlen(nextchar); - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += strlen(nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - nextchar = NULL; - return 'W'; /* Let the application handle it. - */ - } - if (temp[1] == ':') { - if (temp[2] == ':') { - /* This is an option that accepts an argument optionally. */ - if (*nextchar != '\0') { - optarg = nextchar; - optind++; - } else - optarg = NULL; - nextchar = NULL; - } else { - /* This is an option that requires an argument. */ - if (*nextchar != '\0') { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } else if (optind == argc) { - if (opterr) { - /* 1003.2 specifies the format of this message. */ - fprintf(stderr, - _("%s: option requires an argument -- %c\n"), argv[0], c); - } - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - } else - /* We already incremented `optind' once; increment it again - when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - nextchar = NULL; - } - } - return c; - } -} - -int getopt(argc, argv, optstring) -int argc; -char *const *argv; -const char *optstring; -{ - return _getopt_internal(argc, argv, optstring, (const struct option *) 0, (int *) 0, 0); -} - -#endif /* Not ELIDE_CODE. */ - -#ifdef TEST - -/* Compile with -DTEST to make an executable for use in testing - the above definition of `getopt'. */ - -int main(argc, argv) -int argc; -char **argv; -{ - int c; - int digit_optind = 0; - - while (1) { - int this_option_optind = optind ? optind : 1; - - c = getopt(argc, argv, "abc:d:0123456789"); - if (c == -1) - break; - - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf("option %c\n", c); - break; - - case 'a': - printf("option a\n"); - break; - - case 'b': - printf("option b\n"); - break; - - case 'c': - printf("option c with value `%s'\n", optarg); - break; - - case '?': - break; - - default: - printf("?? getopt returned character code 0%o ??\n", c); - } - } - - if (optind < argc) { - printf("non-option ARGV-elements: "); - while (optind < argc) - printf("%s ", argv[optind++]); - printf("\n"); - } - - exit(0); -} - -#endif /* TEST */ diff --git a/src/getopt.h b/src/getopt.h deleted file mode 100644 index e8fe13f..0000000 --- a/src/getopt.h +++ /dev/null @@ -1,130 +0,0 @@ -/* Declarations for getopt. - Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#ifndef _GETOPT_H -#define _GETOPT_H 1 - -#ifdef __cplusplus -extern "C" { -#endif - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - - extern char *optarg; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - - extern int optind; - -/* Callers store zero here to inhibit the error message `getopt' prints - for unrecognized options. */ - - extern int opterr; - -/* Set to an option character which was unrecognized. */ - - extern int optopt; - -/* Describe the long-named options requested by the application. - The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector - of `struct option' terminated by an element containing a name which is - zero. - - The field `has_arg' is: - no_argument (or 0) if the option does not take an argument, - required_argument (or 1) if the option requires an argument, - optional_argument (or 2) if the option takes an optional argument. - - If the field `flag' is not NULL, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - - struct option { -#if defined (__STDC__) && __STDC__ - const char *name; -#else - char *name; -#endif - /* has_arg can't be an enum because some compilers complain about type - mismatches in all the code that assumes it is an int. */ - int has_arg; - int *flag; - int val; - }; - -/* Names for the values of the `has_arg' field of `struct option'. */ - -#define no_argument 0 -#define required_argument 1 -#define optional_argument 2 - -#if defined (__STDC__) && __STDC__ -#ifdef __GNU_LIBRARY__ -/* Many other libraries have conflicting prototypes for getopt, with - differences in the consts, in stdlib.h. To avoid compilation - errors, only prototype getopt for the GNU C library. */ - extern int getopt(int argc, char *const *argv, const char *shortopts); -#else /* not __GNU_LIBRARY__ */ -#ifndef __cplusplus -/* C++ is more pedantic, and demands a full prototype, not this. - Hope that stdlib.h has a prototype for `getopt'. */ - extern int getopt(); -#endif /* __cplusplus */ -#endif /* __GNU_LIBRARY__ */ - extern int getopt_long(int argc, char *const *argv, - const char *shortopts, const struct option *longopts, int *longind); - extern int getopt_long_only(int argc, char *const *argv, - const char *shortopts, const struct option *longopts, int *longind); - -/* Internal only. Users should not call this directly. */ - extern int _getopt_internal(int argc, char *const *argv, - const char *shortopts, const struct option *longopts, int *longind, int long_only); -#else /* not __STDC__ */ - extern int getopt(); - extern int getopt_long(); - extern int getopt_long_only(); - - extern int _getopt_internal(); -#endif /* __STDC__ */ - -#ifdef __cplusplus -} -#endif -#endif /* getopt.h */ diff --git a/src/getopt1.c b/src/getopt1.c deleted file mode 100644 index 5a5c483..0000000 --- a/src/getopt1.c +++ /dev/null @@ -1,173 +0,0 @@ -/* getopt_long and getopt_long_only entry points for GNU getopt. - Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#include "getopt.h" - -#if !defined (__STDC__) || !__STDC__ -/* This is a separate conditional since some stdc systems - reject `defined (const)'. */ -#ifndef const -#define const -#endif -#endif - -#include <stdio.h> - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -#define GETOPT_INTERFACE_VERSION 2 -#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 -#include <gnu-versions.h> -#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION -#define ELIDE_CODE -#endif -#endif - -#ifndef ELIDE_CODE - -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -#include <stdlib.h> -#endif - -#ifndef NULL -#define NULL 0 -#endif - -int getopt_long(argc, argv, options, long_options, opt_index) -int argc; -char *const *argv; -const char *options; -const struct option *long_options; -int *opt_index; -{ - return _getopt_internal(argc, argv, options, long_options, opt_index, 0); -} - -/* Like getopt_long, but '-' as well as '--' can indicate a long option. - If an option that starts with '-' (not '--') doesn't match a long option, - but does match a short option, it is parsed as a short option - instead. */ - -int getopt_long_only(argc, argv, options, long_options, opt_index) -int argc; -char *const *argv; -const char *options; -const struct option *long_options; -int *opt_index; -{ - return _getopt_internal(argc, argv, options, long_options, opt_index, 1); -} - -#endif /* Not ELIDE_CODE. */ - -#ifdef TEST - -#include <stdio.h> - -int main(argc, argv) -int argc; -char **argv; -{ - int c; - int digit_optind = 0; - - while (1) { - int this_option_optind = optind ? optind : 1; - int option_index = 0; - static struct option long_options[] = { - {"add", 1, 0, 0}, - {"append", 0, 0, 0}, - {"delete", 1, 0, 0}, - {"verbose", 0, 0, 0}, - {"create", 0, 0, 0}, - {"file", 1, 0, 0}, - {0, 0, 0, 0} - }; - - c = getopt_long(argc, argv, "abc:d:0123456789", long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 0: - printf("option %s", long_options[option_index].name); - if (optarg) - printf(" with arg %s", optarg); - printf("\n"); - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf("option %c\n", c); - break; - - case 'a': - printf("option a\n"); - break; - - case 'b': - printf("option b\n"); - break; - - case 'c': - printf("option c with value `%s'\n", optarg); - break; - - case 'd': - printf("option d with value `%s'\n", optarg); - break; - - case '?': - break; - - default: - printf("?? getopt returned character code 0%o ??\n", c); - } - } - - if (optind < argc) { - printf("non-option ARGV-elements: "); - while (optind < argc) - printf("%s ", argv[optind++]); - printf("\n"); - } - - exit(0); -} - -#endif /* TEST */ diff --git a/src/gib_hash.c b/src/gib_hash.c new file mode 100644 index 0000000..9497d04 --- /dev/null +++ b/src/gib_hash.c @@ -0,0 +1,149 @@ +/* gib_hash.c + +Copyright (C) 1999,2000 Paul Duncan. + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ +#include <strings.h> + +#include "gib_hash.h" +#include "utils.h" +#include "debug.h" + +gib_hash_node *gib_hash_node_new(char *key, void *data) +{ + gib_hash_node *node = emalloc(sizeof(gib_hash_node)); + node->key = strdup(key); + GIB_LIST(node)->data = data; + GIB_LIST(node)->next = NULL; + GIB_LIST(node)->prev = NULL; + return node; + +} + +void gib_hash_node_free(gib_hash_node *node) +{ + free(node->key); + free(node); + return; +} + +void gib_hash_node_free_and_data(gib_hash_node *node) +{ + free(node->list.data); + gib_hash_node_free(node); + return; +} + +gib_hash *gib_hash_new(void) +{ + gib_hash *hash = emalloc(sizeof(gib_hash)); + hash->base = gib_hash_node_new("__gib_hash_new",NULL); + return hash; +} + +void gib_hash_free(gib_hash *hash) +{ + /* free hash keys as it's not taken care of by gib_list_free */ + gib_list *i; + for (i = GIB_LIST(hash->base); i; i = i->next) + free(GIB_HASH_NODE(i)->key); + + gib_list_free(GIB_LIST(hash->base)); + free(hash); + return; +} + +void gib_hash_free_and_data(gib_hash *hash) +{ + /* free hash keys as it's not taken care of by gib_list_free */ + gib_list *i; + for (i = GIB_LIST(hash->base); i; i = i->next) + free(GIB_HASH_NODE(i)->key); + + gib_list_free_and_data(GIB_LIST(hash->base)); + free(hash); + return; +} + +static unsigned char gib_hash_find_callback(gib_list *list, void *data) +{ + gib_hash_node *node = GIB_HASH_NODE(list); + char *key = (char*) data; + + /* strncasecmp causes similar keys like key1 and key11 clobber each other */ + return !strcasecmp(node->key, key); +} + +void gib_hash_set(gib_hash *hash, char *key, void *data) +{ + gib_list *l; + gib_hash_node *n; + + n = GIB_HASH_NODE(gib_list_find(GIB_LIST(hash->base), + gib_hash_find_callback, + key)); + if (n) { + GIB_LIST(n)->data = data; + } else { + /* not using gib_list_add_end here as that would nest the + data one level, doing manual insert instead */ + n = gib_hash_node_new(key,data); + l = gib_list_last(GIB_LIST(hash->base)); + + GIB_LIST(n)->next = NULL; + GIB_LIST(n)->prev = l; + + if (l) + l->next = GIB_LIST(n); + } +} + +void *gib_hash_get(gib_hash *hash, char *key) +{ + gib_list *n = gib_list_find(GIB_LIST(hash->base),gib_hash_find_callback,key); + return n?n->data:NULL; +} + +/* unused + +void gib_hash_remove(gib_hash *hash, char *key) +{ + gib_list *n = gib_list_find(GIB_LIST(hash->base), gib_hash_find_callback, key); + if (n) { + gib_list_unlink(GIB_LIST(hash->base), n); + gib_hash_node_free(GIB_HASH_NODE(n->data)); + } + + return; +} + +void gib_hash_foreach(gib_hash *hash, void (*foreach_cb)(gib_hash_node *node, void *data), void *data) +{ + gib_hash_node *i, *next; + for (i=hash->base; i; i=next) { + next = GIB_HASH_NODE(GIB_LIST(i)->next); + foreach_cb(i,data); + } + return; +} + +*/ diff --git a/src/gib_hash.h b/src/gib_hash.h new file mode 100644 index 0000000..125e280 --- /dev/null +++ b/src/gib_hash.h @@ -0,0 +1,75 @@ + +/* gib_hash.h + +Copyright (C) 1999,2000 Paul Duncan. + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + +#ifndef GIB_HASH_H +#define GIB_HASH_H + +#include "gib_list.h" + +#define GIB_HASH(a) ((gib_hash*)a) +#define GIB_HASH_NODE(a) ((gib_hash_node*)a) + +typedef struct __gib_hash gib_hash; +typedef struct __gib_hash_node gib_hash_node; + +struct __gib_hash +{ + gib_hash_node *base; +}; + +struct __gib_hash_node +{ + gib_list list; + char *key; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +gib_hash_node *gib_hash_node_new(char *key, void *data); +void gib_hash_node_free(gib_hash_node *node); +void gib_hash_node_free_and_data(gib_hash_node *node); + +gib_hash *gib_hash_new(void); +void gib_hash_free(gib_hash *hash); +void gib_hash_free_and_data(gib_hash *hash); + +void gib_hash_set(gib_hash *hash, char *key, void *data); +void *gib_hash_get(gib_hash *hash, char *key); + +/* unused +void gib_hash_remove(gib_hash *hash, char *key); + +void gib_hash_foreach(gib_hash *hash, void (*foreach_cb)(gib_hash_node *node, void *data), void *data); +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* GIB_HASH_H */ diff --git a/src/gib_imlib.c b/src/gib_imlib.c new file mode 100644 index 0000000..39d0081 --- /dev/null +++ b/src/gib_imlib.c @@ -0,0 +1,732 @@ +/* gib_imlib.c + +Copyright (C) 1999,2000 Tom Gilbert. + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + +#include "gib_imlib.h" +#include "utils.h" +#include "debug.h" + +/* +int +gib_imlib_load_image(Imlib_Image * im, char *filename) +{ + Imlib_Load_Error err; + + imlib_context_set_progress_function(NULL); + if (!filename) + return (0); + *im = imlib_load_image_with_error_return(filename, &err); + if ((err) || (!im)) + { + switch (err) + { + case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST: + weprintf("%s - File does not exist", filename); + break; + case IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY: + weprintf("%s - Directory specified for image filename", filename); + break; + case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ: + weprintf("%s - No read access to directory", filename); + break; + case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT: + weprintf("%s - No Imlib2 loader for that file format", filename); + break; + case IMLIB_LOAD_ERROR_PATH_TOO_LONG: + weprintf("%s - Path specified is too long", filename); + break; + case IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT: + weprintf("%s - Path component does not exist", filename); + break; + case IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY: + weprintf("%s - Path component is not a directory", filename); + break; + case IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE: + weprintf("%s - Path points outside address space", filename); + break; + case IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS: + weprintf("%s - Too many levels of symbolic links", filename); + break; + case IMLIB_LOAD_ERROR_OUT_OF_MEMORY: + eprintf("While loading %s - Out of memory", filename); + break; + case IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS: + eprintf("While loading %s - Out of file descriptors", filename); + break; + case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE: + weprintf("%s - Cannot write to directory", filename); + break; + case IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE: + weprintf("%s - Cannot write - out of disk space", filename); + break; + case IMLIB_LOAD_ERROR_UNKNOWN: + default: + weprintf + ("While loading %s - Unknown error. Attempting to continue", + filename); + break; + } + return (0); + } + return (1); +} +*/ + +int +gib_imlib_image_get_width(Imlib_Image im) +{ + imlib_context_set_image(im); + return imlib_image_get_width(); +} + +int +gib_imlib_image_get_height(Imlib_Image im) +{ + imlib_context_set_image(im); + return imlib_image_get_height(); +} + +int +gib_imlib_image_has_alpha(Imlib_Image im) +{ + imlib_context_set_image(im); + return imlib_image_has_alpha(); +} + +void +gib_imlib_free_image_and_decache(Imlib_Image im) +{ + imlib_context_set_image(im); + imlib_free_image_and_decache(); +} + +void +gib_imlib_free_image(Imlib_Image im) +{ + imlib_context_set_image(im); + imlib_free_image(); +} + +const char * +gib_imlib_image_get_filename(Imlib_Image im) +{ + if (im) + { + imlib_context_set_image(im); + return imlib_image_get_filename(); + } + else + return NULL; +} + +void +gib_imlib_render_image_on_drawable(Drawable d, Imlib_Image im, int x, int y, + char dither, char blend, char alias) +{ + imlib_context_set_image(im); + imlib_context_set_drawable(d); + imlib_context_set_anti_alias(alias); + imlib_context_set_dither(dither); + imlib_context_set_blend(blend); + imlib_context_set_angle(0); + imlib_render_image_on_drawable(x, y); +} + +/* +void +gib_imlib_render_image_on_drawable_with_rotation(Drawable d, Imlib_Image im, + int x, int y, double angle, + char dither, char blend, + char alias) +{ + Imlib_Image new_im; + + imlib_context_set_image(im); + imlib_context_set_anti_alias(alias); + imlib_context_set_dither(dither); + imlib_context_set_blend(blend); + imlib_context_set_angle(angle); + imlib_context_set_drawable(d); + new_im = imlib_create_rotated_image(angle); + imlib_context_set_image(new_im); + imlib_render_image_on_drawable(x, y); + imlib_free_image(); +} +*/ + +void +gib_imlib_render_image_on_drawable_at_size(Drawable d, Imlib_Image im, int x, + int y, int w, int h, char dither, + char blend, char alias) +{ + imlib_context_set_image(im); + imlib_context_set_drawable(d); + imlib_context_set_anti_alias(alias); + imlib_context_set_dither(dither); + imlib_context_set_blend(blend); + imlib_context_set_angle(0); + imlib_render_image_on_drawable_at_size(x, y, w, h); +} + + +/* +void +gib_imlib_render_image_on_drawable_at_size_with_rotation(Drawable d, + Imlib_Image im, + int x, int y, int w, + int h, double angle, + char dither, + char blend, + char alias) +{ + Imlib_Image new_im; + + imlib_context_set_image(im); + imlib_context_set_drawable(d); + imlib_context_set_anti_alias(alias); + imlib_context_set_dither(dither); + imlib_context_set_blend(blend); + imlib_context_set_angle(angle); + new_im = imlib_create_rotated_image(angle); + imlib_context_set_image(new_im); + imlib_render_image_on_drawable_at_size(x, y, w, h); + imlib_free_image_and_decache(); +} +*/ + + +void +gib_imlib_render_image_part_on_drawable_at_size(Drawable d, Imlib_Image im, + int sx, int sy, int sw, + int sh, int dx, int dy, + int dw, int dh, char dither, + char blend, char alias) +{ + imlib_context_set_image(im); + imlib_context_set_drawable(d); + imlib_context_set_anti_alias(alias); + imlib_context_set_dither(dither); + imlib_context_set_blend(blend); + imlib_context_set_angle(0); + imlib_render_image_part_on_drawable_at_size(sx, sy, sw, sh, dx, dy, dw, + dh); +} + + +void +gib_imlib_render_image_part_on_drawable_at_size_with_rotation(Drawable d, + Imlib_Image im, + int sx, int sy, + int sw, int sh, + int dx, int dy, + int dw, int dh, + double angle, + char dither, + char blend, + char alias) +{ + Imlib_Image new_im; + + imlib_context_set_image(im); + imlib_context_set_drawable(d); + imlib_context_set_anti_alias(alias); + imlib_context_set_dither(dither); + imlib_context_set_angle(angle); + imlib_context_set_blend(blend); + new_im = imlib_create_rotated_image(angle); + imlib_context_set_image(new_im); + imlib_render_image_part_on_drawable_at_size(sx, sy, sw, sh, dx, dy, dw, + dh); + imlib_free_image_and_decache(); +} + + +void +gib_imlib_image_fill_rectangle(Imlib_Image im, int x, int y, int w, int h, + int r, int g, int b, int a) +{ + imlib_context_set_image(im); + imlib_context_set_color(r, g, b, a); + imlib_image_fill_rectangle(x, y, w, h); +} + +void +gib_imlib_image_fill_polygon(Imlib_Image im, ImlibPolygon poly, int r, int g, + int b, int a, unsigned char alias, int cx, + int cy, int cw, int ch) +{ + imlib_context_set_image(im); + imlib_context_set_color(r, g, b, a); + imlib_context_set_anti_alias(alias); + imlib_context_set_cliprect(cx, cy, cw, ch); + imlib_image_fill_polygon(poly); + imlib_context_set_cliprect(0, 0, 0, 0); +} + +void +gib_imlib_image_draw_polygon(Imlib_Image im, ImlibPolygon poly, int r, int g, + int b, int a, unsigned char closed, + unsigned char alias, int cx, int cy, int cw, + int ch) +{ + imlib_context_set_image(im); + imlib_context_set_color(r, g, b, a); + imlib_context_set_anti_alias(alias); + imlib_context_set_cliprect(cx, cy, cw, ch); + imlib_image_draw_polygon(poly, closed); + imlib_context_set_cliprect(0, 0, 0, 0); +} + + +void +gib_imlib_image_draw_rectangle(Imlib_Image im, int x, int y, int w, int h, + int r, int g, int b, int a) +{ + imlib_context_set_image(im); + imlib_context_set_color(r, g, b, a); + imlib_image_draw_rectangle(x, y, w, h); +} + + +void +gib_imlib_text_draw(Imlib_Image im, Imlib_Font fn, gib_style * s, int x, + int y, char *text, Imlib_Text_Direction dir, int r, int g, + int b, int a) +{ + imlib_context_set_image(im); + imlib_context_set_font(fn); + imlib_context_set_direction(dir); + if (s) + { + int min_x = 0, min_y = 0; + gib_style_bit *bb; + gib_list *l; + + /* here we shift the draw to accommodate bits with negative offsets, + * which would be drawn at negative coords otherwise */ + l = s->bits; + while (l) + { + bb = (gib_style_bit *) l->data; + if (bb) + { + if (bb->x_offset < min_x) + min_x = bb->x_offset; + if (bb->y_offset < min_y) + min_y = bb->y_offset; + } + l = l->next; + } + x -= min_x; + y -= min_y; + + /* Now draw the bits */ + l = s->bits; + while (l) + { + bb = (gib_style_bit *) l->data; + if (bb) + { + if ((bb->r + bb->g + bb->b + bb->a) == 0) + imlib_context_set_color(r, g, b, a); + else + imlib_context_set_color(bb->r, bb->g, bb->b, bb->a); + imlib_text_draw(x + bb->x_offset, y + bb->y_offset, text); + } + l = l->next; + } + } + else + { + imlib_context_set_color(r, g, b, a); + imlib_text_draw(x, y, text); + } +} + +char ** +gib_imlib_list_fonts(int *num) +{ + return imlib_list_fonts(num); +} + + +void +gib_imlib_get_text_size(Imlib_Font fn, char *text, gib_style * s, int *w, + int *h, Imlib_Text_Direction dir) +{ + + imlib_context_set_font(fn); + imlib_context_set_direction(dir); + imlib_get_text_size(text, w, h); + if (s) + { + gib_style_bit *b; + int max_x_off = 0, min_x_off = 0, max_y_off = 0, min_y_off = 0; + gib_list *l; + + l = s->bits; + while (l) + { + b = (gib_style_bit *) l->data; + if (b) + { + if (b->x_offset > max_x_off) + max_x_off = b->x_offset; + else if (b->x_offset < min_x_off) + min_x_off = b->x_offset; + if (b->y_offset > max_y_off) + max_y_off = b->y_offset; + else if (b->y_offset < min_y_off) + min_y_off = b->y_offset; + } + l = l->next; + } + if (h) + { + *h += max_y_off; + *h -= min_y_off; + } + if (w) + { + *w += max_x_off; + *w -= min_x_off; + } + } +} + +Imlib_Image gib_imlib_clone_image(Imlib_Image im) +{ + imlib_context_set_image(im); + return imlib_clone_image(); +} + +char * +gib_imlib_image_format(Imlib_Image im) +{ + imlib_context_set_image(im); + return imlib_image_format(); +} + + +void +gib_imlib_blend_image_onto_image(Imlib_Image dest_image, + Imlib_Image source_image, char merge_alpha, + int sx, int sy, int sw, int sh, int dx, + int dy, int dw, int dh, char dither, + char blend, char alias) +{ + imlib_context_set_image(dest_image); + imlib_context_set_anti_alias(alias); + imlib_context_set_dither(dither); + imlib_context_set_blend(blend); + imlib_context_set_angle(0); + imlib_blend_image_onto_image(source_image, merge_alpha, sx, sy, sw, sh, dx, + dy, dw, dh); +} + + +/* +void +gib_imlib_blend_image_onto_image_with_rotation(Imlib_Image dest_image, + Imlib_Image source_image, + char merge_alpha, int sx, + int sy, int sw, int sh, int dx, + int dy, int dw, int dh, + double angle, char dither, + char blend, char alias) +{ + imlib_context_set_image(dest_image); + imlib_context_set_anti_alias(alias); + imlib_context_set_dither(dither); + imlib_context_set_blend(blend); + imlib_context_set_angle(angle); + imlib_blend_image_onto_image_at_angle(source_image, merge_alpha, sx, sy, + sw, sh, dx, dy, (int) angle, + (int) angle); + return; + dw = 0; + dh = 0; +} +*/ + +Imlib_Image gib_imlib_create_cropped_scaled_image(Imlib_Image im, int sx, + int sy, int sw, int sh, + int dw, int dh, char alias) +{ + imlib_context_set_image(im); + imlib_context_set_anti_alias(alias); + return imlib_create_cropped_scaled_image(sx, sy, sw, sh, dw, dh); +} + +void +gib_imlib_apply_color_modifier_to_rectangle(Imlib_Image im, int x, int y, + int w, int h, DATA8 * rtab, + DATA8 * gtab, DATA8 * btab, + DATA8 * atab) +{ + Imlib_Color_Modifier cm; + + imlib_context_set_image(im); + cm = imlib_create_color_modifier(); + imlib_context_set_color_modifier(cm); + imlib_set_color_modifier_tables(rtab, gtab, btab, atab); + imlib_apply_color_modifier_to_rectangle(x, y, w, h); + imlib_free_color_modifier(); +} + +void +gib_imlib_image_set_has_alpha(Imlib_Image im, int alpha) +{ + imlib_context_set_image(im); + imlib_image_set_has_alpha(alpha); +} + +void +gib_imlib_save_image(Imlib_Image im, char *file) +{ + char *tmp; + + imlib_context_set_image(im); + tmp = strrchr(file, '.'); + if (tmp) + { + char *p, *pp; + p = strdup(tmp + 1); + pp = p; + while(*pp) { + *pp = tolower(*pp); + pp++; + } + imlib_image_set_format(p); + free(p); + } + imlib_save_image(file); +} + +void +gib_imlib_save_image_with_error_return(Imlib_Image im, char *file, + Imlib_Load_Error * error_return) +{ + char *tmp; + + imlib_context_set_image(im); + tmp = strrchr(file, '.'); + if (tmp) { + char *p, *pp; + p = estrdup(tmp + 1); + pp = p; + while(*pp) { + *pp = tolower(*pp); + pp++; + } + imlib_image_set_format(p); + free(p); + } + imlib_save_image_with_error_return(file, error_return); +} + +void +gib_imlib_free_font(Imlib_Font fn) +{ + imlib_context_set_font(fn); + imlib_free_font(); +} + +void +gib_imlib_image_draw_line(Imlib_Image im, int x1, int y1, int x2, int y2, + char make_updates, int r, int g, int b, int a) +{ + imlib_context_set_image(im); + imlib_context_set_color(r, g, b, a); + imlib_image_draw_line(x1, y1, x2, y2, make_updates); +} + +Imlib_Image gib_imlib_create_rotated_image(Imlib_Image im, double angle) +{ + imlib_context_set_image(im); + return (imlib_create_rotated_image(angle)); +} + +void +gib_imlib_image_tile(Imlib_Image im) +{ + imlib_context_set_image(im); + imlib_image_tile(); +} + +void +gib_imlib_image_blur(Imlib_Image im, int radius) +{ + imlib_context_set_image(im); + imlib_image_blur(radius); +} + +void +gib_imlib_image_sharpen(Imlib_Image im, int radius) +{ + imlib_context_set_image(im); + imlib_image_sharpen(radius); +} + +void +gib_imlib_line_clip_and_draw(Imlib_Image dest, int x0, int y0, int x1, int y1, + int cx, int cy, int cw, int ch, int r, int g, + int b, int a) +{ + imlib_context_set_cliprect(cx, cy, cw, ch); + gib_imlib_image_draw_line(dest, x0, y0, x1, y1, 0, r, g, b, a); + imlib_context_set_cliprect(0, 0, 0, 0); +} + +Imlib_Image +gib_imlib_create_image_from_drawable(Drawable d, Pixmap mask, int x, int y, + int width, int height, + char need_to_grab_x) +{ + imlib_context_set_drawable(d); + return imlib_create_image_from_drawable(mask, x, y, width, height, + need_to_grab_x); +} + +void gib_imlib_parse_color(char *col, int *r, int *g, int *b, int *a) + { + gib_list *ll; + unsigned long cc, rr, gg, bb, aa; + int len; + + if (col[0] == '#') + { + /* #RRGGBBAA style */ + /* skip the '#' */ + col++; + len = strlen(col); + if (len == 8) + { + cc = (unsigned long) strtoul(col, NULL, 16); + rr = (cc & 0xff000000) >> 24; + gg = (cc & 0x00ff0000) >> 16; + bb = (cc & 0x0000ff00) >> 8; + aa = (cc & 0x000000ff); + } + else if (len == 6) + { + cc = (unsigned long) strtoul(col, NULL, 16); + rr = (cc & 0xff0000) >> 16; + gg = (cc & 0x00ff00) >> 8; + bb = (cc & 0x0000ff); + aa = 255; + } + else + { + weprintf("unable to parse color %s\n", col); + return; + } + } + else + { + /* r,g,b,a style */ + ll = gib_string_split(col, ","); + if (!ll) + { + weprintf("unable to parse color %s\n", col); + return; + } + len = gib_list_length(ll); + if (len == 3) + { + rr = atoi(ll->data); + gg = atoi(ll->next->data); + bb = atoi(ll->next->next->data); + aa = 255; + } + else if (len == 4) + { + rr = atoi(ll->data); + gg = atoi(ll->next->data); + bb = atoi(ll->next->next->data); + aa = atoi(ll->next->next->next->data); + } + else + { + weprintf("unable to parse color %s\n", col); + return; + } + } + *r = rr; + *g = gg; + *b = bb; + *a = aa; + } + +void +gib_imlib_parse_fontpath(char *path) +{ + gib_list *l, *ll; + + if (!path) + return; + + l = gib_string_split(path, ":"); + if (!l) + return; + ll = l; + while (ll) + { + imlib_add_path_to_font_path((char *) ll->data); + ll = ll->next; + } + gib_list_free_and_data(l); +} + +Imlib_Font +gib_imlib_load_font(char *name) +{ + Imlib_Font fn; + + if ((fn = imlib_load_font(name))) + return fn; + weprintf("couldn't load font %s, attempting to fall back to fixed.", name); + if ((fn = imlib_load_font("fixed"))) + return fn; + weprintf("failed to even load fixed! Attempting to find any font."); + return imlib_load_font("*"); +} + +void gib_imlib_image_orientate(Imlib_Image im, int orientation) +{ + imlib_context_set_image(im); + imlib_image_orientate(orientation); +} + +void gib_imlib_image_flip_horizontal(Imlib_Image im) +{ + imlib_context_set_image(im); + imlib_image_flip_horizontal(); +} + +void gib_imlib_image_flip_vertical(Imlib_Image im) +{ + imlib_context_set_image(im); + imlib_image_flip_vertical(); +} diff --git a/src/gib_imlib.h b/src/gib_imlib.h new file mode 100644 index 0000000..6a35e7e --- /dev/null +++ b/src/gib_imlib.h @@ -0,0 +1,194 @@ +/* gib_imlib.h + +Copyright (C) 1999,2000 Tom Gilbert. + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + +#ifndef GIB_IMLIB_H +#define GIB_IMLIB_H + +#include <stdio.h> +#include <stdlib.h> +#include <X11/Xlib.h> +#include <Imlib2.h> +#include <stdarg.h> +#include <ctype.h> +#include "gib_style.h" + +#define GIBCLIP(x, y, w, h, xx, yy, ww, hh) \ +{ \ + if ((yy) > y) { h -= (yy) - y; y = (yy); } \ + if ((yy) + hh < y + h) { h -= y + h - ((yy) + (hh)); } \ + if ((xx) > x) { w -= (xx) - x; x = (xx); } \ + if ((xx) + (ww) < x + w) { w -= x + w - ((xx) + (ww)); } \ +} + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* +int gib_imlib_load_image(Imlib_Image * im, char *filename); +*/ +int gib_imlib_image_get_width(Imlib_Image im); +int gib_imlib_image_get_height(Imlib_Image im); +int gib_imlib_image_has_alpha(Imlib_Image im); +const char *gib_imlib_image_get_filename(Imlib_Image im); +void gib_imlib_free_image_and_decache(Imlib_Image im); +void gib_imlib_render_image_on_drawable(Drawable d, Imlib_Image im, int x, + int y, char dither, char blend, + + char alias); +/* +void gib_imlib_render_image_on_drawable_with_rotation(Drawable d, + Imlib_Image im, int x, + int y, double angle, + + char dither, char blend, + char alias); +*/ +void gib_imlib_render_image_part_on_drawable_at_size(Drawable d, + Imlib_Image im, int sx, + int sy, int sw, int sh, + int dx, int dy, int dw, + int dh, char dither, + + char blend, char alias); + +void gib_imlib_render_image_part_on_drawable_at_size_with_rotation(Drawable d, + Imlib_Image + + im, int sx, + int sy, + int sw, + int sh, + int dx, + int dy, + int dw, + int dh, + double + angle, + char + dither, + char blend, + char + alias); + +void gib_imlib_image_fill_rectangle(Imlib_Image im, int x, int y, int w, + int h, int r, int g, int b, int a); +void gib_imlib_text_draw(Imlib_Image im, Imlib_Font fn, gib_style * s, int x, + int y, char *text, Imlib_Text_Direction dir, int r, + int g, int b, int a); +void gib_imlib_get_text_size(Imlib_Font fn, char *text, gib_style * s, int *w, + int *h, Imlib_Text_Direction dir); +Imlib_Image gib_imlib_clone_image(Imlib_Image im); +char *gib_imlib_image_format(Imlib_Image im); +char **gib_imlib_list_fonts(int *num); +void gib_imlib_render_image_on_drawable_at_size(Drawable d, Imlib_Image im, + int x, int y, int w, int h, + char dither, char blend, + + char alias); +/* +void gib_imlib_render_image_on_drawable_at_size_with_rotation(Drawable d, + Imlib_Image im, + int x, int y, + + int w, int h, + double angle, + char dither, + char blend, + char alias); +*/ + +void gib_imlib_blend_image_onto_image(Imlib_Image dest_image, + Imlib_Image source_image, + char merge_alpha, int sx, int sy, + int sw, int sh, int dx, int dy, int dw, + int dh, char dither, char blend, + + char alias); + +/* +void gib_imlib_blend_image_onto_image_with_rotation(Imlib_Image dest_image, + Imlib_Image source_image, + char merge_alpha, int sx, + int sy, int sw, int sh, + int dx, int dy, int dw, + int dh, double angle, + + char dither, char blend, + char alias); +*/ +Imlib_Image gib_imlib_create_cropped_scaled_image(Imlib_Image im, int sx, + int sy, int sw, int sh, + int dw, int dh, char alias); +void gib_imlib_apply_color_modifier_to_rectangle(Imlib_Image im, int x, int y, + int w, int h, DATA8 * rtab, + DATA8 * gtab, DATA8 * btab, + DATA8 * atab); +void gib_imlib_image_set_has_alpha(Imlib_Image im, int alpha); +void gib_imlib_save_image(Imlib_Image im, char *file); +void gib_imlib_save_image_with_error_return(Imlib_Image im, char *file, + Imlib_Load_Error * error_return); +void gib_imlib_free_font(Imlib_Font fn); +void gib_imlib_free_image(Imlib_Image im); +void gib_imlib_image_draw_line(Imlib_Image im, int x1, int y1, int x2, int y2, + char make_updates, int r, int g, int b, int a); +void gib_imlib_image_set_has_alpha(Imlib_Image im, int alpha); +void gib_imlib_free_font(Imlib_Font fn); +Imlib_Image gib_imlib_create_rotated_image(Imlib_Image im, double angle); +void gib_imlib_image_tile(Imlib_Image im); +void gib_imlib_image_blur(Imlib_Image im, int radius); +void gib_imlib_image_sharpen(Imlib_Image im, int radius); +void gib_imlib_image_draw_rectangle(Imlib_Image im, int x, int y, int w, + int h, int r, int g, int b, int a); +void gib_imlib_line_clip_and_draw(Imlib_Image dest, int x0, int y0, int x1, + int y1, int cx, int cy, int cw, int ch, + int r, int g, int b, int a); +void gib_imlib_image_fill_polygon(Imlib_Image im, ImlibPolygon poly, int r, + int g, int b, int a, unsigned char alias, + int cx, int cy, int cw, int ch); +void gib_imlib_image_draw_polygon(Imlib_Image im, ImlibPolygon poly, int r, + int g, int b, int a, unsigned char closed, + unsigned char alias, int cx, int cy, int cw, + + int ch); +Imlib_Image gib_imlib_create_image_from_drawable(Drawable d, Pixmap mask, + int x, int y, int width, + + int height, + char need_to_grab_x); +void gib_imlib_parse_color(char *col, int *r, int *g, int *b, int *a); +void gib_imlib_parse_fontpath(char *path); +Imlib_Font gib_imlib_load_font(char *name); +void gib_imlib_image_orientate(Imlib_Image im, int orientation); +void gib_imlib_image_flip_horizontal(Imlib_Image im); +void gib_imlib_image_flip_vertical(Imlib_Image im); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/gib_list.c b/src/gib_list.c new file mode 100644 index 0000000..e7710bc --- /dev/null +++ b/src/gib_list.c @@ -0,0 +1,579 @@ +/* gib_list.c + +Copyright (C) 1999,2000 Tom Gilbert. + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + +#include <time.h> +#include "gib_list.h" +#include "utils.h" +#include "debug.h" + +gib_list * +gib_list_new(void) +{ + gib_list *l; + + l = (gib_list *) emalloc(sizeof(gib_list)); + l->data = NULL; + l->next = NULL; + l->prev = NULL; + return (l); +} + +void +gib_list_free(gib_list * l) +{ + gib_list *ll; + + if (!l) + return; + + while (l) + { + ll = l; + l = l->next; + free(ll); + } + + return; +} + +void +gib_list_free_and_data(gib_list * l) +{ + gib_list *ll; + + if (!l) + return; + + while (l) + { + ll = l; + l = l->next; + free(ll->data); + free(ll); + } + return; +} + +#if 0 +gib_list * +gib_list_dup(gib_list * list) +{ + gib_list *ret = NULL; + + if (list) + { + gib_list *last; + + ret = gib_list_new(); + ret->data = list->data; + last = ret; + list = list->next; + while (list) + { + last->next = gib_list_new(); + last->next->prev = last; + last = last->next; + last->data = list->data; + list = list->next; + } + } + return (ret); +} + +gib_list * +gib_list_dup_special(gib_list * list, + void (*cpy_func) (void **dest, void *data)) +{ + gib_list *ret = NULL; + + if (list) + { + gib_list *last; + + ret = gib_list_new(); + cpy_func(&(ret->data), list->data); + last = ret; + list = list->next; + while (list) + { + last->next = gib_list_new(); + last->next->prev = last; + last = last->next; + cpy_func(&(last->data), list->data); + list = list->next; + } + } + return (ret); +} +#endif + +gib_list * +gib_list_add_front(gib_list * root, void *data) +{ + gib_list *l; + + l = gib_list_new(); + l->next = root; + l->data = data; + if (root) + root->prev = l; + return (l); +} + +gib_list * +gib_list_add_end(gib_list * root, void *data) +{ + gib_list *l, *last; + + last = gib_list_last(root); + l = gib_list_new(); + l->prev = last; + l->data = data; + if (last) + { + last->next = l; + return (root); + } + else + { + return (l); + } +} + +#if 0 +gib_list * +gib_list_add_at_pos(gib_list * root, int pos, void *data) +{ + gib_list *l, *top; + + if (pos == gib_list_length(root)) + root = gib_list_add_end(root, data); + else if (pos == 0) + root = gib_list_add_front(root, data); + else + { + top = gib_list_nth(root, pos); + + if (!top) + return (root); + + l = gib_list_new(); + l->next = top; + l->prev = top->prev; + l->data = data; + if (top->prev) + top->prev->next = l; + + top->prev = l; + } + return (root); +} + +gib_list * +gib_list_move_up_by_one(gib_list * root, gib_list * l) +{ + if (l || l->prev) + root = gib_list_move_down_by_one(root, l->prev); + return (root); +} + +gib_list * +gib_list_move_down_by_one(gib_list * root, gib_list * l) +{ + gib_list *temp; + + if (!l || !l->next) + return (root); + + /* store item we link next to */ + temp = l->next; + /* remove from list */ + root = gib_list_unlink(root, l); + /* add back one before */ + l->next = temp->next; + l->prev = temp; + if (temp->next) + temp->next->prev = l; + temp->next = l; + + return (root); +} +#endif + + +unsigned char +gib_list_has_more_than_one_item(gib_list * root) +{ + if (root->next) + return (1); + else + return (0); +} + +#if 0 +gib_list * +gib_list_pop_to_end(gib_list * root, gib_list * l) +{ + root = gib_list_unlink(root, l); + root = gib_list_add_end(root, l->data); + free(l); + + return (root); +} +#endif + +gib_list * +gib_list_cat(gib_list * root, gib_list * l) +{ + gib_list *last; + + if (!l) + return (root); + if (!root) + return (l); + last = gib_list_last(root); + last->next = l; + l->prev = last; + return (root); +} + +int +gib_list_length(gib_list * l) +{ + int length; + + length = 0; + while (l) + { + length++; + l = l->next; + } + return (length); +} + +gib_list * +gib_list_last(gib_list * l) +{ + if (l) + { + while (l->next) + l = l->next; + } + return (l); +} + +gib_list * +gib_list_first(gib_list * l) +{ + if (l) + { + while (l->prev) + l = l->prev; + } + return (l); +} + +gib_list * +gib_list_jump(gib_list * root, gib_list * l, int direction, int num) +{ + int i; + gib_list *ret = NULL; + + if (!root) + return (NULL); + if (!l) + return (root); + + ret = l; + + for (i = 0; i < num; i++) + { + if (direction == FORWARD) + { + if (ret->next) + ret = ret->next; + else + ret = root; + } + else + { + if (ret->prev) + ret = ret->prev; + else + ret = gib_list_last(ret); + } + } + return (ret); +} + +gib_list * +gib_list_reverse(gib_list * l) +{ + gib_list *last; + + last = NULL; + while (l) + { + last = l; + l = last->next; + last->next = last->prev; + last->prev = l; + } + return (last); +} + +gib_list * +gib_list_randomize(gib_list * list) +{ + int len, r, i; + gib_list **farray, *f, *t; + + if (!list) + return (NULL); + len = gib_list_length(list); + if (len <= 1) + return (list); + farray = (gib_list **) emalloc(sizeof(gib_list *) * len); + for (f = list, i = 0; f; f = f->next, i++) + { + farray[i] = f; + } + for (i = 0; i < len - 1; i++) + { + r = i + random() / (RAND_MAX / (len - i) + 1 ); + t = farray[r]; + farray[r] = farray[i]; + farray[i] = t; + } + list = farray[0]; + list->prev = NULL; + list->next = farray[1]; + for (i = 1, f = farray[1]; i < len - 1; i++, f = f->next) + { + f->prev = farray[i - 1]; + f->next = farray[i + 1]; + } + f->prev = farray[len - 2]; + f->next = NULL; + free(farray); + return (list); +} + +int +gib_list_num(gib_list * root, gib_list * l) +{ + int i = 0; + + while (root) + { + if (root == l) + return (i); + i++; + root = root->next; + } + return (-1); +} + +gib_list * +gib_list_unlink(gib_list * root, gib_list * l) +{ + if (!l) + return (root); + + if ((!root) || ((l == root) && (!l->next))) + return (NULL); + + if (l->prev) + l->prev->next = l->next; + if (l->next) + l->next->prev = l->prev; + if (root == l) + root = root->next; + return (root); +} + + +gib_list * +gib_list_remove(gib_list * root, gib_list * l) +{ + root = gib_list_unlink(root, l); + free(l); + return (root); +} + +gib_list * +gib_list_sort(gib_list * list, gib_compare_fn cmp) +{ + gib_list *l1, *l2; + + if (!list) + return (NULL); + if (!list->next) + return (list); + + l1 = list; + l2 = list->next; + + while ((l2 = l2->next) != NULL) + { + if ((l2 = l2->next) == NULL) + break; + l1 = l1->next; + } + l2 = l1->next; + l1->next = NULL; + + return (gib_list_sort_merge + (gib_list_sort(list, cmp), gib_list_sort(l2, cmp), cmp)); +} + +gib_list * +gib_list_sort_merge(gib_list * l1, gib_list * l2, gib_compare_fn cmp) +{ + gib_list list, *l, *lprev; + + l = &list; + lprev = NULL; + + while (l1 && l2) + { + if (cmp(l1->data, l2->data) < 0) + { + l->next = l1; + l = l->next; + l->prev = lprev; + lprev = l; + l1 = l1->next; + } + else + { + l->next = l2; + l = l->next; + l->prev = lprev; + lprev = l; + l2 = l2->next; + } + } + l->next = l1 ? l1 : l2; + l->next->prev = l; + + return (list.next); +} + +#if 0 +gib_list * +gib_list_nth(gib_list * root, unsigned int num) +{ + unsigned int i; + gib_list *l; + + if (num > (unsigned int) gib_list_length(root)) + return (gib_list_last(root)); + l = root; + for (i = 0; l; ++i) + { + if (i == num) + return (l); + l = l->next; + } + return (root); +} +#endif + +gib_list * +gib_list_foreach(gib_list *root, void (*fe_func)(gib_list *node, void *data), void *data) +{ + gib_list *i, *next = NULL; + for (i=root; i; i=next) { + next=i->next; + fe_func(i, data); + } + return root; +} + +gib_list * +gib_list_find(gib_list *root, unsigned char (*find_func)(gib_list *node, void *data), void *data) +{ + gib_list *i = NULL; + for (i=root; i; i=i->next) + if (find_func(i,data)) + return i; + + return NULL; +} + +static unsigned char gib_list_find_by_data_callback(gib_list *list, void *data) +{ + return (list->data==data); +} + +gib_list * +gib_list_find_by_data(gib_list *root, void *data) +{ + return gib_list_find(root, gib_list_find_by_data_callback, data); +} + +gib_list * +gib_string_split(const char *string, const char *delimiter) +{ + gib_list *string_list = NULL; + char *s; + unsigned int n = 1; + + if (!string || !delimiter) + return NULL; + + s = strstr(string, delimiter); + if (s) + { + unsigned int delimiter_len = strlen(delimiter); + + do + { + unsigned int len; + char *new_string; + + len = s - string; + new_string = emalloc(sizeof(char) * (len + 1)); + + strncpy(new_string, string, len); + new_string[len] = 0; + string_list = gib_list_add_front(string_list, new_string); + n++; + string = s + delimiter_len; + s = strstr(string, delimiter); + } + while (s); + } + if (*string) + { + n++; + string_list = gib_list_add_front(string_list, strdup((char *)string)); + } + + string_list = gib_list_reverse(string_list); + + return string_list; +} diff --git a/src/gib_list.h b/src/gib_list.h new file mode 100644 index 0000000..94c4c39 --- /dev/null +++ b/src/gib_list.h @@ -0,0 +1,105 @@ +/* gib_list.h + +Copyright (C) 1999,2000 Tom Gilbert. + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + +#ifndef GIB_LIST_H +#define GIB_LIST_H + +#include <stdarg.h> + +#define GIB_LIST(a) ((gib_list*)a) + +enum __direction +{ FORWARD, BACK }; + +typedef struct __gib_list gib_list; + +struct __gib_list +{ + void *data; + + gib_list *next; + gib_list *prev; +}; + +typedef int (gib_compare_fn) (void *data1, void *data2); + +#ifdef __cplusplus +extern "C" +{ +#endif + +gib_list *gib_list_new(void); +void gib_list_free(gib_list * l); +gib_list *gib_list_add_front(gib_list * root, void *data); +gib_list *gib_list_add_end(gib_list * root, void *data); +/* +gib_list *gib_list_add_at_pos(gib_list * root, int pos, void *data); +gib_list *gib_list_pop_to_end(gib_list * root, gib_list * l); +*/ +gib_list *gib_list_unlink(gib_list * root, gib_list * l); +gib_list *gib_list_cat(gib_list * root, gib_list * l); +int gib_list_length(gib_list * l); +gib_list *gib_list_last(gib_list * l); +gib_list *gib_list_first(gib_list * l); +gib_list *gib_list_jump(gib_list * root, gib_list * l, int direction, + + int num); +gib_list *gib_list_reverse(gib_list * l); +gib_list *gib_list_randomize(gib_list * list); +int gib_list_num(gib_list * root, gib_list * l); +gib_list *gib_list_remove(gib_list * root, gib_list * l); +gib_list *gib_list_sort(gib_list * list, gib_compare_fn cmp); +gib_list *gib_list_sort_merge(gib_list * l1, gib_list * l2, + + gib_compare_fn cmp); +/* +gib_list *gib_list_nth(gib_list * root, unsigned int num); +*/ +unsigned char gib_list_has_more_than_one_item(gib_list * root); +void gib_list_free_and_data(gib_list * l); +/* +gib_list *gib_list_dup(gib_list * list); +gib_list *gib_list_dup_special(gib_list * list, + void (*cpy_func) (void **dest, void *data)); +gib_list *gib_list_move_down_by_one(gib_list * root, gib_list * l); +gib_list *gib_list_move_up_by_one(gib_list * root, gib_list * l); +*/ + +gib_list *gib_list_foreach(gib_list *root, void (*fe_func)(gib_list *node, void *data), void *data); +gib_list *gib_list_find(gib_list *root, unsigned char (*find_func)(gib_list *node, void *data), void *data); +gib_list *gib_list_find_by_data(gib_list *root, void *data); + +/* don't really belong here, will do for now */ +gib_list *gib_string_split(const char *string, const char *delimiter); +/* +char *gib_strjoin(const char *separator, ...); +*/ + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/gib_style.c b/src/gib_style.c new file mode 100644 index 0000000..9280217 --- /dev/null +++ b/src/gib_style.c @@ -0,0 +1,114 @@ +/* gib_style.c + +Copyright (C) 1999,2000 Tom Gilbert. + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + +#include "gib_style.h" +#include "utils.h" +#include "debug.h" + +gib_style * +gib_style_new(char *name) +{ + gib_style *s = NULL; + + s = emalloc(sizeof(gib_style)); + + memset(s, 0, sizeof(gib_style)); + if (name) + s->name = strdup(name); + + return (s); +} + +void +gib_style_free(gib_style * s) +{ + if (s) + { + if (s->name) + free(s->name); + if (s->bits) + { + gib_list *l; + + l = s->bits; + while (l) + { + gib_style_bit_free((gib_style_bit *) l->data); + l = l->next; + } + gib_list_free(s->bits); + } + free(s); + } + return; +} + +gib_style_bit * +gib_style_bit_new(int x_offset, int y_offset, int r, int g, int b, int a) +{ + gib_style_bit *sb; + + sb = emalloc(sizeof(gib_style_bit)); + memset(sb, 0, sizeof(gib_style_bit)); + + sb->x_offset = x_offset; + sb->y_offset = y_offset; + sb->r = r; + sb->g = g; + sb->b = b; + sb->a = a; + + return (sb); +} + +void +gib_style_bit_free(gib_style_bit * s) +{ + if (s) + free(s); + return; +} + +#if 0 +gib_style * +gib_style_dup(gib_style * s) +{ + gib_style *ret; + + ret = gib_style_new(s->name); + ret->bits = gib_list_dup_special(s->bits, gib_dup_style_bit); + + return (ret); +} + +void +gib_dup_style_bit(void **dest, void *data) +{ + *dest = malloc(sizeof(gib_style_bit)); + memcpy(*dest, data, sizeof(gib_style_bit)); + + return; +} +#endif diff --git a/src/gib_style.h b/src/gib_style.h new file mode 100644 index 0000000..d54e2d4 --- /dev/null +++ b/src/gib_style.h @@ -0,0 +1,69 @@ +/* gib_style.h + +Copyright (C) 1999,2000 Tom Gilbert. + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + + +#ifndef GIB_STYLE_H +#define GIB_STYLE_H + +#include "gib_list.h" + +#define GIB_STYLE(O) ((gib_style *)O) +#define GIB_STYLE_BIT(O) ((gib_style_bit *)O) + +typedef struct __gib_style_bit gib_style_bit; +typedef struct __gib_style gib_style; + +struct __gib_style_bit +{ + int x_offset, y_offset; + int r,g,b,a; +}; + +struct __gib_style +{ + gib_list *bits; + char *name; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +gib_style_bit *gib_style_bit_new(int x_offset, int y_offset, int r, int g, int b, int a); +gib_style *gib_style_new(char *name); +void gib_style_bit_free(gib_style_bit *s); +void gib_style_free(gib_style *s); +/* +gib_style *gib_style_dup(gib_style *s); +void gib_dup_style_bit(void **dest, void *data); +*/ + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/help.raw b/src/help.raw new file mode 100644 index 0000000..0e99c68 --- /dev/null +++ b/src/help.raw @@ -0,0 +1,211 @@ +" PACKAGE " version " VERSION " + +Usage : " PACKAGE " [options] <files or directories ...> + + This is just a short option summary. Please see \"man " PACKAGE "\" for details. + +OPTIONS + -h, --help Show help and exit + -v, --version Show version information and exit + -V, --verbose Show progress bars and other extra information + -q, --quiet Hide non-fatal errors. May be used with --verbose + -T, --theme THEME Load options with name THEME + -r, --recursive Recursively expand any directories in FILE to + the content of those directories + --no-recursive Do not recursively expand directories + (this is the default) + -z, --randomize Randomize the filelist + --no-jump-on-resort Don't jump to the first image when the filelist + is resorted + -g, --geometry WxH[+X+Y] Limit the window size to DIMENSION[+OFFSET] + -f, --filelist FILE Load/save images from/to the FILE filelist + -|, --start-at FILENAME Start at FILENAME in the filelist + -p, --preload Remove unloadable files from the internal filelist + before attempting to display anything + -., --scale-down Automatically scale down images to fit screen size + -F, --fullscreen Make the window full screen + -Z, --auto-zoom Zoom picture to screen size in fullscreen/geom mode + --zoom PERCENT Zooms images by a PERCENT, when in full screen + mode or when window geometry is fixed. If combined + with --auto-zoom, zooming will be limited to the + the size. Also support \"max\" and \"fill\" + --zoom-step PERCENT Zoom images in and out by PERCENT (default: 25) + when using the zoom keys / buttons + --keep-zoom-vp Keep viewport zoom and settings while changing images + -w, --multiwindow Open all files at once, one window per image + -x, --borderless Create borderless windows + -d, --draw-filename Show the filename in the image window + --draw-tinted Show overlay texts on semi-transparent background + --draw-exif Show some Exif information (if compiled with exif=1) + --edit Make flip/rotation keys flip/rotate the underlying file + --auto-rotate Rotate images according to Exif info (if compiled with exif=1) + -^, --title TITLE Set window title (see FORMAT SPECIFIERS) + -D, --slideshow-delay NUM Set delay between automatically changing slides + --on-last-slide quit Exit after one loop through the slide show (old --cycle-once) + --on-last-slide hold Stop at both ends of the filelist + -R, --reload NUM Reload images after NUM seconds + -k, --keep-http Keep local copies when viewing HTTP/FTP files + --insecure Disable peer/host verification when using HTTPS. + -K, --caption-path PATH Path to caption directory, enables caption display + -j, --output-dir With -k: Output directory for saved files + -l, --list list mode: ls-style output with image information + -L, --customlist FORMAT list mode with custom output, see FORMAT SPECIFIERS + -U, --loadable List all loadable files. No image display + -u, --unloadable List all unloadable files. No image display + -S, --sort SORT_TYPE Sort files by: + name, filename, mtime, width, height, pixels, size, + or format + -n, --reverse Reverse sort order + --version-sort Natural sort of (version) numbers within text + -A, --action [;]ACTION Specify action to perform when pressing <return>. + Executed by /bin/sh, may contain FORMAT SPECIFIERS + reloads image with \";\", switches to next otherwise + --action[1-9] Extra actions triggered by pressing keys <1>to <9> + -G, --draw-actions Show the defined actions in the image window + --tap-zones Enable tap zones for previous/next file in slide show mode + --force-aliasing Disable antialiasing + -m, --montage Enable montage mode + -i, --index Create an index print of all images + --info CMD Run CMD and show its output in the image window + -t, --thumbnails Show images as clickable thumbnails + -P, --cache-thumbnails Enable thumbnail caching for thumbnail mode. + Only works with thumbnails <= 256x256 pixels + -J, --thumb-redraw N Redraw thumbnail window every N images + -~, --thumb-title STRING Title for windows opened from thumbnail mode + -I, --fullindex Index mode with additional image information + --index-info FORMAT Show FORMAT below images in index/thumbnail mode + --bg-center FILE Set FILE as centered desktop background + --bg-fill FILE Like --bg-scale, but preserves aspect ratio by + zooming the image until it fits. May cut off + corners + --bg-max FILE Like --bg-fill, but scale the image to the maximum + size that fits the screen with black borders on one + side + --bg-scale FILE Set FILE as scaled desktop background. This will + fill the whole background, but the images' aspect + ratio may not be preserved + --bg-tile FILE Set FILE as tiled desktop background + --no-fehbg Do not write a ~/.fehbg file + -C, --fontpath PATH Specify an extra directory to look in for fonts, + can be used multiple times to add multiple paths. + -M, --menu-font FONT Use FONT for the font in menus. + -B, --image-bg STYLE Set background for transparent images and the like. + Accepted values: default, checks, or a XColor (eg. #428bdd) + --xinerama-index I Assumee that I is the active xinerama screen + -N, --no-menus Don't load or show any menus. + --no-xinerama Disable Xinerama support + --no-screen-clip Do not limit window size to screen size + -Y, --hide-pointer Hide the pointer + --conversion-timeout INT Load unknown files with dcraw or ImageMagick, + timeout after INT seconds (0: no timeout) + --min-dimension WxH Only show images with width >= W and height >= H + --max-dimension WxH Only show images with width <= W and height <= H + --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 ID Draw to an existing X11 window by its ID + +MONTAGE MODE OPTIONS + -X, --ignore-aspect Set thumbnail to specified width/height without + retaining aspect ratio + -s, --stretch Scale up images if they are smaller than the + specified thumbnail size + -y, --thumb-width NUM Set thumbnail width in pixels + -E, --thumb-height NUM Set thumbnail height in pixels + -W, --limit-width NUM Limit the width of the montage in pixels + -H, --limit-height NUM Limit the height of the montage in pixels + (at least one of these two must be specified) + -b, --bg FILE|trans Set montage background + -a, --alpha NUM Set thumbnail transparency level (0 .. 255) + -o, --output FILE Save the created montage to FILE + -O, --output-only FILE Just save the created montage to FILE + WITHOUT displaying it + -e, --font FONT Set font for thumbnail information, in the form + fontname/pointsize + +INDEX MODE OPTIONS + -@, --title-font FONT Use FONT to print a title on the index, if no + 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 + %h image height + %l total number of files in the filelist + %L path to temporary copy of filelist + %m current mode (slideshow, multiwindow...) + %n image name + %N image name (shell-escaped) + %o offset of top-left image corner to window (\"x,y\") in pixels + %p image pixel size + %P image pixel size in kilo-/megapixels + %r image rotation. half right turn == 3.1415 (pi) + %s image size in bytes + %S image size with appropriate unit (kB/MB) + %t image format + %u current file number + %w image width + %v " PACKAGE " version + %V process ID + %z current image zoom, rounded to two decimal places + %Z current image zoom, high precision + %% % + \\n newline + +DEFAULT KEYS + a Toggle action display (--draw-actions) + A Toggle anti-aliasing + c Enable caption entry mode + d Toggle filename display (--draw-filename) + e Toggle exif tag display (if compiled with exif=1) + f Toggle fullscreen + g Toggle fixed geometry mode + h pause/continue slideshow + i Toggle --info display + k Toggle zoom/viewport freeze when switching images + L Save current filelist to unique filename + m Show/hide menu + n, <SPACE>, <RIGHT> Go to next image + o Toggle pointer visibility + p, <BACKSPACE>, <LEFT> Go to previous image + q, <ESCAPE> Quit + r Reload image + R Render/anti-alias image + s Save current image to unique filename + w Resize window to current image dimensions + x Close current window + z Jump to a random position in the current filelist + Z Toggle auto-zoom + [, ] Jump to previous/next directory + <, > Rotate 90 degrees right/left + _ Vertical flip + | Horizontal flip + 0, <ENTER> Run action specified by --action option + 1-9 Run action 1-9 specified by --action[1-9] options + <HOME> Go to first slide + <END> Go to last slide + <PAGEUP> Go forward 5% of the filelist + <PAGEDOWN> Go backward 5% of the filelist + + Increase reload delay by 1 second + - Decrease reload delay by 1 second + <DELETE> Remove the currently viewed file from the filelist + <CTRL+DELETE> Like <DELETE>, but also removes the file from the + filesystem. Caution: Does not ask for confirmation + <KEYPAD LEFT> Move the image to the left + <KEYPAD RIGHT> Move the image to the right + <KEYPAD UP> Move the image up + <KEYPAD DOWN> Move the image down + <KEYPAD BEGIN> Antialias the image + <KEYPAD +>, <UP> Zoom in + <KEYPAD ->, <DOWN> Zoom out + <KEYPAD *> Zoom to 100% + <KEYPAD /> Zoom to fit the window + +This program is free software, see the file COPYING for licensing info. +Copyright Tom Gilbert (and various contributors) 1999-2003. +Copyright Birte Kristina Friesel (and various contributors) 2010-2020. + +Homepage: http://feh.finalrewind.org +Report bugs to <derf+feh@finalrewind.org> or #feh on irc.oftc.net. diff --git a/src/imlib.c b/src/imlib.c index 6c40245..d2352fd 100644 --- a/src/imlib.c +++ b/src/imlib.c @@ -1,6 +1,7 @@ /* imlib.c Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2024 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -25,6 +26,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "feh.h" #include "filelist.h" +#include "signals.h" #include "winwidget.h" #include "options.h" @@ -34,6 +36,20 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include <arpa/inet.h> #include <netdb.h> +#ifdef HAVE_LIBCURL +#include <curl/curl.h> +#endif + +#ifdef HAVE_LIBEXIF +#include "exif.h" +#endif + +#ifdef HAVE_LIBMAGIC +#include <magic.h> + +magic_t magic = NULL; +#endif + Display *disp = NULL; Visual *vis = NULL; Screen *scr = NULL; @@ -50,34 +66,60 @@ int xinerama_screen; int num_xinerama_screens; #endif /* HAVE_LIBXINERAMA */ +gib_hash* conversion_cache = NULL; + +int childpid = 0; + +static int feh_file_is_raw(char *filename); +static char *feh_http_load_image(char *url); +static char *feh_dcraw_load_image(char *filename); +static char *feh_magick_load_image(char *filename); + #ifdef HAVE_LIBXINERAMA void init_xinerama(void) { if (opt.xinerama && XineramaIsActive(disp)) { - int major, minor; - xinerama_screen = 0; + int major, minor, px, py, i; + + /* discarded */ + Window dw; + int di; + unsigned int du; + XineramaQueryVersion(disp, &major, &minor); xinerama_screens = XineramaQueryScreens(disp, &num_xinerama_screens); + + if (opt.xinerama_index >= 0) + xinerama_screen = opt.xinerama_index; + else { + xinerama_screen = 0; + XQueryPointer(disp, root, &dw, &dw, &px, &py, &di, &di, &du); + for (i = 0; i < num_xinerama_screens; i++) { + if (XY_IN_RECT(px, py, + xinerama_screens[i].x_org, + xinerama_screens[i].y_org, + xinerama_screens[i].width, + xinerama_screens[i].height)) { + xinerama_screen = i; + break; + } + } + } } } #endif /* HAVE_LIBXINERAMA */ void init_imlib_fonts(void) { - D_ENTER(4); - /* Set up the font stuff */ imlib_add_path_to_font_path("."); imlib_add_path_to_font_path(PREFIX "/share/feh/fonts"); - imlib_add_path_to_font_path("./ttfonts"); - D_RETURN_(4); + return; } void init_x_and_imlib(void) { - D_ENTER(4); - disp = XOpenDisplay(NULL); if (!disp) eprintf("Can't open X display. It *is* running, yeah?"); @@ -100,10 +142,21 @@ void init_x_and_imlib(void) imlib_context_set_operation(IMLIB_OP_COPY); wmDeleteWindow = XInternAtom(disp, "WM_DELETE_WINDOW", False); - /* Initialise random numbers */ - srand(getpid() * time(NULL) % ((unsigned int) -1)); + imlib_set_cache_size(opt.cache_size * 1024 * 1024); - D_RETURN_(4); + return; +} + +int feh_should_ignore_image(Imlib_Image * im) +{ + if (opt.filter_by_dimensions) { + unsigned int w = gib_imlib_image_get_width(im); + unsigned int h = gib_imlib_image_get_height(im); + if (w < opt.min_width || w > opt.max_width || h < opt.min_height || h > opt.max_height) { + return 1; + } + } + return 0; } int feh_load_image_char(Imlib_Image * im, char *filename) @@ -111,470 +164,858 @@ int feh_load_image_char(Imlib_Image * im, char *filename) feh_file *file; int i; - D_ENTER(4); file = feh_file_new(filename); i = feh_load_image(im, file); feh_file_free(file); - D_RETURN(4, i); + return(i); } -int feh_load_image(Imlib_Image * im, feh_file * file) +void feh_print_load_error(char *file, winwidget w, Imlib_Load_Error err, enum feh_load_error feh_err) { - Imlib_Load_Error err; - - D_ENTER(4); - D(3, ("filename is %s, image is %p\n", file->filename, im)); - - if (!file || !file->filename) - D_RETURN(4, 0); - - /* Handle URLs */ - if ((!strncmp(file->filename, "http://", 7)) || (!strncmp(file->filename, "https://", 8)) - || (!strncmp(file->filename, "ftp://", 6))) { - char *tmpname = NULL; - char *tempcpy; - - tmpname = feh_http_load_image(file->filename); - if (tmpname == NULL) - D_RETURN(4, 0); - *im = imlib_load_image_with_error_return(tmpname, &err); - if (im) { - /* load the info now, in case it's needed after we delete the - temporary image file */ - tempcpy = file->filename; - file->filename = tmpname; - feh_file_info_load(file, *im); - file->filename = tempcpy; - } - if ((opt.slideshow) && (opt.reload == 0)) { - /* Http, no reload, slideshow. Let's keep this image on hand... */ - free(file->filename); - file->filename = estrdup(tmpname); - } else { - /* Don't cache the image if we're doing reload + http (webcams etc) */ - if (!opt.keep_http) - unlink(tmpname); + 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 (!opt.keep_http) - add_file_to_rm_filelist(tmpname); - free(tmpname); - } else { - *im = imlib_load_image_with_error_return(file->filename, &err); - } - - if ((err) || (!im)) { - if (opt.verbose && !opt.quiet) { - fprintf(stdout, "\n"); - reset_output = 1; + if (feh_err != LOAD_ERROR_IMLIB) { + return; } - /* Check error code */ switch (err) { case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST: - if (!opt.quiet) - weprintf("%s - File does not exist", file->filename); + im_weprintf(w, "%s - File does not exist", file); break; case IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY: - if (!opt.quiet) - weprintf("%s - Directory specified for image filename", file->filename); + im_weprintf(w, "%s - Directory specified for image filename", file); break; case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ: - if (!opt.quiet) - weprintf("%s - No read access to directory", file->filename); + im_weprintf(w, "%s - No read access", file); break; case IMLIB_LOAD_ERROR_UNKNOWN: case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT: - if (!opt.quiet) - weprintf("%s - No Imlib2 loader for that file format", file->filename); + im_weprintf(w, "%s - No Imlib2 loader for that file format", file); break; case IMLIB_LOAD_ERROR_PATH_TOO_LONG: - if (!opt.quiet) - weprintf("%s - Path specified is too long", file->filename); + im_weprintf(w, "%s - Path specified is too long", file); break; case IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT: - if (!opt.quiet) - weprintf("%s - Path component does not exist", file->filename); + im_weprintf(w, "%s - Path component does not exist", file); break; case IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY: - if (!opt.quiet) - weprintf("%s - Path component is not a directory", file->filename); + im_weprintf(w, "%s - Path component is not a directory", file); break; case IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE: - if (!opt.quiet) - weprintf("%s - Path points outside address space", file->filename); + im_weprintf(w, "%s - Path points outside address space", file); break; case IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS: - if (!opt.quiet) - weprintf("%s - Too many levels of symbolic links", file->filename); + im_weprintf(w, "%s - Too many levels of symbolic links", file); break; case IMLIB_LOAD_ERROR_OUT_OF_MEMORY: - if (!opt.quiet) - weprintf("While loading %s - Out of memory", file->filename); - break; - case IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS: - eprintf("While loading %s - Out of file descriptors", file->filename); + im_weprintf(w, "While loading %s - Out of memory", file); break; case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE: - if (!opt.quiet) - weprintf("%s - Cannot write to directory", file->filename); + im_weprintf(w, "%s - Cannot write to directory", file); break; case IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE: - if (!opt.quiet) - weprintf("%s - Cannot write - out of disk space", file->filename); + im_weprintf(w, "%s - Cannot write - out of disk space", file); + break; +#if defined(IMLIB2_VERSION_MAJOR) && defined(IMLIB2_VERSION_MINOR) && (IMLIB2_VERSION_MAJOR > 1 || IMLIB2_VERSION_MINOR > 7) + case IMLIB_LOAD_ERROR_IMAGE_READ: + im_weprintf(w, "%s - Invalid image file", file); break; + case IMLIB_LOAD_ERROR_IMAGE_FRAME: + im_weprintf(w, "%s - Requested frame not in image", file); + break; +#endif default: - if (!opt.quiet) - weprintf("While loading %s - Unknown error (%d). Attempting to continue", - file->filename, err); + im_weprintf(w, "While loading %s - Unknown error (%d)", + file, err); break; } - D(3, ("Load *failed*\n")); - D_RETURN(4, 0); + } +} + +#ifdef HAVE_LIBMAGIC +void uninit_magic(void) +{ + if (!magic) { + return; } - D(3, ("Loaded ok\n")); - D_RETURN(4, 1); + magic_close(magic); + magic = NULL; } +void init_magic(void) +{ + if (getenv("FEH_SKIP_MAGIC")) { + return; + } -char *feh_http_load_image(char *url) + if (!(magic = magic_open(MAGIC_NONE))) { + weprintf("unable to initialize magic library\n"); + return; + } + + if (magic_load(magic, NULL) != 0) { + weprintf("cannot load magic database: %s\n", magic_error(magic)); + uninit_magic(); + } +} + +/* + * 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>. + */ +int feh_is_image(feh_file * file, int magic_flags) { - char *tmpname; - char *tmpname_timestamper = NULL; - char *basename; - char *newurl = NULL; - char randnum[20]; - int rnum; - char *path = NULL; + const char * mime_type = NULL; + + if (!magic) { + return 1; + } - D_ENTER(4); + magic_setflags(magic, MAGIC_MIME_TYPE | MAGIC_SYMLINK | magic_flags); + mime_type = magic_file(magic, file->filename); - if (opt.keep_http) { - if (opt.output_dir) - path = opt.output_dir; - else - path = ""; - } else - path = "/tmp/"; + if (!mime_type) { + return 0; + } - basename = strrchr(url, '/') + 1; - tmpname = feh_unique_filename(path, basename); + D(("file %s has mime type: %s\n", file->filename, mime_type)); - if (opt.wget_timestamp) { - char cppid[10]; - pid_t ppid; + if (strncmp(mime_type, "image/", 6) == 0) { + return 1; + } - ppid = getpid(); - snprintf(cppid, sizeof(cppid), "%06ld", (long) ppid); - tmpname_timestamper = estrjoin("", "/tmp/feh_", cppid, "_", basename, NULL); - newurl = estrdup(url); - } else { - rnum = rand(); - snprintf(randnum, sizeof(randnum), "%d", rnum); - newurl = estrjoin("?", url, randnum, NULL); - } - D(3, ("newurl: %s\n", newurl)); - - if (opt.builtin_http) { - /* state for HTTP header parser */ -#define SAW_NONE 1 -#define SAW_ONE_CM 2 -#define SAW_ONE_CJ 3 -#define SAW_TWO_CM 4 -#define IN_BODY 5 - -#define OUR_BUF_SIZE 1024 -#define EOL "\015\012" - - int sockno = 0; - int size; - int body = SAW_NONE; - struct sockaddr_in addr; - struct hostent *hptr; - char *hostname; - char *get_string; - char *host_string; - char *query_string; - char *get_url; - static char buf[OUR_BUF_SIZE]; - char ua_string[] = "User-Agent: feh image viewer"; - char accept_string[] = "Accept: image/*"; - FILE *fp; - - D(4, ("using builtin http collection\n")); - fp = fopen(tmpname, "w"); - if (!fp) { - weprintf("couldn't write to file %s:", tmpname); - free(tmpname); - D_RETURN(4, NULL); - } + /* no infinite loop on compressed content, please */ + if (magic_flags) { + return 0; + } - hostname = feh_strip_hostname(newurl); - if (!hostname) { - weprintf("couldn't work out hostname from %s:", newurl); - fclose(fp); - unlink(tmpname); - free(tmpname); - free(newurl); - D_RETURN(4, NULL); - } + /* imlib2 supports loading compressed images, let's have a look inside */ + if (strcmp(mime_type, "application/gzip") == 0 || + strcmp(mime_type, "application/x-bzip2") == 0 || + strcmp(mime_type, "application/x-xz") == 0) { + return feh_is_image(file, MAGIC_COMPRESS); + } - D(4, ("trying hostname %s\n", hostname)); + return 0; +} +#else +int feh_is_image(__attribute__((unused)) feh_file * file, __attribute__((unused)) int magic_flags) +{ + return 1; +} +#endif - if (!(hptr = feh_gethostbyname(hostname))) { - weprintf("error resolving host %s:", hostname); - fclose(fp); - unlink(tmpname); - free(hostname); - free(tmpname); - free(newurl); - D_RETURN(4, NULL); +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; + + D(("filename is %s, image is %p\n", file->filename, im)); + + if (!file || !file->filename) + return 0; + + if (path_is_url(file->filename)) { + image_source = SRC_HTTP; + + if ((tmpname = feh_http_load_image(file->filename)) == NULL) { + feh_err = LOAD_ERROR_CURL; + err = IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST; + } + } + else { + if (feh_is_image(file, 0)) { + *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; } + } - /* Copy the address of the host to socket description. */ - memcpy(&addr.sin_addr, hptr->h_addr, hptr->h_length); + if (opt.conversion_timeout >= 0 && ( + (err == IMLIB_LOAD_ERROR_UNKNOWN) || + (err == IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT))) { + 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; + } + } + } - /* Set port and protocol */ - addr.sin_family = AF_INET; - addr.sin_port = htons(80); + if (tmpname) { + *im = imlib_load_image_with_error_return(tmpname, &err); + if (!err && im) { + real_filename = file->filename; + file->filename = tmpname; - if ((sockno = socket(PF_INET, SOCK_STREAM, 0)) == -1) { - weprintf("error opening socket:"); - fclose(fp); - unlink(tmpname); - free(tmpname); - free(hostname); - free(newurl); - D_RETURN(4, NULL); + /* + * feh does not associate a non-native image with its temporary + * filename and may delete the temporary file right after loading. + * To ensure that it is still aware of image size, dimensions, etc., + * file_info is preloaded here. To avoid a memory leak when loading + * a non-native file multiple times in a slideshow, the file_info + * struct is freed first. If file->info is not set, feh_file_info_free + * is a no-op. + */ + feh_file_info_free(file->info); + feh_file_info_load(file, *im); + + file->filename = real_filename; +#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(tmpname); +#endif } - if (connect(sockno, (struct sockaddr *) &addr, sizeof(addr)) == -1) { - weprintf("error connecting socket:"); - fclose(fp); + if (!opt.use_conversion_cache && ((image_source != SRC_HTTP) || !opt.keep_http)) unlink(tmpname); + // keep_http already performs an add_file_to_rm_filelist call + else if (opt.use_conversion_cache && !opt.keep_http) + // add_file_to_rm_filelist duplicates tmpname + add_file_to_rm_filelist(tmpname); + + if (!opt.use_conversion_cache) free(tmpname); - free(hostname); - free(newurl); - D_RETURN(4, NULL); + } 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 + } - get_url = strchr(newurl, '/') + 2; - get_url = strchr(get_url, '/'); - - get_string = estrjoin(" ", "GET", get_url, "HTTP/1.0", NULL); - host_string = estrjoin(" ", "Host:", hostname, NULL); - query_string = estrjoin(EOL, get_string, host_string, accept_string, ua_string, "", "", NULL); - /* At this point query_string looks something like - ** - ** GET /dir/foo.jpg?123456 HTTP/1.0^M^J - ** Host: www.example.com^M^J - ** Accept: image/ *^M^J - ** User-Agent: feh image viewer^M^J - ** ^M^J - ** - ** Host: is required by HTTP/1.1 and very important for some sites, - ** even with HTTP/1.0 - ** - ** -- BEG - */ - if ((send(sockno, query_string, strlen(query_string), 0)) == -1) { - free(get_string); - free(host_string); - free(query_string); - free(tmpname); - free(hostname); - free(newurl); - weprintf("error sending over socket:"); - D_RETURN(4, NULL); + if ((err) || (!im)) { + if (opt.verbose && !opt.quiet) { + fputs("\n", stderr); + reset_output = 1; } - free(get_string); - free(host_string); - free(query_string); - free(hostname); - free(newurl); - - while ((size = read(sockno, &buf, OUR_BUF_SIZE))) { - if (body == IN_BODY) { - fwrite(buf, 1, size, fp); - } else { - int i; - - for (i = 0; i < size; i++) { - /* We are looking for ^M^J^M^J, but will accept - ** ^J^J from broken servers. Stray ^Ms will be - ** ignored. - ** - ** TODO: - ** Checking the headers for a - ** Content-Type: image/ * - ** header would help detect problems with results. - ** Maybe look at the response code too? But there is - ** no fundamental reason why a 4xx or 5xx response - ** could not return an image, it is just the 3xx - ** series we need to worry about. - ** - ** Also, grabbing the size from the Content-Length - ** header and killing the connection after that - ** many bytes where read would speed up closing the - ** socket. - ** -- BEG - */ + feh_print_load_error(file->filename, NULL, err, feh_err); + D(("Load *failed*\n")); + return(0); + } + + /* + * By default, Imlib2 unconditionally loads a cached file without checking + * if it was modified on disk. However, feh (or rather its users) should + * expect image changes to appear at the next reload. So we tell Imlib2 to + * always check the file modification time and only use a cached image if + * the mtime was not changed. The performance penalty is usually negligible. + */ + imlib_context_set_image(*im); + imlib_image_set_changes_on_disk(); + +#ifdef HAVE_LIBEXIF + int orientation = 0; + 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); + } + } - switch (body) { + if (orientation == 2) + gib_imlib_image_flip_horizontal(*im); + else if (orientation == 3) + gib_imlib_image_orientate(*im, 2); + else if (orientation == 4) + gib_imlib_image_flip_vertical(*im); + else if (orientation == 5) { + gib_imlib_image_orientate(*im, 3); + gib_imlib_image_flip_vertical(*im); + } + else if (orientation == 6) + gib_imlib_image_orientate(*im, 1); + else if (orientation == 7) { + gib_imlib_image_orientate(*im, 3); + gib_imlib_image_flip_horizontal(*im); + } + else if (orientation == 8) + gib_imlib_image_orientate(*im, 3); +#endif - case IN_BODY: - fwrite(buf + i, 1, size - i, fp); - i = size; - break; + D(("Loaded ok\n")); + return(1); +} - case SAW_ONE_CM: - if (buf[i] == '\012') { - body = SAW_ONE_CJ; - } else { - body = SAW_NONE; - } - break; - - case SAW_ONE_CJ: - if (buf[i] == '\015') { - body = SAW_TWO_CM; - } else { - if (buf[i] == '\012') { - body = IN_BODY; - } else { - body = SAW_NONE; - } - } - break; +void feh_reload_image(winwidget w, int resize, int force_new) +{ + char *new_title; + int len; + Imlib_Image tmp; + int old_w, old_h; - case SAW_TWO_CM: - if (buf[i] == '\012') { - body = IN_BODY; - } else { - body = SAW_NONE; - } - break; - - case SAW_NONE: - if (buf[i] == '\015') { - body = SAW_ONE_CM; - } else { - if (buf[i] == '\012') { - body = SAW_ONE_CJ; - } - } - break; + if (!w->file) { + im_weprintf(w, "couldn't reload, this image has no file associated with it."); + winwidget_render_image(w, 0, 0); + return; + } - } /* switch */ - } /* for i */ - } - } /* while read */ - close(sockno); - fclose(fp); + D(("resize %d, force_new %d\n", resize, force_new)); + + free(FEH_FILE(w->file->data)->caption); + FEH_FILE(w->file->data)->caption = NULL; + + len = strlen(w->name) + sizeof("Reloading: ") + 1; + new_title = emalloc(len); + snprintf(new_title, len, "Reloading: %s", w->name); + winwidget_rename(w, new_title); + free(new_title); + + old_w = gib_imlib_image_get_width(w->im); + old_h = gib_imlib_image_get_height(w->im); + + /* + * If we don't free the old image before loading the new one, Imlib2's + * caching will get in our way. + * However, if --reload is used (force_new == 0), we want to continue if + * the new image cannot be loaded, so we must not free the old image yet. + */ + if (force_new) + winwidget_free_image(w); + + // if it's an external image, our own cache will also get in your way + char *sfn; + if (opt.use_conversion_cache && conversion_cache && (sfn = gib_hash_get(conversion_cache, FEH_FILE(w->file->data)->filename)) != NULL) { + free(sfn); + gib_hash_set(conversion_cache, FEH_FILE(w->file->data)->filename, NULL); + } + + if ((feh_load_image(&tmp, FEH_FILE(w->file->data))) == 0) { + if (force_new) + eprintf("failed to reload image\n"); + else { + im_weprintf(w, "Couldn't reload image. Is it still there?"); + winwidget_render_image(w, 0, 0); + } + return; + } + + if (!resize && ((old_w != gib_imlib_image_get_width(tmp)) || + (old_h != gib_imlib_image_get_height(tmp)))) + resize = 1; + + if (!force_new) + winwidget_free_image(w); + + w->im = tmp; + winwidget_reset_image(w); + + w->mode = MODE_NORMAL; + if ((w->im_w != gib_imlib_image_get_width(w->im)) + || (w->im_h != gib_imlib_image_get_height(w->im))) + w->had_resize = 1; + if (w->has_rotated) { + Imlib_Image temp; + + temp = gib_imlib_create_rotated_image(w->im, 0.0); + w->im_w = gib_imlib_image_get_width(temp); + w->im_h = gib_imlib_image_get_height(temp); + gib_imlib_free_image_and_decache(temp); } else { - int pid; - int status; + w->im_w = gib_imlib_image_get_width(w->im); + w->im_h = gib_imlib_image_get_height(w->im); + } + winwidget_render_image(w, resize, 0); - if ((pid = fork()) < 0) { - weprintf("open url: fork failed:"); - free(tmpname); - free(newurl); - D_RETURN(4, NULL); - } else if (pid == 0) { - char *quiet = NULL; + return; +} - if (!opt.verbose) - quiet = estrdup("-q"); +static int feh_file_is_raw(char *filename) +{ + childpid = fork(); + if (childpid == -1) { + perror("fork"); + return 0; + } - if (opt.wget_timestamp) { - execlp("wget", "wget", "-N", "-O", tmpname_timestamper, newurl, quiet, (char *) NULL); - } else { - execlp("wget", "wget", "--cache=off", "-O", tmpname, newurl, quiet, NULL); + if (childpid == 0) { + int devnull = open("/dev/null", O_WRONLY); + dup2(devnull, 1); + dup2(devnull, 2); + execlp("dcraw", "dcraw", "-i", filename, NULL); + _exit(1); + } else { + int status; + do { + waitpid(childpid, &status, WUNTRACED); + if (WIFEXITED(status)) { + return !WEXITSTATUS(status); } - eprintf("url: exec failed: wget:"); + } while (!WIFEXITED(status) && !WIFSIGNALED(status)); + } + + return 0; +} + +static char *feh_dcraw_load_image(char *filename) +{ + char *basename; + char *tmpname; + char *sfn; + int fd = -1; + + if (opt.use_conversion_cache) { + if (!conversion_cache) + conversion_cache = gib_hash_new(); + if ((sfn = gib_hash_get(conversion_cache, filename)) != NULL) + return sfn; + } + + basename = strrchr(filename, '/'); + + if (basename == NULL) + basename = filename; + else + basename++; + + tmpname = feh_unique_filename("/tmp/", basename); + + if (strlen(tmpname) > (NAME_MAX-6)) + tmpname[NAME_MAX-7] = '\0'; + + sfn = estrjoin("_", tmpname, "XXXXXX", NULL); + free(tmpname); + + fd = mkstemp(sfn); + + if (fd == -1) { + free(sfn); + return NULL; + } + + childpid = fork(); + if (childpid == -1) { + weprintf("%s: Can't load with dcraw. Fork failed:", filename); + unlink(sfn); + free(sfn); + close(fd); + return NULL; + } else if (childpid == 0) { + dup2(fd, STDOUT_FILENO); + close(fd); + + alarm(opt.conversion_timeout); + execlp("dcraw", "dcraw", "-c", "-e", filename, NULL); + _exit(1); + } + + int status; + waitpid(-1, &status, 0); + if (WIFSIGNALED(status)) { + unlink(sfn); + free(sfn); + sfn = NULL; + if (!opt.quiet) + weprintf("%s - Conversion took too long, skipping", filename); + } + + if ((sfn != NULL) && opt.use_conversion_cache) + gib_hash_set(conversion_cache, filename, sfn); + + return sfn; +} + +static char *feh_magick_load_image(char *filename) +{ + char *argv_fn; + char *basename; + char *tmpname; + char *sfn; + char tempdir[] = "/tmp/.feh-magick-tmp-XXXXXX"; + int fd = -1, devnull = -1; + int status; + char created_tempdir = 0; + + if (opt.use_conversion_cache) { + if (!conversion_cache) + conversion_cache = gib_hash_new(); + if ((sfn = gib_hash_get(conversion_cache, filename)) != NULL) + return sfn; + } + + basename = strrchr(filename, '/'); + + if (basename == NULL) + basename = filename; + else + basename++; + + tmpname = feh_unique_filename("/tmp/", basename); + + if (strlen(tmpname) > (NAME_MAX-6)) + tmpname[NAME_MAX-7] = '\0'; + + sfn = estrjoin("_", tmpname, "XXXXXX", NULL); + free(tmpname); + + fd = mkstemp(sfn); + + if (fd == -1) { + free(sfn); + return NULL; + } + + /* + * We could use png:fd:(whatever mkstemp returned) as target filename + * for convert, but this seems to be broken in some ImageMagick versions. + * So we resort to png:(sfn) instead. + */ + argv_fn = estrjoin(":", "png", sfn, NULL); + + /* + * By default, ImageMagick saves (occasionally lots of) temporary files + * in /tmp. It doesn't remove them if it runs into a timeout and is killed + * by us, no matter whether we use SIGINT, SIGTERM or SIGKILL. So, unless + * MAGICK_TMPDIR has already been set by the user, we create our own + * temporary directory for ImageMagick and remove its contents at the end of + * this function. + */ + if (getenv("MAGICK_TMPDIR") == NULL) { + if (mkdtemp(tempdir) == NULL) { + weprintf("%s: ImageMagick may leave temporary files in /tmp. mkdtemp failed:", filename); } else { - waitpid(pid, &status, 0); - - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - weprintf("url: wget failed to load URL %s\n", url); - unlink(opt.wget_timestamp ? tmpname_timestamper : tmpname); - free(newurl); - free(tmpname); - D_RETURN(4, NULL); + created_tempdir = 1; + } + } + + if ((childpid = fork()) < 0) { + weprintf("%s: Can't load with imagemagick. Fork failed:", filename); + unlink(sfn); + free(sfn); + sfn = NULL; + } + else if (childpid == 0) { + + devnull = open("/dev/null", O_WRONLY); + dup2(devnull, 0); + if (opt.quiet) { + /* discard convert output */ + dup2(devnull, 1); + dup2(devnull, 2); + } + + /* + * convert only accepts SIGINT via killpg, a normal kill doesn't work + */ + setpgid(0, 0); + + if (created_tempdir) { + // no error checking - this is a best-effort code path + setenv("MAGICK_TMPDIR", tempdir, 0); + } + + execlp("convert", "convert", filename, argv_fn, NULL); + _exit(1); + } + else { + alarm(opt.conversion_timeout); + waitpid(childpid, &status, 0); + kill(childpid, SIGKILL); + if (opt.conversion_timeout > 0 && !alarm(0)) { + unlink(sfn); + free(sfn); + sfn = NULL; + + if (!opt.quiet) { + weprintf("%s: Conversion took too long, skipping", filename); } - if (opt.wget_timestamp) { - char cmd[2048]; + } + close(fd); + childpid = 0; + } - snprintf(cmd, sizeof(cmd), "/bin/cp %s %s", tmpname_timestamper, tmpname); - system(cmd); + if (created_tempdir) { + DIR *dir; + struct dirent *de; + if ((dir = opendir(tempdir)) == NULL) { + weprintf("%s: Cannot remove temporary ImageMagick files from %s:", filename, tempdir); + } else { + while ((de = readdir(dir)) != NULL) { + if (de->d_name[0] != '.') { + char *temporary_file_name = estrjoin("/", tempdir, de->d_name, NULL); + /* + * We assume that ImageMagick only creates temporary files and + * not directories. + */ + if (unlink(temporary_file_name) == -1) { + weprintf("unlink %s:", temporary_file_name); + } + free(temporary_file_name); + } + } + if (rmdir(tempdir) == -1) { + weprintf("rmdir %s:", tempdir); } - free(newurl); } + closedir(dir); } - D_RETURN(4, tmpname); + free(argv_fn); + + if ((sfn != NULL) && opt.use_conversion_cache) + gib_hash_set(conversion_cache, filename, sfn); + + return sfn; +} + +#ifdef HAVE_LIBCURL + +#if LIBCURL_VERSION_NUM >= 0x072000 /* 07.32.0 */ +static int curl_quit_function(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) +#else +static int curl_quit_function(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) +#endif +{ + // ignore "unused parameter" warnings + (void)clientp; + (void)dltotal; + (void)dlnow; + (void)ultotal; + (void)ulnow; + if (sig_exit) { + /* + * The user wants to quit feh. Tell libcurl to abort the transfer and + * return control to the main loop, where we can quit gracefully. + */ + return 1; + } + return 0; } -struct hostent *feh_gethostbyname(const char *name) +static char *feh_http_load_image(char *url) { - struct hostent *hp; - unsigned long addr; + CURL *curl; + CURLcode res; + char *sfn; + FILE *sfp; + int fd = -1; + char *ebuff; + char *tmpname; + char *basename; + char *path = NULL; - D_ENTER(3); - addr = (unsigned long) inet_addr(name); - if ((int) addr != -1) - hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET); - else - hp = gethostbyname(name); - D_RETURN(3, hp); + if (opt.use_conversion_cache) { + if (!conversion_cache) + conversion_cache = gib_hash_new(); + if ((sfn = gib_hash_get(conversion_cache, url)) != NULL) + return sfn; + } + + if (opt.keep_http) { + if (opt.output_dir) + path = opt.output_dir; + else + path = ""; + } else + path = "/tmp/"; + + curl = curl_easy_init(); + if (!curl) { + weprintf("open url: libcurl initialization failure"); + return NULL; + } + + basename = strrchr(url, '/') + 1; + +#ifdef HAVE_MKSTEMPS + tmpname = estrjoin("_", "feh_curl_XXXXXX", basename, NULL); + + if (strlen(tmpname) > NAME_MAX) { + tmpname[NAME_MAX] = '\0'; + } +#else + if (strlen(basename) > NAME_MAX-7) { + tmpname = estrdup("feh_curl_XXXXXX"); + } else { + tmpname = estrjoin("_", "feh_curl", basename, "XXXXXX", NULL); + } +#endif + + sfn = estrjoin("", path, tmpname, NULL); + free(tmpname); + + D(("sfn is %s\n", sfn)) + +#ifdef HAVE_MKSTEMPS + fd = mkstemps(sfn, strlen(basename) + 1); +#else + fd = mkstemp(sfn); +#endif + + if (fd != -1) { + sfp = fdopen(fd, "w+"); + if (sfp != NULL) { +#ifdef DEBUG + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); +#endif + /* + * Do not allow requests to take longer than 30 minutes. + * This should be sufficiently high to accommodate use cases with + * unusually high latencies, while at the same time avoiding + * feh hanging indefinitely in unattended slideshows. + */ + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1800); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, sfp); + ebuff = emalloc(CURL_ERROR_SIZE); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, ebuff); + curl_easy_setopt(curl, CURLOPT_USERAGENT, PACKAGE "/" VERSION); + curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); +#if LIBCURL_VERSION_NUM >= 0x072000 /* 07.32.0 */ + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, curl_quit_function); +#else + curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, curl_quit_function); +#endif + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); + if (opt.insecure_ssl) { + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); + } else if (getenv("CURL_CA_BUNDLE") != NULL) { + // Allow the user to specify custom CA certificates. + curl_easy_setopt(curl, CURLOPT_CAINFO, + getenv("CURL_CA_BUNDLE")); + } + + res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + if (res != CURLE_OK) { + if (res != CURLE_ABORTED_BY_CALLBACK) { + weprintf("open url: %s", ebuff); + } + unlink(sfn); + close(fd); + free(sfn); + sfn = NULL; + } + + free(ebuff); + fclose(sfp); + if (opt.use_conversion_cache) + gib_hash_set(conversion_cache, url, sfn); + return sfn; + } else { + weprintf("open url: fdopen failed:"); + unlink(sfn); + free(sfn); + close(fd); + } + } else { +#ifdef HAVE_MKSTEMPS + weprintf("open url: mkstemps failed:"); +#else + weprintf("open url: mkstemp failed:"); +#endif + free(sfn); + } + curl_easy_cleanup(curl); + return NULL; } -char *feh_strip_hostname(char *url) +#else /* HAVE_LIBCURL */ + +char *feh_http_load_image(char *url) { - char *ret; - char *start; - char *finish; - int len; + weprintf( + "Cannot load image %s\nPlease recompile feh with libcurl support", + url + ); + return NULL; +} + +#endif /* HAVE_LIBCURL */ + +void feh_imlib_image_fill_text_bg(Imlib_Image im, int w, int h) +{ + gib_imlib_image_set_has_alpha(im, 1); + + imlib_context_set_blend(0); + + if (opt.text_bg == TEXT_BG_CLEAR) + gib_imlib_image_fill_rectangle(im, 0, 0, w, h, 0, 0, 0, 0); + else + gib_imlib_image_fill_rectangle(im, 0, 0, w, h, 0, 0, 0, 127); - D_ENTER(3); + imlib_context_set_blend(1); +} - start = strchr(url, '/'); - if (!start) - D_RETURN(3, NULL); +static Imlib_Font feh_load_font(winwidget w) +{ + static Imlib_Font fn = NULL; - start += 2; + if (opt.font) + fn = gib_imlib_load_font(opt.font); - finish = strchr(start, '/'); - if (!finish) - D_RETURN(3, NULL); + if (!fn) { + if (w && w->full_screen) + fn = gib_imlib_load_font(DEFAULT_FONT_BIG); + else + fn = gib_imlib_load_font(DEFAULT_FONT); + } - len = finish - start; + if (!fn) { + eprintf("Couldn't load font to draw a message"); + } - ret = emalloc(len + 1); - strncpy(ret, start, len); - ret[len] = '\0'; - D_RETURN(3, ret); + return fn; } + void feh_draw_zoom(winwidget w) { static Imlib_Font fn = NULL; int tw = 0, th = 0; Imlib_Image im = NULL; char buf[100]; - static DATA8 atab[256]; - - D_ENTER(4); if (!w->im) - D_RETURN_(4); - - if (!fn) { - fn = gib_imlib_load_font(DEFAULT_FONT); - memset(atab, 0, sizeof(atab)); - } + return; - if (!fn) { - weprintf("Couldn't load font for zoom printing"); - D_RETURN_(4); - } + fn = feh_load_font(w); snprintf(buf, sizeof(buf), "%.0f%%, %dx%d", w->zoom * 100, (int) (w->im_w * w->zoom), (int) (w->im_h * w->zoom)); @@ -588,72 +1029,118 @@ void feh_draw_zoom(winwidget w) if (!im) eprintf("Couldn't create image. Out of memory?"); - gib_imlib_image_set_has_alpha(im, 1); - gib_imlib_apply_color_modifier_to_rectangle(im, 0, 0, tw, th, NULL, NULL, NULL, atab); - gib_imlib_image_fill_rectangle(im, 0, 0, tw, th, 0, 0, 0, 0); + feh_imlib_image_fill_text_bg(im, tw, th); gib_imlib_text_draw(im, fn, NULL, 2, 2, buf, IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255); gib_imlib_text_draw(im, fn, NULL, 1, 1, buf, IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0, w->h - th, 1, 1, 0); gib_imlib_free_image_and_decache(im); - D_RETURN_(4); + return; } -void feh_draw_filename(winwidget w) +void im_weprintf(winwidget w, char *fmt, ...) +{ + va_list args; + char *errstr = emalloc(1024); + + fflush(stdout); + fputs(PACKAGE " WARNING: ", stderr); + + va_start(args, fmt); + vsnprintf(errstr, 1024, fmt, args); + va_end(args); + + if (w) + w->errstr = errstr; + + fputs(errstr, stderr); + if (fmt[0] != '\0' && fmt[strlen(fmt) - 1] == ':') + fprintf(stderr, " %s", strerror(errno)); + fputs("\n", stderr); + if (!w) + free(errstr); +} + + +void feh_draw_errstr(winwidget w) { static Imlib_Font fn = NULL; int tw = 0, th = 0; Imlib_Image im = NULL; - static DATA8 atab[256]; + + if (!w->im) + return; + + fn = feh_load_font(NULL); + + /* Work out how high the font is */ + gib_imlib_get_text_size(fn, w->errstr, NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT); + + tw += 3; + th += 3; + im = imlib_create_image(tw, th); + if (!im) + eprintf("Couldn't create errstr image. Out of memory?"); + + feh_imlib_image_fill_text_bg(im, tw, th); + + gib_imlib_text_draw(im, fn, NULL, 2, 2, w->errstr, IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255); + gib_imlib_text_draw(im, fn, NULL, 1, 1, w->errstr, IMLIB_TEXT_TO_RIGHT, 255, 0, 0, 255); + free(w->errstr); + w->errstr = NULL; + gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0, w->h - th, 1, 1, 0); + gib_imlib_free_image_and_decache(im); +} + +void feh_draw_filename(winwidget w) +{ + static Imlib_Font fn = NULL; + int tw = 0, th = 0, nw = 0; + Imlib_Image im = NULL; char *s = NULL; int len = 0; - D_ENTER(4); - if ((!w->file) || (!FEH_FILE(w->file->data)) || (!FEH_FILE(w->file->data)->filename)) - D_RETURN_(4); + return; - if (!fn) { - memset(atab, 0, sizeof(atab)); - if (w->full_screen) - fn = gib_imlib_load_font(DEFAULT_FONT_BIG); + fn = feh_load_font(w); + + /* Work out how high the font is */ + gib_imlib_get_text_size(fn, FEH_FILE(w->file->data)->filename, NULL, &tw, + &th, IMLIB_TEXT_TO_RIGHT); + + if (gib_list_length(filelist) > 1) { + len = snprintf(NULL, 0, "%d of %d", gib_list_length(filelist), + gib_list_length(filelist)) + 1; + s = emalloc(len); + if (w->file) + snprintf(s, len, "%d of %d", gib_list_num(filelist, w->file) + + 1, gib_list_length(filelist)); else - fn = gib_imlib_load_font(DEFAULT_FONT); - } + snprintf(s, len, "%d of %d", gib_list_num(filelist, current_file) + + 1, gib_list_length(filelist)); - if (!fn) { - weprintf("Couldn't load font for filename printing"); - D_RETURN_(4); - } + gib_imlib_get_text_size(fn, s, NULL, &nw, NULL, IMLIB_TEXT_TO_RIGHT); - /* Work out how high the font is */ - gib_imlib_get_text_size(fn, FEH_FILE(w->file->data)->filename, NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT); + if (nw > tw) + tw = nw; + } - /* tw is no longer correct, if the filename is shorter than - * the string "%d of %d" used below in fullscreen mode */ tw += 3; th += 3; im = imlib_create_image(tw, 2 * th); if (!im) eprintf("Couldn't create image. Out of memory?"); - gib_imlib_image_set_has_alpha(im, 1); - gib_imlib_apply_color_modifier_to_rectangle(im, 0, 0, tw, 2 * th, NULL, NULL, NULL, atab); - gib_imlib_image_fill_rectangle(im, 0, 0, tw, 2 * th, 0, 0, 0, 0); + feh_imlib_image_fill_text_bg(im, tw, 2 * th); gib_imlib_text_draw(im, fn, NULL, 2, 2, FEH_FILE(w->file->data)->filename, IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255); gib_imlib_text_draw(im, fn, NULL, 1, 1, FEH_FILE(w->file->data)->filename, IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); - /* Print the position in the filelist, if we are in fullscreen and the - * list has more than one element */ - if (w->full_screen && (gib_list_length(filelist) - 1)) { - /* sic! */ - len = snprintf(NULL, 0, "%d of %d", gib_list_length(filelist), gib_list_length(filelist)) + 1; - s = emalloc(len); - snprintf(s, len, "%d of %d", gib_list_num(filelist, current_file) + 1, gib_list_length(filelist)); - /* This should somehow be right-aligned */ + + if (s) { gib_imlib_text_draw(im, fn, NULL, 2, th + 1, s, IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255); gib_imlib_text_draw(im, fn, NULL, 1, th, s, IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); free(s); @@ -662,13 +1149,195 @@ void feh_draw_filename(winwidget w) gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0, 0, 1, 1, 0); gib_imlib_free_image_and_decache(im); - D_RETURN_(4); + return; } -char *build_caption_filename(feh_file * file) +#ifdef HAVE_LIBEXIF +void feh_draw_exif(winwidget w) +{ + static Imlib_Font fn = NULL; + int width = 0, height = 0, line_width = 0, line_height = 0; + Imlib_Image im = NULL; + int no_lines = 0, i; + int pos = 0; + int pos2 = 0; + char info_line[256]; + char *info_buf[128]; + char buffer[EXIF_MAX_DATA]; + + if ( (!w->file) || (!FEH_FILE(w->file->data)) + || (!FEH_FILE(w->file->data)->filename) ) + { + return; + } + + + buffer[0] = '\0'; + exif_get_info(FEH_FILE(w->file->data)->ed, buffer, EXIF_MAX_DATA); + + fn = feh_load_font(w); + + if (buffer[0] == '\0') + { + snprintf(buffer, EXIF_MAX_DATA, "%s", "Failed to run exif command"); + gib_imlib_get_text_size(fn, buffer, NULL, &width, &height, IMLIB_TEXT_TO_RIGHT); + info_buf[no_lines] = estrdup(buffer); + no_lines++; + } + else + { + + while ( (no_lines < 128) && (pos < EXIF_MAX_DATA) ) + { + /* max 128 lines */ + pos2 = 0; + while ( pos2 < 255 ) /* max 255 chars + 1 null byte per line */ + { + if ( (buffer[pos] != '\n') + && (buffer[pos] != '\0') ) + { + info_line[pos2] = buffer[pos]; + } + else if ( buffer[pos] == '\0' ) + { + pos = EXIF_MAX_DATA; /* all data seen */ + break; + } + else + { + pos++; /* line finished, continue with next line*/ + break; + } + + pos++; + pos2++; + } + info_line[pos2] = '\0'; + + gib_imlib_get_text_size(fn, info_line, NULL, &line_width, + &line_height, IMLIB_TEXT_TO_RIGHT); + + if (line_height > height) + height = line_height; + if (line_width > width) + width = line_width; + info_buf[no_lines] = estrdup(info_line); + + no_lines++; + } + } + + if (no_lines == 0) + return; + + height *= no_lines; + width += 4; + + im = imlib_create_image(width, height); + if (!im) + { + eprintf("Couldn't create image. Out of memory?"); + } + + feh_imlib_image_fill_text_bg(im, width, height); + + for (i = 0; i < no_lines; i++) + { + gib_imlib_text_draw(im, fn, NULL, 2, (i * line_height) + 2, + 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]); + + } + + gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0, w->h - height, 1, 1, 0); + + gib_imlib_free_image_and_decache(im); + return; + +} +#endif + +void feh_draw_info(winwidget w) +{ + static Imlib_Font fn = NULL; + int width = 0, height = 0, line_width = 0, line_height = 0; + Imlib_Image im = NULL; + int no_lines = 0, i; + char *info_cmd; + char info_line[256]; + char *info_buf[128]; + FILE *info_pipe; + + if ((!w->file) || (!FEH_FILE(w->file->data)) + || (!FEH_FILE(w->file->data)->filename)) + return; + + fn = feh_load_font(w); + + info_cmd = feh_printf(opt.info_cmd, FEH_FILE(w->file->data), w); + + info_pipe = popen(info_cmd, "r"); + + if (!info_pipe) { + info_buf[0] = estrdup("Failed to run info command"); + gib_imlib_get_text_size(fn, info_buf[0], NULL, &width, &height, IMLIB_TEXT_TO_RIGHT); + no_lines = 1; + } + else { + while ((no_lines < 128) && fgets(info_line, 256, info_pipe)) { + if (info_line[strlen(info_line)-1] == '\n') + info_line[strlen(info_line)-1] = '\0'; + + gib_imlib_get_text_size(fn, info_line, NULL, &line_width, + &line_height, IMLIB_TEXT_TO_RIGHT); + + if (line_height > height) + height = line_height; + if (line_width > width) + width = line_width; + + info_buf[no_lines] = estrdup(info_line); + + no_lines++; + } + pclose(info_pipe); + } + + if (no_lines == 0) + return; + + height *= no_lines; + width += 4; + + im = imlib_create_image(width, height); + if (!im) + eprintf("Couldn't create image. Out of memory?"); + + feh_imlib_image_fill_text_bg(im, width, height); + + for (i = 0; i < no_lines; i++) { + gib_imlib_text_draw(im, fn, NULL, 2, (i * line_height) + 2, + 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]); + } + + gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0, + w->h - height, 1, 1, 0); + + gib_imlib_free_image_and_decache(im); + return; +} + +char *build_caption_filename(feh_file * file, short create_dir) { char *caption_filename; - char *s, *dir; + char *s, *dir, *caption_dir; + struct stat cdir_stat; s = strrchr(file->filename, '/'); if (s) { dir = estrdup(file->filename); @@ -677,6 +1346,22 @@ char *build_caption_filename(feh_file * file) } else { dir = estrdup("."); } + + caption_dir = estrjoin("/", dir, opt.caption_path, NULL); + + D(("dir %s, cp %s, cdir %s\n", dir, opt.caption_path, caption_dir)) + + if (stat(caption_dir, &cdir_stat) == -1) { + if (!create_dir) + return NULL; + if (mkdir(caption_dir, 0755) == -1) + eprintf("Failed to create caption directory %s:", caption_dir); + } else if (!S_ISDIR(cdir_stat.st_mode)) + eprintf("Caption directory (%s) exists, but is not a directory.", + caption_dir); + + free(caption_dir); + caption_filename = estrjoin("", dir, "/", opt.caption_path, "/", file->name, ".txt", NULL); free(dir); return caption_filename; @@ -688,27 +1373,27 @@ void feh_draw_caption(winwidget w) int tw = 0, th = 0, ww, hh; int x, y; Imlib_Image im = NULL; - static DATA8 atab[256]; char *p; gib_list *lines, *l; static gib_style *caption_style = NULL; feh_file *file; - D_ENTER(4); - if (!w->file) { - D_RETURN_(4); + return; } file = FEH_FILE(w->file->data); if (!file->filename) { - D_RETURN_(4); + return; } if (!file->caption) { char *caption_filename; - caption_filename = build_caption_filename(file); - /* read caption from file */ - file->caption = ereadfile(caption_filename); + caption_filename = build_caption_filename(file, 0); + if (caption_filename) + /* read caption from file */ + file->caption = ereadfile(caption_filename); + else + file->caption = estrdup(""); free(caption_filename); } @@ -722,35 +1407,28 @@ void feh_draw_caption(winwidget w) * winwidget_render_image(). */ file->caption = estrdup(""); - D_RETURN_(4); } - if (file->caption == '\0') { - D_RETURN_(4); - } + if (*(file->caption) == '\0' && !w->caption_entry) + return; - if (!caption_style) { - caption_style = gib_style_new("caption"); - caption_style->bits = gib_list_add_front(caption_style->bits, gib_style_bit_new(0, 0, 0, 0, 0, 0)); - caption_style->bits = gib_list_add_front(caption_style->bits, gib_style_bit_new(1, 1, 0, 0, 0, 255)); - } + caption_style = gib_style_new("caption"); + caption_style->bits = gib_list_add_front(caption_style->bits, + gib_style_bit_new(0, 0, 0, 0, 0, 0)); + caption_style->bits = gib_list_add_front(caption_style->bits, + gib_style_bit_new(1, 1, 0, 0, 0, 255)); - if (!fn) { - memset(atab, 0, sizeof(atab)); - if (w->full_screen) - fn = gib_imlib_load_font(DEFAULT_FONT_BIG); - else - fn = gib_imlib_load_font(DEFAULT_FONT); - } + fn = feh_load_font(w); - if (!fn) { - weprintf("Couldn't load font for caption printing"); - D_RETURN_(4); - } + if (*(file->caption) == '\0') { + p = estrdup("Caption entry mode - Hit ESC to cancel"); + lines = feh_wrap_string(p, w->w, fn, NULL); + free(p); + } else + lines = feh_wrap_string(file->caption, w->w, fn, NULL); - lines = feh_wrap_string(file->caption, w->w, w->h, fn, NULL); if (!lines) - D_RETURN_(4); + return; /* Work out how high/wide the caption is */ l = lines; @@ -775,22 +1453,23 @@ void feh_draw_caption(winwidget w) if (!im) eprintf("Couldn't create image. Out of memory?"); - gib_imlib_image_set_has_alpha(im, 1); - gib_imlib_apply_color_modifier_to_rectangle(im, 0, 0, tw, th, NULL, NULL, NULL, atab); - gib_imlib_image_fill_rectangle(im, 0, 0, tw, th, 0, 0, 0, 0); + feh_imlib_image_fill_text_bg(im, tw, th); l = lines; - x = 0; y = 0; while (l) { p = (char *) l->data; gib_imlib_get_text_size(fn, p, caption_style, &ww, &hh, IMLIB_TEXT_TO_RIGHT); x = (tw - ww) / 2; - if (w->caption_entry) { - gib_imlib_text_draw(im, fn, caption_style, x, y, p, IMLIB_TEXT_TO_RIGHT, 255, 255, 0, 255); - } else { - gib_imlib_text_draw(im, fn, caption_style, x, y, p, IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); - } + if (w->caption_entry && (*(file->caption) == '\0')) + gib_imlib_text_draw(im, fn, caption_style, x, y, p, + IMLIB_TEXT_TO_RIGHT, 255, 255, 127, 255); + else if (w->caption_entry) + gib_imlib_text_draw(im, fn, caption_style, x, y, p, + IMLIB_TEXT_TO_RIGHT, 255, 255, 0, 255); + else + gib_imlib_text_draw(im, fn, caption_style, x, y, p, + IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); y += hh + 1; /* line spacing */ l = l->next; @@ -799,7 +1478,7 @@ void feh_draw_caption(winwidget w) gib_imlib_render_image_on_drawable(w->bg_pmap, im, (w->w - tw) / 2, w->h - th, 1, 1, 0); gib_imlib_free_image_and_decache(im); gib_list_free_and_data(lines); - D_RETURN_(4); + return; } unsigned char reset_output = 0; @@ -810,9 +1489,14 @@ void feh_display_status(char stat) static int init_len = 0; int j = 0; - D_ENTER(5); + D(("filelist %p, filelist->next %p\n", filelist, filelist->next)); - D(5, ("filelist %p, filelist->next %p\n", filelist, filelist->next)); + if (!stat) { + putc('\n', stderr); + init_len = 0; + i = 0; + return; + } if (!init_len) init_len = gib_list_length(filelist); @@ -821,60 +1505,112 @@ void feh_display_status(char stat) if (reset_output) { /* There's just been an error message. Unfortunate ;) */ for (j = 0; j < (((i % 50) + ((i % 50) / 10)) + 7); j++) - fprintf(stdout, " "); + putc(' ', stderr); } if (!(i % 50)) { - int len; - char buf[50]; + int len = gib_list_length(filelist); + + fprintf(stderr, " %5d/%d (%d)\n[%3d%%] ", + i, init_len, len, ((int) ((float) i / init_len * 100))); - len = gib_list_length(filelist); - snprintf(buf, sizeof(buf), - " %5d/%d (%d)\n[%3d%%] ", i, init_len, len, ((int) ((float) i / init_len * 100))); - fprintf(stdout, buf); } else if ((!(i % 10)) && (!reset_output)) - fprintf(stdout, " "); + putc(' ', stderr); reset_output = 0; } else - fprintf(stdout, "[ 0%%] "); + fputs("[ 0%] ", stderr); - fprintf(stdout, "%c", stat); - fflush(stdout); + fprintf(stderr, "%c", stat); + fflush(stderr); i++; - D_RETURN_(5); + return; } -void feh_edit_inplace_orient(winwidget w, int orientation) +void feh_edit_inplace(winwidget w, int op) { - int ret; - Imlib_Image old; - D_ENTER(4); + int tmp; + Imlib_Image old = NULL; + Imlib_Load_Error err = IMLIB_LOAD_ERROR_NONE; if (!w->file || !w->file->data || !FEH_FILE(w->file->data)->filename) - D_RETURN_(4); + return; - if (!strcmp(gib_imlib_image_format(w->im), "jpeg")) { - feh_edit_inplace_lossless_rotate(w, orientation); + if (!opt.edit) { + imlib_context_set_image(w->im); + if (op == INPLACE_EDIT_FLIP) + imlib_image_flip_vertical(); + else if (op == INPLACE_EDIT_MIRROR) + imlib_image_flip_horizontal(); + else { + imlib_image_orientate(op); + if(op != 2) { + tmp = w->im_w; + w->im_w = w->im_h; + w->im_h = tmp; + } + if (FEH_FILE(w->file->data)->info) { + FEH_FILE(w->file->data)->info->width = w->im_w; + FEH_FILE(w->file->data)->info->height = w->im_h; + } + } + winwidget_render_image(w, 1, 0); + return; + } + + // 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); - D_RETURN_(4); + return; } - ret = feh_load_image(&old, FEH_FILE(w->file->data)); - if (ret) { - gib_imlib_image_orientate(old, orientation); - gib_imlib_save_image(old, FEH_FILE(w->file->data)->filename); + old = imlib_load_image_with_error_return(FEH_FILE(w->file->data)->filename, &err); + + if ((old != NULL) && (err == IMLIB_LOAD_ERROR_NONE)) { + imlib_context_set_image(old); + if (op == INPLACE_EDIT_FLIP) + imlib_image_flip_vertical(); + else if (op == INPLACE_EDIT_MIRROR) + imlib_image_flip_horizontal(); + else + imlib_image_orientate(op); + gib_imlib_save_image_with_error_return(old, + FEH_FILE(w->file->data)->filename, &err); gib_imlib_free_image(old); + if (err) + feh_print_load_error(FEH_FILE(w->file->data)->filename, + w, err, LOAD_ERROR_IMLIB); feh_reload_image(w, 1, 1); } else { - weprintf("failed to load image from disk to edit it in place\n"); + /* + * Image was opened using curl/magick or has been deleted after + * opening it + */ + imlib_context_set_image(w->im); + if (op == INPLACE_EDIT_FLIP) + imlib_image_flip_vertical(); + else if (op == INPLACE_EDIT_MIRROR) + imlib_image_flip_horizontal(); + else { + imlib_image_orientate(op); + tmp = w->im_w; + w->im_w = w->im_h; + w->im_h = tmp; + if (FEH_FILE(w->file->data)->info) { + FEH_FILE(w->file->data)->info->width = w->im_w; + FEH_FILE(w->file->data)->info->height = w->im_h; + } + } + im_weprintf(w, "unable to edit in place. Changes have not been saved."); + winwidget_render_image(w, 1, 0); } - D_RETURN_(4); + return; } -/* TODO max_height is ignored... Could use a function which generates a - * transparent text overlay image, with wrapping and all. Would be useful */ -gib_list *feh_wrap_string(char *text, int wrap_width, int max_height, Imlib_Font fn, gib_style * style) +gib_list *feh_wrap_string(char *text, int wrap_width, Imlib_Font fn, gib_style * style) { gib_list *ll, *lines = NULL, *list = NULL, *words; gib_list *l = NULL; @@ -959,7 +1695,6 @@ gib_list *feh_wrap_string(char *text, int wrap_width, int max_height, Imlib_Font list = gib_list_add_end(list, estrdup(line)); free(line); line = NULL; - line_width = 0; } gib_list_free_and_data(words); } @@ -972,38 +1707,79 @@ gib_list *feh_wrap_string(char *text, int wrap_width, int max_height, Imlib_Font return lines; } -void feh_edit_inplace_lossless_rotate(winwidget w, int orientation) +void feh_edit_inplace_lossless(winwidget w, int op) { char *filename = FEH_FILE(w->file->data)->filename; - char rotate_str[4]; int len = strlen(filename) + 1; char *file_str = emalloc(len); - int rotatearg = 90 * orientation; int pid, status; + int devnull = -1; + char op_name[] = "rotate"; /* message */ + char op_op[] = "-rotate"; /* jpegtran option */ + char op_value[] = "horizontal"; /* jpegtran option's value */ + + if (op == INPLACE_EDIT_FLIP) { + sprintf(op_name, "flip"); + sprintf(op_op, "-flip"); + sprintf(op_value, "vertical"); + } else if (op == INPLACE_EDIT_MIRROR) { + sprintf(op_name, "mirror"); + sprintf(op_op, "-flip"); + } else + snprintf(op_value, 4, "%d", 90 * op); - snprintf(rotate_str, 4, "%d", rotatearg); snprintf(file_str, len, "%s", filename); if ((pid = fork()) < 0) { - weprintf("lossless rotate: fork failed:"); - D_RETURN_(4); - } else if (pid == 0) { + im_weprintf(w, "lossless %s: fork failed:", op_name); + free(file_str); + return; + } + else if (pid == 0) { - execlp("jpegtran", "jpegtran", "-copy", "all", "-rotate", - rotate_str, "-outfile", file_str, file_str, NULL); + execlp("jpegtran", "jpegtran", "-copy", "all", op_op, op_value, + "-outfile", file_str, file_str, NULL); - eprintf("lossless rotate: exec failed: jpegtran:"); - } else { + weprintf("lossless %s: Is 'jpegtran' installed? Failed to exec:", op_name); + _exit(1); + } + else { waitpid(pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - weprintf("lossless rotate: Got exitcode %d from jpegtran." - " Commandline was:\n" - "jpegtran -copy all -rotate %d -outfile %s %s\n", - status >> 8, rotate_str, file_str, file_str); - D_RETURN_(4); + im_weprintf(w, + "lossless %s: Got exitcode %d from jpegtran." + " Commandline was: " + "jpegtran -copy all %s %s -outfile %s %s", + op_name, status >> 8, op_op, op_value, file_str, file_str); + free(file_str); + return; } } + if ((pid = fork()) < 0) { + im_weprintf(w, "lossless %s: fork failed while updating EXIF tags:", op_name); + } + else if (pid == 0) { + + /* discard normal output */ + devnull = open("/dev/null", O_WRONLY); + dup2(devnull, 1); + + execlp("jpegexiforient", "jpegexiforient", "-1", file_str, NULL); + weprintf("lossless %s: Failed to exec jpegexiforient:", op_name); + _exit(1); + } + else { + waitpid(pid, &status, 0); + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + im_weprintf(w, + "lossless %s: Failed to update EXIF orientation tag:" + " jpegexiforient returned %d", + op_name, status >> 8); + } + } + free(file_str); } void feh_draw_actions(winwidget w) @@ -1014,15 +1790,12 @@ void feh_draw_actions(winwidget w) int max_tw = 0; int line_th = 0; Imlib_Image im = NULL; - static DATA8 atab[256]; int i = 0; int num_actions = 0; int cur_action = 0; - char index[1]; + char index[3]; char *line; - D_ENTER(4); - /* Count number of defined actions. This method sucks a bit since it needs * to be changed if the number of actions changes, but at least it doesn't * miss actions 2 to 9 if action1 isn't defined @@ -1037,20 +1810,9 @@ void feh_draw_actions(winwidget w) if ((!w->file) || (!FEH_FILE(w->file->data)) || (!FEH_FILE(w->file->data)->filename)) - D_RETURN_(4); - - if (!fn) { - memset(atab, 0, sizeof(atab)); - if (w->full_screen) - fn = gib_imlib_load_font(DEFAULT_FONT_BIG); - else - fn = gib_imlib_load_font(DEFAULT_FONT); - } + return; - if (!fn) { - weprintf("Couldn't load font for actions printing"); - D_RETURN_(4); - } + fn = feh_load_font(w); gib_imlib_get_text_size(fn, "defined actions:", NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT); /* Check for the widest line */ @@ -1058,9 +1820,9 @@ void feh_draw_actions(winwidget w) for (i = 0; i < 10; i++) { if (opt.actions[i]) { - line = emalloc(strlen(opt.actions[i]) + 5); + line = emalloc(strlen(opt.action_titles[i]) + 5); strcpy(line, "0: "); - line = strcat(line, opt.actions[i]); + line = strcat(line, opt.action_titles[i]); gib_imlib_get_text_size(fn, line, NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT); free(line); if (tw > max_tw) @@ -1077,30 +1839,26 @@ void feh_draw_actions(winwidget w) /* This depends on feh_draw_filename internals... * should be fixed some time */ - if (opt.draw_filename && w->full_screen) + if (opt.draw_filename) th_offset = line_th * 2; - else if (opt.draw_filename) - th_offset = line_th; im = imlib_create_image(tw, th); if (!im) eprintf("Couldn't create image. Out of memory?"); - gib_imlib_image_set_has_alpha(im, 1); - gib_imlib_apply_color_modifier_to_rectangle(im, 0, 0, tw, th, NULL, NULL, NULL, atab); - gib_imlib_image_fill_rectangle(im, 0, 0, tw, th, 0, 0, 0, 0); + feh_imlib_image_fill_text_bg(im, tw, th); gib_imlib_text_draw(im, fn, NULL, 2, 2, "defined actions:", IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255); gib_imlib_text_draw(im, fn, NULL, 1, 1, "defined actions:", IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); for (i = 0; i < 10; i++) { - if (opt.actions[i]) { + if (opt.action_titles[i]) { cur_action++; - line = emalloc(strlen(opt.actions[i]) + 5); + line = emalloc(strlen(opt.action_titles[i]) + 5); sprintf(index, "%d", i); strcpy(line, index); strcat(line, ": "); - strcat(line, opt.actions[i]); + strcat(line, opt.action_titles[i]); gib_imlib_text_draw(im, fn, NULL, 2, (cur_action * line_th) + 2, line, @@ -1115,5 +1873,5 @@ void feh_draw_actions(winwidget w) gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0, 0 + th_offset, 1, 1, 0); gib_imlib_free_image_and_decache(im); - D_RETURN_(4); + return; } diff --git a/src/index.c b/src/index.c index c7c0a84..b0b6923 100644 --- a/src/index.c +++ b/src/index.c @@ -1,6 +1,7 @@ /* index.c Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2020 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -27,15 +28,14 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "filelist.h" #include "winwidget.h" #include "options.h" +#include "index.h" -static char *create_index_dimension_string(int w, int h); -static char *create_index_size_string(char *file); -static char *create_index_title_string(int num, int w, int h); /* TODO Break this up a bit ;) */ /* TODO s/bit/lot */ void init_index_mode(void) { + Imlib_Load_Error err; Imlib_Image im_main; Imlib_Image im_temp; int w = 800, h = 600, ww = 0, hh = 0, www, hhh, xxx, yyy; @@ -50,19 +50,16 @@ void init_index_mode(void) Imlib_Font title_fn = NULL; int text_area_w = 0; int tw = 0, th = 0; - int fw_name, fw_size, fw_dim, fw, fh; + int fw, fh; int vertical = 0; int max_column_w = 0; int thumbnailcount = 0; gib_list *l = NULL, *last = NULL; feh_file *file = NULL; - int lines; + int lineno; unsigned char trans_bg = 0; int index_image_width, index_image_height; - int x_offset_name = 0, x_offset_dim = 0, x_offset_size = 0; - char *s; - - D_ENTER(3); + gib_list *line, *lines; if (opt.montage) { mode = "montage"; @@ -70,14 +67,18 @@ void init_index_mode(void) mode = "index"; } - fn = gib_imlib_load_font(opt.font); + if (opt.font) + fn = gib_imlib_load_font(opt.font); + + if (!fn) + fn = gib_imlib_load_font(DEFAULT_FONT); if (opt.title_font) { - int fh, fw; title_fn = gib_imlib_load_font(opt.title_font); - if (!fn) + if (!title_fn) title_fn = gib_imlib_load_font(DEFAULT_FONT_TITLE); + gib_imlib_get_text_size(title_fn, "W", NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); title_area_h = fh + 4; } else @@ -88,8 +89,9 @@ void init_index_mode(void) /* Work out how tall the font is */ gib_imlib_get_text_size(fn, "W", NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT); + get_index_string_dim(NULL, fn, &fw, &fh); /* For now, allow room for the right number of lines with small gaps */ - text_area_h = ((th + 2) * (opt.index_show_name + opt.index_show_size + opt.index_show_dim)) + 5; + text_area_h = fh + 5; /* This includes the text area for index data */ tot_thumb_h = opt.thumb_h + text_area_h; @@ -99,7 +101,7 @@ void init_index_mode(void) if (!strcmp(opt.bg_file, "trans")) trans_bg = 1; else { - D(3, ("Time to apply a background to blend onto\n")); + D(("Time to apply a background to blend onto\n")); if (feh_load_image_char(&bg_im, opt.bg_file) != 0) { bg_w = gib_imlib_image_get_width(bg_im); bg_h = gib_imlib_image_get_height(bg_im); @@ -109,174 +111,52 @@ void init_index_mode(void) if (!opt.limit_w && !opt.limit_h) { if (bg_im) { - if (opt.verbose) - fprintf(stdout, - PACKAGE - " - No size restriction specified for index.\n" - " You did specify a background however, so the\n" - " index size has defaulted to the size of the image\n"); opt.limit_w = bg_w; opt.limit_h = bg_h; - } else { - if (opt.verbose) - fprintf(stdout, - PACKAGE - " - No size restriction specified for index.\n" - " Using defaults (width limited to 800)\n"); + } else opt.limit_w = 800; - } } /* Here we need to whiz through the files, and look at the filenames and info in the selected font, work out how much space we need, and calculate the size of the image we will require */ - if (opt.limit_w && opt.limit_h) { - int rec_h = 0; - + if (opt.limit_w) { w = opt.limit_w; - h = opt.limit_h; - - /* Work out if this is big enough, and give a warning if not */ - - /* Pretend we are limiting width by that specified, loop through, and - see it we fit in the height specified. If not, continue the loop, - and recommend the final value instead. Carry on and make the index - anyway. */ - - for (l = filelist; l; l = l->next) { - file = FEH_FILE(l->data); - text_area_w = opt.thumb_w; - if (opt.index_show_name) { - gib_imlib_get_text_size(fn, file->name, NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > text_area_w) - text_area_w = fw; - } - if (opt.index_show_dim) { - gib_imlib_get_text_size(fn, - create_index_dimension_string - (1000, 1000), NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > text_area_w) - text_area_w = fw; - } - if (opt.index_show_size) { - gib_imlib_get_text_size(fn, - create_index_size_string - (file->filename), NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > text_area_w) - text_area_w = fw; - } - if (text_area_w > opt.thumb_w) - text_area_w += 5; - if ((x > w - text_area_w)) { - x = 0; - y += tot_thumb_h; - } - - x += text_area_w; - } - rec_h = y + tot_thumb_h; - - if (h < rec_h) { - weprintf( - "The image size you specified (%d by %d) is not large\n" - "enough to hold all the thumnails you specified (%d). To fit all\n" - "the thumnails, either decrease their size, choose a smaller font,\n" - "or use a larger image (may I recommend %d by %d?)", - opt.limit_w, opt.limit_h, filelist_len, opt.limit_w, rec_h); + index_calculate_height(fn, w, &h, &tot_thumb_h); + + if (opt.limit_h) { + if (h > opt.limit_h) + weprintf( + "The image size you specified (%dx%d) is not large\n" + "enough to hold all %d thumbnails. To fit all the thumbnails,\n" + "either decrease their size, choose a smaller font,\n" + "or use a larger image (like %dx%d)", + opt.limit_w, opt.limit_h, filelist_len, w, h); + h = opt.limit_h; } } else if (opt.limit_h) { vertical = 1; h = opt.limit_h; - /* calc w */ - for (l = filelist; l; l = l->next) { - file = FEH_FILE(l->data); - text_area_w = opt.thumb_w; - /* Calc width of text */ - if (opt.index_show_name) { - gib_imlib_get_text_size(fn, file->name, NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > text_area_w) - text_area_w = fw; - } - if (opt.index_show_dim) { - gib_imlib_get_text_size(fn, - create_index_dimension_string - (1000, 1000), NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > text_area_w) - text_area_w = fw; - } - if (opt.index_show_size) { - gib_imlib_get_text_size(fn, - create_index_size_string - (file->filename), NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > text_area_w) - text_area_w = fw; - } - if (text_area_w > opt.thumb_w) - text_area_w += 5; - - if (text_area_w > max_column_w) - max_column_w = text_area_w; - - if ((y > h - tot_thumb_h)) { - y = 0; - x += max_column_w; - max_column_w = 0; - } - - y += tot_thumb_h; - } - w = x + text_area_w; - max_column_w = 0; - } else if (opt.limit_w) { - w = opt.limit_w; - /* calc h */ - - for (l = filelist; l; l = l->next) { - file = FEH_FILE(l->data); - text_area_w = opt.thumb_w; - if (opt.index_show_name) { - gib_imlib_get_text_size(fn, file->name, NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > text_area_w) - text_area_w = fw; - } - if (opt.index_show_dim) { - gib_imlib_get_text_size(fn, - create_index_dimension_string - (1000, 1000), NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > text_area_w) - text_area_w = fw; - } - if (opt.index_show_size) { - gib_imlib_get_text_size(fn, - create_index_size_string - (file->filename), NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > text_area_w) - text_area_w = fw; - } - - if (text_area_w > opt.thumb_w) - text_area_w += 5; - - if ((x > w - text_area_w)) { - x = 0; - y += tot_thumb_h; - } - x += text_area_w; - } - h = y + tot_thumb_h; + index_calculate_width(fn, &w, h, &tot_thumb_h); } - x = y = 0; - index_image_width = w; index_image_height = h + title_area_h; im_main = imlib_create_image(index_image_width, index_image_height); - if (!im_main) - eprintf("Imlib error creating index image, are you low on RAM?"); + if (!im_main) { + if (index_image_height >= 32768 || index_image_width >= 32768) { + eprintf("Failed to create %dx%d pixels (%d MB) index image.\n" + "This is probably due to Imlib2 issues when dealing with images larger than 32k x 32k pixels.", + index_image_width, index_image_height, index_image_width * index_image_height * 4 / (1024*1024)); + } else { + eprintf("Failed to create %dx%d pixels (%d MB) index image. Do you have enough RAM?", + index_image_width, index_image_height, index_image_width * index_image_height * 4 / (1024*1024)); + } + } if (bg_im) gib_imlib_blend_image_onto_image(im_main, bg_im, @@ -290,15 +170,9 @@ void init_index_mode(void) gib_imlib_image_fill_rectangle(im_main, 0, 0, w, h + title_area_h, 0, 0, 0, 255); } - /* Create the window title at this point */ - - if (!opt.title) - s = estrdup(PACKAGE " [index mode]"); - else - s = estrdup(feh_printf(opt.title, NULL)); - if (opt.display) { - winwid = winwidget_create_from_image(im_main, s, WIN_TYPE_SINGLE); + winwid = winwidget_create_from_image(im_main, WIN_TYPE_SINGLE); + winwidget_rename(winwid, PACKAGE " [index mode]"); winwidget_show(winwid); } @@ -308,11 +182,11 @@ void init_index_mode(void) filelist = feh_file_remove_from_list(filelist, last); last = NULL; } - D(4, ("About to load image %s\n", file->filename)); + D(("About to load image %s\n", file->filename)); if (feh_load_image(&im_temp, file) != 0) { if (opt.verbose) feh_display_status('.'); - D(4, ("Successfully loaded %s\n", file->filename)); + D(("Successfully loaded %s\n", file->filename)); www = opt.thumb_w; hhh = opt.thumb_h; ww = gib_imlib_image_get_width(im_temp); @@ -343,7 +217,7 @@ void init_index_mode(void) if (opt.alpha) { DATA8 atab[256]; - D(3, ("Applying alpha options\n")); + D(("Applying alpha options\n")); gib_imlib_image_set_has_alpha(im_thumb, 1); memset(atab, opt.alpha_level, sizeof(atab)); gib_imlib_apply_color_modifier_to_rectangle @@ -352,33 +226,14 @@ void init_index_mode(void) text_area_w = opt.thumb_w; /* Now draw on the info text */ - if (opt.index_show_name) { - gib_imlib_get_text_size(fn, file->name, NULL, &fw_name, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw_name > text_area_w) - text_area_w = fw_name; - } - if (opt.index_show_dim) { - gib_imlib_get_text_size(fn, - create_index_dimension_string - (ww, hh), NULL, &fw_dim, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw_dim > text_area_w) - text_area_w = fw_dim; - } - if (opt.index_show_size) { - gib_imlib_get_text_size(fn, - create_index_size_string - (file->filename), NULL, &fw_size, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw_size > text_area_w) - text_area_w = fw_size; + if (opt.index_info) { + get_index_string_dim(file, fn, &fw, &fh); + if (fw > text_area_w) + text_area_w = fw; } if (text_area_w > opt.thumb_w) text_area_w += 5; - /* offsets for centering text */ - x_offset_name = (text_area_w - fw_name) / 2; - x_offset_dim = (text_area_w - fw_dim) / 2; - x_offset_size = (text_area_w - fw_size) / 2; - if (vertical) { if (text_area_w > max_column_w) max_column_w = text_area_w; @@ -398,14 +253,12 @@ void init_index_mode(void) break; } - if (opt.aspect) { - xxx = x + ((opt.thumb_w - www) / 2); - yyy = y + ((opt.thumb_h - hhh) / 2); - } else { - /* Ignore the aspect ratio and squash the image in */ - xxx = x; - yyy = y; - } + /* center image relative to the text below it (if any) */ + xxx = x + ((text_area_w - www) / 2); + yyy = y; + + if (opt.aspect) + yyy += (opt.thumb_h - hhh) / 2; /* Draw now */ gib_imlib_blend_image_onto_image(im_main, im_thumb, @@ -416,29 +269,23 @@ void init_index_mode(void) gib_imlib_free_image_and_decache(im_thumb); - lines = 0; - if (opt.index_show_name) - gib_imlib_text_draw(im_main, fn, NULL, - x + x_offset_name, - y + opt.thumb_h + - (lines++ * (th + 2)) + - 2, file->name, IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); - if (opt.index_show_dim) - gib_imlib_text_draw(im_main, fn, NULL, - x + x_offset_dim, - y + opt.thumb_h + - (lines++ * (th + 2)) + - 2, - create_index_dimension_string - (ww, hh), IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); - if (opt.index_show_size) - gib_imlib_text_draw(im_main, fn, NULL, - x + x_offset_size, - y + opt.thumb_h + - (lines++ * (th + 2)) + - 2, - create_index_size_string - (file->filename), IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); + lineno = 0; + if (opt.index_info) { + line = lines = feh_wrap_string(create_index_string(file), + opt.thumb_w * 3, fn, NULL); + + while (line) { + gib_imlib_get_text_size(fn, (char *) line->data, + NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + gib_imlib_text_draw(im_main, fn, NULL, + x + ((text_area_w - fw) >> 1), + y + opt.thumb_h + (lineno++ * (th + 2)) + 2, + (char *) line->data, + IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); + line = line->next; + } + gib_list_free_and_data(lines); + } if (vertical) y += tot_thumb_h; @@ -457,7 +304,7 @@ void init_index_mode(void) } } if (opt.verbose) - fprintf(stdout, "\n"); + putc('\n', stderr); if (opt.title_font) { int fw, fh, fx, fy; @@ -468,6 +315,9 @@ void init_index_mode(void) fx = (index_image_width - fw) >> 1; fy = index_image_height - fh - 2; gib_imlib_text_draw(im_main, title_fn, NULL, fx, fy, s, IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); + + if (opt.display) + winwidget_render_image(winwid, 0, 0); } if (opt.output && opt.output_file) { @@ -475,17 +325,22 @@ void init_index_mode(void) if (opt.output_dir) snprintf(output_buf, 1024, "%s/%s", opt.output_dir, opt.output_file); - else - strncpy(output_buf, opt.output_file, 1024); + else { + strncpy(output_buf, opt.output_file, 1023); + output_buf[1023] = '\0'; + } - gib_imlib_save_image(im_main, output_buf); - if (opt.verbose) { + gib_imlib_save_image_with_error_return(im_main, output_buf, &err); + if (err) { + feh_print_load_error(output_buf, im_main, err, LOAD_ERROR_IMLIB); + } + else if (opt.verbose) { int tw, th; tw = gib_imlib_image_get_width(im_main); th = gib_imlib_image_get_height(im_main); - fprintf(stdout, PACKAGE " - File saved as %s\n", output_buf); - fprintf(stdout, + fprintf(stderr, PACKAGE " - File saved as %s\n", output_buf); + fprintf(stderr, " - Image is %dx%d pixels and contains %d thumbnails\n", tw, th, thumbnailcount); } } @@ -493,49 +348,136 @@ void init_index_mode(void) if (!opt.display) gib_imlib_free_image_and_decache(im_main); - free(s); - D_RETURN_(3); + return; +} + +void index_calculate_height(Imlib_Font fn, int w, int *h, int *tot_thumb_h) +{ + gib_list *l; + feh_file *file = NULL; + int x = 0, y = 0; + int fw = 0, fh = 0; + int text_area_w = 0, text_area_h = 0; + + for (l = filelist; l; l = l->next) { + file = FEH_FILE(l->data); + text_area_w = opt.thumb_w; + if (opt.index_info) { + get_index_string_dim(file, fn, &fw, &fh); + if (fw > text_area_w) + text_area_w = fw; + if (fh > text_area_h) { + text_area_h = fh + 5; + *tot_thumb_h = opt.thumb_h + text_area_h; + } + } + + if (text_area_w > opt.thumb_w) + text_area_w += 5; + + if ((x > w - text_area_w)) { + x = 0; + y += *tot_thumb_h; + } + + x += text_area_w; + } + *h = y + *tot_thumb_h; } -char *chop_file_from_full_path(char *str) +void index_calculate_width(Imlib_Font fn, int *w, int h, int *tot_thumb_h) { - D_ENTER(4); - D_RETURN(4, strrchr(str, '/') + 1); + gib_list *l; + feh_file *file = NULL; + int x = 0, y = 0; + int fw = 0, fh = 0; + int text_area_w = 0, text_area_h = 0; + int max_column_w = 0; + + for (l = filelist; l; l = l->next) { + file = FEH_FILE(l->data); + text_area_w = opt.thumb_w; + /* Calc width of text */ + if (opt.index_info) { + get_index_string_dim(file, fn, &fw, &fh); + if (fw > text_area_w) + text_area_w = fw; + if (fh > text_area_h) { + text_area_h = fh + 5; + *tot_thumb_h = opt.thumb_h + text_area_h; + } + } + if (text_area_w > opt.thumb_w) + text_area_w += 5; + + if (text_area_w > max_column_w) + max_column_w = text_area_w; + + if ((y > h - *tot_thumb_h)) { + y = 0; + x += max_column_w; + max_column_w = 0; + } + + y += *tot_thumb_h; + } + *w = x + text_area_w; } -static char *create_index_size_string(char *file) +void get_index_string_dim(feh_file *file, Imlib_Font fn, int *fw, int *fh) { - static char str[50]; - int size = 0; - double kbs = 0.0; - struct stat st; - - D_ENTER(4); - if (stat(file, &st)) - kbs = 0.0; - else { - size = st.st_size; - kbs = (double) size / 1000; + int line_w, line_h; + char fake_file = 0; + gib_list *line, *lines; + int max_w = 0, total_h = 0; + + if (!opt.index_info) { + *fw = 0; + *fh = 0; + return; + } + + /* called with file = NULL in the setup phase. + * We need a fake file, otherwise feh_printf will remove format specifiers, + * leading e.g. to a 0x0 report for index_dim = "%n". + */ + if (file == NULL) { + fake_file = 1; + file = feh_file_new("foo"); + file->info = feh_file_info_new(); } - snprintf(str, sizeof(str), "%.2fKb", kbs); - D_RETURN(4, str); + line = lines = feh_wrap_string(create_index_string(file), opt.thumb_w * 3, fn, NULL); + + while (line) { + gib_imlib_get_text_size(fn, (char *) line->data, + NULL, &line_w, &line_h, IMLIB_TEXT_TO_RIGHT); + + if (line_w > max_w) + max_w = line_w; + total_h += line_h + 2; + + line = line->next; + } + + gib_list_free_and_data(lines); + if (fake_file) + feh_file_free(file); + + *fw = max_w; + *fh = total_h; + return; } -static char *create_index_dimension_string(int w, int h) +char *create_index_string(feh_file * file) { - static char str[50]; - - D_ENTER(4); - snprintf(str, sizeof(str), "%dx%d", w, h); - D_RETURN(4, str); + return feh_printf(opt.index_info, file, NULL); } -static char *create_index_title_string(int num, int w, int h) +char *create_index_title_string(int num, int w, int h) { static char str[50]; - D_ENTER(4); snprintf(str, sizeof(str), PACKAGE " index - %d thumbnails, %d by %d pixels", num, w, h); - D_RETURN(4, str); + return(str); } diff --git a/src/index.h b/src/index.h new file mode 100644 index 0000000..b805cc0 --- /dev/null +++ b/src/index.h @@ -0,0 +1,35 @@ +/* index.h + +Copyright (C) 2018 Birte Kristina Friesel. + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + +#ifndef INDEX_H +#define INDEX_H + +char *create_index_string(feh_file *file); +char *create_index_title_string(int num, int w, int h); +void get_index_string_dim(feh_file *file, Imlib_Font fn, int *w, int *h); +void index_calculate_height(Imlib_Font fn, int w, int *h, int *tot_thumb_w); +void index_calculate_width(Imlib_Font fn, int *w, int h, int *tot_thumb_h); + +#endif diff --git a/src/ipc.c b/src/ipc.c deleted file mode 100644 index 5299404..0000000 --- a/src/ipc.c +++ /dev/null @@ -1,78 +0,0 @@ -/* ipc.c - -Copyright (C) 1999-2003 Tom Gilbert. - -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 of the Software and its documentation and acknowledgment shall be -given in the documentation and software packages that this Software was -used. - -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 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. - -*/ - -#include "feh.h" -#include "debug.h" -#include "options.h" - -static int session_id = 0; -static char *socket_name; -static int socket_fd = 0; - -void feh_ipc_create_socket(void) -{ - struct sockaddr_un saddr; - int i; - - if ((socket_fd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1) { - for (i = 0;; i++) { - saddr.sun_family = AF_UNIX; - snprintf(saddr.sun_path, 108, "%s/feh_%s.%d", feh_get_tmp_dir(), feh_get_user_name(), i); -/* - if (!feh_remote_is_running(i)) { - if ((unlink(saddr.sun_path) == -1) && errno != ENOENT) { - close(socket_fd); - eprintf("feh_ipc_create_socket: failed to unlink %s:", saddr.sun_path); - } - } else { - continue; - } -*/ - if (bind(socket_fd, (struct sockaddr *) &saddr, sizeof(saddr)) != -1) { - session_id = i; - socket_name = estrdup(saddr.sun_path); - listen(socket_fd, 50); - break; - } else { - close(socket_fd); - eprintf("feh_ipc_create_socket: failed to bind %s to a socket:", saddr.sun_path); - } - } - } else { - eprintf("feh_ipc_create_socket: failed to open socket:"); - } -} - -int feh_ipc_get_session_id(void) -{ - return session_id; -} - -void feh_ipc_cleanup(void) -{ - close(socket_fd); - unlink(socket_name); - free(socket_name); -} diff --git a/src/keyevents.c b/src/keyevents.c index 3f3cdcc..2f9b1d6 100644 --- a/src/keyevents.c +++ b/src/keyevents.c @@ -1,6 +1,7 @@ /* keyevents.c Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2020 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -28,41 +29,352 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "filelist.h" #include "winwidget.h" #include "options.h" +#include <termios.h> -void feh_event_invoke_action(winwidget winwid, char *action) +struct __fehkey keys[EVENT_LIST_END]; +struct termios old_term_settings; +unsigned char control_via_stdin = 0; + +void setup_stdin(void) { + struct termios ctrl; + + control_via_stdin = 1; + + if (tcgetattr(STDIN_FILENO, &old_term_settings) == -1) + eprintf("tcgetattr failed"); + if (tcgetattr(STDIN_FILENO, &ctrl) == -1) + eprintf("tcgetattr failed"); + + ctrl.c_iflag &= ~(PARMRK | ISTRIP + | INLCR | IGNCR | IXON); + ctrl.c_lflag &= ~(ECHO | ICANON | IEXTEN); + ctrl.c_cflag &= ~(CSIZE | PARENB); + ctrl.c_cflag |= CS8; + + if (tcsetattr(STDIN_FILENO, TCSANOW, &ctrl) == -1) + eprintf("tcsetattr failed"); +} + +void restore_stdin(void) { + if (tcsetattr(STDIN_FILENO, TCSANOW, &old_term_settings) == -1) + eprintf("tcsetattr failed"); +} + +static void feh_set_kb(char *name, unsigned int s0, unsigned int y0, + unsigned int s1, unsigned int y1, unsigned int s2, unsigned int y2) { + static int key_index = 0; + fehkey *key = &keys[key_index]; + key->keystates[0] = s0; + key->keystates[1] = s1; + key->keystates[2] = s2; + key->keysyms[0] = y0; + key->keysyms[1] = y1; + key->keysyms[2] = y2; + key->state = 0; + key->button = 0; + key->name = name; + key_index++; +} + +static inline int ignore_space(int keysym) { + /* + * Passing values which do not fit inside a signed 8bit char to isprint, + * isspace and the likes is undefined behaviour... which glibc (for some + * values) implements as a segmentation fault. So let's not do that. + */ + return ((keysym <= 127) && (keysym >= -128) && isprint(keysym) && !isspace(keysym)); +} + +static void feh_set_parse_kb_partial(fehkey *key, int index, char *ks) { + char *cur = ks; + int mod = 0; + + if (!*ks) { + key->keysyms[index] = 0; + return; + } + + while (cur[1] == '-') { + switch (cur[0]) { + case 'C': + mod |= ControlMask; + break; + case 'S': + mod |= ShiftMask; + break; + case '1': + mod |= Mod1Mask; + break; + case '4': + mod |= Mod4Mask; + break; + default: + weprintf("keys: invalid modifier %c in \"%s\"", cur[0], ks); + break; + } + cur += 2; + } + + key->keysyms[index] = XStringToKeysym(cur); + if (ignore_space(key->keysyms[index])) + mod &= ~ShiftMask; + key->keystates[index] = mod; + + if (key->keysyms[index] == NoSymbol) + weprintf("keys: Invalid keysym: %s", cur); +} + +void init_keyevents(void) { + char *home = NULL; + char *confhome = NULL; + char *confpath = NULL; + char line[128]; + char action[32], k1[32], k2[32], k3[32]; + fehkey *cur_kb = NULL; + FILE *conf = NULL; + int read = 0; + + /* + * The feh_set_kb statements must have the same order as the key_action + * enum. + */ + + feh_set_kb("menu_close" , 0, XK_Escape , 0, 0 , 0, 0); + feh_set_kb("menu_parent", 0, XK_Left , 0, 0 , 0, 0); + feh_set_kb("menu_down", 0, XK_Down , 0, 0 , 0, 0); + feh_set_kb("menu_up", 0, XK_Up , 0, 0 , 0, 0); + feh_set_kb("menu_child", 0, XK_Right , 0, 0 , 0, 0); + feh_set_kb("menu_select", 0, XK_Return , 0, XK_space , 0, 0); + feh_set_kb("scroll_left",0, XK_KP_Left , 4, XK_Left , 0, 0); + feh_set_kb("scroll_right", 0,XK_KP_Right , 4, XK_Right , 0, 0); + feh_set_kb("scroll_down",0, XK_KP_Down , 4, XK_Down , 0, 0); + feh_set_kb("scroll_up", 0, XK_KP_Up , 4, XK_Up , 0, 0); + feh_set_kb("scroll_left_page" , 8, XK_Left , 0, 0 , 0, 0); + feh_set_kb("scroll_right_page", 8, XK_Right, 0, 0 , 0, 0); + feh_set_kb("scroll_down_page" , 8, XK_Down , 0, 0 , 0, 0); + feh_set_kb("scroll_up_page" , 8, XK_Up , 0, 0 , 0, 0); + feh_set_kb("prev_img" , 0, XK_Left , 0, XK_p , 0, XK_BackSpace); + feh_set_kb("next_img" , 0, XK_Right , 0, XK_n , 0, XK_space); + feh_set_kb("jump_back" , 0, XK_Page_Up , 0, XK_KP_Page_Up, 0, 0); + feh_set_kb("jump_fwd" , 0, XK_Page_Down , 0, XK_KP_Page_Down,0,0); + feh_set_kb("prev_dir" , 0, XK_bracketleft, 0, 0 , 0, 0); + feh_set_kb("next_dir" , 0, XK_bracketright, 0, 0 , 0, 0); + feh_set_kb("jump_random" ,0, XK_z , 0, 0 , 0, 0); + feh_set_kb("quit" , 0, XK_Escape , 0, XK_q , 0, 0); + feh_set_kb("close" , 0, XK_x , 0, 0 , 0, 0); + feh_set_kb("remove" , 0, XK_Delete , 0, 0 , 0, 0); + feh_set_kb("delete" , 4, XK_Delete , 0, 0 , 0, 0); + feh_set_kb("jump_first" , 0, XK_Home , 0, XK_KP_Home , 0, 0); + feh_set_kb("jump_last" , 0, XK_End , 0, XK_KP_End , 0, 0); + feh_set_kb("action_0" , 0, XK_Return , 0, XK_0 , 0, XK_KP_0); + feh_set_kb("action_1" , 0, XK_1 , 0, XK_KP_1 , 0, 0); + feh_set_kb("action_2" , 0, XK_2 , 0, XK_KP_2 , 0, 0); + feh_set_kb("action_3" , 0, XK_3 , 0, XK_KP_3 , 0, 0); + feh_set_kb("action_4" , 0, XK_4 , 0, XK_KP_4 , 0, 0); + feh_set_kb("action_5" , 0, XK_5 , 0, XK_KP_5 , 0, 0); + feh_set_kb("action_6" , 0, XK_6 , 0, XK_KP_6 , 0, 0); + feh_set_kb("action_7" , 0, XK_7 , 0, XK_KP_7 , 0, 0); + feh_set_kb("action_8" , 0, XK_8 , 0, XK_KP_8 , 0, 0); + feh_set_kb("action_9" , 0, XK_9 , 0, XK_KP_9 , 0, 0); + feh_set_kb("zoom_in" , 0, XK_Up , 0, XK_KP_Add , 0, 0); + feh_set_kb("zoom_out" , 0, XK_Down , 0, XK_KP_Subtract,0, 0); + feh_set_kb("zoom_default" , 0, XK_KP_Multiply, 0, XK_asterisk,0, 0); + feh_set_kb("zoom_fit" , 0, XK_KP_Divide , 0, XK_slash , 0, 0); + feh_set_kb("zoom_fill" , 0, XK_exclam , 0, 0 , 0, 0); + feh_set_kb("size_to_image" , 0, XK_w , 0, 0 , 0, 0); + feh_set_kb("render" , 0, XK_KP_Begin , 0, XK_R , 0, 0); + feh_set_kb("toggle_actions" , 0, XK_a, 0, 0, 0, 0); + feh_set_kb("toggle_aliasing" , 0, XK_A, 0, 0, 0, 0); + feh_set_kb("toggle_auto_zoom" , 0, XK_Z, 0, 0, 0, 0); +#ifdef HAVE_LIBEXIF + feh_set_kb("toggle_exif" , 0, XK_e, 0, 0, 0, 0); +#endif + feh_set_kb("toggle_filenames" , 0, XK_d, 0, 0, 0, 0); + feh_set_kb("toggle_info" , 0, XK_i, 0, 0, 0, 0); + feh_set_kb("toggle_pointer" , 0, XK_o, 0, 0, 0, 0); + feh_set_kb("toggle_caption" , 0, XK_c, 0, 0, 0, 0); + feh_set_kb("toggle_pause" , 0, XK_h, 0, 0, 0, 0); + feh_set_kb("toggle_menu" , 0, XK_m, 0, 0, 0, 0); + feh_set_kb("toggle_fullscreen" , 0, XK_f, 0, 0, 0, 0); + feh_set_kb("reload_image" , 0, XK_r, 0, 0, 0, 0); + feh_set_kb("save_image" , 0, XK_s, 0, 0, 0, 0); + feh_set_kb("save_filelist" , 0, XK_L, 0, 0, 0, 0); + feh_set_kb("orient_1" , 0, XK_greater, 0, 0, 0, 0); + feh_set_kb("orient_3" , 0, XK_less, 0, 0, 0, 0); + feh_set_kb("flip" , 0, XK_underscore, 0, 0, 0, 0); + feh_set_kb("mirror" , 0, XK_bar, 0, 0, 0, 0); + feh_set_kb("reload_minus" , 0, XK_minus, 0, 0, 0, 0); + feh_set_kb("reload_plus" , 0, XK_plus, 0, 0, 0, 0); + feh_set_kb("toggle_keep_vp" , 0, XK_k, 0, 0, 0, 0); + feh_set_kb("toggle_fixed_geometry" , 0, XK_g, 0, 0, 0, 0); + feh_set_kb("pan" , 0, 0, 0, 0, 0, 0); + feh_set_kb("zoom" , 0, 0, 0, 0, 0, 0); + feh_set_kb("blur" , 0, 0, 0, 0, 0, 0); + feh_set_kb("rotate" , 0, 0, 0, 0, 0, 0); + + home = getenv("HOME"); + confhome = getenv("XDG_CONFIG_HOME"); + + if (confhome) + confpath = estrjoin("/", confhome, "feh/keys", NULL); + else if (home) + confpath = estrjoin("/", home, ".config/feh/keys", NULL); + else + return; + + conf = fopen(confpath, "r"); + + free(confpath); + + if (!conf && ((conf = fopen("/etc/feh/keys", "r")) == NULL)) + return; + + while (fgets(line, sizeof(line), conf)) { + *action = '\0'; + *k1 = '\0'; + *k2 = '\0'; + *k3 = '\0'; + cur_kb = NULL; + + read = sscanf(line, "%31s %31s %31s %31s\n", + (char *) &action, (char *) &k1, (char* ) &k2, (char *) &k3); + + if ((read == EOF) || (read == 0) || (line[0] == '#')) + continue; + + cur_kb = feh_str_to_kb(action); + + if (cur_kb) { + feh_set_parse_kb_partial(cur_kb, 0, k1); + feh_set_parse_kb_partial(cur_kb, 1, k2); + feh_set_parse_kb_partial(cur_kb, 2, k3); + } else { + weprintf("keys: Invalid action: %s", action); + } + } + fclose(conf); +} + +static short feh_is_kp(unsigned int key_index, unsigned int state, + unsigned int sym, unsigned int button) { + int i; + + if (sym != NoSymbol) { + for (i = 0; i < 3; i++) { + if ( + (keys[key_index].keysyms[i] == sym) && + (keys[key_index].keystates[i] == state)) + return 1; + else if (keys[key_index].keysyms[i] == 0) + return 0; + } + return 0; + } + if ((keys[key_index].state == state) + && (keys[key_index].button == button)) { + return 1; + } + return 0; +} + +void feh_event_invoke_action(winwidget winwid, unsigned char action) { - D_ENTER(4); - D(4, ("action is '%s'\n", action)); - D(4, ("winwid is '%p'\n", winwid)); - if (action) { + struct stat st; + if (opt.actions[action]) { if (opt.slideshow) { - feh_action_run(FEH_FILE(winwid->file->data), action); - slideshow_change_image(winwid, SLIDE_NEXT); + feh_action_run(FEH_FILE(winwid->file->data), opt.actions[action], winwid); + + if (opt.hold_actions[action]) + feh_reload_image(winwid, 1, 1); + else if (stat(FEH_FILE(winwid->file->data)->filename, &st) == -1) + feh_filelist_image_remove(winwid, 0); + else + slideshow_change_image(winwid, SLIDE_NEXT, 1); + } else if ((winwid->type == WIN_TYPE_SINGLE) || (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER)) { - feh_action_run(FEH_FILE(winwid->file->data), action); - winwidget_destroy(winwid); + feh_action_run(FEH_FILE(winwid->file->data), opt.actions[action], winwid); + + if (opt.hold_actions[action]) + feh_reload_image(winwid, 1, 1); + else + winwidget_destroy(winwid); } else if (winwid->type == WIN_TYPE_THUMBNAIL) { - printf("actions from the main thumb window aren't currentl supported!\n"); - printf("For now, open the image to perform the action on it.\n"); + feh_file *thumbfile; + thumbfile = feh_thumbnail_get_selected_file(); + + if (thumbfile) { + feh_action_run(thumbfile, opt.actions[action], winwid); + + if (!opt.hold_actions[action]) + feh_thumbnail_mark_removed(thumbfile, 0); + } } } - D_RETURN_(4); + return; +} + +void feh_event_handle_stdin(void) +{ + char stdin_buf[2]; + static char is_esc = 0; + KeySym keysym = NoSymbol; + 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"); + restore_stdin(); + } + return; + } + stdin_buf[1] = '\0'; + + // escape? + if (stdin_buf[0] == 0x1b) { + is_esc = 1; + return; + } + if ((is_esc == 1) && (stdin_buf[0] == '[')) { + is_esc = 2; + return; + } + + if (stdin_buf[0] == ' ') + keysym = XK_space; + else if (stdin_buf[0] == '\n') + keysym = XK_Return; + else if ((stdin_buf[0] == '\b') || (stdin_buf[0] == 127)) + keysym = XK_BackSpace; + else if (is_esc == 2) { + if (stdin_buf[0] == 'A') + keysym = XK_Up; + else if (stdin_buf[0] == 'B') + keysym = XK_Down; + else if (stdin_buf[0] == 'C') + keysym = XK_Right; + else if (stdin_buf[0] == 'D') + keysym = XK_Left; + is_esc = 0; + } + else + keysym = XStringToKeysym(stdin_buf); + + if (window_num && keysym) + feh_event_handle_generic(windows[0], is_esc * Mod1Mask, keysym, 0); + + is_esc = 0; } void feh_event_handle_keypress(XEvent * ev) { - int len; + int state; char kbuf[20]; KeySym keysym; XKeyEvent *kev; winwidget winwid = NULL; - int curr_screen = 0; feh_menu_item *selected_item; feh_menu *selected_menu; - D_ENTER(4); - winwid = winwidget_get_from_window(ev->xkey.window); /* nuke dupe events, unless we're typing text */ @@ -71,67 +383,53 @@ void feh_event_handle_keypress(XEvent * ev) } kev = (XKeyEvent *) ev; - len = XLookupString(&ev->xkey, (char *) kbuf, sizeof(kbuf), &keysym, NULL); + XLookupString(&ev->xkey, (char *) kbuf, sizeof(kbuf), &keysym, NULL); + state = kev->state & (ControlMask | ShiftMask | Mod1Mask | Mod4Mask); + + if (ignore_space(keysym)) + state &= ~ShiftMask; /* menus are showing, so this is a menu control keypress */ if (ev->xbutton.window == menu_cover) { selected_item = feh_menu_find_selected_r(menu_root, &selected_menu); - switch (keysym) { - case XK_Escape: + if (feh_is_kp(EVENT_menu_close, state, keysym, 0)) feh_menu_hide(menu_root, True); - break; - case XK_Left: - feh_menu_select_parent(selected_menu, selected_item); - break; - case XK_Down: + else if (feh_is_kp(EVENT_menu_parent, state, keysym, 0)) + feh_menu_select_parent(selected_menu); + else if (feh_is_kp(EVENT_menu_down, state, keysym, 0)) feh_menu_select_next(selected_menu, selected_item); - break; - case XK_Up: + else if (feh_is_kp(EVENT_menu_up, state, keysym, 0)) feh_menu_select_prev(selected_menu, selected_item); - break; - case XK_Right: - feh_menu_select_submenu(selected_menu, selected_item); - break; - case XK_Return: + else if (feh_is_kp(EVENT_menu_child, state, keysym, 0)) + feh_menu_select_submenu(selected_menu); + else if (feh_is_kp(EVENT_menu_select, state, keysym, 0)) feh_menu_item_activate(selected_menu, selected_item); - break; - default: - break; - } - if (len <= 0 || len > (int) sizeof(kbuf)) - D_RETURN_(4); - kbuf[len] = '\0'; + return; + } - switch (*kbuf) { - case 'h': - feh_menu_select_parent(selected_menu, selected_item); - break; - case 'j': - feh_menu_select_next(selected_menu, selected_item); - break; - case 'k': - feh_menu_select_prev(selected_menu, selected_item); - break; - case 'l': - feh_menu_select_submenu(selected_menu, selected_item); - break; - case ' ': - feh_menu_item_activate(selected_menu, selected_item); - break; - default: - break; - } + if (winwid == NULL) + return; + + feh_event_handle_generic(winwid, state, keysym, 0); +} - D_RETURN_(4); +fehkey *feh_str_to_kb(char *action) +{ + for (unsigned int i = 0; i < EVENT_LIST_END; i++) { + if (!strcmp(action, keys[i].name)) { + return &keys[i]; + } } + return NULL; +} - if (winwid == NULL) - D_RETURN_(4); +void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysym, unsigned int button) { + int curr_screen = 0; - if (winwid->caption_entry) { + if (winwid->caption_entry && (keysym != NoSymbol)) { switch (keysym) { case XK_Return: - if (kev->state & ControlMask) { + if (state & ControlMask) { /* insert actual newline */ ESTRAPPEND(FEH_FILE(winwid->file->data)->caption, "\n"); winwidget_render_image_cached(winwid); @@ -139,15 +437,16 @@ void feh_event_handle_keypress(XEvent * ev) /* finish caption entry, write to captions file */ FILE *fp; char *caption_filename; - caption_filename = build_caption_filename(FEH_FILE(winwid->file->data)); + caption_filename = + build_caption_filename(FEH_FILE(winwid->file->data), 1); winwid->caption_entry = 0; winwidget_render_image_cached(winwid); XFreePixmap(disp, winwid->bg_pmap_cache); winwid->bg_pmap_cache = 0; fp = fopen(caption_filename, "w"); if (!fp) { - weprintf("couldn't write to captions file %s:", caption_filename); - D_RETURN_(4); + eprintf("couldn't write to captions file %s:", caption_filename); + return; } fprintf(fp, "%s", FEH_FILE(winwid->file->data)->caption); free(caption_filename); @@ -176,223 +475,283 @@ void feh_event_handle_keypress(XEvent * ev) } break; } - D_RETURN_(4); + return; } - switch (keysym) { - case XK_Left: + if (feh_is_kp(EVENT_next_img, state, keysym, button)) { if (opt.slideshow) - slideshow_change_image(winwid, SLIDE_PREV); - break; - case XK_Right: + slideshow_change_image(winwid, SLIDE_NEXT, 1); + else if (winwid->type == WIN_TYPE_THUMBNAIL) + feh_thumbnail_select_next(winwid, 1); + } + else if (feh_is_kp(EVENT_prev_img, state, keysym, button)) { if (opt.slideshow) - slideshow_change_image(winwid, SLIDE_NEXT); - break; - case XK_Page_Up: - case XK_KP_Page_Up: + slideshow_change_image(winwid, SLIDE_PREV, 1); + else if (winwid->type == WIN_TYPE_THUMBNAIL) + feh_thumbnail_select_prev(winwid, 1); + } + else if (feh_is_kp(EVENT_scroll_right, state, keysym, button)) { + winwid->im_x -= opt.scroll_step;; + winwidget_sanitise_offsets(winwid); + winwidget_render_image(winwid, 0, 1); + } + else if (feh_is_kp(EVENT_scroll_left, state, keysym, button)) { + winwid->im_x += opt.scroll_step; + winwidget_sanitise_offsets(winwid); + winwidget_render_image(winwid, 0, 1); + } + else if (feh_is_kp(EVENT_scroll_down, state, keysym, button)) { + winwid->im_y -= opt.scroll_step; + winwidget_sanitise_offsets(winwid); + winwidget_render_image(winwid, 0, 1); + } + else if (feh_is_kp(EVENT_scroll_up, state, keysym, button)) { + winwid->im_y += opt.scroll_step; + winwidget_sanitise_offsets(winwid); + winwidget_render_image(winwid, 0, 1); + } + else if (feh_is_kp(EVENT_scroll_right_page, state, keysym, button)) { + winwid->im_x -= winwid->w; + winwidget_sanitise_offsets(winwid); + winwidget_render_image(winwid, 0, 0); + } + else if (feh_is_kp(EVENT_scroll_left_page, state, keysym, button)) { + winwid->im_x += winwid->w; + winwidget_sanitise_offsets(winwid); + winwidget_render_image(winwid, 0, 0); + } + else if (feh_is_kp(EVENT_scroll_down_page, state, keysym, button)) { + winwid->im_y -= winwid->h; + winwidget_sanitise_offsets(winwid); + winwidget_render_image(winwid, 0, 0); + } + else if (feh_is_kp(EVENT_scroll_up_page, state, keysym, button)) { + winwid->im_y += winwid->h; + winwidget_sanitise_offsets(winwid); + winwidget_render_image(winwid, 0, 0); + } + else if (feh_is_kp(EVENT_jump_back, state, keysym, button)) { if (opt.slideshow) - slideshow_change_image(winwid, SLIDE_JUMP_BACK); - break; - case XK_Escape: - winwidget_destroy_all(); - break; - case XK_Page_Down: - case XK_KP_Page_Down: + slideshow_change_image(winwid, SLIDE_JUMP_BACK, 1); + else if (winwid->type == WIN_TYPE_THUMBNAIL) + feh_thumbnail_select_prev(winwid, 10); + } + else if (feh_is_kp(EVENT_jump_fwd, state, keysym, button)) { if (opt.slideshow) - slideshow_change_image(winwid, SLIDE_JUMP_FWD); - break; - case XK_Delete: - /* Holding ctrl gets you a filesystem deletion and removal from the * - filelist. Just DEL gets you filelist removal only. */ - if (kev->state & ControlMask) { - if (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER) - feh_thumbnail_mark_removed(FEH_FILE(winwid->file->data), 1); - feh_filelist_image_remove(winwid, 1); - } else { - if (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER) - feh_thumbnail_mark_removed(FEH_FILE(winwid->file->data), 0); - feh_filelist_image_remove(winwid, 0); - } - break; - case XK_Home: - case XK_KP_Home: + slideshow_change_image(winwid, SLIDE_JUMP_FWD, 1); + else if (winwid->type == WIN_TYPE_THUMBNAIL) + feh_thumbnail_select_next(winwid, 10); + } + else if (feh_is_kp(EVENT_next_dir, state, keysym, button)) { if (opt.slideshow) - slideshow_change_image(winwid, SLIDE_FIRST); - break; - case XK_End: - case XK_KP_End: + slideshow_change_image(winwid, SLIDE_JUMP_NEXT_DIR, 1); + } + else if (feh_is_kp(EVENT_prev_dir, state, keysym, button)) { if (opt.slideshow) - slideshow_change_image(winwid, SLIDE_LAST); - break; - case XK_Return: - feh_event_invoke_action(winwid, opt.actions[0]); - break; - case XK_0: - feh_event_invoke_action(winwid, opt.actions[0]); - break; - case XK_1: - feh_event_invoke_action(winwid, opt.actions[1]); - break; - case XK_2: - feh_event_invoke_action(winwid, opt.actions[2]); - break; - case XK_3: - feh_event_invoke_action(winwid, opt.actions[3]); - break; - case XK_4: - feh_event_invoke_action(winwid, opt.actions[4]); - break; - case XK_5: - feh_event_invoke_action(winwid, opt.actions[5]); - break; - case XK_6: - feh_event_invoke_action(winwid, opt.actions[6]); - break; - case XK_7: - feh_event_invoke_action(winwid, opt.actions[7]); - break; - case XK_8: - feh_event_invoke_action(winwid, opt.actions[8]); - break; - case XK_9: - feh_event_invoke_action(winwid, opt.actions[9]); - break; - case XK_KP_Left: - winwid->im_x = winwid->im_x - 10; - winwidget_render_image(winwid, 0, 0); - break; - case XK_KP_Right: - winwid->im_x = winwid->im_x + 10; - winwidget_render_image(winwid, 0, 0); - break; - case XK_KP_Up: - winwid->im_y = winwid->im_y - 10; - winwidget_render_image(winwid, 0, 0); - break; - case XK_KP_Down: - winwid->im_y = winwid->im_y + 10; + slideshow_change_image(winwid, SLIDE_JUMP_PREV_DIR, 1); + } + else if (feh_is_kp(EVENT_quit, state, keysym, button)) { + winwidget_destroy_all(); + } + else if (feh_is_kp(EVENT_delete, state, keysym, button)) { + if (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER) + feh_thumbnail_mark_removed(FEH_FILE(winwid->file->data), 1); + feh_filelist_image_remove(winwid, 1); + } + else if (feh_is_kp(EVENT_remove, state, keysym, button)) { + if (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER) + feh_thumbnail_mark_removed(FEH_FILE(winwid->file->data), 0); + feh_filelist_image_remove(winwid, 0); + } + else if (feh_is_kp(EVENT_jump_first, state, keysym, button)) { + if (opt.slideshow) + slideshow_change_image(winwid, SLIDE_FIRST, 1); + } + else if (feh_is_kp(EVENT_jump_last, state, keysym, button)) { + if (opt.slideshow) + slideshow_change_image(winwid, SLIDE_LAST, 1); + } + else if (feh_is_kp(EVENT_action_0, state, keysym, button)) { + feh_event_invoke_action(winwid, 0); + } + else if (feh_is_kp(EVENT_action_1, state, keysym, button)) { + feh_event_invoke_action(winwid, 1); + } + else if (feh_is_kp(EVENT_action_2, state, keysym, button)) { + feh_event_invoke_action(winwid, 2); + } + else if (feh_is_kp(EVENT_action_3, state, keysym, button)) { + feh_event_invoke_action(winwid, 3); + } + else if (feh_is_kp(EVENT_action_4, state, keysym, button)) { + feh_event_invoke_action(winwid, 4); + } + else if (feh_is_kp(EVENT_action_5, state, keysym, button)) { + feh_event_invoke_action(winwid, 5); + } + else if (feh_is_kp(EVENT_action_6, state, keysym, button)) { + feh_event_invoke_action(winwid, 6); + } + else if (feh_is_kp(EVENT_action_7, state, keysym, button)) { + feh_event_invoke_action(winwid, 7); + } + else if (feh_is_kp(EVENT_action_8, state, keysym, button)) { + feh_event_invoke_action(winwid, 8); + } + else if (feh_is_kp(EVENT_action_9, state, keysym, button)) { + feh_event_invoke_action(winwid, 9); + } + else if (feh_is_kp(EVENT_zoom_in, state, keysym, button)) { + winwid->old_zoom = winwid->zoom; + winwid->zoom = winwid->zoom * opt.zoom_rate; + + if (winwid->zoom > ZOOM_MAX) + winwid->zoom = ZOOM_MAX; + + winwid->im_x = (winwid->w / 2) - (((winwid->w / 2) - winwid->im_x) / + winwid->old_zoom * winwid->zoom); + winwid->im_y = (winwid->h / 2) - (((winwid->h / 2) - winwid->im_y) / + winwid->old_zoom * winwid->zoom); + winwidget_sanitise_offsets(winwid); winwidget_render_image(winwid, 0, 0); - break; - case XK_KP_Add: - /* erroneously recognized as '+' in the *kbuf switch. Work around this. */ - len = 0; - winwid->zoom = winwid->zoom * 1.25; + } + else if (feh_is_kp(EVENT_zoom_out, state, keysym, button)) { + winwid->old_zoom = winwid->zoom; + winwid->zoom = winwid->zoom / opt.zoom_rate; + + if (winwid->zoom < ZOOM_MIN) + winwid->zoom = ZOOM_MIN; + + winwid->im_x = (winwid->w / 2) - (((winwid->w / 2) - winwid->im_x) / + winwid->old_zoom * winwid->zoom); + winwid->im_y = (winwid->h / 2) - (((winwid->h / 2) - winwid->im_y) / + winwid->old_zoom * winwid->zoom); + winwidget_sanitise_offsets(winwid); winwidget_render_image(winwid, 0, 0); - break; - case XK_KP_Subtract: - len = 0; - winwid->zoom = winwid->zoom * 0.75; + } + else if (feh_is_kp(EVENT_zoom_default, state, keysym, button)) { + winwid->zoom = 1.0; + winwidget_center_image(winwid); winwidget_render_image(winwid, 0, 0); - break; - case XK_KP_Multiply: - len = 0; - winwid->zoom = 1; + } + else if (feh_is_kp(EVENT_zoom_fit, state, keysym, button)) { + feh_calc_needed_zoom(&winwid->zoom, winwid->im_w, winwid->im_h, winwid->w, winwid->h); + winwidget_center_image(winwid); winwidget_render_image(winwid, 0, 0); - break; - case XK_KP_Divide: - len = 0; + } + else if (feh_is_kp(EVENT_zoom_fill, state, keysym, button)) { + int save_zoom = opt.zoom_mode; + opt.zoom_mode = ZOOM_MODE_FILL; feh_calc_needed_zoom(&winwid->zoom, winwid->im_w, winwid->im_h, winwid->w, winwid->h); - winwidget_render_image(winwid, 0, 1); - break; - case XK_KP_Begin: - winwidget_render_image(winwid, 0, 1); - break; - default: - break; + winwidget_center_image(winwid); + winwidget_render_image(winwid, 0, 0); + opt.zoom_mode = save_zoom; } - - if (len <= 0 || len > (int) sizeof(kbuf)) - D_RETURN_(4); - kbuf[len] = '\0'; - - switch (*kbuf) { - case 'a': - case 'A': + else if (feh_is_kp(EVENT_render, state, keysym, button)) { + if (winwid->type == WIN_TYPE_THUMBNAIL) + feh_thumbnail_show_selected(); + else + winwidget_render_image(winwid, 0, 0); + } + else if (feh_is_kp(EVENT_toggle_actions, state, keysym, button)) { opt.draw_actions = !opt.draw_actions; - winwidget_rerender_all(0, 1); - break; - case 'd': - case 'D': + winwidget_rerender_all(0); + } + else if (feh_is_kp(EVENT_toggle_aliasing, state, keysym, button)) { + opt.force_aliasing = !opt.force_aliasing; + winwid->force_aliasing = !winwid->force_aliasing; + winwidget_render_image(winwid, 0, 0); + } + else if (feh_is_kp(EVENT_toggle_auto_zoom, state, keysym, button)) { + opt.zoom_mode = (opt.zoom_mode == 0 ? ZOOM_MODE_MAX : 0); + winwidget_rerender_all(1); + } + else if (feh_is_kp(EVENT_toggle_filenames, state, keysym, button)) { opt.draw_filename = !opt.draw_filename; - winwidget_rerender_all(0, 1); - break; - case 'n': - case 'N': - case ' ': - if (opt.slideshow) - slideshow_change_image(winwid, SLIDE_NEXT); - break; - case 'p': - case 'P': - case '\b': - if (opt.slideshow) - slideshow_change_image(winwid, SLIDE_PREV); - break; - case 'z': - case 'Z': - if (opt.slideshow) - slideshow_change_image(winwid, SLIDE_RAND); - break; - case 'q': - case 'Q': - winwidget_destroy_all(); - break; - case 'c': - case 'C': - if (opt.caption_path) + winwidget_rerender_all(0); + } +#ifdef HAVE_LIBEXIF + else if (feh_is_kp(EVENT_toggle_exif, state, keysym, button)) { + opt.draw_exif = !opt.draw_exif; + winwidget_rerender_all(0); + } +#endif + else if (feh_is_kp(EVENT_toggle_info, state, keysym, button)) { + opt.draw_info = !opt.draw_info; + winwidget_rerender_all(0); + } + else if (feh_is_kp(EVENT_toggle_pointer, state, keysym, button)) { + winwidget_set_pointer(winwid, opt.hide_pointer); + opt.hide_pointer = !opt.hide_pointer; + } + else if (feh_is_kp(EVENT_jump_random, state, keysym, button)) { + if (winwid->type == WIN_TYPE_THUMBNAIL) + feh_thumbnail_select_next(winwid, random() % (filelist_len - 1)); + else + slideshow_change_image(winwid, SLIDE_RAND, 1); + } + else if (feh_is_kp(EVENT_toggle_caption, state, keysym, button)) { + if (opt.caption_path && path_is_url(FEH_FILE(winwid->file->data)->filename)) { + im_weprintf(winwid, "Caption entry is not supported on URLs"); + } + else if (opt.caption_path) { + /* + * editing captions in slideshow mode does not make any sense + * at all; this is just in case someone accidentally does it... + */ + if (opt.slideshow_delay) + opt.paused = 1; winwid->caption_entry = 1; - winwidget_render_image(winwid, 0, 1); - break; - case 'r': - case 'R': + } + winwidget_render_image(winwid, 0, 0); + } + else if (feh_is_kp(EVENT_reload_image, state, keysym, button)) { feh_reload_image(winwid, 0, 0); - break; - case 'h': - case 'H': + } + else if (feh_is_kp(EVENT_toggle_pause, state, keysym, button)) { slideshow_pause_toggle(winwid); - break; - case 's': - case 'S': + /* 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); - break; - case 'f': - case 'F': - feh_save_filelist(); - break; - case 'w': - case 'W': + } + else if (feh_is_kp(EVENT_save_filelist, state, keysym, button)) { + if ((winwid->type == WIN_TYPE_THUMBNAIL) + || (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER)) + weprintf("Filelist saving is not supported in thumbnail mode"); + else + feh_save_filelist(); + } + else if (feh_is_kp(EVENT_size_to_image, state, keysym, button)) { winwidget_size_to_image(winwid); - break; - case 'm': - case 'M': + } + else if (!opt.no_menus && feh_is_kp(EVENT_toggle_menu, state, keysym, button)) { winwidget_show_menu(winwid); - break; - case 'x': - case 'X': + } + else if (feh_is_kp(EVENT_close, state, keysym, button)) { winwidget_destroy(winwid); - break; - case '>': - feh_edit_inplace_orient(winwid, 1); - break; - case '<': - feh_edit_inplace_orient(winwid, 3); - break; - case 'v': - case 'V': + } + else if (feh_is_kp(EVENT_orient_1, state, keysym, button)) { + feh_edit_inplace(winwid, 1); + } + else if (feh_is_kp(EVENT_orient_3, state, keysym, button)) { + feh_edit_inplace(winwid, 3); + } + else if (feh_is_kp(EVENT_flip, state, keysym, button)) { + feh_edit_inplace(winwid, INPLACE_EDIT_FLIP); + } + else if (feh_is_kp(EVENT_mirror, state, keysym, button)) { + feh_edit_inplace(winwid, INPLACE_EDIT_MIRROR); + } + else if (feh_is_kp(EVENT_toggle_fullscreen, state, keysym, button)) { #ifdef HAVE_LIBXINERAMA if (opt.xinerama && xinerama_screens) { int i, rect[4]; winwidget_get_geometry(winwid, rect); - /* printf("window: (%d, %d)\n", rect[0], rect[1]); - printf("found %d screens.\n", num_xinerama_screens); */ for (i = 0; i < num_xinerama_screens; i++) { xinerama_screen = 0; - /* printf("%d: [%d, %d, %d, %d] (%d, %d)\n", - i, - xinerama_screens[i].x_org, xinerama_screens[i].y_org, - xinerama_screens[i].width, xinerama_screens[i].height, - rect[0], rect[1]); */ if (XY_IN_RECT(rect[0], rect[1], xinerama_screens[i].x_org, xinerama_screens[i].y_org, @@ -402,12 +761,14 @@ void feh_event_handle_keypress(XEvent * ev) break; } } + if (opt.xinerama_index >= 0) + curr_screen = xinerama_screen = opt.xinerama_index; } #endif /* HAVE_LIBXINERAMA */ winwid->full_screen = !winwid->full_screen; winwidget_destroy_xwin(winwid); winwidget_create_window(winwid, winwid->im_w, winwid->im_h); - winwidget_render_image(winwid, 1, 1); + winwidget_render_image(winwid, 1, 0); winwidget_show(winwid); #ifdef HAVE_LIBXINERAMA /* if we have xinerama and we're using it, then full screen the window @@ -418,22 +779,31 @@ void feh_event_handle_keypress(XEvent * ev) xinerama_screens[curr_screen].x_org, xinerama_screens[curr_screen].y_org); } #endif /* HAVE_LIBXINERAMA */ - case '=': - case '+': + } + else if (feh_is_kp(EVENT_reload_plus, state, keysym, button)){ if (opt.reload < SLIDESHOW_RELOAD_MAX) opt.reload++; else if (opt.verbose) - weprintf("Cannot set RELOAD higher than %d seconds.", opt.reload); - break; - case '-': - case '_': + weprintf("Cannot set RELOAD higher than %f seconds.", opt.reload); + } + else if (feh_is_kp(EVENT_reload_minus, state, keysym, button)) { if (opt.reload > 1) opt.reload--; else if (opt.verbose) weprintf("Cannot set RELOAD lower than 1 second."); - break; - default: - break; } - D_RETURN_(4); + else if (feh_is_kp(EVENT_toggle_keep_vp, state, keysym, button)) { + opt.keep_zoom_vp = !opt.keep_zoom_vp; + } + else if (feh_is_kp(EVENT_toggle_fixed_geometry, state, keysym, button)) { + if (opt.geom_flags & ((WidthValue | HeightValue))) { + opt.geom_flags &= ~(WidthValue | HeightValue); + } else { + opt.geom_flags |= (WidthValue | HeightValue); + opt.geom_w = winwid->w; + opt.geom_h = winwid->h; + } + winwidget_render_image(winwid, 1, 0); + } + return; } @@ -1,6 +1,7 @@ /* list.c Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2024 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -33,51 +34,51 @@ void init_list_mode(void) feh_file *file = NULL; int j = 0; - D_ENTER(4); - mode = "list"; if (!opt.customlist) - printf("NUM\tFORMAT\tWIDTH\tHEIGHT\tPIXELS\tSIZE(bytes)\tALPHA\tFILENAME\n"); + fputs("NUM\tFORMAT\tWIDTH\tHEIGHT\tPIXELS\tSIZE\tALPHA\tFILENAME\n", + stdout); for (l = filelist; l; l = l->next) { file = FEH_FILE(l->data); if (opt.customlist) - printf("%s\n", feh_printf(opt.customlist, file)); - else - printf("%d\t%s\t%d\t%d\t%d\t%d\t\t%c\t%s\n", ++j, + printf("%s\n", feh_printf(opt.customlist, file, NULL)); + else { + printf("%d\t%s\t%d\t%d\t%s", ++j, file->info->format, file->info->width, - file->info->height, file->info->pixels, - file->info->size, + file->info->height, + format_size(file->info->pixels)); + printf("\t%s\t%c\t%s\n", + format_size(file->size), file->info->has_alpha ? 'X' : '-', file->filename); + } - feh_action_run(file, opt.actions[0]); + feh_action_run(file, opt.actions[0], NULL); } exit(0); } void init_loadables_mode(void) { - D_ENTER(4); mode = "loadables"; real_loadables_mode(1); - D_RETURN_(4); + return; } void init_unloadables_mode(void) { - D_ENTER(4); mode = "unloadables"; real_loadables_mode(0); - D_RETURN_(4); + return; } void real_loadables_mode(int loadable) { feh_file *file; gib_list *l; + char ret = 0; - D_ENTER(4); opt.quiet = 1; for (l = filelist; l; l = l->next) { @@ -88,17 +89,35 @@ void real_loadables_mode(int loadable) if (feh_load_image(&im, file)) { /* loaded ok */ if (loadable) { - fprintf(stdout, "%s\n", file->filename); - feh_action_run(file, opt.actions[0]); + if (opt.verbose) + feh_display_status('.'); + puts(file->filename); + fflush(stdout); + feh_action_run(file, opt.actions[0], NULL); + } + else { + if (opt.verbose) + feh_display_status('s'); + ret = 1; } gib_imlib_free_image_and_decache(im); } else { /* Oh dear. */ if (!loadable) { - fprintf(stdout, "%s\n", file->filename); - feh_action_run(file, opt.actions[0]); + if (opt.verbose) + feh_display_status('.'); + puts(file->filename); + fflush(stdout); + feh_action_run(file, opt.actions[0], NULL); + } + else { + if (opt.verbose) + feh_display_status('s'); + ret = 1; } } } - exit(0); + if (opt.verbose) + feh_display_status(0); + exit(ret); } @@ -1,6 +1,7 @@ /* main.c Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2023 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -29,31 +30,54 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "timers.h" #include "options.h" #include "events.h" -#include "support.h" +#include "signals.h" +#include "wallpaper.h" +#include <termios.h> + +#ifdef HAVE_INOTIFY +#include <sys/inotify.h> +#endif char **cmdargv = NULL; int cmdargc = 0; -int call_level = 0; char *mode = NULL; int main(int argc, char **argv) { - D_ENTER(4); atexit(feh_clean_exit); + srandom(getpid() * time(NULL) % ((unsigned int) -1)); + + setup_signal_handlers(); + +#ifdef HAVE_LIBMAGIC + init_magic(); +#endif + init_parse_options(argc, argv); init_imlib_fonts(); - if (opt.display) + if (opt.display) { init_x_and_imlib(); + init_keyevents(); + init_buttonbindings(); +#ifdef HAVE_INOTIFY + if (opt.auto_reload) { + opt.inotify_fd = inotify_init(); + if (opt.inotify_fd < 0) { + opt.auto_reload = 0; + weprintf("inotify_init failed:"); + weprintf("Disabling inotify-based auto-reload"); + } + } +#endif + } feh_event_init(); if (opt.index) init_index_mode(); - else if (opt.collage) - init_collage_mode(); else if (opt.multiwindow) init_multiwindow_mode(); else if (opt.list || opt.customlist) @@ -65,19 +89,44 @@ int main(int argc, char **argv) else if (opt.thumbs) init_thumbnail_mode(); else if (opt.bgmode) { - feh_wm_set_bg_file(opt.output_file, opt.bgmode); + feh_wm_set_bg_filelist(opt.bgmode); exit(0); } - else { + else if (opt.display){ /* Slideshow mode is the default. Because it's spiffy */ opt.slideshow = 1; init_slideshow_mode(); } + else { + eprintf("Invalid option combination"); + } /* main event loop */ while (feh_main_iteration(1)); - D_RETURN(4, 0); + return(sig_exit); +} + +static void feh_process_signal(void) +{ + winwidget winwid = winwidget_get_first_window_of_type(WIN_TYPE_SLIDESHOW); + int i; + int signo = sig_received; + sig_received = 0; + + if (winwid) { + if (filelist_len > 1) { + if (signo == SIGUSR1) + slideshow_change_image(winwid, SLIDE_NEXT, 1); + else if (signo == SIGUSR2) + slideshow_change_image(winwid, SLIDE_PREV, 1); + } else { + feh_reload_image(winwid, 0, 0); + } + } else if (opt.multiwindow) { + for (i = window_num - 1; i >= 0; i--) + feh_reload_image(windows[i], 0, 0); + } } /* Return 0 to stop iterating, 1 if ok to continue. */ @@ -94,10 +143,12 @@ int feh_main_iteration(int block) double t1 = 0.0, t2 = 0.0; fehtimer ft; - D_ENTER(5); + if (window_num == 0 || sig_exit != 0) + return(0); - if (window_num == 0) - D_RETURN(5, 0); + if (sig_received) { + feh_process_signal(); + } if (first) { /* Only need to set these up the first time */ @@ -105,6 +156,19 @@ int feh_main_iteration(int block) fdsize = xfd + 1; pt = feh_get_time(); first = 0; + /* + * Only accept commands from stdin if + * - stdin is a terminal (otherwise it's probably used as an image / filelist) + * - we aren't running in multiwindow mode (cause it's not clear which + * window commands should be applied to in that case) + * - we're in the same process group as stdin, AKA we're not running + * in the background. Background processes are stopped with SIGTTOU + * if they try to write to stdout or change terminal attributes. They + * also don't get input from stdin anyway. + */ + if (isatty(STDIN_FILENO) && !opt.multiwindow && getpgrp() == (tcgetpgrp(STDIN_FILENO))) { + setup_stdin(); + } } /* Timers */ @@ -116,8 +180,12 @@ int feh_main_iteration(int block) if (ev_handler[ev.type]) (*(ev_handler[ev.type])) (&ev); - if (window_num == 0) - D_RETURN(5, 0); + if (window_num == 0 || sig_exit != 0) + return(0); + + if (sig_received) { + feh_process_signal(); + } } XFlush(disp); @@ -125,19 +193,29 @@ int feh_main_iteration(int block) FD_ZERO(&fdset); FD_SET(xfd, &fdset); + if (control_via_stdin) { + FD_SET(STDIN_FILENO, &fdset); + } +#ifdef HAVE_INOTIFY + if (opt.auto_reload) { + FD_SET(opt.inotify_fd, &fdset); + if (opt.inotify_fd >= fdsize) + fdsize = opt.inotify_fd + 1; + } +#endif /* Timers */ ft = first_timer; /* Don't do timers if we're zooming/panning/etc or if we are paused */ if (ft && (opt.mode == MODE_NORMAL) && !opt.paused) { - D(5, ("There are timers in the queue\n")); + D(("There are timers in the queue\n")); if (ft->just_added) { - D(5, ("The first timer has just been added\n")); - D(5, ("ft->in = %f\n", ft->in)); + D(("The first timer has just been added\n")); + D(("ft->in = %f\n", ft->in)); ft->just_added = 0; t1 = ft->in; } else { - D(5, ("The first timer was not just added\n")); + D(("The first timer was not just added\n")); t1 = ft->in - t2; if (t1 < 0.0) t1 = 0.0; @@ -145,7 +223,7 @@ int feh_main_iteration(int block) } XSync(disp, False); - D(5, ("I next need to action a timer in %f seconds\n", t1)); + D(("I next need to action a timer in %f seconds\n", t1)); /* Only do a blocking select if there's a timer due, or no events waiting */ if (t1 == 0.0 || (block && !XPending(disp))) { @@ -156,44 +234,90 @@ int feh_main_iteration(int block) if (tval.tv_usec <= 1000) tval.tv_usec = 1000; errno = 0; - D(5, ("Performing blocking select - waiting for timer or event\n")); + D(("Performing blocking select - waiting for timer or event\n")); count = select(fdsize, &fdset, NULL, NULL, &tval); if ((count < 0) && ((errno == ENOMEM) || (errno == EINVAL) || (errno == EBADF))) eprintf("Connection to X display lost"); - if ((ft) && (count == 0)) { + if (count == 0) { /* This means the timer is due to be executed. If count was > 0, that would mean an X event had woken us, we're not interested in that */ feh_handle_timer(); } + /* + * Beware: If stdin is not connected, we may end up with xfd == 0. + * However, STDIN_FILENO == 0 holds as well in most cases. So we must + * check control_via_stdin to avoid mistaking an X11 event for stdin. + */ + else if ((count > 0) && control_via_stdin && (FD_ISSET(STDIN_FILENO, &fdset))) + feh_event_handle_stdin(); +#ifdef HAVE_INOTIFY + else if ((count > 0) && (FD_ISSET(opt.inotify_fd, &fdset))) + feh_event_handle_inotify(); +#endif } } else { /* Don't block if there are events in the queue. That's a bit rude ;-) */ if (block && !XPending(disp)) { errno = 0; - D(5, ("Performing blocking select - no timers, or zooming\n")); + D(("Performing blocking select - no timers, or zooming\n")); count = select(fdsize, &fdset, NULL, NULL, NULL); if ((count < 0) && ((errno == ENOMEM) || (errno == EINVAL) || (errno == EBADF))) eprintf("Connection to X display lost"); + else if ((count > 0) && control_via_stdin && (FD_ISSET(STDIN_FILENO, &fdset))) + feh_event_handle_stdin(); +#ifdef HAVE_INOTIFY + else if ((count > 0) && (FD_ISSET(opt.inotify_fd, &fdset))) + feh_event_handle_inotify(); +#endif } } - if (window_num == 0) - D_RETURN(5, 0); - D_RETURN(5, 1); + if (window_num == 0 || sig_exit != 0) + return(0); + + if (sig_received) { + feh_process_signal(); + } + + return(1); } void feh_clean_exit(void) { - D_ENTER(4); - delete_rm_files(); + free(opt.menu_font); + +#ifdef HAVE_INOTIFY + if (opt.auto_reload) + if (close(opt.inotify_fd)) + eprintf("inotify close failed"); +#endif + + if(disp) + XCloseDisplay(disp); + +#ifdef HAVE_LIBMAGIC + uninit_magic(); +#endif + + /* + * Only restore the old terminal settings if + * - we changed them in the first place + * - stdin still is a terminal (it might have been closed) + * - stdin still belongs to us (we might have been detached from the + * controlling terminal, in that case we probably shouldn't be messing + * around with it) <https://github.com/derf/feh/issues/324> + */ + if (control_via_stdin && isatty(STDIN_FILENO) && getpgrp() == (tcgetpgrp(STDIN_FILENO))) + restore_stdin(); + if (opt.filelistfile) feh_write_filelist(filelist, opt.filelistfile); - D_RETURN_(4); + return; } @@ -1,6 +1,7 @@ /* menu.c Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2024 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -24,8 +25,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "feh.h" -#include "support.h" #include "thumbnail.h" +#include "wallpaper.h" #include "winwidget.h" #include "filelist.h" #include "options.h" @@ -34,53 +35,56 @@ Window menu_cover = 0; feh_menu *menu_root = NULL; feh_menu *menu_main = NULL; feh_menu *menu_single_win = NULL; -feh_menu *menu_about_win = NULL; feh_menu *menu_thumbnail_viewer = NULL; feh_menu *menu_thumbnail_win = NULL; feh_menu *menu_bg = NULL; static feh_menu_list *menus = NULL; static int common_menus = 0; -static void feh_menu_cb_about(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_close(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_exit(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_reload(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_remove(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_delete(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_reset(feh_menu * m, feh_menu_item * i, void *data); - -static void feh_menu_cb_remove_thumb(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_delete_thumb(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_background_set_tiled(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_background_set_scaled(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_background_set_seamless(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_background_set_centered(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_background_set_filled(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_background_set_tiled_no_file(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_background_set_scaled_no_file(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_background_set_centered_no_file(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_background_set_filled_no_file(feh_menu * m, feh_menu_item * i, void *data); - -static void feh_menu_cb_sort_filename(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_sort_imagename(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_sort_filesize(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_sort_randomize(feh_menu * m, feh_menu_item * i, void *data); -static feh_menu *feh_menu_func_gen_info(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_func_free_info(feh_menu * m, void *data); -static void feh_menu_cb_save_image(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_save_filelist(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_fit(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_opt_draw_filename(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_opt_keep_http(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_opt_freeze_window(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_opt_fullscreen(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_func_free_options(feh_menu * m, void *data); -static feh_menu *feh_menu_func_gen_options(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_edit_rotate(feh_menu * m, feh_menu_item * i, void *data); -static void feh_menu_cb_opt_auto_zoom(feh_menu * m, feh_menu_item * i, void *data); -#ifdef HAVE_LIBXINERAMA -static void feh_menu_cb_opt_xinerama(feh_menu * m, feh_menu_item * i, void *data); -#endif /* HAVE_LIBXINERAMA */ +static feh_menu *feh_menu_func_gen_info(feh_menu * m); +static void feh_menu_func_free_info(feh_menu * m); +static void feh_menu_func_free_options(feh_menu * m); +static feh_menu *feh_menu_func_gen_options(feh_menu * m); +void feh_menu_cb(feh_menu * m, feh_menu_item * i, int action, unsigned short data); +void feh_menu_cb_opt_fullscreen(feh_menu * m, feh_menu_item * i); + +enum { + CB_CLOSE = 1, + CB_EXIT, + CB_RELOAD, + CB_REMOVE, + CB_DELETE, + CB_RESET, + CB_REMOVE_THUMB, + CB_DELETE_THUMB, + CB_BG_TILED, + CB_BG_SCALED, + CB_BG_CENTERED, + CB_BG_FILLED, + CB_BG_TILED_NOFILE, + CB_BG_SCALED_NOFILE, + CB_BG_CENTERED_NOFILE, + CB_BG_FILLED_NOFILE, + CB_SORT_FILENAME, + CB_SORT_IMAGENAME, + CB_SORT_DIRNAME, + CB_SORT_MTIME, + CB_SORT_FILESIZE, + CB_SORT_RANDOMIZE, + CB_SAVE_IMAGE, + CB_SAVE_FILELIST, + CB_FIT, + CB_OPT_DRAW_FILENAME, + CB_OPT_DRAW_ACTIONS, + CB_OPT_KEEP_HTTP, + 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 +}; feh_menu *feh_menu_new(void) { @@ -90,8 +94,6 @@ feh_menu *feh_menu_new(void) static Imlib_Image bg = NULL; static Imlib_Border border; - D_ENTER(4); - m = (feh_menu *) emalloc(sizeof(feh_menu)); attr.backing_store = NotUseful; @@ -124,7 +126,7 @@ feh_menu *feh_menu_new(void) m->updates = NULL; m->needs_redraw = 1; m->func_free = NULL; - m->data = NULL; + m->data = 0; m->calc = 0; m->bg = NULL; @@ -134,12 +136,10 @@ feh_menu *feh_menu_new(void) menus = l; if (!bg) { - feh_load_image_char(&bg, opt.menu_bg); + feh_load_image_char(&bg, PREFIX "/share/feh/images/menubg_default.png"); if (bg) { - border.left = opt.menu_border; - border.right = opt.menu_border; - border.top = opt.menu_border; - border.bottom = opt.menu_border; + border.left = border.right = border.top = border.bottom + = 4; imlib_context_set_image(bg); imlib_image_set_border(&border); } @@ -148,7 +148,7 @@ feh_menu *feh_menu_new(void) if (bg) m->bg = gib_imlib_clone_image(bg); - D_RETURN(4, m); + return(m); } void feh_menu_free(feh_menu * m) @@ -156,8 +156,6 @@ void feh_menu_free(feh_menu * m) feh_menu_item *i; feh_menu_list *l, *pl = NULL; - D_ENTER(4); - if (m->name) free(m->name); XDestroyWindow(disp, m->win); @@ -170,8 +168,6 @@ void feh_menu_free(feh_menu * m) ii = i; i = i->next; - if (ii->func_free) - (ii->func_free) (ii->data); if (ii->text) free(ii->text); if (ii->submenu) @@ -190,24 +186,25 @@ void feh_menu_free(feh_menu * m) } pl = l; } + if (m->bg) + gib_imlib_free_image_and_decache(m->bg); + free(m); - D_RETURN_(4); + return; } feh_menu_item *feh_menu_find_selected(feh_menu * m) { feh_menu_item *i; - D_ENTER(4); - - D(5, ("menu %p\n", m)); + D(("menu %p\n", m)); for (i = m->items; i; i = i->next) { if (MENU_ITEM_IS_SELECTED(i)) - D_RETURN(4, i); + return(i); } - D_RETURN(4, NULL); + return(NULL); } feh_menu_item *feh_menu_find_selected_r(feh_menu * m, feh_menu ** parent) @@ -215,27 +212,25 @@ feh_menu_item *feh_menu_find_selected_r(feh_menu * m, feh_menu ** parent) feh_menu_item *i, *ii; feh_menu *mm; - D_ENTER(4); - - D(5, ("menu %p\n", m)); + D(("menu %p\n", m)); for (i = m->items; i; i = i->next) { if (MENU_ITEM_IS_SELECTED(i)) { if (parent) *parent = m; - D_RETURN(4, i); + return(i); } else if (i->submenu) { mm = feh_menu_find(i->submenu); if (mm) { ii = feh_menu_find_selected_r(mm, parent); if (ii) - D_RETURN(4, ii); + return(ii); } } } if (parent) *parent = m; - D_RETURN(4, NULL); + return(NULL); } void feh_menu_select_next(feh_menu * selected_menu, feh_menu_item * selected_item) @@ -250,7 +245,7 @@ void feh_menu_select_next(feh_menu * selected_menu, feh_menu_item * selected_ite i = i->next; if (!i) i = selected_menu->items; - if (i->func || i->submenu || i->func_gen_sub || i->text) { + if (i->action || i->submenu || i->func_gen_sub || i->text) { break; } } @@ -271,11 +266,10 @@ void feh_menu_select_prev(feh_menu * selected_menu, feh_menu_item * selected_ite while (1) { i = i->prev; if (!i) { - i = selected_menu->items; for (ii = selected_menu->items; ii->next; ii = ii->next); i = ii; } - if (i->func || i->submenu || i->func_gen_sub || i->text) { + if (i->action || i->submenu || i->func_gen_sub || i->text) { break; } } @@ -284,7 +278,7 @@ void feh_menu_select_prev(feh_menu * selected_menu, feh_menu_item * selected_ite } } -void feh_menu_select_parent(feh_menu * selected_menu, feh_menu_item * selected_item) +void feh_menu_select_parent(feh_menu * selected_menu) { feh_menu *m; feh_menu_item *i; @@ -303,7 +297,7 @@ void feh_menu_select_parent(feh_menu * selected_menu, feh_menu_item * selected_i } } -void feh_menu_select_submenu(feh_menu * selected_menu, feh_menu_item * selected_item) +void feh_menu_select_submenu(feh_menu * selected_menu) { if (selected_menu->next) { feh_menu_deselect_selected(selected_menu); @@ -316,12 +310,12 @@ void feh_menu_item_activate(feh_menu * m, feh_menu_item * i) /* watch out for this. I put it this way around so the menu goes away *before* we perform the action, if we start freeing menus on hiding, it will break ;-) */ - if ((i) && (i->func)) { + if ((i) && (i->action)) { feh_menu_hide(menu_root, False); feh_main_iteration(0); - (i->func) (m, i, i->data); + feh_menu_cb(m, i, i->action, i->data); if (m->func_free) - m->func_free(m, m->data); + m->func_free(m); } } @@ -329,40 +323,36 @@ feh_menu_item *feh_menu_find_at_xy(feh_menu * m, int x, int y) { feh_menu_item *i; - D_ENTER(4); - D(4, ("looking for menu item at %d,%d\n", x, y)); + D(("looking for menu item at %d,%d\n", x, y)); for (i = m->items; i; i = i->next) { if (XY_IN_RECT(x, y, i->x, i->y, i->w, i->h)) { - D(4, ("Found an item\n")); - D_RETURN(4, i); + D(("Found an item\n")); + return(i); } } - D(4, ("didn't find an item\n")); - D_RETURN(4, NULL); + D(("didn't find an item\n")); + return(NULL); } void feh_menu_deselect_selected(feh_menu * m) { feh_menu_item *i; - D_ENTER(4); - if (!m) - D_RETURN_(4); + return; i = feh_menu_find_selected(m); if (i) { - D(4, ("found a selected menu, deselecting it\n")); + D(("found a selected menu, deselecting it\n")); MENU_ITEM_SET_NORMAL(i); m->updates = imlib_update_append_rect(m->updates, i->x, i->y, i->w, i->h); m->needs_redraw = 1; } - D_RETURN_(4); + return; } void feh_menu_select(feh_menu * m, feh_menu_item * i) { - D_ENTER(4); MENU_ITEM_SET_SELECTED(i); m->updates = imlib_update_append_rect(m->updates, i->x, i->y, i->w, i->h); m->needs_redraw = 1; @@ -378,21 +368,19 @@ void feh_menu_select(feh_menu * m, feh_menu_item * i) if (mm) feh_menu_show_at_submenu(mm, m, i); else if (i->func_gen_sub) - feh_menu_show_at_submenu(i->func_gen_sub(m, i, i->data), m, i); + feh_menu_show_at_submenu(i->func_gen_sub(m), m, i); } - D_RETURN_(4); + return; } void feh_menu_show_at(feh_menu * m, int x, int y) { - D_ENTER(4); - if (m->calc) feh_menu_calc_size(m); if (!menu_cover) { XSetWindowAttributes attr; - D(4, ("creating menu cover window\n")); + D(("creating menu cover window\n")); attr.override_redirect = True; attr.do_not_propagate_mask = True; menu_cover = XCreateWindow( @@ -417,15 +405,13 @@ void feh_menu_show_at(feh_menu * m, int x, int y) XRaiseWindow(disp, m->win); feh_menu_redraw(m); XMapWindow(disp, m->win); - D_RETURN_(4); + return; } void feh_menu_show_at_xy(feh_menu * m, winwidget winwid, int x, int y) { - D_ENTER(4); - if (!m) - D_RETURN_(4); + return; if (m->calc) feh_menu_calc_size(m); @@ -453,17 +439,15 @@ void feh_menu_show_at_xy(feh_menu * m, winwidget winwid, int x, int y) y = 0; feh_menu_move(m, x, y); feh_menu_show(m); - D_RETURN_(4); + return; } void feh_menu_show_at_submenu(feh_menu * m, feh_menu * parent_m, feh_menu_item * i) { int mx, my; - D_ENTER(4); - if (!m) - D_RETURN_(4); + return; if (m->calc) feh_menu_calc_size(m); @@ -474,24 +458,20 @@ void feh_menu_show_at_submenu(feh_menu * m, feh_menu * parent_m, feh_menu_item * m->prev = parent_m; feh_menu_move(m, mx, my); feh_menu_show(m); - D_RETURN_(4); + return; } void feh_menu_move(feh_menu * m, int x, int y) { - int dx, dy; - - D_ENTER(4); - if (!m) - D_RETURN_(4); - dx = x - m->x; - dy = y - m->y; + return; + if (m->visible) XMoveWindow(disp, m->win, x, y); + m->x = x; m->y = y; - D_RETURN_(4); + return; } void feh_menu_slide_all_menus_relative(int dx, int dy) @@ -502,7 +482,6 @@ void feh_menu_slide_all_menus_relative(int dx, int dy) int stepx = 0; int stepy = 0; - D_ENTER(4); vector_len = sqrt(dx * dx + dy * dy); if (vector_len) { if (dx) @@ -520,15 +499,13 @@ void feh_menu_slide_all_menus_relative(int dx, int dy) } XWarpPointer(disp, None, None, 0, 0, 0, 0, stepx, stepy); } - D_RETURN_(4); + return; } void feh_menu_hide(feh_menu * m, int func_free) { - D_ENTER(4); - if (!m->visible) - D_RETURN_(4); + return; if (m->next) { m->next->prev = NULL; feh_menu_hide(m->next, func_free); @@ -536,7 +513,7 @@ void feh_menu_hide(feh_menu * m, int func_free) } if (m == menu_root) { if (menu_cover) { - D(4, ("DESTROYING menu cover\n")); + D(("DESTROYING menu cover\n")); XDestroyWindow(disp, menu_cover); menu_cover = 0; } @@ -545,44 +522,40 @@ void feh_menu_hide(feh_menu * m, int func_free) m->visible = 0; XUnmapWindow(disp, m->win); if (func_free && m->func_free) - m->func_free(m, m->data); + m->func_free(m); else feh_menu_deselect_selected(m); - D_RETURN_(4); + return; } void feh_menu_show(feh_menu * m) { - D_ENTER(4); if (!m) - D_RETURN_(4); + return; feh_menu_show_at(m, m->x, m->y); - D_RETURN_(4); + return; } feh_menu_item *feh_menu_add_toggle_entry(feh_menu * m, char *text, - Imlib_Image icon, char *submenu, menu_func func, - void *data, void (*func_free) (void *data), int setting) + char *submenu, int action, + unsigned short data, void (*func_free) (void *data), int setting) { feh_menu_item *mi; - D_ENTER(4); - mi = feh_menu_add_entry(m, text, icon, submenu, func, data, func_free); + mi = feh_menu_add_entry(m, text, submenu, action, data, func_free); mi->is_toggle = TRUE; MENU_ITEM_TOGGLE_SET(mi, setting); - D_RETURN(4, mi); + return(mi); } -feh_menu_item *feh_menu_add_entry(feh_menu * m, char *text, Imlib_Image icon, - char *submenu, menu_func func, void *data, void (*func_free) (void *data)) +feh_menu_item *feh_menu_add_entry(feh_menu * m, char *text, char *submenu, + int action, unsigned short data, void (*func_free) (void *data)) { feh_menu_item *mi, *ptr; - D_ENTER(4); mi = (feh_menu_item *) emalloc(sizeof(feh_menu_item)); mi->state = MENU_ITEM_STATE_NORMAL; - mi->icon = icon; mi->is_toggle = FALSE; if (text) mi->text = estrdup(text); @@ -592,7 +565,7 @@ feh_menu_item *feh_menu_add_entry(feh_menu * m, char *text, Imlib_Image icon, mi->submenu = estrdup(submenu); else mi->submenu = NULL; - mi->func = func; + mi->action = action; mi->func_free = func_free; mi->data = data; mi->func_gen_sub = NULL; @@ -611,17 +584,15 @@ feh_menu_item *feh_menu_add_entry(feh_menu * m, char *text, Imlib_Image icon, } } m->calc = 1; - D_RETURN(4, mi); + return(mi); } -void feh_menu_entry_get_size(feh_menu * m, feh_menu_item * i, int *w, int *h) +void feh_menu_entry_get_size(feh_menu_item * i, int *w, int *h) { int tw, th; - D_ENTER(4); - if (i->text) { - gib_imlib_get_text_size(opt.menu_fn, i->text, opt.menu_style_l, &tw, &th, IMLIB_TEXT_TO_RIGHT); + gib_imlib_get_text_size(opt.menu_fn, i->text, NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT); *w = tw + FEH_MENUITEM_PAD_LEFT + FEH_MENUITEM_PAD_RIGHT; *h = th + FEH_MENUITEM_PAD_TOP + FEH_MENUITEM_PAD_BOTTOM; } else { @@ -629,18 +600,15 @@ void feh_menu_entry_get_size(feh_menu * m, feh_menu_item * i, int *w, int *h) *h = FEH_MENUITEM_PAD_TOP + FEH_MENUITEM_PAD_BOTTOM; } - D_RETURN_(4); - m = NULL; + return; } void feh_menu_calc_size(feh_menu * m) { int prev_w, prev_h; feh_menu_item *i; - int j = 0, count = 0, max_w = 0, max_h = 0, icon_w = 0, next_w = 0; - int toggle_w = 0; - - D_ENTER(4); + int j = 0, count = 0, max_w = 0, max_h = 0, next_w = 0; + int toggle_w = 1; prev_w = m->w; prev_h = m->h; @@ -649,7 +617,7 @@ void feh_menu_calc_size(feh_menu * m) for (i = m->items; i; i = i->next) { int w, h; - feh_menu_entry_get_size(m, i, &w, &h); + feh_menu_entry_get_size(i, &w, &h); if (w > max_w) max_w = w; if (h > max_h) @@ -667,35 +635,12 @@ void feh_menu_calc_size(feh_menu * m) count++; } - for (i = m->items; i; i = i->next) { - if (i->icon) { - Imlib_Image im; - - im = i->icon; - if (im) { - int iw, ih, ow, oh; - - iw = gib_imlib_image_get_width(im); - ih = gib_imlib_image_get_height(im); - if (ih <= max_h) { - ow = iw; - oh = ih; - } else { - ow = (iw * max_h) / ih; - oh = max_h; - } - if (ow > icon_w) - icon_w = ow; - } - } - } m->h = FEH_MENU_PAD_TOP; for (i = m->items; i; i = i->next) { i->x = FEH_MENU_PAD_LEFT; i->y = m->h; - i->w = max_w + icon_w + toggle_w + next_w; - i->icon_x = FEH_MENUITEM_PAD_LEFT; - i->toggle_x = i->icon_x + icon_w; + i->w = max_w + toggle_w + next_w; + i->toggle_x = 1; i->text_x = i->toggle_x + toggle_w; i->sub_x = i->text_x + max_w; if (i->text) @@ -706,7 +651,7 @@ void feh_menu_calc_size(feh_menu * m) j++; } m->h += FEH_MENU_PAD_BOTTOM; - m->w = next_w + toggle_w + icon_w + max_w + FEH_MENU_PAD_LEFT + FEH_MENU_PAD_RIGHT; + m->w = next_w + toggle_w + max_w + FEH_MENU_PAD_LEFT + FEH_MENU_PAD_RIGHT; if ((prev_w != m->w) || (prev_h != m->h)) { if (m->pmap) @@ -716,7 +661,7 @@ void feh_menu_calc_size(feh_menu * m) XResizeWindow(disp, m->win, m->w, m->h); m->updates = imlib_update_append_rect(m->updates, 0, 0, m->w, m->h); } - D(4, ("menu size calculated. w=%d h=%d\n", m->w, m->h)); + D(("menu size calculated. w=%d h=%d\n", m->w, m->h)); /* Make sure bg is same size */ if (m->bg) { @@ -728,7 +673,7 @@ void feh_menu_calc_size(feh_menu * m) if (m->w != bg_w || m->h != bg_h) { Imlib_Image newim = imlib_create_image(m->w, m->h); - D(3, ("resizing bg to %dx%d\n", m->w, m->h)); + D(("resizing bg to %dx%d\n", m->w, m->h)); gib_imlib_blend_image_onto_image(newim, m->bg, 0, 0, 0, bg_w, bg_h, 0, 0, m->w, m->h, 0, 0, 1); gib_imlib_free_image_and_decache(m->bg); @@ -736,66 +681,31 @@ void feh_menu_calc_size(feh_menu * m) } } - D_RETURN_(4); + return; } -void feh_menu_draw_item(feh_menu * m, feh_menu_item * i, Imlib_Image im, int ox, int oy) +void feh_menu_draw_item(feh_menu_item * i, Imlib_Image im, int ox, int oy) { - D_ENTER(5); - - D(5, ("drawing item %p (text %s) on menu %p (name %s)\n", i, i->text, m, m->name)); + D(("drawing item %p (text %s)\n", i, i->text)); if (i->text) { - D(5, ("text item\n")); + D(("text item\n")); if (MENU_ITEM_IS_SELECTED(i)) { - D(5, ("selected item\n")); + D(("selected item\n")); /* draw selected image */ feh_menu_item_draw_at(i->x, i->y, i->w, i->h, im, ox, oy, 1); } else { - D(5, ("unselected item\n")); + D(("unselected item\n")); /* draw unselected image */ feh_menu_item_draw_at(i->x, i->y, i->w, i->h, im, ox, oy, 0); } /* draw text */ - gib_imlib_text_draw(im, opt.menu_fn, opt.menu_style_l, + gib_imlib_text_draw(im, opt.menu_fn, NULL, i->x - ox + i->text_x, i->y - oy + FEH_MENUITEM_PAD_TOP, i->text, IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255); - if (i->icon) { - Imlib_Image im2; - - D(5, ("icon item\n")); - - im2 = i->icon; - if (im2) { - int iw, ih, ow, oh; - - iw = gib_imlib_image_get_width(im2); - ih = gib_imlib_image_get_height(im2); - if (ih <= (i->h - FEH_MENUITEM_PAD_TOP - FEH_MENUITEM_PAD_BOTTOM)) { - ow = iw; - oh = ih; - } else { - ow = (iw * (i->h - FEH_MENUITEM_PAD_TOP - FEH_MENUITEM_PAD_BOTTOM)) / ih; - oh = i->h - FEH_MENUITEM_PAD_TOP - FEH_MENUITEM_PAD_BOTTOM; - } - gib_imlib_blend_image_onto_image(im, im2, - 0, 0, 0, - iw, ih, - i->x + - i->icon_x - - ox, - i->y + - FEH_MENUITEM_PAD_TOP - + - (((i->h - - FEH_MENUITEM_PAD_TOP - FEH_MENUITEM_PAD_BOTTOM) - - oh) / 2) - oy, ow, oh, 1, 1, 1); - gib_imlib_free_image(im2); - } - } if (i->submenu) { - D(5, ("submenu item\n")); + D(("submenu item\n")); feh_menu_draw_submenu_at(i->x + i->sub_x, i->y + FEH_MENUITEM_PAD_TOP + @@ -804,11 +714,10 @@ void feh_menu_draw_item(feh_menu * m, feh_menu_item * i, Imlib_Image im, int ox, FEH_MENUITEM_PAD_BOTTOM - FEH_MENU_SUBMENU_H) / - 2), FEH_MENU_SUBMENU_W, - FEH_MENU_SUBMENU_H, im, ox, oy, MENU_ITEM_IS_SELECTED(i)); + 2), im, ox, oy); } if (i->is_toggle) { - D(5, ("toggleable item\n")); + D(("toggleable item\n")); feh_menu_draw_toggle_at(i->x + i->toggle_x, i->y + FEH_MENUITEM_PAD_TOP + @@ -819,21 +728,18 @@ void feh_menu_draw_item(feh_menu * m, feh_menu_item * i, Imlib_Image im, int ox, FEH_MENU_TOGGLE_W, FEH_MENU_TOGGLE_H, im, ox, oy, MENU_ITEM_IS_ON(i)); } } else { - D(5, ("separator item\n")); + D(("separator item\n")); feh_menu_draw_separator_at(i->x, i->y, i->w, i->h, im, ox, oy); } - D_RETURN_(5); - m = NULL; + return; } void feh_menu_redraw(feh_menu * m) { Imlib_Updates u, uu; - D_ENTER(5); - if ((!m->needs_redraw) || (!m->visible) || (!m->updates)) - D_RETURN_(5); + return; m->needs_redraw = 0; if (!m->pmap) m->pmap = XCreatePixmap(disp, m->win, m->w, m->h, depth); @@ -842,13 +748,13 @@ void feh_menu_redraw(feh_menu * m) u = imlib_updates_merge_for_rendering(m->updates, m->w, m->h); m->updates = NULL; if (u) { - D(5, ("I have updates to render\n")); + D(("I have updates to render\n")); for (uu = u; u; u = imlib_updates_get_next(u)) { int x, y, w, h; Imlib_Image im; imlib_updates_get_coordinates(u, &x, &y, &w, &h); - D(5, ("update coords %d,%d %d*%d\n", x, y, w, h)); + D(("update coords %d,%d %d*%d\n", x, y, w, h)); im = imlib_create_image(w, h); gib_imlib_image_fill_rectangle(im, 0, 0, w, h, 0, 0, 0, 0); if (im) { @@ -860,19 +766,18 @@ void feh_menu_redraw(feh_menu * m) } imlib_updates_free(uu); } - D_RETURN_(5); + return; } feh_menu *feh_menu_find(char *name) { feh_menu_list *l; - D_ENTER(4); for (l = menus; l; l = l->next) { if ((l->menu->name) && (!strcmp(l->menu->name, name))) - D_RETURN(4, l->menu); + return(l->menu); } - D_RETURN(4, NULL); + return(NULL); } void feh_menu_draw_to_buf(feh_menu * m, Imlib_Image im, int ox, int oy) @@ -880,7 +785,6 @@ void feh_menu_draw_to_buf(feh_menu * m, Imlib_Image im, int ox, int oy) feh_menu_item *i; int w, h; - D_ENTER(5); w = gib_imlib_image_get_width(im); h = gib_imlib_image_get_height(im); @@ -888,17 +792,15 @@ void feh_menu_draw_to_buf(feh_menu * m, Imlib_Image im, int ox, int oy) for (i = m->items; i; i = i->next) { if (RECTS_INTERSECT(i->x, i->y, i->w, i->h, ox, oy, w, h)) - feh_menu_draw_item(m, i, im, ox, oy); + feh_menu_draw_item(i, im, ox, oy); } - D_RETURN_(5); + return; } void feh_menu_draw_menu_bg(feh_menu * m, Imlib_Image im, int ox, int oy) { int w, h; - D_ENTER(5); - w = gib_imlib_image_get_width(im); h = gib_imlib_image_get_height(im); @@ -907,103 +809,81 @@ void feh_menu_draw_menu_bg(feh_menu * m, Imlib_Image im, int ox, int oy) else gib_imlib_image_fill_rectangle(im, 0, 0, w, h, 205, 203, 176, 255); - D_RETURN_(5); + return; } void feh_menu_draw_toggle_at(int x, int y, int w, int h, Imlib_Image dst, int ox, int oy, int on) { - D_ENTER(5); x -= ox; y -= oy; if (on) gib_imlib_image_fill_rectangle(dst, x, y, w, h, 0, 0, 0, 255); else gib_imlib_image_draw_rectangle(dst, x, y, w, h, 0, 0, 0, 255); - D_RETURN_(5); + return; } -void feh_menu_draw_submenu_at(int x, int y, int w, int h, Imlib_Image dst, int ox, int oy, int selected) +void feh_menu_draw_submenu_at(int x, int y, Imlib_Image dst, int ox, int oy) { - ImlibPolygon poly; - - D_ENTER(5); - - x -= ox; + // Draw filled triangle + x -= ox; y -= oy; imlib_context_set_image(dst); - poly = imlib_polygon_new(); - imlib_polygon_add_point(poly, x + 2, y + 5); - imlib_polygon_add_point(poly, x + 5, y + 7); - imlib_polygon_add_point(poly, x + 2, y + 11); - imlib_context_set_color(0, 0, 0, 60); - imlib_image_fill_polygon(poly); - imlib_polygon_free(poly); - - poly = imlib_polygon_new(); - imlib_polygon_add_point(poly, x, y + 3); - imlib_polygon_add_point(poly, x + 3, y + 6); - imlib_polygon_add_point(poly, x, y + 9); imlib_context_set_color(0, 0, 0, 255); - imlib_image_fill_polygon(poly); - imlib_polygon_free(poly); - D_RETURN_(5); - selected = 0; + for (int i= 0; i <= 3; i++) { + imlib_image_draw_line(x+i, y+3+i, x+i, y+9-i, 0); + } + + return; } void feh_menu_draw_separator_at(int x, int y, int w, int h, Imlib_Image dst, int ox, int oy) { - D_ENTER(5); gib_imlib_image_fill_rectangle(dst, x - ox + 2, y - oy + 2, w - 4, h - 4, 0, 0, 0, 255); - D_RETURN_(5); + return; } void feh_menu_item_draw_at(int x, int y, int w, int h, Imlib_Image dst, int ox, int oy, int selected) { - D_ENTER(5); imlib_context_set_image(dst); if (selected) - gib_imlib_image_fill_rectangle(dst, x - ox, y - oy, w, h, 255, 255, 255, 178); - D_RETURN_(5); + gib_imlib_image_fill_rectangle(dst, x - ox, y - oy, w, h, 127, 127, 127, 178); + return; } void feh_raise_all_menus(void) { feh_menu_list *l; - D_ENTER(5); - for (l = menus; l; l = l->next) { if (l->menu->visible) XRaiseWindow(disp, l->menu->win); } - D_RETURN_(5); + return; } void feh_redraw_menus(void) { feh_menu_list *l; - D_ENTER(5); - for (l = menus; l; l = l->next) { if (l->menu->needs_redraw) feh_menu_redraw(l->menu); } - D_RETURN_(5); + return; } feh_menu *feh_menu_get_from_window(Window win) { feh_menu_list *l; - D_ENTER(5); for (l = menus; l; l = l->next) if (l->menu->win == win) - D_RETURN(5, l->menu); - D_RETURN(5, NULL); + return(l->menu); + return(NULL); } void feh_menu_init_main(void) @@ -1011,116 +891,104 @@ void feh_menu_init_main(void) feh_menu *m; feh_menu_item *mi; - D_ENTER(4); if (!common_menus) feh_menu_init_common(); menu_main = feh_menu_new(); menu_main->name = estrdup("MAIN"); - feh_menu_add_entry(menu_main, "File", NULL, "FILE", NULL, NULL, NULL); + feh_menu_add_entry(menu_main, "File", "FILE", 0, 0, NULL); if (opt.slideshow || opt.multiwindow) { - feh_menu_add_entry(menu_main, "Sort List", NULL, "SORT", NULL, NULL, NULL); - mi = feh_menu_add_entry(menu_main, "Image Info", NULL, "INFO", NULL, NULL, NULL); + feh_menu_add_entry(menu_main, "Sort List", "SORT", 0, 0, NULL); + mi = feh_menu_add_entry(menu_main, "Image Info", "INFO", 0, 0, NULL); mi->func_gen_sub = feh_menu_func_gen_info; - feh_menu_add_entry(menu_main, NULL, NULL, NULL, NULL, NULL, NULL); + feh_menu_add_entry(menu_main, NULL, NULL, 0, 0, NULL); } - mi = feh_menu_add_entry(menu_main, "Options", NULL, "OPTIONS", NULL, NULL, NULL); + mi = feh_menu_add_entry(menu_main, "Options", "OPTIONS", 0, 0, NULL); mi->func_gen_sub = feh_menu_func_gen_options; - if (!opt.full_screen) - feh_menu_add_entry(menu_main, "About " PACKAGE, NULL, NULL, feh_menu_cb_about, NULL, NULL); if (opt.multiwindow) - feh_menu_add_entry(menu_main, "Close", NULL, NULL, feh_menu_cb_close, NULL, NULL); - feh_menu_add_entry(menu_main, "Exit", NULL, NULL, feh_menu_cb_exit, NULL, NULL); + feh_menu_add_entry(menu_main, "Close", NULL, CB_CLOSE, 0, NULL); + feh_menu_add_entry(menu_main, "Exit", NULL, CB_EXIT, 0, NULL); m = feh_menu_new(); m->name = estrdup("FILE"); - feh_menu_add_entry(m, "Reset", NULL, NULL, feh_menu_cb_reset, NULL, NULL); - feh_menu_add_entry(m, "Resize Window", NULL, NULL, feh_menu_cb_fit, NULL, NULL); - feh_menu_add_entry(m, "Reload", NULL, NULL, feh_menu_cb_reload, NULL, NULL); - feh_menu_add_entry(m, "Save Image", NULL, NULL, feh_menu_cb_save_image, NULL, NULL); - feh_menu_add_entry(m, "Save List", NULL, NULL, feh_menu_cb_save_filelist, NULL, NULL); - feh_menu_add_entry(m, "Edit in Place", NULL, "EDIT", NULL, NULL, NULL); - feh_menu_add_entry(m, "Background", NULL, "BACKGROUND", NULL, NULL, NULL); - feh_menu_add_entry(m, NULL, NULL, NULL, NULL, NULL, NULL); - feh_menu_add_entry(m, "Hide", NULL, NULL, feh_menu_cb_remove, NULL, NULL); - feh_menu_add_entry(m, "Delete", NULL, "CONFIRM", NULL, NULL, NULL); - - D_RETURN_(4); + feh_menu_add_entry(m, "Reset", NULL, CB_RESET, 0, NULL); + feh_menu_add_entry(m, "Resize Window", NULL, CB_FIT, 0, NULL); + 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); + 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); + feh_menu_add_entry(m, "Delete", "CONFIRM", 0, 0, NULL); + + return; } -void feh_menu_init_common() +void feh_menu_init_common(void) { int num_desks, i; char buf[30]; feh_menu *m; - D_ENTER(4); - if (!opt.menu_fn) { opt.menu_fn = gib_imlib_load_font(opt.menu_font); if (!opt.menu_fn) eprintf - ("couldn't load menu font %s, did you make install?\nAre you specifying a nonexistant font?\nDid you tell feh where to find it with --fontpath?", + ("couldn't load menu font %s, did you make install?\nAre you specifying a nonexistent font?\nDid you tell feh where to find it with --fontpath?", opt.menu_font); } - if (!opt.menu_style_l) { - opt.menu_style_l = gib_style_new_from_ascii(opt.menu_style); - if (!opt.menu_style_l) { - weprintf - ("couldn't load style file for menu fonts, (%s).\nDid you make install? Menus will look boring without the style file.", - opt.menu_style); - } - } m = feh_menu_new(); m->name = estrdup("SORT"); - feh_menu_add_entry(m, "By File Name", NULL, NULL, feh_menu_cb_sort_filename, NULL, NULL); - feh_menu_add_entry(m, "By Image Name", NULL, NULL, feh_menu_cb_sort_imagename, NULL, NULL); - if (opt.preload || (opt.sort > SORT_FILENAME)) - feh_menu_add_entry(m, "By File Size", NULL, NULL, feh_menu_cb_sort_filesize, NULL, NULL); - feh_menu_add_entry(m, "Randomize", NULL, NULL, feh_menu_cb_sort_randomize, NULL, NULL); + feh_menu_add_entry(m, "By File Name", NULL, CB_SORT_FILENAME, 0, NULL); + feh_menu_add_entry(m, "By Image Name", NULL, CB_SORT_IMAGENAME, 0, NULL); + feh_menu_add_entry(m, "By Directory Name", NULL, CB_SORT_DIRNAME, 0, NULL); + feh_menu_add_entry(m, "By Modification Date", NULL, CB_SORT_MTIME, 0, NULL); + if (opt.preload || (opt.sort > SORT_MTIME)) + feh_menu_add_entry(m, "By File Size", NULL, CB_SORT_FILESIZE, 0, NULL); + feh_menu_add_entry(m, "Randomize", NULL, CB_SORT_RANDOMIZE, 0, NULL); m = feh_menu_new(); m->name = estrdup("CONFIRM"); - feh_menu_add_entry(m, "Confirm", NULL, NULL, feh_menu_cb_delete, NULL, NULL); + feh_menu_add_entry(m, "Confirm", NULL, CB_DELETE, 0, NULL); m = feh_menu_new(); m->name = estrdup("EDIT"); - feh_menu_add_entry(m, "Rotate 90 CW", NULL, NULL, feh_menu_cb_edit_rotate, (void *) 1, NULL); - feh_menu_add_entry(m, "Rotate 180", NULL, NULL, feh_menu_cb_edit_rotate, (void *) 2, NULL); - feh_menu_add_entry(m, "Rotate 90 CCW", NULL, NULL, feh_menu_cb_edit_rotate, (void *) 3, NULL); + 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"); num_desks = feh_wm_get_num_desks(); if (num_desks > 1) { - feh_menu_add_entry(menu_bg, "Set Tiled", NULL, "TILED", NULL, NULL, NULL); - feh_menu_add_entry(menu_bg, "Set Seamless", NULL, "SEAMLESS", NULL, NULL, NULL); - feh_menu_add_entry(menu_bg, "Set Scaled", NULL, "SCALED", NULL, NULL, NULL); - feh_menu_add_entry(menu_bg, "Set Centered", NULL, "CENTERED", NULL, NULL, NULL); - feh_menu_add_entry(menu_bg, "Set Filled", NULL, "FILLED", NULL, NULL, NULL); + feh_menu_add_entry(menu_bg, "Set Tiled", "TILED", 0, 0, NULL); + feh_menu_add_entry(menu_bg, "Set Scaled", "SCALED", 0, 0, NULL); + feh_menu_add_entry(menu_bg, "Set Centered", "CENTERED", 0, 0, NULL); + feh_menu_add_entry(menu_bg, "Set Filled", "FILLED", 0, 0, NULL); m = feh_menu_new(); m->name = estrdup("TILED"); for (i = 0; i < num_desks; i++) { snprintf(buf, sizeof(buf), "Desktop %d", i + 1); if (opt.slideshow || opt.multiwindow) - feh_menu_add_entry(m, buf, NULL, NULL, - feh_menu_cb_background_set_tiled, (void *) i, NULL); + feh_menu_add_entry(m, buf, NULL, CB_BG_TILED, + i, NULL); else - feh_menu_add_entry(m, buf, NULL, NULL, - feh_menu_cb_background_set_tiled_no_file, (void *) i, NULL); - } - - m = feh_menu_new(); - m->name = estrdup("SEAMLESS"); - for (i = 0; i < num_desks; i++) { - snprintf(buf, sizeof(buf), "Desktop %d", i + 1); - feh_menu_add_entry(m, buf, NULL, NULL, feh_menu_cb_background_set_seamless, (void *) i, NULL); + feh_menu_add_entry(m, buf, NULL, CB_BG_TILED_NOFILE, + i, NULL); } m = feh_menu_new(); @@ -1129,11 +997,11 @@ void feh_menu_init_common() snprintf(buf, sizeof(buf), "Desktop %d", i + 1); if (opt.slideshow || opt.multiwindow) - feh_menu_add_entry(m, buf, NULL, NULL, - feh_menu_cb_background_set_scaled, (void *) i, NULL); + feh_menu_add_entry(m, buf, NULL, CB_BG_SCALED, + i, NULL); else - feh_menu_add_entry(m, buf, NULL, NULL, - feh_menu_cb_background_set_scaled_no_file, (void *) i, NULL); + feh_menu_add_entry(m, buf, NULL, CB_BG_SCALED_NOFILE, + i, NULL); } m = feh_menu_new(); @@ -1141,12 +1009,11 @@ void feh_menu_init_common() for (i = 0; i < num_desks; i++) { snprintf(buf, sizeof(buf), "Desktop %d", i + 1); if (opt.slideshow || opt.multiwindow) - feh_menu_add_entry(m, buf, NULL, NULL, - feh_menu_cb_background_set_centered, (void *) i, NULL); + feh_menu_add_entry(m, buf, NULL, + CB_BG_CENTERED, i, NULL); else - feh_menu_add_entry(m, buf, NULL, NULL, - feh_menu_cb_background_set_centered_no_file, - (void *) i, NULL); + feh_menu_add_entry(m, buf, NULL, + CB_BG_CENTERED_NOFILE, i, NULL); } m = feh_menu_new(); @@ -1154,55 +1021,38 @@ void feh_menu_init_common() for (i = 0; i < num_desks; i++) { snprintf(buf, sizeof(buf), "Desktop %d", i + 1); if (opt.slideshow || opt.multiwindow) - feh_menu_add_entry(m, buf, NULL, NULL, - feh_menu_cb_background_set_filled, - (void *) i, NULL); + feh_menu_add_entry(m, buf, NULL, + CB_BG_FILLED, + i, NULL); else - feh_menu_add_entry(m, buf, NULL, NULL, - feh_menu_cb_background_set_filled_no_file, - (void *) i, NULL); + feh_menu_add_entry(m, buf, NULL, + CB_BG_FILLED_NOFILE, + i, NULL); } } else { if (opt.slideshow || opt.multiwindow) { - feh_menu_add_entry(menu_bg, "Set Tiled", NULL, - NULL, feh_menu_cb_background_set_tiled, NULL, NULL); - feh_menu_add_entry(menu_bg, "Set Seamless", NULL, - NULL, feh_menu_cb_background_set_seamless, NULL, NULL); - feh_menu_add_entry(menu_bg, "Set Scaled", NULL, - NULL, feh_menu_cb_background_set_scaled, NULL, NULL); - feh_menu_add_entry(menu_bg, "Set Centered", NULL, - NULL, feh_menu_cb_background_set_centered, NULL, NULL); - feh_menu_add_entry(menu_bg, "Set Filled", NULL, - NULL, feh_menu_cb_background_set_filled, NULL, NULL); + feh_menu_add_entry(menu_bg, "Set Tiled", + NULL, CB_BG_TILED, 0, NULL); + feh_menu_add_entry(menu_bg, "Set Scaled", + NULL, CB_BG_SCALED, 0, NULL); + feh_menu_add_entry(menu_bg, "Set Centered", + NULL, CB_BG_CENTERED, 0, NULL); + feh_menu_add_entry(menu_bg, "Set Filled", + NULL, CB_BG_FILLED, 0, NULL); } else { - feh_menu_add_entry(menu_bg, "Set Tiled", NULL, - NULL, feh_menu_cb_background_set_tiled_no_file, NULL, NULL); - feh_menu_add_entry(menu_bg, "Set Seamless", NULL, - NULL, feh_menu_cb_background_set_seamless, NULL, NULL); - feh_menu_add_entry(menu_bg, "Set Scaled", NULL, - NULL, feh_menu_cb_background_set_scaled_no_file, NULL, NULL); - feh_menu_add_entry(menu_bg, "Set Centered", NULL, - NULL, feh_menu_cb_background_set_centered_no_file, NULL, NULL); - feh_menu_add_entry(menu_bg, "Set Filled", NULL, - NULL, feh_menu_cb_background_set_filled_no_file, NULL, NULL); + feh_menu_add_entry(menu_bg, "Set Tiled", + NULL, CB_BG_TILED_NOFILE, 0, NULL); + feh_menu_add_entry(menu_bg, "Set Scaled", + NULL, CB_BG_SCALED_NOFILE, 0, NULL); + feh_menu_add_entry(menu_bg, "Set Centered", + NULL, CB_BG_CENTERED_NOFILE, 0, NULL); + feh_menu_add_entry(menu_bg, "Set Filled", + NULL, CB_BG_FILLED_NOFILE, 0, NULL); } } common_menus = 1; - D_RETURN_(4); -} - -void feh_menu_init_about_win(void) -{ - D_ENTER(4); - - menu_about_win = feh_menu_new(); - menu_about_win->name = estrdup("ABOUTWIN"); - - feh_menu_add_entry(menu_about_win, "Close", NULL, NULL, feh_menu_cb_close, NULL, NULL); - feh_menu_add_entry(menu_about_win, "Exit", NULL, NULL, feh_menu_cb_exit, NULL, NULL); - - D_RETURN_(4); + return; } void feh_menu_init_single_win(void) @@ -1210,39 +1060,37 @@ void feh_menu_init_single_win(void) feh_menu *m; feh_menu_item *mi; - D_ENTER(4); if (!common_menus) feh_menu_init_common(); menu_single_win = feh_menu_new(); menu_single_win->name = estrdup("SINGLEWIN"); - feh_menu_add_entry(menu_single_win, "File", NULL, "SINGLEWIN_FILE", NULL, NULL, NULL); + feh_menu_add_entry(menu_single_win, "File", "SINGLEWIN_FILE", 0, 0, NULL); m = feh_menu_new(); m->name = estrdup("SINGLEWIN_FILE"); - feh_menu_add_entry(m, "Reset", NULL, NULL, feh_menu_cb_reset, NULL, NULL); - feh_menu_add_entry(m, "Resize Window", NULL, NULL, feh_menu_cb_fit, NULL, NULL); - feh_menu_add_entry(m, "Reload", NULL, NULL, feh_menu_cb_reload, NULL, NULL); - feh_menu_add_entry(m, "Save Image", NULL, NULL, feh_menu_cb_save_image, NULL, NULL); - feh_menu_add_entry(m, "Save List", NULL, NULL, feh_menu_cb_save_filelist, NULL, NULL); - feh_menu_add_entry(m, "Edit in Place", NULL, "EDIT", NULL, NULL, NULL); - feh_menu_add_entry(m, "Background", NULL, "BACKGROUND", NULL, NULL, NULL); + feh_menu_add_entry(m, "Reset", NULL, CB_RESET, 0, NULL); + feh_menu_add_entry(m, "Resize Window", NULL, CB_FIT, 0, NULL); + 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); + feh_menu_add_entry(m, "Background", "BACKGROUND", 0, 0, NULL); if (opt.multiwindow || opt.slideshow) { - feh_menu_add_entry(m, NULL, NULL, NULL, NULL, NULL, NULL); - feh_menu_add_entry(m, "Hide", NULL, NULL, feh_menu_cb_remove, NULL, NULL); - feh_menu_add_entry(m, "Delete", NULL, "CONFIRM", NULL, NULL, NULL); + feh_menu_add_entry(m, NULL, NULL, 0, 0, NULL); + feh_menu_add_entry(m, "Hide", NULL, CB_REMOVE, 0, NULL); + feh_menu_add_entry(m, "Delete", "CONFIRM", 0, 0, NULL); } - mi = feh_menu_add_entry(menu_single_win, "Image Info", NULL, "INFO", NULL, NULL, NULL); + mi = feh_menu_add_entry(menu_single_win, "Image Info", "INFO", 0, 0, NULL); mi->func_gen_sub = feh_menu_func_gen_info; - feh_menu_add_entry(menu_single_win, NULL, NULL, NULL, NULL, NULL, NULL); - mi = feh_menu_add_entry(menu_single_win, "Options", NULL, "OPTIONS", NULL, NULL, NULL); + feh_menu_add_entry(menu_single_win, NULL, NULL, 0, 0, NULL); + mi = feh_menu_add_entry(menu_single_win, "Options", "OPTIONS", 0, 0, NULL); mi->func_gen_sub = feh_menu_func_gen_options; - feh_menu_add_entry(menu_single_win, "About " PACKAGE, NULL, NULL, feh_menu_cb_about, NULL, NULL); - feh_menu_add_entry(menu_single_win, "Close", NULL, NULL, feh_menu_cb_close, NULL, NULL); - feh_menu_add_entry(menu_single_win, "Exit", NULL, NULL, feh_menu_cb_exit, NULL, NULL); + feh_menu_add_entry(menu_single_win, "Close", NULL, CB_CLOSE, 0, NULL); + feh_menu_add_entry(menu_single_win, "Exit", NULL, CB_EXIT, 0, NULL); - D_RETURN_(4); + return; } void feh_menu_init_thumbnail_win(void) @@ -1250,28 +1098,26 @@ void feh_menu_init_thumbnail_win(void) feh_menu *m; feh_menu_item *mi; - D_ENTER(4); if (!common_menus) feh_menu_init_common(); menu_thumbnail_win = feh_menu_new(); menu_thumbnail_win->name = estrdup("THUMBWIN"); - feh_menu_add_entry(menu_thumbnail_win, "File", NULL, "THUMBWIN_FILE", NULL, NULL, NULL); + feh_menu_add_entry(menu_thumbnail_win, "File", "THUMBWIN_FILE", 0, 0, NULL); m = feh_menu_new(); m->name = estrdup("THUMBWIN_FILE"); - feh_menu_add_entry(m, "Reset", NULL, NULL, feh_menu_cb_reset, NULL, NULL); - feh_menu_add_entry(m, "Resize Window", NULL, NULL, feh_menu_cb_fit, NULL, NULL); - feh_menu_add_entry(m, "Save Image", NULL, NULL, feh_menu_cb_save_image, NULL, NULL); - feh_menu_add_entry(m, "Save List", NULL, NULL, feh_menu_cb_save_filelist, NULL, NULL); - feh_menu_add_entry(m, "Background", NULL, "BACKGROUND", NULL, NULL, NULL); - feh_menu_add_entry(menu_thumbnail_win, NULL, NULL, NULL, NULL, NULL, NULL); - mi = feh_menu_add_entry(menu_thumbnail_win, "Options", NULL, "OPTIONS", NULL, NULL, NULL); + feh_menu_add_entry(m, "Reset", NULL, CB_RESET, 0, NULL); + feh_menu_add_entry(m, "Resize Window", NULL, CB_FIT, 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, "Background", "BACKGROUND", 0, 0, NULL); + feh_menu_add_entry(menu_thumbnail_win, NULL, NULL, 0, 0, NULL); + mi = feh_menu_add_entry(menu_thumbnail_win, "Options", "OPTIONS", 0, 0, NULL); mi->func_gen_sub = feh_menu_func_gen_options; - feh_menu_add_entry(menu_thumbnail_win, "About " PACKAGE, NULL, NULL, feh_menu_cb_about, NULL, NULL); - feh_menu_add_entry(menu_thumbnail_win, "Close", NULL, NULL, feh_menu_cb_close, NULL, NULL); - feh_menu_add_entry(menu_thumbnail_win, "Exit", NULL, NULL, feh_menu_cb_exit, NULL, NULL); - D_RETURN_(4); + feh_menu_add_entry(menu_thumbnail_win, "Close", NULL, CB_CLOSE, 0, NULL); + feh_menu_add_entry(menu_thumbnail_win, "Exit", NULL, CB_EXIT, 0, NULL); + return; } void feh_menu_init_thumbnail_viewer(void) @@ -1279,485 +1125,330 @@ void feh_menu_init_thumbnail_viewer(void) feh_menu *m; feh_menu_item *mi; - D_ENTER(4); if (!common_menus) feh_menu_init_common(); menu_thumbnail_viewer = feh_menu_new(); menu_thumbnail_viewer->name = estrdup("THUMBVIEW"); - feh_menu_add_entry(menu_thumbnail_viewer, "File", NULL, "THUMBVIEW_FILE", NULL, NULL, NULL); + feh_menu_add_entry(menu_thumbnail_viewer, "File", "THUMBVIEW_FILE", + 0, 0, NULL); m = feh_menu_new(); m->name = estrdup("THUMBVIEW_FILE"); - feh_menu_add_entry(m, "Reset", NULL, NULL, feh_menu_cb_reset, NULL, NULL); - feh_menu_add_entry(m, "Resize Window", NULL, NULL, feh_menu_cb_fit, NULL, NULL); - feh_menu_add_entry(m, "Reload", NULL, NULL, feh_menu_cb_reload, NULL, NULL); - feh_menu_add_entry(m, "Save Image", NULL, NULL, feh_menu_cb_save_image, NULL, NULL); - feh_menu_add_entry(m, "Save List", NULL, NULL, feh_menu_cb_save_filelist, NULL, NULL); - feh_menu_add_entry(m, "Edit in Place", NULL, "EDIT", NULL, NULL, NULL); - feh_menu_add_entry(m, "Background", NULL, "BACKGROUND", NULL, NULL, NULL); - feh_menu_add_entry(m, NULL, NULL, NULL, NULL, NULL, NULL); - feh_menu_add_entry(m, "Hide", NULL, NULL, feh_menu_cb_remove_thumb, NULL, NULL); - feh_menu_add_entry(m, "Delete", NULL, "THUMBVIEW_CONFIRM", NULL, NULL, NULL); - mi = feh_menu_add_entry(menu_thumbnail_viewer, "Image Info", NULL, "INFO", NULL, NULL, NULL); + feh_menu_add_entry(m, "Reset", NULL, CB_RESET, 0, NULL); + feh_menu_add_entry(m, "Resize Window", NULL, CB_FIT, 0, NULL); + 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); + 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_THUMB, 0, NULL); + feh_menu_add_entry(m, "Delete", "THUMBVIEW_CONFIRM", 0, 0, NULL); + mi = feh_menu_add_entry(menu_thumbnail_viewer, "Image Info", + "INFO", 0, 0, NULL); mi->func_gen_sub = feh_menu_func_gen_info; - feh_menu_add_entry(menu_thumbnail_viewer, NULL, NULL, NULL, NULL, NULL, NULL); - mi = feh_menu_add_entry(menu_thumbnail_viewer, "Options", NULL, "OPTIONS", NULL, NULL, NULL); + feh_menu_add_entry(menu_thumbnail_viewer, NULL, NULL, 0, 0, NULL); + mi = feh_menu_add_entry(menu_thumbnail_viewer, "Options", + "OPTIONS", 0, 0, NULL); mi->func_gen_sub = feh_menu_func_gen_options; - feh_menu_add_entry(menu_thumbnail_viewer, "About " PACKAGE, NULL, NULL, feh_menu_cb_about, NULL, NULL); - feh_menu_add_entry(menu_thumbnail_viewer, "Close", NULL, NULL, feh_menu_cb_close, NULL, NULL); - feh_menu_add_entry(menu_thumbnail_viewer, "Exit", NULL, NULL, feh_menu_cb_exit, NULL, NULL); + feh_menu_add_entry(menu_thumbnail_viewer, "Close", NULL, CB_CLOSE, 0, NULL); + feh_menu_add_entry(menu_thumbnail_viewer, "Exit", NULL, CB_EXIT, 0, NULL); m = feh_menu_new(); m->name = estrdup("THUMBVIEW_CONFIRM"); - feh_menu_add_entry(m, "Confirm", NULL, NULL, feh_menu_cb_delete_thumb, NULL, NULL); - D_RETURN_(4); -} - -static void feh_menu_cb_background_set_tiled(feh_menu * m, feh_menu_item * i, void *data) -{ - char *path; - - D_ENTER(4); - path = feh_absolute_path(FEH_FILE(m->fehwin->file->data)->filename); - feh_wm_set_bg(path, m->fehwin->im, 0, 0, 0, (int) data, 1); - free(path); - D_RETURN_(4); - i = NULL; -} - -static void feh_menu_cb_background_set_seamless(feh_menu * m, feh_menu_item * i, void *data) -{ - Imlib_Image im; - - D_ENTER(4); - im = gib_imlib_clone_image(m->fehwin->im); - gib_imlib_image_tile(im); - feh_wm_set_bg(NULL, im, 0, 0, 0, (int) data, 1); - gib_imlib_free_image_and_decache(im); - D_RETURN_(4); - i = NULL; + feh_menu_add_entry(m, "Confirm", NULL, CB_DELETE_THUMB, 0, NULL); + return; } -static void feh_menu_cb_background_set_scaled(feh_menu * m, feh_menu_item * i, void *data) +void feh_menu_cb_opt_fullscreen(feh_menu * m, feh_menu_item * i) { - char *path; - - D_ENTER(4); - path = feh_absolute_path(FEH_FILE(m->fehwin->file->data)->filename); - feh_wm_set_bg(path, m->fehwin->im, 0, 1, 0, (int) data, 1); - free(path); - D_RETURN_(4); - i = NULL; -} - -static void feh_menu_cb_background_set_centered(feh_menu * m, feh_menu_item * i, void *data) -{ - char *path; - - D_ENTER(4); - path = feh_absolute_path(FEH_FILE(m->fehwin->file->data)->filename); - feh_wm_set_bg(path, m->fehwin->im, 1, 0, 0, (int) data, 1); - free(path); - D_RETURN_(4); - i = NULL; -} - -static void feh_menu_cb_background_set_filled(feh_menu * m, feh_menu_item * i, void *data) -{ - char *path; - - D_ENTER(4); - path = feh_absolute_path(FEH_FILE(m->fehwin->file->data)->filename); - feh_wm_set_bg(path, m->fehwin->im, 0, 0, 1, (int) data, 1); - free(path); - D_RETURN_(4); - i = NULL; -} - -static void feh_menu_cb_background_set_tiled_no_file(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - feh_wm_set_bg(NULL, m->fehwin->im, 0, 0, 0, (int) data, 1); - D_RETURN_(4); - i = NULL; -} + int curr_screen = 0; -static void feh_menu_cb_background_set_scaled_no_file(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - feh_wm_set_bg(NULL, m->fehwin->im, 0, 1, 0, (int) data, 1); - D_RETURN_(4); - i = NULL; -} + MENU_ITEM_TOGGLE(i); + if (MENU_ITEM_IS_ON(i)) + m->fehwin->full_screen = TRUE; + else + m->fehwin->full_screen = FALSE; -static void feh_menu_cb_background_set_centered_no_file(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - feh_wm_set_bg(NULL, m->fehwin->im, 1, 0, 0, (int) data, 1); - D_RETURN_(4); - i = NULL; -} +#ifdef HAVE_LIBXINERAMA + if (opt.xinerama && xinerama_screens) { + int i, rect[4]; -static void feh_menu_cb_background_set_filled_no_file(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - feh_wm_set_bg(NULL, m->fehwin->im, 0, 0, 1, (int) data, 1); - D_RETURN_(4); - i = NULL; -} + winwidget_get_geometry(m->fehwin, rect); + for (i = 0; i < num_xinerama_screens; i++) { + xinerama_screen = 0; + if (XY_IN_RECT(rect[0], rect[1], + xinerama_screens[i].x_org, + xinerama_screens[i].y_org, + xinerama_screens[i].width, xinerama_screens[i].height)) { + curr_screen = xinerama_screen = i; + break; + } -static void feh_menu_cb_about(feh_menu * m, feh_menu_item * i, void *data) -{ - Imlib_Image im; - winwidget winwid; - - D_ENTER(4); - if (feh_load_image_char(&im, PREFIX "/share/feh/images/about.png") - != 0) { - winwid = winwidget_create_from_image(im, "About " PACKAGE, WIN_TYPE_ABOUT); - winwid->file = gib_list_add_front(NULL, feh_file_new(PREFIX "/share/feh/images/about.png")); - winwidget_show(winwid); + } + if (opt.xinerama_index >= 0) + curr_screen = xinerama_screen = opt.xinerama_index; } - D_RETURN_(4); - m = NULL; - i = NULL; - data = NULL; -} +#endif /* HAVE_LIBXINERAMA */ -static void feh_menu_cb_close(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - winwidget_destroy(m->fehwin); - D_RETURN_(4); - i = NULL; - data = NULL; -} + winwidget_destroy_xwin(m->fehwin); + winwidget_create_window(m->fehwin, m->fehwin->im_w, m->fehwin->im_h); -static void feh_menu_cb_exit(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - winwidget_destroy_all(); - D_RETURN_(4); - m = NULL; - i = NULL; - data = NULL; -} + winwidget_render_image(m->fehwin, 1, 0); + winwidget_show(m->fehwin); -static void feh_menu_cb_reset(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - if (m->fehwin->has_rotated) { - m->fehwin->im_w = gib_imlib_image_get_width(m->fehwin->im); - m->fehwin->im_h = gib_imlib_image_get_height(m->fehwin->im); - winwidget_resize(m->fehwin, m->fehwin->im_w, m->fehwin->im_h); +#ifdef HAVE_LIBXINERAMA + /* if we have xinerama and we're using it, then full screen the window + * on the head that the window was active on */ + if (m->fehwin->full_screen == TRUE && opt.xinerama && xinerama_screens) { + xinerama_screen = curr_screen; + winwidget_move(m->fehwin, xinerama_screens[curr_screen].x_org, xinerama_screens[curr_screen].y_org); } - winwidget_reset_image(m->fehwin); - winwidget_render_image(m->fehwin, 1, 1); - D_RETURN_(4); - i = NULL; - data = NULL; -} - -static void feh_menu_cb_reload(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - feh_reload_image(m->fehwin, 0, 0); - D_RETURN_(4); - i = NULL; - data = NULL; -} - -static void feh_menu_cb_remove(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - feh_filelist_image_remove(m->fehwin, 0); - D_RETURN_(4); - i = NULL; - data = NULL; -} - -static void feh_menu_cb_delete(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - feh_filelist_image_remove(m->fehwin, 1); - D_RETURN_(4); - i = NULL; - data = NULL; -} - -static void feh_menu_cb_remove_thumb(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - feh_thumbnail_mark_removed(FEH_FILE(m->fehwin->file->data), 0); - feh_filelist_image_remove(m->fehwin, 0); - D_RETURN_(4); - i = NULL; - data = NULL; -} - -static void feh_menu_cb_delete_thumb(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - feh_thumbnail_mark_removed(FEH_FILE(m->fehwin->file->data), 1); - feh_filelist_image_remove(m->fehwin, 1); - D_RETURN_(4); - i = NULL; - data = NULL; -} - -static void feh_menu_cb_sort_filename(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - filelist = gib_list_sort(filelist, feh_cmp_filename); - if (!opt.no_jump_on_resort) { - slideshow_change_image(m->fehwin, SLIDE_FIRST); - }; - D_RETURN_(4); - i = NULL; - data = NULL; -} - -static void feh_menu_cb_sort_imagename(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - filelist = gib_list_sort(filelist, feh_cmp_name); - if (!opt.no_jump_on_resort) { - slideshow_change_image(m->fehwin, SLIDE_FIRST); - }; - D_RETURN_(4); - i = NULL; - data = NULL; +#endif /* HAVE_LIBXINERAMA */ } -static void feh_menu_cb_sort_filesize(feh_menu * m, feh_menu_item * i, void *data) +void feh_menu_cb(feh_menu * m, feh_menu_item * i, int action, unsigned short data) { - D_ENTER(4); - filelist = gib_list_sort(filelist, feh_cmp_size); - if (!opt.no_jump_on_resort) { - slideshow_change_image(m->fehwin, SLIDE_FIRST); - }; - D_RETURN_(4); - i = NULL; - data = NULL; -} + char *path; -static void feh_menu_cb_sort_randomize(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - filelist = gib_list_randomize(filelist); - if (!opt.no_jump_on_resort) { - slideshow_change_image(m->fehwin, SLIDE_FIRST); - }; - D_RETURN_(4); - i = NULL; - data = NULL; + switch (action) { + case CB_BG_TILED: + path = FEH_FILE(m->fehwin->file->data)->filename; + feh_wm_set_bg(path, m->fehwin->im, 0, 0, 0, data, 0); + break; + case CB_BG_SCALED: + path = FEH_FILE(m->fehwin->file->data)->filename; + feh_wm_set_bg(path, m->fehwin->im, 0, 1, 0, data, 0); + break; + case CB_BG_CENTERED: + path = FEH_FILE(m->fehwin->file->data)->filename; + feh_wm_set_bg(path, m->fehwin->im, 1, 0, 0, data, 0); + break; + case CB_BG_FILLED: + path = FEH_FILE(m->fehwin->file->data)->filename; + feh_wm_set_bg(path, m->fehwin->im, 0, 0, 1, data, 0); + break; + case CB_BG_TILED_NOFILE: + feh_wm_set_bg(NULL, m->fehwin->im, 0, 0, 0, data, 0); + break; + case CB_BG_SCALED_NOFILE: + feh_wm_set_bg(NULL, m->fehwin->im, 0, 1, 0, data, 0); + break; + case CB_BG_CENTERED_NOFILE: + feh_wm_set_bg(NULL, m->fehwin->im, 1, 0, 0, data, 0); + break; + case CB_BG_FILLED_NOFILE: + feh_wm_set_bg(NULL, m->fehwin->im, 0, 0, 1, data, 0); + break; + case CB_CLOSE: + winwidget_destroy(m->fehwin); + break; + case CB_EXIT: + winwidget_destroy_all(); + break; + case CB_RESET: + if (m->fehwin->has_rotated) { + m->fehwin->im_w = gib_imlib_image_get_width(m->fehwin->im); + m->fehwin->im_h = gib_imlib_image_get_height(m->fehwin->im); + winwidget_resize(m->fehwin, m->fehwin->im_w, m->fehwin->im_h, 0); + } + winwidget_reset_image(m->fehwin); + winwidget_render_image(m->fehwin, 1, 0); + break; + case CB_RELOAD: + feh_reload_image(m->fehwin, 0, 1); + break; + case CB_REMOVE: + feh_filelist_image_remove(m->fehwin, 0); + break; + case CB_DELETE: + feh_filelist_image_remove(m->fehwin, 1); + break; + case CB_REMOVE_THUMB: + feh_thumbnail_mark_removed(FEH_FILE(m->fehwin->file->data), 0); + feh_filelist_image_remove(m->fehwin, 0); + break; + case CB_DELETE_THUMB: + feh_thumbnail_mark_removed(FEH_FILE(m->fehwin->file->data), 1); + feh_filelist_image_remove(m->fehwin, 1); + break; + case CB_SORT_FILENAME: + filelist = gib_list_sort(filelist, feh_cmp_filename); + if (opt.jump_on_resort) { + slideshow_change_image(m->fehwin, SLIDE_FIRST, 1); + } + break; + case CB_SORT_IMAGENAME: + filelist = gib_list_sort(filelist, feh_cmp_name); + if (opt.jump_on_resort) { + slideshow_change_image(m->fehwin, SLIDE_FIRST, 1); + } + break; + case CB_SORT_DIRNAME: + filelist = gib_list_sort(filelist, feh_cmp_dirname); + if (opt.jump_on_resort) { + slideshow_change_image(m->fehwin, SLIDE_FIRST, 1); + } + break; + case CB_SORT_MTIME: + filelist = gib_list_sort(filelist, feh_cmp_mtime); + if (opt.jump_on_resort) { + slideshow_change_image(m->fehwin, SLIDE_FIRST, 1); + } + break; + case CB_SORT_FILESIZE: + filelist = gib_list_sort(filelist, feh_cmp_size); + if (opt.jump_on_resort) { + slideshow_change_image(m->fehwin, SLIDE_FIRST, 1); + } + break; + case CB_SORT_RANDOMIZE: + filelist = gib_list_randomize(filelist); + if (opt.jump_on_resort) { + slideshow_change_image(m->fehwin, SLIDE_FIRST, 1); + } + break; + case CB_FIT: + winwidget_size_to_image(m->fehwin); + break; + 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; + case CB_SAVE_FILELIST: + feh_save_filelist(); + break; + case CB_OPT_DRAW_FILENAME: + MENU_ITEM_TOGGLE(i); + if (MENU_ITEM_IS_ON(i)) + opt.draw_filename = TRUE; + else + opt.draw_filename = FALSE; + winwidget_rerender_all(0); + break; + case CB_OPT_DRAW_ACTIONS: + MENU_ITEM_TOGGLE(i); + if (MENU_ITEM_IS_ON(i)) + opt.draw_actions = TRUE; + else + opt.draw_actions = FALSE; + winwidget_rerender_all(0); + break; + case CB_OPT_KEEP_HTTP: + MENU_ITEM_TOGGLE(i); + if (MENU_ITEM_IS_ON(i)) + opt.keep_http = TRUE; + else + opt.keep_http = FALSE; + break; + case CB_OPT_FREEZE_WINDOW: + MENU_ITEM_TOGGLE(i); + if (MENU_ITEM_IS_ON(i)) { + opt.geom_flags = (WidthValue | HeightValue); + opt.geom_w = m->fehwin->w; + opt.geom_h = m->fehwin->h; + } else { + opt.geom_flags = 0; + } + break; + case CB_OPT_FULLSCREEN: + feh_menu_cb_opt_fullscreen(m, i); + break; + case CB_OPT_AUTO_ZOOM: + MENU_ITEM_TOGGLE(i); + if (MENU_ITEM_IS_ON(i)) + opt.zoom_mode = ZOOM_MODE_MAX; + else + opt.zoom_mode = 0; + winwidget_rerender_all(1); + break; + case CB_OPT_KEEP_ZOOM_VP: + MENU_ITEM_TOGGLE(i); + if (MENU_ITEM_IS_ON(i)) + opt.keep_zoom_vp = 1; + else + opt.keep_zoom_vp = 0; + break; + } + return; } -static feh_menu *feh_menu_func_gen_info(feh_menu * m, feh_menu_item * i, void *data) +static feh_menu *feh_menu_func_gen_info(feh_menu * m) { Imlib_Image im; feh_menu *mm; feh_file *file; char buffer[400]; - D_ENTER(4); if (!m->fehwin->file) - D_RETURN(4, NULL); + return(NULL); file = FEH_FILE(m->fehwin->file->data); im = m->fehwin->im; if (!im) - D_RETURN(4, NULL); + return(NULL); mm = feh_menu_new(); mm->name = estrdup("INFO"); snprintf(buffer, sizeof(buffer), "Filename: %s", file->name); - feh_menu_add_entry(mm, buffer, NULL, NULL, NULL, NULL, NULL); + feh_menu_add_entry(mm, buffer, NULL, 0, 0, NULL); if (!file->info) feh_file_info_load(file, im); if (file->info) { - snprintf(buffer, sizeof(buffer), "Size: %dKb", file->info->size / 1024); - feh_menu_add_entry(mm, buffer, NULL, NULL, NULL, NULL, NULL); + snprintf(buffer, sizeof(buffer), "Size: %dKb", file->size / 1024); + feh_menu_add_entry(mm, buffer, NULL, 0, 0, NULL); snprintf(buffer, sizeof(buffer), "Dimensions: %dx%d", file->info->width, file->info->height); - feh_menu_add_entry(mm, buffer, NULL, NULL, NULL, NULL, NULL); + feh_menu_add_entry(mm, buffer, NULL, 0, 0, NULL); snprintf(buffer, sizeof(buffer), "Type: %s", file->info->format); - feh_menu_add_entry(mm, buffer, NULL, NULL, NULL, NULL, NULL); + feh_menu_add_entry(mm, buffer, NULL, 0, 0, NULL); } mm->func_free = feh_menu_func_free_info; - D_RETURN(4, mm); - i = NULL; - data = NULL; + return(mm); } -static void feh_menu_func_free_info(feh_menu * m, void *data) +static void feh_menu_func_free_info(feh_menu * m) { - D_ENTER(4); feh_menu_free(m); - D_RETURN_(4); - data = NULL; + return; } -static feh_menu *feh_menu_func_gen_options(feh_menu * m, feh_menu_item * i, void *data) +static feh_menu *feh_menu_func_gen_options(feh_menu * m) { feh_menu *mm; - D_ENTER(4); mm = feh_menu_new(); mm->name = estrdup("OPTIONS"); mm->fehwin = m->fehwin; - feh_menu_add_toggle_entry(mm, "Auto-Zoom", NULL, NULL, feh_menu_cb_opt_auto_zoom, NULL, NULL, opt.auto_zoom); - feh_menu_add_toggle_entry(mm, "Freeze Window Size", NULL, NULL, - feh_menu_cb_opt_freeze_window, NULL, NULL, opt.geom_flags); - feh_menu_add_toggle_entry(mm, "Fullscreen", NULL, NULL, - feh_menu_cb_opt_fullscreen, NULL, NULL, m->fehwin->full_screen); -#ifdef HAVE_LIBXINERAMA - feh_menu_add_toggle_entry(mm, "Use Xinerama", NULL, NULL, feh_menu_cb_opt_xinerama, NULL, NULL, opt.xinerama); -#endif /* HAVE_LIBXINERAMA */ - feh_menu_add_entry(mm, NULL, NULL, NULL, NULL, NULL, NULL); - feh_menu_add_toggle_entry(mm, "Draw Filename", NULL, NULL, - feh_menu_cb_opt_draw_filename, NULL, NULL, opt.draw_filename); - feh_menu_add_toggle_entry(mm, "Keep HTTP Files", NULL, NULL, - feh_menu_cb_opt_keep_http, NULL, NULL, opt.keep_http); + feh_menu_add_toggle_entry(mm, "Auto-Zoom", NULL, CB_OPT_AUTO_ZOOM, + 0, NULL, opt.zoom_mode); + feh_menu_add_toggle_entry(mm, "Freeze Window Size", NULL, + CB_OPT_FREEZE_WINDOW, 0, NULL, opt.geom_flags); + feh_menu_add_toggle_entry(mm, "Fullscreen", NULL, + CB_OPT_FULLSCREEN, 0, NULL, m->fehwin->full_screen); + feh_menu_add_toggle_entry(mm, "Keep viewport zoom & pos", NULL, + CB_OPT_KEEP_ZOOM_VP, 0, NULL, opt.keep_zoom_vp); + + feh_menu_add_entry(mm, NULL, NULL, 0, 0, NULL); + + feh_menu_add_toggle_entry(mm, "Draw Filename", NULL, + CB_OPT_DRAW_FILENAME, 0, NULL, opt.draw_filename); + feh_menu_add_toggle_entry(mm, "Draw Actions", NULL, + CB_OPT_DRAW_ACTIONS, 0, NULL, opt.draw_actions); + feh_menu_add_toggle_entry(mm, "Keep HTTP Files", NULL, + CB_OPT_KEEP_HTTP, 0, NULL, opt.keep_http); mm->func_free = feh_menu_func_free_options; - D_RETURN(4, mm); - i = NULL; - data = NULL; + return(mm); } -static void feh_menu_func_free_options(feh_menu * m, void *data) +static void feh_menu_func_free_options(feh_menu * m) { - D_ENTER(4); feh_menu_free(m); - D_RETURN_(4); - data = NULL; -} - -static void feh_menu_cb_fit(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - winwidget_size_to_image(m->fehwin); - D_RETURN_(4); - data = NULL; -} - -static void feh_menu_cb_edit_rotate(feh_menu * m, feh_menu_item * i, void *data) -{ - D_ENTER(4); - feh_edit_inplace_orient(m->fehwin, (int) data); - D_RETURN_(4); -} - -static void feh_menu_cb_save_image(feh_menu * m, feh_menu_item * i, void *data) -{ - slideshow_save_image(m->fehwin); -} - -static void feh_menu_cb_save_filelist(feh_menu * m, feh_menu_item * i, void *data) -{ - feh_save_filelist(); -} - -static void feh_menu_cb_opt_draw_filename(feh_menu * m, feh_menu_item * i, void *data) -{ - MENU_ITEM_TOGGLE(i); - if (MENU_ITEM_IS_ON(i)) - opt.draw_filename = TRUE; - else - opt.draw_filename = FALSE; - winwidget_rerender_all(0, 1); -} - -static void feh_menu_cb_opt_keep_http(feh_menu * m, feh_menu_item * i, void *data) -{ - MENU_ITEM_TOGGLE(i); - if (MENU_ITEM_IS_ON(i)) - opt.keep_http = TRUE; - else - opt.keep_http = FALSE; -} - -static void feh_menu_cb_opt_freeze_window(feh_menu * m, feh_menu_item * i, void *data) -{ - MENU_ITEM_TOGGLE(i); - if (MENU_ITEM_IS_ON(i)) { - opt.geom_flags = (WidthValue | HeightValue); - opt.geom_w = m->fehwin->w; - opt.geom_h = m->fehwin->h; - } else { - opt.geom_flags = 0; - } -} - -static void feh_menu_cb_opt_fullscreen(feh_menu * m, feh_menu_item * i, void *data) -{ - int curr_screen = 0; - - MENU_ITEM_TOGGLE(i); - if (MENU_ITEM_IS_ON(i)) - m->fehwin->full_screen = TRUE; - else - m->fehwin->full_screen = FALSE; - -#ifdef HAVE_LIBXINERAMA - if (opt.xinerama && xinerama_screens) { - int i, rect[4]; - - winwidget_get_geometry(m->fehwin, rect); - /* printf("window: (%d, %d)\n", rect[0], rect[1]); - printf("found %d screens.\n", num_xinerama_screens); */ - for (i = 0; i < num_xinerama_screens; i++) { - xinerama_screen = 0; - /* printf("%d: [%d, %d, %d, %d] (%d, %d)\n", - i, - xinerama_screens[i].x_org, xinerama_screens[i].y_org, - xinerama_screens[i].width, xinerama_screens[i].height, - rect[0], rect[1]); */ - if (XY_IN_RECT(rect[0], rect[1], - xinerama_screens[i].x_org, - xinerama_screens[i].y_org, - xinerama_screens[i].width, xinerama_screens[i].height)) { - curr_screen = xinerama_screen = i; - break; - } - - } - } -#endif /* HAVE_LIBXINERAMA */ - - winwidget_destroy_xwin(m->fehwin); - winwidget_create_window(m->fehwin, m->fehwin->im_w, m->fehwin->im_h); - - winwidget_render_image(m->fehwin, 1, 1); - winwidget_show(m->fehwin); - -#ifdef HAVE_LIBXINERAMA - /* if we have xinerama and we're using it, then full screen the window - * on the head that the window was active on */ - if (m->fehwin->full_screen == TRUE && opt.xinerama && xinerama_screens) { - xinerama_screen = curr_screen; - winwidget_move(m->fehwin, xinerama_screens[curr_screen].x_org, xinerama_screens[curr_screen].y_org); - } -#endif /* HAVE_LIBXINERAMA */ -} - -static void feh_menu_cb_opt_auto_zoom(feh_menu * m, feh_menu_item * i, void *data) -{ - MENU_ITEM_TOGGLE(i); - opt.auto_zoom = MENU_ITEM_IS_ON(i) ? 1 : 0; - winwidget_rerender_all(1, 1); + return; } - -#ifdef HAVE_LIBXINERAMA -static void feh_menu_cb_opt_xinerama(feh_menu * m, feh_menu_item * i, void *data) -{ - MENU_ITEM_TOGGLE(i); - opt.xinerama = MENU_ITEM_IS_ON(i) ? 1 : 0; - - if (opt.xinerama) { - init_xinerama(); - } else { - XFree(xinerama_screens); - xinerama_screens = NULL; - } - winwidget_rerender_all(1, 1); -} -#endif /* HAVE_LIBXINERAMA */ @@ -1,6 +1,7 @@ /* menu.h Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2020 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -76,8 +77,7 @@ typedef struct _feh_menu_list feh_menu_list; #define FEH_MENU_TOGGLE_W 7 #define FEH_MENU_TOGGLE_PAD 3 -typedef void (*menu_func) (feh_menu * m, feh_menu_item * i, void *data); -typedef feh_menu *(*menuitem_func_gen) (feh_menu * m, feh_menu_item * i, void *data); +typedef feh_menu *(*menuitem_func_gen) (feh_menu * m); struct _feh_menu_list { feh_menu *menu; @@ -86,12 +86,11 @@ struct _feh_menu_list { struct _feh_menu_item { int state; - Imlib_Image icon; char *text; char *submenu; - menu_func func; + int action; void (*func_free) (void *data); - void *data; + unsigned short data; feh_menu_item *next; feh_menu_item *prev; unsigned char is_toggle; @@ -116,7 +115,7 @@ struct _feh_menu { int needs_redraw; void *data; int calc; - void (*func_free) (feh_menu * m, void *data); + void (*func_free) (feh_menu * m); }; feh_menu *feh_menu_new(void); @@ -131,26 +130,25 @@ void feh_menu_show_at_submenu(feh_menu * m, feh_menu * parent_m, feh_menu_item * void feh_menu_hide(feh_menu * m, int func_free); void feh_menu_show(feh_menu * m); feh_menu_item *feh_menu_add_entry(feh_menu * m, char *text, - Imlib_Image icon, char *submenu, - menu_func func, void *data, void (*func_free) (void *data)); + char *submenu, + int action, unsigned short data, void (*func_free) (void *data)); feh_menu_item *feh_menu_add_toggle_entry(feh_menu * m, char *text, - Imlib_Image icon, char *submenu, - menu_func func, void *data, void (*func_free) (void *data), int setting); -void feh_menu_entry_get_size(feh_menu * m, feh_menu_item * i, int *w, int *h); + char *submenu, + int action, unsigned short data, void (*func_free) (void *data), int setting); +void feh_menu_entry_get_size(feh_menu_item * i, int *w, int *h); void feh_menu_calc_size(feh_menu * m); -void feh_menu_draw_item(feh_menu * m, feh_menu_item * i, Imlib_Image im, int ox, int oy); +void feh_menu_draw_item(feh_menu_item * i, Imlib_Image im, int ox, int oy); void feh_menu_redraw(feh_menu * m); void feh_menu_move(feh_menu * m, int x, int y); void feh_menu_slide_all_menus_relative(int dx, int dy); void feh_menu_init_main(void); void feh_menu_init_single_win(void); -void feh_menu_init_about_win(void); void feh_menu_init_common(void); void feh_menu_init_thumbnail_viewer(void); void feh_menu_init_thumbnail_win(void); void feh_menu_draw_to_buf(feh_menu * m, Imlib_Image im, int ox, int oy); void feh_menu_draw_menu_bg(feh_menu * m, Imlib_Image im, int ox, int oy); -void feh_menu_draw_submenu_at(int x, int y, int w, int h, Imlib_Image dst, int ox, int oy, int selected); +void feh_menu_draw_submenu_at(int x, int y, Imlib_Image dst, int ox, int oy); void feh_menu_draw_separator_at(int x, int y, int w, int h, Imlib_Image dst, int ox, int oy); void feh_menu_item_draw_at(int x, int y, int w, int h, Imlib_Image dst, int ox, int oy, int selected); void feh_menu_draw_toggle_at(int x, int y, int w, int h, Imlib_Image dst, int ox, int oy, int on); @@ -164,12 +162,11 @@ feh_menu_item *feh_menu_find_selected_r(feh_menu * m, feh_menu ** parent); void feh_menu_select_prev(feh_menu * selected_menu, feh_menu_item * selected_item); void feh_menu_select_next(feh_menu * selected_menu, feh_menu_item * selected_item); void feh_menu_item_activate(feh_menu * selected_menu, feh_menu_item * selected_item); -void feh_menu_select_parent(feh_menu * selected_menu, feh_menu_item * selected_item); -void feh_menu_select_submenu(feh_menu * selected_menu, feh_menu_item * selected_item); +void feh_menu_select_parent(feh_menu * selected_menu); +void feh_menu_select_submenu(feh_menu * selected_menu); extern feh_menu *menu_root; extern feh_menu *menu_single_win; -extern feh_menu *menu_about_win; extern feh_menu *menu_thumbnail_viewer; extern feh_menu *menu_thumbnail_win; extern Window menu_cover; diff --git a/src/menubg_aluminium.png b/src/menubg_aluminium.png Binary files differdeleted file mode 100644 index eed00f1..0000000 --- a/src/menubg_aluminium.png +++ /dev/null diff --git a/src/menubg_aqua.png b/src/menubg_aqua.png Binary files differdeleted file mode 100644 index 3a72590..0000000 --- a/src/menubg_aqua.png +++ /dev/null diff --git a/src/menubg_black.png b/src/menubg_black.png Binary files differdeleted file mode 100644 index 08b4c2b..0000000 --- a/src/menubg_black.png +++ /dev/null diff --git a/src/menubg_brushed.png b/src/menubg_brushed.png Binary files differdeleted file mode 100644 index 32fad47..0000000 --- a/src/menubg_brushed.png +++ /dev/null diff --git a/src/menubg_chrome.png b/src/menubg_chrome.png Binary files differdeleted file mode 100644 index 24f3025..0000000 --- a/src/menubg_chrome.png +++ /dev/null diff --git a/src/menubg_default.png b/src/menubg_default.png Binary files differdeleted file mode 100644 index dd21188..0000000 --- a/src/menubg_default.png +++ /dev/null diff --git a/src/menubg_pastel.png b/src/menubg_pastel.png Binary files differdeleted file mode 100644 index ec96566..0000000 --- a/src/menubg_pastel.png +++ /dev/null diff --git a/src/menubg_sky.png b/src/menubg_sky.png Binary files differdeleted file mode 100644 index e0be8ca..0000000 --- a/src/menubg_sky.png +++ /dev/null diff --git a/src/menubg_wood.png b/src/menubg_wood.png Binary files differdeleted file mode 100644 index 4dc7440..0000000 --- a/src/menubg_wood.png +++ /dev/null diff --git a/src/multiwindow.c b/src/multiwindow.c index cd4e82c..abbf6c9 100644 --- a/src/multiwindow.c +++ b/src/multiwindow.c @@ -28,42 +28,30 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "timers.h" #include "filelist.h" #include "options.h" +#include "signals.h" void init_multiwindow_mode(void) { winwidget w = NULL; gib_list *l; - feh_file *file = NULL; - D_ENTER(2); + if (!opt.title) + opt.title = PACKAGE " - %f"; mode = "multiwindow"; for (l = filelist; l; l = l->next) { - char *s = NULL; - int len = 0; - file = FEH_FILE(l->data); - current_file = l; - - if (!opt.title) { - len = strlen(PACKAGE " - ") + strlen(file->filename) + 1; - s = emalloc(len); - snprintf(s, len, PACKAGE " - %s", file->filename); - } else { - s = estrdup(feh_printf(opt.title, file)); - } - - if ((w = winwidget_create_from_file(l, s, WIN_TYPE_SINGLE)) != NULL) { + if ((w = winwidget_create_from_file(l, WIN_TYPE_SINGLE)) != NULL) { winwidget_show(w); if (opt.reload > 0) feh_add_unique_timer(cb_reload_timer, w, opt.reload); if (!feh_main_iteration(0)) exit(0); } else { - D(3, ("EEEK. Couldn't load image in multiwindow mode. " + D(("EEEK. Couldn't load image in multiwindow mode. " "I 'm not sure if this is a problem\n")); } - free(s); } - D_RETURN_(2); + + return; } diff --git a/src/options.c b/src/options.c index 9afa33a..d38ce45 100644 --- a/src/options.c +++ b/src/options.c @@ -1,6 +1,7 @@ /* options.c Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2024 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -23,23 +24,26 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include <strings.h> #include "feh.h" #include "filelist.h" #include "options.h" static void check_options(void); -static void feh_parse_option_array(int argc, char **argv); -static void feh_parse_environment_options(void); -static void feh_check_theme_options(int arg, char **argv); +static void feh_getopt_theme(int argc, char **argv); +static void feh_parse_option_array(int argc, char **argv, int finalrun); +static void feh_check_theme_options(char **argv); static void feh_parse_options_from_string(char *opts); static void feh_load_options_for_theme(char *theme); +static void show_usage(void); +static void show_version(void); static char *theme; fehoptions opt; void init_parse_options(int argc, char **argv) { - D_ENTER(4); + /* TODO: sort these to match declaration of __fehoptions */ /* For setting the command hint on X windows */ cmdargc = argc; @@ -49,64 +53,52 @@ void init_parse_options(int argc, char **argv) memset(&opt, 0, sizeof(fehoptions)); opt.display = 1; opt.aspect = 1; - opt.slideshow_delay = -1.0; + opt.slideshow_delay = 0.0; + opt.conversion_timeout = -1; opt.thumb_w = 60; opt.thumb_h = 60; + opt.thumb_redraw = 10; + opt.scroll_step = 20; opt.menu_font = estrdup(DEFAULT_MENU_FONT); - opt.font = estrdup(DEFAULT_FONT); - opt.image_bg = estrdup("default"); - opt.menu_bg = estrdup(PREFIX "/share/feh/images/menubg_default.png"); - opt.menu_style = estrdup(PREFIX "/share/feh/fonts/menu.style"); - opt.menu_border = 4; - - opt.reload_button = 0; - opt.pan_button = 1; - opt.zoom_button = 2; - opt.menu_button = 3; - opt.menu_ctrl_mask = 0; - opt.prev_button = 4; - opt.next_button = 5; - - opt.rotate_button = 2; - opt.no_rotate_ctrl_mask = 0; - opt.blur_button = 1; - opt.no_blur_ctrl_mask = 0; - - opt.no_jump_on_resort = 0; - - opt.builtin_http = 0; - - opt.xinerama = 0; + opt.font = NULL; + opt.max_height = opt.max_width = UINT_MAX; + + opt.zoom_rate = 1.25; + + opt.start_list_at = NULL; + opt.jump_on_resort = 1; + opt.screen_clip = 1; + opt.cache_size = 4; #ifdef HAVE_LIBXINERAMA /* if we're using xinerama, then enable it by default */ opt.xinerama = 1; + opt.xinerama_index = -1; #endif /* HAVE_LIBXINERAMA */ +#ifdef HAVE_INOTIFY + opt.auto_reload = 1; +#endif /* HAVE_INOTIFY */ + opt.use_conversion_cache = 1; - D(3, ("About to parse env options (if any)\n")); - /* Check for and parse any options in FEH_OPTIONS */ - feh_parse_environment_options(); + feh_getopt_theme(argc, argv); - D(3, ("About to parse commandline options\n")); - /* Parse the cmdline args */ - feh_parse_option_array(argc, argv); + D(("About to check for theme configuration\n")); + feh_check_theme_options(argv); - D(3, ("About to check for theme configuration\n")); - feh_check_theme_options(argc, argv); + D(("About to parse commandline options\n")); + /* Parse the cmdline args */ + feh_parse_option_array(argc, argv, 1); /* If we have a filelist to read, do it now */ if (opt.filelistfile) { /* joining two reverse-sorted lists in this manner works nicely for us here, as files specified on the commandline end up at the *end* of the combined filelist, in the specified order. */ - D(3, ("About to load filelist from file\n")); + D(("About to load filelist from file\n")); filelist = gib_list_cat(filelist, feh_read_filelist(opt.filelistfile)); } - D(4, ("Options parsed\n")); - - if (opt.bgmode) - D_RETURN_(4); + D(("Options parsed\n")); filelist_len = gib_list_length(filelist); if (!filelist_len) @@ -115,12 +107,11 @@ void init_parse_options(int argc, char **argv) check_options(); feh_prepare_filelist(); - D_RETURN_(4); + return; } -static void feh_check_theme_options(int arg, char **argv) +static void feh_check_theme_options(char **argv) { - D_ENTER(4); if (!theme) { /* This prevents screw up when running src/feh or ./feh */ char *pos = strrchr(argv[0], '/'); @@ -130,132 +121,151 @@ static void feh_check_theme_options(int arg, char **argv) else theme = estrdup(argv[0]); } - D(3, ("Theme name is %s\n", theme)); + D(("Theme name is %s\n", theme)); feh_load_options_for_theme(theme); free(theme); - D_RETURN_(4); - arg = 0; + return; } static void feh_load_options_for_theme(char *theme) { FILE *fp = NULL; - char *home; + char *home = getenv("HOME"); char *rcpath = NULL; + char *oldrcpath = NULL; + char *confbase = getenv("XDG_CONFIG_HOME"); + // s, s1 and s2 must always have identical size char s[1024], s1[1024], s2[1024]; + int cont = 0; + int bspos; + + if (confbase) + rcpath = estrjoin("/", confbase, "feh/themes", NULL); + else if (home) + rcpath = estrjoin("/", home, ".config/feh/themes", NULL); + else { + weprintf("You have no HOME, cannot read configuration"); + return; + } - D_ENTER(4); + oldrcpath = estrjoin("/", home, ".fehrc", NULL); - if (opt.rcfile) { - if ((fp = fopen(opt.rcfile, "r")) == NULL) { - weprintf("couldn't load the specified rcfile %s\n", opt.rcfile); - D_RETURN_(4); - } - } else { - home = getenv("HOME"); - if (!home) - eprintf("D'oh! Please define HOME in your environment! " - "It would really help me out...\n"); - rcpath = estrjoin("/", home, ".fehrc", NULL); - D(3, ("Trying %s for config\n", rcpath)); - fp = fopen(rcpath, "r"); - - if (!fp && ((fp = fopen("/etc/fehrc", "r")) == NULL)) { - feh_create_default_config(rcpath); - - if ((fp = fopen(rcpath, "r")) == NULL) - D_RETURN_(4); - } + fp = fopen(rcpath, "r"); - free(rcpath); - } + free(rcpath); + + if (!fp && ((fp = fopen(oldrcpath, "r")) != NULL)) + weprintf("The theme config file was moved from ~/.fehrc to " + "~/.config/feh/themes. Run\n" + " mkdir -p ~/.config/feh; mv ~/.fehrc ~/.config/feh/themes\n" + "to fix this."); + + free(oldrcpath); + + if (!fp && ((fp = fopen("/etc/feh/themes", "r")) == NULL)) + return; /* Oooh. We have an options file :) */ for (; fgets(s, sizeof(s), fp);) { s1[0] = '\0'; s2[0] = '\0'; - sscanf(s, "%s %[^\n]\n", (char *) &s1, (char *) &s2); - if (!(*s1) || (!*s2) || (*s1 == '\n') || (*s1 == '#')) - continue; - D(5, ("Got theme/options pair %s/%s\n", s1, s2)); - if (!strcmp(s1, theme)) { - D(4, ("A match. Using options %s\n", s2)); - feh_parse_options_from_string(s2); - break; + + if (cont) { + /* + * fgets ensures that s contains no more than 1023 characters + * (+ 1 null byte) + */ + sscanf(s, " %[^\n]\n", (char *) &s2); + if (!*s2) + break; + D(("Got continued options %s\n", s2)); + } else { + /* + * fgets ensures that s contains no more than 1023 characters + * (+ 1 null byte) + */ + sscanf(s, "%s %[^\n]\n", (char *) &s1, (char *) &s2); + if (!(*s1) || (!*s2) || (*s1 == '\n') || (*s1 == '#')) { + cont = 0; + continue; + } + D(("Got theme/options pair %s/%s\n", s1, s2)); } - } - fclose(fp); - D_RETURN_(4); -} -static void feh_parse_environment_options(void) -{ - char *opts; + if (!strcmp(s1, theme) || cont) { - D_ENTER(4); + bspos = strlen(s2)-1; - if ((opts = getenv("FEH_OPTIONS")) == NULL) - D_RETURN_(4); + if (s2[bspos] == '\\') { + D(("Continued line\n")); + s2[bspos] = '\0'; + cont = 1; + /* A trailing whitespace confuses the option parser */ + if (bspos && (s2[bspos-1] == ' ')) + s2[bspos-1] = '\0'; + } else + cont = 0; - weprintf - ("The FEH_OPTIONS configuration method is depreciated and will soon die.\n" - "Use the .fehrc configuration file instead."); + D(("A match. Using options %s\n", s2)); + feh_parse_options_from_string(s2); - /* We definitely have some options to parse */ - feh_parse_options_from_string(opts); - D_RETURN_(4); + if (!cont) + break; + } + } + fclose(fp); + return; } /* FIXME This function is a crufty bitch ;) */ static void feh_parse_options_from_string(char *opts) { - char **list = NULL; + char *list[sizeof(char *) * 64]; int num = 0; char *s; char *t; char last = 0; - int inquote = 0; + char inquote = 0; int i = 0; - D_ENTER(4); - /* So we don't reinvent the wheel (not again, anyway), we use the getopt_long function to do this parsing as well. This means it has to look like the real argv ;) */ - list = malloc(sizeof(char *)); - list[num++] = estrdup(PACKAGE); for (s = opts, t = opts;; t++) { - if ((*t == ' ') && !(inquote)) { + + if (num > 64) + eprintf(PACKAGE " does not support more than 64 words per " + "theme definition.\n Please shorten your lines."); + + if ((*t == ' ') && !inquote) { *t = '\0'; num++; - list = erealloc(list, sizeof(char *) * num); list[num - 1] = feh_string_normalize(s); s = t + 1; } else if (*t == '\0') { num++; - list = erealloc(list, sizeof(char *) * num); list[num - 1] = feh_string_normalize(s); break; - } else if (*t == '\"' && last != '\\') - inquote = !(inquote); + } else if ((*t == inquote) && (last != '\\')) { + inquote = 0; + } else if (((*t == '\"') || (*t == '\'')) && (last != '\\') && !inquote) + inquote = *t; last = *t; } - feh_parse_option_array(num, list); + feh_parse_option_array(num, list, 0); for (i = 0; i < num; i++) if (list[i]) free(list[i]); - if (list) - free(list); - D_RETURN_(4); + return; } char *feh_string_normalize(char *str) @@ -265,8 +275,7 @@ char *feh_string_normalize(char *str) int i = 0; char last = 0; - D_ENTER(4); - D(4, ("normalizing %s\n", str)); + D(("normalizing %s\n", str)); ret[0] = '\0'; for (s = str;; s++) { @@ -275,6 +284,9 @@ char *feh_string_normalize(char *str) else if ((*s == '\"') && (last == '\\')) ret[i++] = '\"'; else if ((*s == '\"') && (last == 0)); + else if ((*s == '\'') && (last == '\\')) + ret[i++] = '\''; + else if ((*s == '\'') && (last == 0)); else if ((*s == ' ') && (last == '\\')) ret[i++] = ' '; else @@ -282,239 +294,258 @@ char *feh_string_normalize(char *str) last = *s; } - if (i && ret[i - 1] == '\"') + if (i && ((ret[i - 1] == '\"') || (ret[i - 1] == '\''))) ret[i - 1] = '\0'; else ret[i] = '\0'; - D(4, ("normalized to %s\n", ret)); + D(("normalized to %s\n", ret)); - D_RETURN(4, estrdup(ret)); + return(estrdup(ret)); } -static void feh_parse_option_array(int argc, char **argv) +static void feh_getopt_theme(int argc, char **argv) { - static char stropts[] = - "a:A:b:B:cC:dD:e:E:f:Fg:GhH:iIj:klL:mM:nNo:O:pqQrR:sS:tT:uUvVwW:xXy:zZ0:1:2:4:5:8:9:.@:^:~:):|:_:+:"; + static char stropts[] = "-T:"; static struct option lopts[] = { - /* actions */ - {"help", 0, 0, 'h'}, /* okay */ - {"version", 0, 0, 'v'}, /* okay */ - /* toggles */ - {"montage", 0, 0, 'm'}, /* okay */ - {"collage", 0, 0, 'c'}, /* okay */ - {"index", 0, 0, 'i'}, /* okay */ - {"fullindex", 0, 0, 'I'}, /* okay */ - {"verbose", 0, 0, 'V'}, /* okay */ - {"borderless", 0, 0, 'x'}, /* okay */ - {"keep-http", 0, 0, 'k'}, /* okay */ - {"stretch", 0, 0, 's'}, /* okay */ - {"multiwindow", 0, 0, 'w'}, /* okay */ - {"recursive", 0, 0, 'r'}, /* okay */ - {"randomize", 0, 0, 'z'}, /* okay */ - {"list", 0, 0, 'l'}, /* okay */ - {"quiet", 0, 0, 'q'}, /* okay */ - {"loadable", 0, 0, 'U'}, /* okay */ - {"unloadable", 0, 0, 'u'}, /* okay */ - {"no-menus", 0, 0, 'N'}, - {"full-screen", 0, 0, 'F'}, - {"auto-zoom", 0, 0, 'Z'}, - {"ignore-aspect", 0, 0, 'X'}, - {"draw-filename", 0, 0, 'd'}, - {"preload", 0, 0, 'p'}, - {"reverse", 0, 0, 'n'}, - {"thumbnails", 0, 0, 't'}, - {"wget-timestamp", 0, 0, 'G'}, - {"builtin", 0, 0, 'Q'}, - {"scale-down", 0, 0, '.'}, /* okay */ - {"no-jump-on-resort", 0, 0, 220}, - {"hide-pointer", 0, 0, 221}, - {"draw-actions", 0, 0, 222}, - {"cache-thumbnails", 0, 0, 223}, - {"cycle-once", 0, 0, 224}, - {"no-xinerama", 0, 0, 225}, - {"no-rotate-ctrl-mask", 0, 0, 226}, - {"no-blur-ctrl-mask", 0, 0, 227}, - {"menu-ctrl-mask", 0, 0, 228}, /* okay */ - /* options with values */ - {"output", 1, 0, 'o'}, /* okay */ - {"output-only", 1, 0, 'O'}, /* okay */ - {"action", 1, 0, 'A'}, /* okay */ - {"limit-width", 1, 0, 'W'}, /* okay */ - {"limit-height", 1, 0, 'H'}, /* okay */ - {"reload", 1, 0, 'R'}, /* okay */ - {"alpha", 1, 0, 'a'}, /* okay */ - {"sort", 1, 0, 'S'}, /* okay */ - {"theme", 1, 0, 'T'}, /* okay */ - {"filelist", 1, 0, 'f'}, /* okay */ - {"customlist", 1, 0, 'L'}, /* okay */ - {"geometry", 1, 0, 'g'}, /* okay */ - {"menu-font", 1, 0, 'M'}, - {"thumb-width", 1, 0, 'y'}, - {"thumb-height", 1, 0, 'E'}, - {"slideshow-delay", 1, 0, 'D'}, - {"font", 1, 0, 'e'}, - {"title-font", 1, 0, '@'}, - {"title", 1, 0, '^'}, - {"thumb-title", 1, 0, '~'}, - {"bg", 1, 0, 'b'}, - {"fontpath", 1, 0, 'C'}, - {"menu-bg", 1, 0, ')'}, - {"image-bg", 1, 0, 'B'}, - {"reload-button", 1, 0, '0'}, - {"pan-button", 1, 0, '1'}, - {"zoom-button", 1, 0, '2'}, - {"menu-button", 1, 0, '3'}, - {"prev-button", 1, 0, '4'}, - {"next-button", 1, 0, '5'}, - {"rotate-button", 1, 0, '8'}, - {"blur-button", 1, 0, '9'}, - {"start-at", 1, 0, '|'}, - {"rcfile", 1, 0, '_'}, - {"debug-level", 1, 0, '+'}, - {"output-dir", 1, 0, 'j'}, - {"bg-tile", 1, 0, 200}, - {"bg-center", 1, 0, 201}, - {"bg-scale", 1, 0, 202}, - {"bg-seamless", 1, 0, 203}, - {"menu-style", 1, 0, 204}, - {"zoom", 1, 0, 205}, - {"screen-clip", 1, 0, 206}, - {"menu-border", 1, 0, 207}, - {"caption-path", 1, 0, 208}, - {"action1", 1, 0, 209}, - {"action2", 1, 0, 210}, - {"action3", 1, 0, 211}, - {"action4", 1, 0, 212}, - {"action5", 1, 0, 213}, - {"action6", 1, 0, 214}, - {"action7", 1, 0, 215}, - {"action8", 1, 0, 216}, - {"action9", 1, 0, 217}, - {"bg-fill", 1, 0, 218}, - {"index-name", 1, 0, 230}, - {"index-size", 1, 0, 231}, - {"index-dim", 1, 0, 232}, + {"theme", 1, 0, 'T'}, {0, 0, 0, 0} }; int optch = 0, cmdx = 0; - D_ENTER(4); + opterr = 0; + + while ((optch = getopt_long(argc, argv, stropts, lopts, &cmdx)) != EOF) { + if (optch == 'T') + theme = estrdup(optarg); + } + + opterr = 1; + optind = 0; +} + +static void feh_parse_option_array(int argc, char **argv, int finalrun) +{ + int discard; + static char stropts[] = + "a:A:b:B:C:dD:e:E:f:Fg:GhH:iIj:J:kK:lL:mM:nNo:O:pPqrR:sS:tT:uUvVwW:xXy:YzZ" + ".@:^:~:|:+:<:>:"; + + /* (*name, has_arg, *flag, val) See: struct option in getopts.h */ + static struct option lopts[] = { + {"debug" , 0, 0, OPTION_debug}, + {"scale-down" , 0, 0, OPTION_scale_down}, + {"max-dimension" , 1, 0, OPTION_max_dimension}, + {"min-dimension" , 1, 0, OPTION_min_dimension}, + {"title-font" , 1, 0, OPTION_title_font}, + {"action" , 1, 0, OPTION_action}, + {"image-bg" , 1, 0, OPTION_image_bg}, + {"fontpath" , 1, 0, OPTION_fontpath}, + {"slideshow-delay",1, 0, OPTION_slideshow_delay}, + {"thumb-height" , 1, 0, OPTION_thumb_height}, + {"full-screen" , 0, 0, OPTION_fullscreen}, /* deprecated */ + {"fullscreen" , 0, 0, OPTION_fullscreen}, + {"draw-actions" , 0, 0, OPTION_draw_actions}, + {"limit-height" , 1, 0, OPTION_limit_height}, + {"fullindex" , 0, 0, OPTION_fullindex}, + {"thumb-redraw" , 1, 0, OPTION_thumb_redraw}, + {"caption-path" , 1, 0, OPTION_caption_path}, + {"customlist" , 1, 0, OPTION_customlist}, + {"menu-font" , 1, 0, OPTION_menu_font}, + {"no-menus" , 0, 0, OPTION_no_menus}, + {"output-only" , 1, 0, OPTION_output_only}, + {"cache-thumbnails", 0, 0, OPTION_cache_thumbnails}, + {"reload" , 1, 0, OPTION_reload}, + {"sort" , 1, 0, OPTION_sort}, + {"theme" , 1, 0, OPTION_theme}, + {"loadable" , 0, 0, OPTION_loadable}, + {"verbose" , 0, 0, OPTION_verbose}, + {"limit-width" , 1, 0, OPTION_limit_width}, + {"ignore-aspect" , 0, 0, OPTION_ignore_aspect}, + {"hide-pointer" , 0, 0, OPTION_hide_pointer}, + {"auto-zoom" , 0, 0, OPTION_auto_zoom}, + {"title" , 1, 0, OPTION_title}, + {"alpha" , 1, 0, OPTION_alpha}, + {"bg" , 1, 0, OPTION_bg}, + {"draw-filename" , 0, 0, OPTION_draw_filename}, + {"font" , 1, 0, OPTION_font}, + {"filelist" , 1, 0, OPTION_filelist}, + {"geometry" , 1, 0, OPTION_geometry}, + {"help" , 0, 0, OPTION_help}, + {"index" , 0, 0, OPTION_index}, + {"output-dir" , 1, 0, OPTION_output_dir}, + {"keep-http" , 0, 0, OPTION_keep_http}, + {"list" , 0, 0, OPTION_list}, + {"montage" , 0, 0, OPTION_montage}, + {"reverse" , 0, 0, OPTION_reverse}, + {"output" , 1, 0, OPTION_output}, + {"preload" , 0, 0, OPTION_preload}, + {"quiet" , 0, 0, OPTION_quiet}, + {"recursive" , 0, 0, OPTION_recursive}, + {"stretch" , 0, 0, OPTION_stretch}, + {"thumbnails" , 0, 0, OPTION_thumbnails}, + {"unloadable" , 0, 0, OPTION_unloadable}, + {"version" , 0, 0, OPTION_version}, + {"multiwindow" , 0, 0, OPTION_multiwindow}, + {"borderless" , 0, 0, OPTION_borderless}, + {"thumb-width" , 1, 0, OPTION_thumb_width}, + {"randomize" , 0, 0, OPTION_randomize}, + {"start-at" , 1, 0, OPTION_start_at}, + {"thumb-title" , 1, 0, OPTION_thumb_title}, + {"bg-tile" , 0, 0, OPTION_bg_title}, + {"bg-center" , 0, 0, OPTION_bg_center}, + {"bg-scale" , 0, 0, OPTION_bg_scale}, + {"zoom" , 1, 0, OPTION_zoom}, + {"zoom-step" , 1, 0, OPTION_zoom_step}, + {"no-screen-clip", 0, 0, OPTION_no_screen_clip}, + {"index-info" , 1, 0, OPTION_index_info}, + {"magick-timeout", 1, 0, OPTION_magick_timeout}, + {"action1" , 1, 0, OPTION_action1}, + {"action2" , 1, 0, OPTION_action2}, + {"action3" , 1, 0, OPTION_action3}, + {"action4" , 1, 0, OPTION_action4}, + {"action5" , 1, 0, OPTION_action5}, + {"action6" , 1, 0, OPTION_action6}, + {"action7" , 1, 0, OPTION_action7}, + {"action8" , 1, 0, OPTION_action8}, + {"action9" , 1, 0, OPTION_action9}, + {"bg-fill" , 0, 0, OPTION_bg_fill}, + {"bg-max" , 0, 0, OPTION_bg_max}, + {"no-jump-on-resort", 0, 0, OPTION_no_jump_on_resort}, + {"edit" , 0, 0, OPTION_edit}, +#ifdef HAVE_LIBEXIF + {"draw-exif" , 0, 0, OPTION_draw_exif}, + {"auto-rotate" , 0, 0, OPTION_auto_rotate}, +#endif + {"no-xinerama" , 0, 0, OPTION_no_xinerama}, + {"draw-tinted" , 0, 0, OPTION_draw_tinted}, + {"info" , 1, 0, OPTION_info}, + {"tap-zones" , 0, 0, OPTION_tap_zones}, + {"force-aliasing", 0, 0, OPTION_force_aliasing}, + {"no-fehbg" , 0, 0, OPTION_no_fehbg}, + {"keep-zoom-vp" , 0, 0, OPTION_keep_zoom_vp}, + {"scroll-step" , 1, 0, OPTION_scroll_step}, + {"xinerama-index", 1, 0, OPTION_xinerama_index}, + {"insecure" , 0, 0, OPTION_insecure}, + {"no-recursive" , 0, 0, OPTION_no_recursive}, + {"cache-size" , 1, 0, OPTION_cache_size}, + {"on-last-slide" , 1, 0, OPTION_on_last_slide}, + {"conversion-timeout" , 1, 0, OPTION_conversion_timeout}, + {"version-sort" , 0, 0, OPTION_version_sort}, + {"offset" , 1, 0, OPTION_offset}, +#ifdef HAVE_INOTIFY + {"auto-reload" , 0, 0, OPTION_auto_reload}, +#endif + {"class" , 1, 0, OPTION_class}, + {"no-conversion-cache", 0, 0, OPTION_no_conversion_cache}, + {"window-id", 1, 0, OPTION_window_id}, + {0, 0, 0, 0} + }; + int optch = 0, cmdx = 0; - /* Now to pass some optionarinos */ while ((optch = getopt_long(argc, argv, stropts, lopts, &cmdx)) != EOF) { - D(5, ("Got option, getopt calls it %d, or %c\n", optch, optch)); + D(("Got option, getopt calls it %d, or %c\n", optch, optch)); switch (optch) { case 0: break; - case 'h': - show_usage(); - break; - case 'v': - show_version(); - break; - case 'm': - opt.index = 1; - opt.index_show_name = 0; - opt.index_show_size = 0; - opt.index_show_dim = 0; + case OPTION_debug: + opt.debug = 1; break; - case 'c': - opt.collage = 1; + case OPTION_max_dimension: + opt.filter_by_dimensions = 1; + XParseGeometry(optarg, &discard, &discard, &opt.max_width, &opt.max_height); + if (opt.max_width == 0) + opt.max_width = UINT_MAX; + if (opt.max_height == 0) + opt.max_height = UINT_MAX; break; - case 'i': - opt.index = 1; - opt.index_show_name = 1; - opt.index_show_size = 0; - opt.index_show_dim = 0; + case OPTION_min_dimension: + opt.filter_by_dimensions = 1; + XParseGeometry(optarg, &discard, &discard, &opt.min_width, &opt.min_height); break; - case '.': + case OPTION_scale_down: opt.scale_down = 1; break; - case 'I': - opt.index = 1; - opt.index_show_name = 1; - opt.index_show_size = 1; - opt.index_show_dim = 1; - break; - case 'l': - opt.list = 1; - opt.display = 0; - break; - case 'G': - opt.wget_timestamp = 1; - break; - case 'Q': - opt.builtin_http = 1; - break; - case 'L': - opt.customlist = estrdup(optarg); - break; - case 'M': - free(opt.menu_font); - opt.menu_font = estrdup(optarg); - break; - case '+': - opt.debug_level = atoi(optarg); - break; - case 'n': - opt.reverse = 1; + case OPTION_title_font: + opt.title_font = estrdup(optarg); break; - case 'g': - opt.geom_flags = XParseGeometry(optarg, &opt.geom_x, &opt.geom_y, &opt.geom_w, &opt.geom_h); + case OPTION_action: + opt.actions[0] = estrdup(optarg); break; - case 'N': - opt.no_menus = 1; + case OPTION_image_bg: + opt.image_bg = estrdup(optarg); break; - case 'V': - opt.verbose = 1; + case OPTION_fontpath: + D(("adding fontpath %s\n", optarg)); + imlib_add_path_to_font_path(optarg); break; - case 'q': - opt.quiet = 1; + case OPTION_slideshow_delay: + opt.slideshow_delay = atof(optarg); + if (opt.slideshow_delay < 0.0) { + opt.slideshow_delay *= (-1); + opt.paused = 1; + } else { + opt.paused = 0; + } break; - case 'x': - opt.borderless = 1; + case OPTION_thumb_height: + opt.thumb_h = atoi(optarg); break; - case 'k': - opt.keep_http = 1; + case OPTION_fullscreen: + opt.full_screen = 1; break; - case 's': - opt.stretch = 1; + case OPTION_draw_actions: + opt.draw_actions = 1; break; - case 'w': - opt.multiwindow = 1; + case OPTION_limit_height: + opt.limit_h = atoi(optarg); break; - case 'r': - opt.recursive = 1; + case OPTION_fullindex: + opt.index = 1; + opt.index_info = estrdup("%n\n%S\n%wx%h"); break; - case 'z': - opt.randomize = 1; + case OPTION_thumb_redraw: + opt.thumb_redraw = atoi(optarg); break; - case 'd': - opt.draw_filename = 1; + case OPTION_caption_path: + opt.caption_path = estrdup(optarg); break; - case 'F': - opt.full_screen = 1; + case OPTION_customlist: + opt.customlist = estrdup(optarg); + opt.display = 0; break; - case 'Z': - opt.auto_zoom = 1; + case OPTION_menu_font: + free(opt.menu_font); + opt.menu_font = estrdup(optarg); break; - case 'U': - opt.loadables = 1; + case OPTION_no_menus: + opt.no_menus = 1; break; - case 'u': - opt.unloadables = 1; + case OPTION_output_only: + opt.output = 1; + opt.output_file = estrdup(optarg); + opt.display = 0; break; - case 'p': - opt.preload = 1; + case OPTION_cache_thumbnails: + opt.cache_thumbnails = 1; break; - case 'X': - opt.aspect = 0; + case OPTION_reload: + opt.reload = atof(optarg); + opt.use_conversion_cache = 0; +#ifdef HAVE_INOTIFY + opt.auto_reload = 0; +#endif break; - case 'S': + case OPTION_sort: if (!strcasecmp(optarg, "name")) opt.sort = SORT_NAME; + else if (!strcasecmp(optarg, "none")) + opt.sort = SORT_NONE; else if (!strcasecmp(optarg, "filename")) opt.sort = SORT_FILENAME; + else if (!strcasecmp(optarg, "dirname")) + opt.sort = SORT_DIRNAME; + else if (!strcasecmp(optarg, "mtime")) + opt.sort = SORT_MTIME; else if (!strcasecmp(optarg, "width")) opt.sort = SORT_WIDTH; else if (!strcasecmp(optarg, "height")) @@ -530,212 +561,304 @@ static void feh_parse_option_array(int argc, char **argv) "sort by filename", optarg); opt.sort = SORT_FILENAME; } + if (opt.randomize) { + weprintf("commandline contains --randomize and --sort. " + "--randomize has been unset"); + opt.randomize = 0; + } break; - case 'o': - opt.output = 1; - opt.output_file = estrdup(optarg); + case OPTION_theme: + theme = estrdup(optarg); break; - case 'O': - opt.output = 1; - opt.output_file = estrdup(optarg); + case OPTION_loadable: + opt.loadables = 1; opt.display = 0; break; - case 'T': - theme = estrdup(optarg); + case OPTION_verbose: + opt.verbose = 1; break; - case 'C': - D(3, ("adding fontpath %s\n", optarg)); - imlib_add_path_to_font_path(optarg); + case OPTION_limit_width: + opt.limit_w = atoi(optarg); break; - case 'e': - opt.font = estrdup(optarg); + case OPTION_ignore_aspect: + opt.aspect = 0; break; - case '@': - opt.title_font = estrdup(optarg); + case OPTION_hide_pointer: + opt.hide_pointer = 1; + break; + case OPTION_auto_zoom: + opt.zoom_mode = ZOOM_MODE_MAX; break; - case '^': + case OPTION_title: opt.title = estrdup(optarg); break; - case '~': - opt.thumb_title = estrdup(optarg); + case OPTION_alpha: + opt.alpha = 1; + opt.alpha_level = 255 - atoi(optarg); break; - case 'b': + case OPTION_bg: opt.bg = 1; opt.bg_file = estrdup(optarg); break; - case '_': - opt.rcfile = estrdup(optarg); + case OPTION_draw_filename: + opt.draw_filename = 1; break; - case 'A': - opt.actions[0] = estrdup(optarg); + case OPTION_font: + opt.font = estrdup(optarg); break; - case 'W': - opt.limit_w = atoi(optarg); + case OPTION_filelist: + if (!strcmp(optarg, "-")) + opt.filelistfile = estrdup("/dev/stdin"); + else + opt.filelistfile = estrdup(optarg); break; - case 'H': - opt.limit_h = atoi(optarg); + case OPTION_geometry: + opt.geom_enabled = 1; + opt.geom_flags = XParseGeometry(optarg, &opt.geom_x, + &opt.geom_y, &opt.geom_w, &opt.geom_h); break; - case 'y': - opt.thumb_w = atoi(optarg); + case OPTION_help: + show_usage(); break; - case 'E': - opt.thumb_h = atoi(optarg); + case OPTION_index: + opt.index = 1; + opt.index_info = estrdup("%n"); break; - case ')': - free(opt.menu_bg); - opt.menu_bg = estrdup(optarg); + case OPTION_output_dir: + opt.output_dir = estrdup(optarg); break; - case 'B': - free(opt.image_bg); - opt.image_bg = estrdup(optarg); + case OPTION_keep_http: + opt.keep_http = 1; break; - case 'D': - opt.slideshow_delay = atof(optarg); + case OPTION_list: + opt.list = 1; + opt.display = 0; break; - case 'R': - opt.reload = atoi(optarg); + case OPTION_montage: + opt.index = 1; break; - case 'a': - opt.alpha = 1; - opt.alpha_level = 255 - atoi(optarg); + case OPTION_reverse: + opt.reverse = 1; + break; + case OPTION_output: + opt.output = 1; + opt.output_file = estrdup(optarg); break; - case 'f': - opt.filelistfile = estrdup(optarg); + case OPTION_preload: + opt.preload = 1; break; - case '0': - opt.reload_button = atoi(optarg); + case OPTION_quiet: + opt.quiet = 1; break; - case '1': - opt.pan_button = atoi(optarg); + case OPTION_recursive: + opt.recursive = 1; break; - case '2': - opt.zoom_button = atoi(optarg); + case OPTION_stretch: + opt.stretch = 1; break; - case '3': - opt.menu_button = atoi(optarg); + case OPTION_thumbnails: + opt.thumbs = 1; + opt.index_info = estrdup("%n"); break; - case '4': - opt.prev_button = atoi(optarg); + case OPTION_unloadable: + opt.unloadables = 1; + opt.display = 0; break; - case '5': - opt.next_button = atoi(optarg); + case OPTION_version: + show_version(); break; - case '8': - opt.rotate_button = atoi(optarg); + case OPTION_multiwindow: + opt.multiwindow = 1; break; - case '9': - opt.blur_button = atoi(optarg); + case OPTION_borderless: + opt.borderless = 1; break; - case '|': - opt.start_list_at = atoi(optarg); + case OPTION_thumb_width: + opt.thumb_w = atoi(optarg); break; - case 't': - opt.thumbs = 1; - opt.index_show_name = 1; - opt.index_show_size = 0; - opt.index_show_dim = 0; + case OPTION_randomize: + opt.randomize = 1; + if (opt.sort != SORT_NONE) { + weprintf("commandline contains --sort and --randomize. " + "--sort has been unset"); + opt.sort = SORT_NONE; + } break; - case 'j': - opt.output_dir = estrdup(optarg); + case OPTION_start_at: + opt.start_list_at = estrdup(optarg); + break; + case OPTION_thumb_title: + opt.thumb_title = estrdup(optarg); break; - case 200: + case OPTION_bg_title: opt.bgmode = BG_MODE_TILE; - opt.output_file = estrdup(optarg); break; - case 201: + case OPTION_bg_center: opt.bgmode = BG_MODE_CENTER; - opt.output_file = estrdup(optarg); break; - case 202: + case OPTION_bg_scale: opt.bgmode = BG_MODE_SCALE; - opt.output_file = estrdup(optarg); break; - case 203: - opt.bgmode = BG_MODE_SEAMLESS; - opt.output_file = estrdup(optarg); - break; - case 218: - opt.bgmode = BG_MODE_FILL; - opt.output_file = estrdup(optarg); + case OPTION_zoom: + if (!strcmp("fill", optarg)) + opt.zoom_mode = ZOOM_MODE_FILL; + else if (!strcmp("max", optarg)) + opt.zoom_mode = ZOOM_MODE_MAX; + else + opt.default_zoom = atoi(optarg); break; - case 204: - free(opt.menu_style); - opt.menu_style = estrdup(optarg); + case OPTION_no_screen_clip: + opt.screen_clip = 0; break; - case 205: - opt.default_zoom = atoi(optarg); + case OPTION_index_info: + opt.index_info = estrdup(optarg); break; - case 206: - opt.screen_clip = atoi(optarg); - break; - case 207: - opt.menu_border = atoi(optarg); - break; - case 208: - opt.caption_path = estrdup(optarg); + case OPTION_magick_timeout: + weprintf("--magick-timeout is deprecated, please use --conversion-timeout instead"); + opt.conversion_timeout = atoi(optarg); break; - case 209: + case OPTION_action1: opt.actions[1] = estrdup(optarg); break; - case 210: + case OPTION_action2: opt.actions[2] = estrdup(optarg); break; - case 211: + case OPTION_action3: opt.actions[3] = estrdup(optarg); break; - case 212: + case OPTION_action4: opt.actions[4] = estrdup(optarg); break; - case 213: + case OPTION_action5: opt.actions[5] = estrdup(optarg); break; - case 214: + case OPTION_action6: opt.actions[6] = estrdup(optarg); break; - case 215: + case OPTION_action7: opt.actions[7] = estrdup(optarg); break; - case 216: + case OPTION_action8: opt.actions[8] = estrdup(optarg); break; - case 217: + case OPTION_action9: opt.actions[9] = estrdup(optarg); break; - case 220: - opt.no_jump_on_resort = 1; + case OPTION_bg_fill: + opt.bgmode = BG_MODE_FILL; break; - case 221: - opt.hide_pointer = 1; + case OPTION_bg_max: + opt.bgmode = BG_MODE_MAX; break; - case 222: - opt.draw_actions = 1; + case OPTION_no_jump_on_resort: + opt.jump_on_resort = 0; break; - case 223: - opt.cache_thumbnails = 1; + case OPTION_edit: + opt.edit = 1; break; - case 224: - opt.cycle_once = 1; +#ifdef HAVE_LIBEXIF + case OPTION_draw_exif: + opt.draw_exif = 1; break; - case 225: + case OPTION_auto_rotate: +#if defined(IMLIB2_VERSION_MAJOR) && defined(IMLIB2_VERSION_MINOR) && defined(IMLIB2_VERSION_MICRO) && (IMLIB2_VERSION_MAJOR > 1 || IMLIB2_VERSION_MINOR > 7 || IMLIB2_VERSION_MICRO >= 5) + weprintf("This feh release was built with Imlib2 version %d.%d.%d, which transparently adjusts for image orientation according to EXIF data.", IMLIB2_VERSION_MAJOR, IMLIB2_VERSION_MINOR, IMLIB2_VERSION_MICRO); + weprintf("--auto-rotate would rotate an already correctly oriented image, resulting in incorrect orientation. It has been disabled in this build. Rebuild feh with Imlib2 <1.7.5 to enable --auto-rotate."); +#else + opt.auto_rotate = 1; +#endif + break; +#endif + case OPTION_no_xinerama: opt.xinerama = 0; break; - case 226: - opt.no_rotate_ctrl_mask = 1; + case OPTION_draw_tinted: + opt.text_bg = TEXT_BG_TINTED; + break; + case OPTION_info: + opt.info_cmd = estrdup(optarg); + if (opt.info_cmd[0] == ';') { + opt.draw_info = 0; + opt.info_cmd++; + } else { + opt.draw_info = 1; + } + break; + case OPTION_tap_zones: + opt.tap_zones = 1; + break; + case OPTION_force_aliasing: + opt.force_aliasing = 1; + break; + case OPTION_no_fehbg: + opt.no_fehbg = 1; + break; + case OPTION_keep_zoom_vp: + opt.keep_zoom_vp = 1; + break; + case OPTION_scroll_step: + opt.scroll_step = atoi(optarg); + break; + case OPTION_xinerama_index: + opt.xinerama_index = atoi(optarg); + break; + case OPTION_insecure: + opt.insecure_ssl = 1; break; - case 227: - opt.no_blur_ctrl_mask = 1; + case OPTION_no_recursive: + opt.recursive = 0; break; - case 228: - opt.menu_ctrl_mask = 1; + case OPTION_cache_size: + opt.cache_size = atoi(optarg); + if (opt.cache_size < 0) + opt.cache_size = 0; + if (opt.cache_size > 2048) + opt.cache_size = 2048; break; - case 230: - opt.index_show_name = atoi(optarg); + case OPTION_on_last_slide: + if (!strcmp(optarg, "quit")) { + opt.on_last_slide = ON_LAST_SLIDE_QUIT; + } else if (!strcmp(optarg, "hold")) { + opt.on_last_slide = ON_LAST_SLIDE_HOLD; + } else if (!strcmp(optarg, "resume")) { + opt.on_last_slide = ON_LAST_SLIDE_RESUME; + } else { + weprintf("Unrecognized on-last-slide action \"%s\"." + "Supported actions: hold, resume, quit\n", optarg); + } + break; + case OPTION_conversion_timeout: + opt.conversion_timeout = atoi(optarg); + break; + case OPTION_version_sort: + opt.version_sort = 1; + break; + case OPTION_offset: + opt.offset_flags = XParseGeometry(optarg, &opt.offset_x, + &opt.offset_y, (unsigned int *)&discard, (unsigned int *)&discard); + break; +#ifdef HAVE_INOTIFY + case OPTION_auto_reload: + opt.auto_reload = 1; + break; +#endif + case OPTION_class: + opt.x11_class = estrdup(optarg); + break; + case OPTION_no_conversion_cache: + opt.use_conversion_cache = 0; break; - case 231: - opt.index_show_size = atoi(optarg); + case OPTION_window_id: + opt.x11_windowid = strtol(optarg, NULL, 0); break; - case 232: - opt.index_show_dim = atoi(optarg); + case OPTION_zoom_step: + opt.zoom_rate = atof(optarg); + if ((opt.zoom_rate <= 0)) { + weprintf("Zooming disabled due to --zoom-step=%f", opt.zoom_rate); + opt.zoom_rate = 1.0; + } else { + opt.zoom_rate = 1 + ((float)opt.zoom_rate / 100); + } break; default: break; @@ -745,494 +868,161 @@ static void feh_parse_option_array(int argc, char **argv) /* Now the leftovers, which must be files */ if (optind < argc) { while (optind < argc) { + if (opt.reload) + original_file_items = gib_list_add_front(original_file_items, estrdup(argv[optind])); /* If recursive is NOT set, but the only argument is a directory name, we grab all the files in there, but not subdirs */ add_file_to_filelist_recursively(argv[optind++], FILELIST_FIRST); } } + else if (finalrun && !opt.filelistfile && !opt.bgmode) { + /* + * if --start-at is a non-local URL (i.e., does not start with file:///), + * behave as if "feh URL" was called (there is no directory we can load) + */ + 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 *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, '/'); + if (filename_start) { + *filename_start = '\0'; + } + add_file_to_filelist_recursively(target_directory, FILELIST_FIRST); + original_file_items = gib_list_add_front(original_file_items, estrdup(target_directory)); + free(target_directory); + } else { + add_file_to_filelist_recursively(".", FILELIST_FIRST); + } + } /* So that we can safely be called again */ - optind = 1; - D_RETURN_(4); + optind = 0; + return; } static void check_options(void) { - D_ENTER(4); - if ((opt.index + opt.collage) > 1) { - weprintf("you can't use collage mode and index mode together.\n" - " I'm going with index"); - opt.collage = 0; - } + int i; + char *endptr; - if (opt.full_screen && opt.multiwindow) { - weprintf("you shouldn't combine multiwindow mode with full-screen mode,\n" - " Multiwindow mode has been disabled."); - opt.multiwindow = 0; + for (i = 0; i < 10; i++) { + if (opt.actions[i] && !opt.hold_actions[i] && (opt.actions[i][0] == ';')) { + opt.hold_actions[i] = 1; + opt.actions[i] = opt.actions[i] + 1; + } + opt.action_titles[i] = opt.actions[i]; + if (opt.actions[i] && (opt.actions[i][0] == '[')) { + if (((endptr = strchr(opt.actions[i], ']')) != NULL) + && (opt.actions[i][1] != ' ')) { + opt.action_titles[i] = opt.actions[i] + 1; + opt.actions[i] = endptr + 1; + *endptr = 0; + } + } } - if (opt.list && (opt.multiwindow || opt.index || opt.collage)) { - weprintf("list mode can't be combined with other processing modes,\n" - " list mode disabled."); - opt.list = 0; + if (opt.full_screen && opt.multiwindow) { + eprintf("You cannot combine --fullscreen with --multiwindow"); } - if (opt.sort && opt.randomize) { - weprintf("You cant sort AND randomize the filelist...\n" - "randomize mode has been unset\n"); - opt.randomize = 0; + if (opt.list && (opt.multiwindow || opt.index)) { + eprintf("You cannot combine --list with other modes"); } if (opt.loadables && opt.unloadables) { - weprintf("You cant show loadables AND unloadables...\n" - "you might as well use ls ;)\n" - "loadables only will be shown\n"); - opt.unloadables = 0; + eprintf("You cannot combine --loadable with --unloadable"); } - if (opt.thumb_title && (!opt.thumbs)) { - weprintf("Doesn't make sense to set thumbnail title when not in\n" - "thumbnail mode.\n"); - free(opt.thumb_title); - opt.thumb_title = NULL; - } - D_RETURN_(4); + return; } -void show_version(void) +static void show_version(void) { - printf(PACKAGE " version " VERSION "\n"); + puts(PACKAGE " version " VERSION); + puts("Compile-time switches: " + +#ifdef HAVE_LIBCURL + "curl " +#endif + +#ifdef DEBUG + "debug " +#endif + +#ifdef HAVE_LIBEXIF + "exif " +#endif + +#ifdef HAVE_INOTIFY + "inotify " +#endif + +#ifdef INCLUDE_HELP + "help " +#endif + +#ifdef HAVE_LIBMAGIC + "magic " +#endif + +#if _FILE_OFFSET_BITS == 64 + "stat64 " +#endif + +#ifdef HAVE_STRVERSCMP + "verscmp " +#endif + +#ifdef HAVE_LIBXINERAMA + "xinerama " +#endif + + ); + exit(0); } void show_mini_usage(void) { - fprintf(stdout, PACKAGE " - No loadable images specified.\n" - "Use " PACKAGE " --help for detailed usage information\n"); - exit(0); + fputs(PACKAGE ": No loadable images specified.\n" +#ifdef INCLUDE_HELP + "See '" PACKAGE " --help' or 'man " PACKAGE "' for detailed usage information\n", +#else + "See 'man " PACKAGE "' for detailed usage information\n", +#endif + stderr); + exit(1); } -void show_usage(void) +static void show_usage(void) { - fprintf(stdout, -"Usage : " PACKAGE " [OPTIONS]... FILES...\n" -" Where a FILE is an image file or a directory.\n" -" Multiple files are supported.\n" -" Urls are supported. They must begin with http:// or ftp:// and you must\n" -" have wget installed to download the files for viewing.\n" -" Options can also be specified in the in the feh configuration file. See\n" -" man feh for more details\n" -" -h, --help display this help and exit\n" -" -v, --version output version information and exit\n" -" -V, --verbose output useful information, progress bars, etc\n" -" -q, --quiet Don't report non-fatal errors for failed loads\n" -" Verbose and quiet modes are not mutually exclusive,\n" -" the first controls informational messages, the\n" -" second only errors.\n" -" -T, --theme THEME Load options from config file with name THEME\n" -" see man feh for more info.\n" -" -_, --rcfile FILE Use FILE to parse themes and options from,\n" -" instead of the default ~/.fehrc, /etc/fehrc files.\n" -" -r, --recursive Recursively expand any directories in FILE to\n" -" the content of those directories. (Take it easy)\n" -" -z, --randomize When viewing multiple files in a slideshow,\n" -" randomise the file list before displaying\n" -" --no-jump-on-resort Don't jump to the first image when the filelist\n" -" is resorted.\n" -" -g, --geometry STRING Limit (and don't change) the window size. Takes\n" -" an X-style geometry string like 640x480.\n" -" Note that larger images will be zoomed out to fit\n" -" but you can see them at 1:1 by clicking the zoom\n" -" button.\n" -" -f, --filelist FILE This option is similar to the playlists used by\n" -" music software. If FILE exists, it will be read\n" -" for a list of files to load, in the order they\n" -" appear. The format is a list of image filenames,\n" -" absolute or relative to the current directory,\n" -" one filename per line.\n" -" If FILE doesn't exist, it will be created from the\n" -" internal filelist at the end of a viewing session.\n" -" This is best used to store the results of complex\n" -" sorts (-Spixels for example) for later viewing.\n" -" Any changes to the internal filelist (such as\n" -" deleting a file or it being pruned for being\n" -" unloadable) will be saved to FILE when feh exits.\n" -" You can add files to filelists by specifying them\n" -" on the commandline when also specifying the list.\n" -" -|, --start-at POSITION Start at POSITION in the filelist\n" -" -p, --preload Preload images. This doesn't mean hold them in\n" -" RAM, it means run through and eliminate unloadable\n" -" images first. Otherwise they will be removed as you\n" -" flick through.\n" -" -., --scale-down Automatically scale down images too big for the\n" -" screen. Currently only works with -P\n" -" -F, --full-screen Make the window fullscreen\n" -" -Z, --auto-zoom Zoom picture to screen size in fullscreen mode,\n" -" is affected by the option --stretch\n" -" --zoom PERCENT Zooms images by a PERCENT, when in full screen\n" -" mode or when window geometry is fixed. If combined\n" -" with --auto-zoom, zooming will be limited to the\n" -" the size.\n" -" -w, --multiwindow Disable slideshow mode. With this setting,\n" -" instead of opening multiple files in slideshow\n" -" mode, multiple windows will be opened.\n" -" -x, --borderless Create borderless windows\n" -" -d, --draw-filename Draw the filename at the top-left of the image.\n" -" -^, --title TITLE Use TITLE as the window title in slideshow mode.\n" -" -D, --slideshow-delay NUM For slideshow mode, specifies time delay (seconds,\n" -" can be a decimal) between automatically changing\n" -" slides.\n" -" --cycle-once exit feh after one loop through a slideshow\n" -" -R, --reload NUM Use this option to tell feh to reload an image\n" -" after NUM seconds. Useful for viewing webcams\n" -" via http, or even on your local machine.\n" -" -Q, --builtin Use builtin http grabber to grab remote files\n" -" instead of wget.\n" -" mechanism, useful if don't have wget.\n" -" -k, --keep-http When viewing files using http, feh normally\n" -" deletes the local copies after viewing, or,\n" -" if caching, on exit. This option prevents this\n" -" so that you get to keep the local copies.\n" -" They will be in the current working directory\n" -" with \"feh\" in the name.\n" -" --caption-path PATH Path to directory containing image captions.\n" -" This turns on caption viewing, and if captions\n" -" are found in PATH, which is relative to the\n" -" directory of each image, they are overlayed\n" -" on the displayed image.\n" -" e.g with caption path \"captions\", and viewing\n" -" image images/foo.jpg, caption will be looked for\n" -" as \"images/captions/foo.jpg.txt\"\n" -" -j, --output-dir Output directory for saved files. Really only\n" -" useful with the -k flag.\n" -" -G, --wget-timestamp When viewing http images with reload set (eg\n" -" webcams), try to only reload the image if the\n" -" remote file has changed.\n" -" -l, --list Don't display images. Analyse them and display an\n" -" 'ls' style listing. Useful in scripts hunt out\n" -" images of a certain size/resolution/type etc.\n" -" -L, --customlist FORMAT Use FORMAT as the format specifier for list\n" -" output. FORMAT is a printf-like string containing\n" -" image info specifiers. See FORMAT SPECIFIERS.\n" -" -U, --loadable Don't display images. Just print out their name\n" -" if imlib2 can successfully load them.\n" -" -u, --unloadable Don't display images. Just print out their name\n" -" if imlib2 can NOT successfully load them.\n" -" -S, --sort SORT_TYPE The file list may be sorted according to image\n" -" parameters. Allowed sort types are: name,\n" -" filename, width, height, pixels, size, format.\n" -" For sort modes other than name or filename, a\n" -" preload run will be necessary, causing a delay\n" -" proportional to the number of images in the list\n" -" -n, --reverse Reverse the sort order. Use this to invert the order\n" -" of the filelist. Eg to sort in reverse width order,\n" -" use -nSwidth\n" -" -A, --action ACTION Specify a string as an action to perform on the\n" -" image. In slideshow or multiwindow modes, the action\n" -" in list mode, or loadable|unloadable modes, the\n" -" action will be run for each file.\n" -" The action will be executed by /bin/sh. Use\n" -" format specifiers to refer to image info. See\n" -" FORMAT SPECIFIERS for examples\n" -" Eg. -A \"mv %%f ~/images/%%n\"\n" -" In slideshow mode, the next image will be shown\n" -" after running the command, in multiwindow mode,\n" -" the window will be closed.\n" -" --action1 ACTION These extra action options allow you to specify\n" -" --action2 ACTION multiple additional actions which can be invoked\n" -" ... using the appropriate number key 1-9\n" -" --action9 ACTION\n" -" --draw-actions Show the defined actions and what they do\n" -" -m, --montage Enable montage mode. Montage mode creates a new\n" -" image consisting of a grid of thumbnails of the\n" -" images specified using FILE... When montage mode\n" -" is selected, certain other options become\n" -" available. See MONTAGE MODE OPTIONS\n" -" -c, --collage Same as montage mode, but the thumbnails are\n" -" distributed randomly. You must specify width and\n" -" height or supply a background image or both\n" -" -i, --index Enable Index mode. Index mode is similar to\n" -" montage mode, and accepts the same options. It\n" -" creates an index print of thumbails, printing the\n" -" images name beneath each thumbnail. Index mode\n" -" enables certain other options, see INDEX MODE\n" -" OPTIONS\n" -" -t, --thumbnails As --index, but clicking an image will open it in\n" -" a new viewing window\n" -" --cache-thumbnails Enable thumbnail caching\n" -" -~, --thumb-title STRING Set window title for images opened from thumbnail mode.\n" -" Supports format specifiers, see there.\n" -" -I, --fullindex Same as index mode, but below each thumbnail you\n" -" get image name, size and dimensions\n" -" --index-name BOOL Show/Don't show filename in index/thumbnail mode\n" -" --index-size BOOL Show/Don't show filesize in index/thumbnail mode\n" -" --index-dim BOOL Show/Don't show dimensions in index/thumbnail mode\n" -" --bg-tile FILE Set FILE as tiled desktop background. Feh can\n" -" use enlightenment IPC if you are running it,\n" -" or will fall back to X methods.\n" -" Feh stores the commandline necessary to restore\n" -" the background you chose in ~/.fehbg. So to have\n" -" feh-set backgrounds restored when you restart X,\n" -" add the line \"eval `cat $HOME/.fehbg`\" to your\n" -" X startup script (e.g. ~/.xsession). Note that\n" -" you only need to do this for non E window\n" -" managers\n" -" --bg-center FILE Set FILE as centered desktop background\n" -" --bg-scale FILE Set FILE as scaled desktop background. This will\n" -" fill the whole background, but the images' aspect\n" -" ratio may not be preserved\n" -" --bg-fill FILE Like --bg-scale, but preserves aspect ratio by\n" -" zooming the image until it fits\n" -" --bg-seamless FILE Like --bg-tile, but with blurry corners\n" -" -C, --fontpath PATH Specify an extra directory to look in for fonts,\n" -" can be used multiple times to add multiple paths.\n" -" -M, --menu-font FONT Use FONT for the font in menus.\n" -" --menu-style FILE Use FILE as the style descriptor for menu text.\n" -" -), --menu-bg BG Use BG for the background image in menus.\n" -" --menu-border INT Specify number of pixels that define the menu\n" -" background's border. Borders are not stretched\n" -" when images are scaled.\n" -" -B, --image-bg STYLE Set background for transparent images and the like.\n" -" Accepted values: white, black, default\n" -" -N, --no-menus Don't load or show any menus.\n" -" -0, --reload-button B Use button B to reload the image (defaults to 0)\n" -" -1, --pan-button B Use button B pan the image (hold button down, move\n" -" the mouse to move the image around. Advancesto the\n" -" next image when the mouse is not moved (defaults to\n" -" 1, usually the left button).\n" -" -2, --zoom-button B Use button B to zoom the current image in any\n" -" mode (defaults to 2, usually the middle button).\n" -" -3, --menu-button B Use CTRL+Button B to activate the menu in any\n" -" mode. Set to 0 for any button. This option\n" -" is disabled if the -N or --no-menus option is set\n" -" (defaults to 3, usually the right button).\n" -" --menu-ctrl-mask Require CTRL+Button for menu activation in\n" -" any mode (default=off).\n" -" -4, --prev-button B Use button B to switch to the previous image\n" -" (defaults to 4, which usually is <mousewheel up>).\n" -" -5, --next-button B Use button B to switch to the next image\n" -" (defaults to 5, which usually is <mousewheel down>).\n" -" -8, --rotate-button B Use CTRL+Button B to rotate the current image in\n" -" any mode (default=2).\n" -" --no-rotate-ctrl-mask Don't require CTRL+Button for rotation in\n" -" any mode -- just use the button (default=off).\n" -" -9, --blur-button B Use CTRL+Button B to blur the current image in\n" -" any mode (default=1).\n" -" --no-blur-ctrl-mask Don't require CTRL+Button for blurring in\n" -" any mode -- just use the button (default=off).\n" -" --no-xinerama Disable Xinerama support. Only useful if\n" -" you have Xinerama compiled in.\n" -" --screen-clip [0|1] Enable/disable window clipping based on screen\n" -" size. WARNING: with this option disabled,\n" -" image windows could become very large, making\n" -" them unmanageable in certain window managers.\n" -" --hide-pointer In full screen mode, hide the X mouse pointer.\n" -" FORMAT SPECIFIERS\n" -" %%f image path/filename\n" -" %%n image name\n" -" %%s image size (bytes)\n" -" %%p image pixel size\n" -" %%w image width\n" -" %%h image height\n" -" %%t image format\n" -" %%P prints feh\n" -" %%v prints the version\n" -" %%m prints the mode (slideshow, multiwindow...)\n" -" %%l prints the total number of files in the filelist\n" -" %%u prints the current file number\n" -" \\n prints a newline\n" -" Eg. feh -A \"mv %%f ~/images/%%n\" *\n" -" MONTAGE MODE OPTIONS\n" -" -X, --ignore-aspect By default, the montage thumbnails will retain\n" -" their aspect ratios, while fitting in --thumb-width\n" -" and --thumb-height. This option will force them to\n" -" be the size set by --thumb-width and --thumb-height\n" -" This will prevent any whitespace in the final\n" -" montage\n" -" -s, --stretch Normally, if an image is smaller than the specified\n" -" thumbnail size, it will not be enlarged. If this\n" -" option is set, the image will be scaled up to fit\n" -" the thumbnail size. (Aspect ratio will be maintained\n" -" unless --ignore-aspect is specified)\n" -" -y, --thumb-width NUM Set thumbnail width in pixels\n" -" -E, --thumb-height NUM Set thumbnail height in pixels\n" -" Thumbnails default to 20x20 pixels\n" -" -W, --limit-width NUM Limit the width of the montage in pixels\n" -" -H, --limit-height NUM Limit the height of the montage in pixels\n" -" These options can be used together (to define the\n" -" image size exactly), or separately. If only one is\n" -" specified, theother is calculated from the number\n" -" of files specified and the size of the thumbnails.\n" -" The default is to limit width to 800 pixels and\n" -" calculate the height\n" -" -b, --bg FILE|trans Use FILE as a background for your montage. With\n" -" this option specified, the size of the montage will\n" -" default to the size of FILE if no size restrictions\n" -" are specified. Alternatively, if FILE is \"trans\",\n" -" make the background transparent.\n" -" -a, --alpha NUM When drawing thumbnails onto the background, apply\n" -" them with a transparency level of NUM (0-255).\n" -" -o, --output FILE Save the created montage to FILE\n" -" -O, --output-only FILE Just save the created montage to FILE\n" -" WITHOUT displaying it (use in scripts)\n" -" INDEX MODE OPTIONS\n" -" -e, --font FONT Use FONT to print the information under each\n" -" thumbnail. FONT should be defined in the form\n" -" fontname/size(points). eg -e myfont/12\n" -" -@, --title-font FONT Use FONT to print a title on the index, if no\n" -" font is specified, a title will not be printed\n" -" SLIDESHOW KEYS\n" -" The default mode for viewing mulitple images is Slideshow mode\n" -" When viewing a slideshow, the following keys may be used:\n" -" p, P, <BACKSPACE>, <LEFT> Goto previous slide\n" -" n, N, <SPACE>, <RIGHT> Goto next slide\n" -" r, R Reload image (good for webcams)\n" -" v, V Toggle fullscreen\n" -" m, M Show popup menu\n" -" c, C Caption entry mode. If --caption-path has been\n" -" specified, then this enables caption editing.\n" -" The caption will turn yellow and be editable,\n" -" hit enter to confirm and save the caption, or\n" -" hit escape to cancel and revert the caption.\n" -" w, W Size window to current image dimensions\n" -" h, H Pause the slideshow (only useful when using\n" -" timed reloading or image changes)\n" -" z, Z Jump to a random position in the current filelist\n" -" a, A Toggle action display (--draw-actions)\n" -" d, D Toggle filename display (--draw-filename)\n" -" s, S Save current image to unique filename\n" -" f, F Save current filelist to unique filename\n" -" <, > In place editing, rotate 90 degrees right/left\n" -" <HOME> Goto first slide\n" -" <END> Goto last slide\n" -" <ESCAPE> Quit the slideshow\n" -" +, = Increase reload delay\n" -" -, _ Decrease reload delay\n" -" <DELETE> Remove the currently viewed file from the filelist\n" -" <CTRL+DELETE> Delete the currently viewed file and remove it\n" -" from the filelist\n" -" x, X Close current window\n" -" q, Q Quit the slideshow\n" -" <KEYPAD LEFT> Move the image to the left\n" -" <KEYPAD RIGHT> Move the image to the right\n" -" <KEYPAD UP> Move the image up\n" -" <KEYPAD DOWN> Move the image down\n" -" <KEYPAD BEGIN> Antialias the image\n" -" <KEYPAD +> Zoom in\n" -" <KEYPAD -> Zoom out\n" -" <KEYPAD *> Zoom to 100%%\n" -" <KEYPAD /> Zoom to fit the window\n" -" <ENTER>,0 Run action specified by --action option\n" -" 1-9 Run action 1-9 specified by --action[1-9] options\n" -"\n" -" MOUSE ACTIONS\n" -" When viewing an image, a click of mouse button 1 moves to the next image\n" -" (slideshow mode only), a drag of mouse button 1 pans the image, if the\n" -" viewable window is smaller than the image, button 2 zooms (click and drag\n" -" left->right to zoom in, right->left to zoom out, click once to restore\n" -" 1x zoom), and mouse button 3 pans.\n" -" Ctrl+button 1 blurs or sharpens the image (drag left to blur and right to\n" -" sharpen). Ctrl+button 2 rotates the image around the center point.\n" -" Button 3 activates the context-sensitive menu. Buttons can be redefined\n" -" with the -1 through -9 (or --*-button) cmdline flags. All you people\n" -" with million button mice can remove the ctrl mask with the --no-*-ctrl-mask\n" -" options.\n" "\n" -"See 'man feh' for more detailed information\n" "\n" -"This program is free software see the file COPYING for licensing info.\n" -"Copyright Tom Gilbert (and various contributors) 1999-2003\n" -"Email bugs to <derf@chaosdorf.de>\n"); + fputs( +#ifdef INCLUDE_HELP +#include "help.inc" +#else + "See 'man " PACKAGE "'\n" +#endif + , stdout); exit(0); } - -void feh_create_default_config(char *rcfile) -{ - FILE *fp; - - D_ENTER(4); - - if ((fp = fopen(rcfile, "w")) == NULL) { - weprintf("Unable to create default config file %s\n", rcfile); - D_RETURN_(4); - } - - fprintf(fp, -"# Feh configuration file.\n" -"# Lines starting with # are comments. Don't use comments mid-line.\n" -"\n" -"# Feh expects to find this as ~/.fehrc or /etc/fehrc\n" -"# If both are available, ~/.fehrc will be used\n" -"\n" -"# Options are defined in theme_name/options pairs.\n" -"# Separate themename and options by whitespace.\n" -"\n" -"# There are two ways of specifying the theme. Either use feh -Tthemename,\n" -"# or use a symbolic link to feh with the name of the theme. eg\n" -"# ln -s `which feh` ~/bin/mkindex\n" -"# Now when you run 'mkindex', feh will load the config specified for the\n" -"# mkindex theme.\n" -"\n" -"# ======================\n" -"# Some examples of usage\n" -"# ======================\n" -"\n" -"# Set the default feh options to be recursive and verbose\n" -"# feh -rV\n" -"\n" -"# Multiple options can of course be used. They should all be on one line\n" -"# imagemap -rV --quiet -W 400 -H 300 --thumb-width 40 --thumb-height 30\n" -"\n" -"# ====================\n" -"# A few default themes\n" -"# ====================\n" -"\n" -"# Webcam mode, simply specify the url(s).\n" -"# e.g. feh -Twebcam http://cam1 http://cam2\n" -"webcam --multiwindow --reload 20\n" -"\n" -"# Create an index of the current directory. This version uses . as the\n" -"# current dir, so you don't even need any commandline arguments.\n" -"mkindex -iVO index.jpg .\n" -"\n" -"# More ambitious version...\n" -"imgidx -iVO .fehindex.jpg --limit-width 1200 --thumb-width 90 --thumb-height 90 .\n" -"\n" -"# Show a presentation\n" -"present --full-screen --sort name\n" -"\n" -"# Booth mode ;-)\n" -"booth --full-screen --hide-pointer --slideshow-delay 20\n" -"\n" -"# Screw xscreensaver, use feh =)\n" -"screensave --full-screen --randomize --slideshow-delay 5\n" -"\n" -"# Add <img> tags to your html with ease :-)\n" -"newimg -q -L \"<img src=\\\"%%f\\\" alt=\\\"%%n\\\" border=\\\"0\\\" " -"width=\\\"%%w\\\" height=\\\"%%h\\\">\"\n" -"\n" -"# Different menus\n" -"chrome --menu-bg " -PREFIX "/share/feh/images/menubg_chrome.png\n" -"brushed --menu-bg " -PREFIX "/share/feh/images/menubg_brushed.png\n" -"pastel --menu-bg " -PREFIX "/share/feh/images/menubg_pastel.png\n" -"aluminium --menu-bg " -PREFIX "/share/feh/images/menubg_aluminium.png\n" -"wood --menu-bg " -PREFIX "/share/feh/images/menubg_wood.png\n" -"aqua --menu-bg " -PREFIX "/share/feh/images/menubg_aqua.png\n" -"sky --menu-bg " -PREFIX "/share/feh/images/menubg_sky.png\n" -"orange --menu-bg " -PREFIX "/share/feh/images/menubg_orange.png\n" -"light --menu-bg " -PREFIX "/share/feh/images/menubg_light.png\n" -"black --menu-bg " -PREFIX "/share/feh/images/menubg_black.png" -" --menu-style " -PREFIX "/share/feh/fonts/black.style\n"); - fclose(fp); - - D_RETURN_(4); -} diff --git a/src/options.h b/src/options.h index 55b997f..74c12cd 100644 --- a/src/options.h +++ b/src/options.h @@ -1,6 +1,7 @@ /* options.h Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2024 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -26,14 +27,16 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef OPTIONS_H #define OPTIONS_H +enum on_last_slide_action { + ON_LAST_SLIDE_RESUME = 0, + ON_LAST_SLIDE_QUIT, + ON_LAST_SLIDE_HOLD +}; + struct __fehoptions { unsigned char multiwindow; unsigned char montage; - unsigned char collage; unsigned char index; - unsigned char index_show_name; - unsigned char index_show_dim; - unsigned char index_show_size; unsigned char thumbs; unsigned char slideshow; unsigned char recursive; @@ -46,12 +49,20 @@ struct __fehoptions { unsigned char aspect; unsigned char stretch; unsigned char keep_http; + unsigned char use_conversion_cache; unsigned char borderless; unsigned char randomize; - unsigned char no_jump_on_resort; + unsigned char jump_on_resort; unsigned char full_screen; - unsigned char auto_zoom; unsigned char draw_filename; +#ifdef HAVE_LIBEXIF + unsigned char draw_exif; + unsigned char auto_rotate; +#endif +#ifdef HAVE_INOTIFY + unsigned char auto_reload; + int inotify_fd; +#endif unsigned char list; unsigned char quiet; unsigned char preload; @@ -60,78 +71,281 @@ struct __fehoptions { unsigned char reverse; unsigned char no_menus; unsigned char scale_down; - unsigned char builtin_http; - unsigned char wget_timestamp; unsigned char bgmode; unsigned char xinerama; unsigned char screen_clip; unsigned char hide_pointer; unsigned char draw_actions; + unsigned char draw_info; unsigned char cache_thumbnails; - unsigned char cycle_once; + unsigned char on_last_slide; + unsigned char hold_actions[10]; + unsigned char text_bg; + unsigned char no_fehbg; + unsigned char keep_zoom_vp; + unsigned char insecure_ssl; + unsigned char filter_by_dimensions; + unsigned char edit; char *output_file; char *output_dir; char *bg_file; + char *image_bg; char *font; char *title_font; char *title; char *thumb_title; char *actions[10]; + char *action_titles[10]; char *fontpath; char *filelistfile; char *menu_font; char *customlist; - char *menu_bg; - char *image_bg; - char *rcfile; - char *menu_style; char *caption_path; + char *start_list_at; + char *info_cmd; + char *index_info; - gib_style *menu_style_l; - - unsigned char pan_button; - unsigned char zoom_button; - unsigned char menu_button; - unsigned char menu_ctrl_mask; - unsigned char prev_button; - unsigned char next_button; - - unsigned char rotate_button; - unsigned char blur_button; - unsigned char reload_button; - unsigned char no_rotate_ctrl_mask; - unsigned char no_blur_ctrl_mask; - unsigned char no_pan_ctrl_mask; - + int force_aliasing; + int tap_zones; int thumb_w; int thumb_h; int limit_w; int limit_h; - int reload; + unsigned int thumb_redraw; + double reload; int sort; - int debug_level; + int version_sort; + int debug; + int geom_enabled; int geom_flags; int geom_x; int geom_y; unsigned int geom_w; unsigned int geom_h; + int offset_flags; + int offset_x; + int offset_y; int default_zoom; - int menu_border; + int zoom_mode; + double zoom_rate; unsigned char adjust_reload; - unsigned int start_list_at; + int xinerama_index; + char *x11_class; + unsigned long int x11_windowid; + + /* signed in case someone wants to invert scrolling real quick */ + int scroll_step; + + // imlib cache size in mebibytes + int cache_size; + + unsigned int min_width, min_height, max_width, max_height; unsigned char mode; unsigned char paused; double slideshow_delay; + signed int conversion_timeout; + Imlib_Font menu_fn; }; +enum __feh_option { +OPTION_debug = '+', +OPTION_scale_down = '.', +OPTION_max_dimension = '<', +OPTION_min_dimension = '>', +OPTION_title_font = '@', +OPTION_action = 'A', +OPTION_image_bg = 'B', +OPTION_fontpath = 'C', +OPTION_slideshow_delay = 'D', +OPTION_thumb_height = 'E', +OPTION_fullscreen = 'F', +OPTION_draw_actions = 'G', +OPTION_limit_height = 'H', +OPTION_fullindex = 'I', +OPTION_thumb_redraw = 'J', +OPTION_caption_path = 'K', +OPTION_customlist = 'L', +OPTION_menu_font = 'M', +OPTION_no_menus = 'N', +OPTION_output_only = 'O', +OPTION_cache_thumbnails = 'P', +OPTION_reload = 'R', +OPTION_sort = 'S', +OPTION_theme = 'T', +OPTION_loadable = 'U', +OPTION_verbose = 'V', +OPTION_limit_width = 'W', +OPTION_ignore_aspect = 'X', +OPTION_hide_pointer = 'Y', +OPTION_auto_zoom = 'Z', +OPTION_title = '^', +OPTION_alpha = 'a', +OPTION_bg = 'b', +OPTION_draw_filename = 'd', +OPTION_font = 'e', +OPTION_filelist = 'f', +OPTION_geometry = 'g', +OPTION_help = 'h', +OPTION_index = 'i', +OPTION_output_dir = 'j', +OPTION_keep_http = 'k', +OPTION_list = 'l', +OPTION_montage = 'm', +OPTION_reverse = 'n', +OPTION_output = 'o', +OPTION_preload = 'p', +OPTION_quiet = 'q', +OPTION_recursive = 'r', +OPTION_stretch = 's', +OPTION_thumbnails = 't', +OPTION_unloadable = 'u', +OPTION_version = 'v', +OPTION_multiwindow = 'w', +OPTION_borderless = 'x', +OPTION_thumb_width = 'y', +OPTION_randomize = 'z', +OPTION_start_at = '|', +OPTION_thumb_title = '~', +OPTION_bg_title, +OPTION_bg_center, +OPTION_bg_scale, +OPTION_bg_fill, +OPTION_bg_max, +OPTION_zoom, +OPTION_zoom_step, +OPTION_zoom_in_rate, +OPTION_zoom_out_rate, +OPTION_keep_zoom_vp, +OPTION_no_screen_clip, +OPTION_index_info, +OPTION_magick_timeout, +OPTION_action1, +OPTION_action2, +OPTION_action3, +OPTION_action4, +OPTION_action5, +OPTION_action6, +OPTION_action7, +OPTION_action8, +OPTION_action9, +OPTION_no_jump_on_resort, +OPTION_edit, +OPTION_draw_exif, +OPTION_auto_rotate, +OPTION_no_xinerama, +OPTION_draw_tinted, +OPTION_info, +OPTION_tap_zones, +OPTION_force_aliasing, +OPTION_no_fehbg, +OPTION_scroll_step, +OPTION_xinerama_index, +OPTION_insecure, +OPTION_no_recursive, +OPTION_cache_size, +OPTION_on_last_slide, +OPTION_conversion_timeout, +OPTION_version_sort, +OPTION_offset, +OPTION_auto_reload, +OPTION_class, +OPTION_no_conversion_cache, +OPTION_window_id, +}; + +//typedef enum __fehoption fehoption; + +struct __fehkey { + unsigned int keysyms[3]; + unsigned int keystates[3]; + unsigned int state; + unsigned int button; + char *name; +}; + +enum key_action { + EVENT_menu_close = 0, + EVENT_menu_parent, + EVENT_menu_down, + EVENT_menu_up, + EVENT_menu_child, + EVENT_menu_select, + EVENT_scroll_left, + EVENT_scroll_right, + EVENT_scroll_down, + EVENT_scroll_up, + EVENT_scroll_left_page, + EVENT_scroll_right_page, + EVENT_scroll_down_page, + EVENT_scroll_up_page, + EVENT_prev_img, + EVENT_next_img, + EVENT_jump_back, + EVENT_jump_fwd, + EVENT_prev_dir, + EVENT_next_dir, + EVENT_jump_random, + EVENT_quit, + EVENT_close, + EVENT_remove, + EVENT_delete, + EVENT_jump_first, + EVENT_jump_last, + EVENT_action_0, + EVENT_action_1, + EVENT_action_2, + EVENT_action_3, + EVENT_action_4, + EVENT_action_5, + EVENT_action_6, + EVENT_action_7, + EVENT_action_8, + EVENT_action_9, + EVENT_zoom_in, + EVENT_zoom_out, + EVENT_zoom_default, + EVENT_zoom_fit, + EVENT_zoom_fill, + EVENT_size_to_image, + EVENT_render, + EVENT_toggle_actions, + EVENT_toggle_aliasing, + EVENT_toggle_auto_zoom, +#ifdef HAVE_LIBEXIF + EVENT_toggle_exif, +#endif + EVENT_toggle_filenames, + EVENT_toggle_info, + EVENT_toggle_pointer, + EVENT_toggle_caption, + EVENT_toggle_pause, + EVENT_toggle_menu, + EVENT_toggle_fullscreen, + EVENT_reload_image, + EVENT_save_image, + EVENT_save_filelist, + EVENT_orient_1, + EVENT_orient_3, + EVENT_flip, + EVENT_mirror, + EVENT_reload_minus, + EVENT_reload_plus, + EVENT_toggle_keep_vp, + EVENT_toggle_fixed_geometry, + EVENT_pan, + EVENT_zoom, + EVENT_blur, + EVENT_rotate, + EVENT_LIST_END +}; + void init_parse_options(int argc, char **argv); char *feh_string_normalize(char *str); -void feh_create_default_config(char *rcfile); extern fehoptions opt; diff --git a/src/signals.c b/src/signals.c new file mode 100644 index 0000000..058b8c9 --- /dev/null +++ b/src/signals.c @@ -0,0 +1,94 @@ +/* signals.c + +Copyright (C) 2010-2023 by Birte Kristina Friesel + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + +#include "feh.h" +#include "filelist.h" +#include "winwidget.h" +#include "options.h" + +void feh_handle_signal(int); +volatile int sig_received = 0; +volatile int sig_exit = 0; + +void setup_signal_handlers(void) +{ + struct sigaction feh_sh; + sigset_t feh_ss; + if ( + (sigemptyset(&feh_ss) == -1) || + (sigaddset(&feh_ss, SIGUSR1) == -1) || + (sigaddset(&feh_ss, SIGUSR2) == -1) || + (sigaddset(&feh_ss, SIGALRM) == -1) || + (sigaddset(&feh_ss, SIGTERM) == -1) || + (sigaddset(&feh_ss, SIGQUIT) == -1) || + (sigaddset(&feh_ss, SIGINT) == -1) || + (sigaddset(&feh_ss, SIGTTIN) == -1)) + { + weprintf("Failed to set up signal masks"); + return; + } + + feh_sh.sa_handler = feh_handle_signal; + feh_sh.sa_mask = feh_ss; + feh_sh.sa_flags = 0; + + if ( + (sigaction(SIGUSR1, &feh_sh, NULL) == -1) || + (sigaction(SIGUSR2, &feh_sh, NULL) == -1) || + (sigaction(SIGALRM, &feh_sh, NULL) == -1) || + (sigaction(SIGTERM, &feh_sh, NULL) == -1) || + (sigaction(SIGQUIT, &feh_sh, NULL) == -1) || + (sigaction(SIGINT, &feh_sh, NULL) == -1) || + (sigaction(SIGTTIN, &feh_sh, NULL) == -1)) + { + weprintf("Failed to set up signal handler"); + return; + } + + return; +} + +void feh_handle_signal(int signo) +{ + switch (signo) { + case SIGALRM: + if (childpid) + killpg(childpid, SIGINT); + return; + case SIGTTIN: + // we were probably backgrounded while we were running + control_via_stdin = 0; + return; + case SIGINT: + case SIGTERM: + case SIGQUIT: + if (childpid) + killpg(childpid, SIGINT); + sig_exit = 128 + signo; + return; + } + + sig_received = signo; +} diff --git a/src/signals.h b/src/signals.h new file mode 100644 index 0000000..3d78b67 --- /dev/null +++ b/src/signals.h @@ -0,0 +1,32 @@ +/* signals.h + +Copyright (C) 2010-2023 by Birte Kristina Friesel + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + +#ifndef SIGNALS_H +#define SIGNALS_H + +void setup_signal_handlers(void); +extern volatile int sig_exit; +extern volatile int sig_received; +#endif diff --git a/src/slideshow.c b/src/slideshow.c index 0d509e4..266cb2e 100644 --- a/src/slideshow.c +++ b/src/slideshow.c @@ -1,6 +1,7 @@ /* slideshow.c Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2024 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -28,160 +29,186 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "timers.h" #include "winwidget.h" #include "options.h" +#include "signals.h" void init_slideshow_mode(void) { winwidget w = NULL; int success = 0; - char *s = NULL; - gib_list *l = NULL, *last = NULL; - feh_file *file = NULL; - - D_ENTER(3); + gib_list *l = filelist, *last = NULL; + + /* + * In theory, --start-at FILENAME is simple: Look for a file called + * FILENAME, start the filelist there, done. + * + * In practice, there are cases where this isn't sufficient. For instance, + * a user running 'feh --start-at hello.jpg /tmp' will expect feh to start + * at /tmp/hello.jpg, as if they had used + * 'feh --start-at /tmp/hello.jpg /tmp'. Similarly, XDG Desktop files + * may lead to the invocation 'feh --start-at /tmp/hello.jpg .' in /tmp, + * expecting the behaviour of 'feh --start-at ./hello.jpg .'. + * + * Since a good user experience is not about being technically correct, but + * about delivering the expected behaviour, we do some fuzzy matching + * here. In the worst case, this will cause --start-at to start at the + * wrong file. + */ + + // 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; + } + } - mode = "slideshow"; + /* + * If it didn't work (opt.start_list_at is still set): Fall back to + * comparing just the filenames without directory prefixes. This may lead + * to false positives, but for now that's just the way it is. + */ if (opt.start_list_at) { - l = gib_list_nth(filelist, opt.start_list_at); - opt.start_list_at = 0; /* for next time */ - } else { - l = filelist; + char *current_filename; + char *start_at_filename = strrchr(opt.start_list_at, '/'); + if (start_at_filename) { + start_at_filename++; // We only care about the part after the '/' + } else { + start_at_filename = opt.start_list_at; + } + for (l = filelist; l && opt.start_list_at; l = l->next) { + current_filename = strrchr(FEH_FILE(l->data)->filename, '/'); + if (current_filename) { + current_filename++; // We only care about the part after the '/' + } else { + 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; + } + } } + + // If that didn't work either, we're out of luck. + if (opt.start_list_at) + eprintf("--start-at %s: File not found in filelist", + opt.start_list_at); + + if (!opt.title) + opt.title = PACKAGE " [%u of %l] - %f"; + + mode = "slideshow"; for (; l; l = l->next) { - file = FEH_FILE(l->data); if (last) { filelist = feh_file_remove_from_list(filelist, last); last = NULL; } current_file = l; - s = slideshow_create_name(file); - if ((w = winwidget_create_from_file(l, s, WIN_TYPE_SLIDESHOW)) != NULL) { - free(s); + if ((w = winwidget_create_from_file(l, WIN_TYPE_SLIDESHOW)) != NULL) { success = 1; winwidget_show(w); - if (opt.slideshow_delay >= 0.0) + if (opt.slideshow_delay > 0.0) feh_add_timer(cb_slide_timer, w, opt.slideshow_delay, "SLIDE_CHANGE"); - else if (opt.reload > 0) + if (opt.reload > 0) feh_add_unique_timer(cb_reload_timer, w, opt.reload); break; } else { - free(s); last = l; } } if (!success) show_mini_usage(); - D_RETURN_(3); + + return; } void cb_slide_timer(void *data) { - D_ENTER(4); - slideshow_change_image((winwidget) data, SLIDE_NEXT); - D_RETURN_(4); + slideshow_change_image((winwidget) data, SLIDE_NEXT, 1); + return; } void cb_reload_timer(void *data) { - winwidget w = (winwidget) data; + gib_list *l; + char *current_filename; - D_ENTER(4); - feh_reload_image(w, 0, 0); - feh_add_unique_timer(cb_reload_timer, w, opt.reload); - D_RETURN_(4); -} + winwidget w = (winwidget) data; -void feh_reload_image(winwidget w, int resize, int force_new) -{ - char *title, *new_title; - int len; - Imlib_Image tmp; + /* + * 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)); + } - D_ENTER(4); + if (!(filelist_len = gib_list_length(filelist))) { + eprintf("No files found to reload."); + } - if (!w->file) { - weprintf("couldn't reload, this image has no file associated with it."); - D_RETURN_(4); - } + feh_prepare_filelist(); - free(FEH_FILE(w->file->data)->caption); - FEH_FILE(w->file->data)->caption = NULL; + /* 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; + } - len = strlen(w->name) + sizeof("Reloading: ") + 1; - new_title = emalloc(len); - snprintf(new_title, len, "Reloading: %s", w->name); - title = estrdup(w->name); - winwidget_rename(w, new_title); + free(current_filename); - /* force imlib2 not to cache */ - if (force_new) { - winwidget_free_image(w); + if (!current_file) + current_file = filelist; + w->file = current_file; } - /* if the image has changed in dimensions - we gotta resize */ - if ((feh_load_image(&tmp, FEH_FILE(w->file->data))) == 0) { - if (force_new) { - eprintf("failed to reload image\n"); - } else { - weprintf("Couldn't reload image. Is it still there?"); - } - winwidget_rename(w, title); - free(title); - free(new_title); - filelist = feh_file_remove_from_list(filelist, w->file); - D_RETURN_(4); - } - if (force_new) { - w->im = tmp; - resize = 1; - winwidget_reset_image(w); - } else { - if ((gib_imlib_image_get_width(w->im) != gib_imlib_image_get_width(tmp)) - || (gib_imlib_image_get_height(w->im) != gib_imlib_image_get_height(tmp))) { - resize = 1; - winwidget_reset_image(w); - } - winwidget_free_image(w); - w->im = tmp; - } - - w->mode = MODE_NORMAL; - if ((w->im_w != gib_imlib_image_get_width(w->im)) - || (w->im_h != gib_imlib_image_get_height(w->im))) - w->had_resize = 1; - if (w->has_rotated) { - Imlib_Image temp; - - temp = gib_imlib_create_rotated_image(w->im, 0.0); - w->im_w = gib_imlib_image_get_width(temp); - w->im_h = gib_imlib_image_get_height(temp); - gib_imlib_free_image_and_decache(temp); - } else { - w->im_w = gib_imlib_image_get_width(w->im); - w->im_h = gib_imlib_image_get_height(w->im); - } - winwidget_render_image(w, resize, 1); - - winwidget_rename(w, title); - free(title); - free(new_title); - - D_RETURN_(4); + feh_reload_image(w, 1, 0); + feh_add_unique_timer(cb_reload_timer, w, opt.reload); + return; } -void slideshow_change_image(winwidget winwid, int change) +void slideshow_change_image(winwidget winwid, int change, int render) { - int success = 0; gib_list *last = NULL; + gib_list *previous_file = current_file; int i = 0; int jmp = 1; - char *s; + /* We can't use filelist_len in the for loop, since that changes when we + * encounter invalid images. + */ + int our_filelist_len = filelist_len; - D_ENTER(4); + if (opt.slideshow_delay > 0.0) + feh_add_timer(cb_slide_timer, winwid, opt.slideshow_delay, "SLIDE_CHANGE"); /* Without this, clicking a one-image slideshow reloads it. Not very * intelligent behaviour :-) */ - if (filelist_len < 2 && opt.cycle_once == 0) - D_RETURN_(4); + if (filelist_len < 2 && opt.on_last_slide != ON_LAST_SLIDE_QUIT) + return; /* Ok. I do this in such an odd way to ensure that if the last or first * image is not loadable, it will go through in the right direction to * @@ -190,14 +217,28 @@ void slideshow_change_image(winwidget winwid, int change) if (change == SLIDE_FIRST) { current_file = gib_list_last(filelist); change = SLIDE_NEXT; + previous_file = NULL; } else if (change == SLIDE_LAST) { current_file = filelist; change = SLIDE_PREV; + previous_file = NULL; } /* The for loop prevents us looping infinitely */ - for (i = 0; i < filelist_len; i++) { + 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); @@ -206,8 +247,11 @@ void slideshow_change_image(winwidget winwid, int change) current_file = feh_list_jump(filelist, current_file, BACK, 1); break; case SLIDE_RAND: - current_file = feh_list_jump(filelist, current_file, FORWARD, rand() % filelist_len); - change = SLIDE_NEXT; + if (filelist_len > 1) { + current_file = feh_list_jump(filelist, current_file, FORWARD, + (random() % (filelist_len - 1)) + 1); + change = SLIDE_NEXT; + } break; case SLIDE_JUMP_FWD: if (filelist_len < 5) @@ -237,6 +281,44 @@ void slideshow_change_image(winwidget winwid, int change) try the previous file, not another jmp */ change = SLIDE_NEXT; break; + case SLIDE_JUMP_NEXT_DIR: + { + char old_dir[PATH_MAX], new_dir[PATH_MAX]; + int j; + + feh_file_dirname(old_dir, FEH_FILE(current_file->data), PATH_MAX); + + for (j = 0; j < our_filelist_len; j++) { + current_file = feh_list_jump(filelist, current_file, FORWARD, 1); + feh_file_dirname(new_dir, FEH_FILE(current_file->data), PATH_MAX); + if (strcmp(old_dir, new_dir) != 0) + break; + } + } + change = SLIDE_NEXT; + break; + case SLIDE_JUMP_PREV_DIR: + { + char old_dir[PATH_MAX], new_dir[PATH_MAX]; + int j; + + /* Start the search from the previous file in case we are on + the first file of a directory */ + current_file = feh_list_jump(filelist, current_file, BACK, 1); + feh_file_dirname(old_dir, FEH_FILE(current_file->data), PATH_MAX); + + for (j = 0; j < our_filelist_len; j++) { + current_file = feh_list_jump(filelist, current_file, BACK, 1); + feh_file_dirname(new_dir, FEH_FILE(current_file->data), PATH_MAX); + if (strcmp(old_dir, new_dir) != 0) + break; + } + + /* Next file is the first entry of prev_dir */ + current_file = feh_list_jump(filelist, current_file, FORWARD, 1); + } + change = SLIDE_NEXT; + break; default: eprintf("BUG!\n"); break; @@ -246,23 +328,30 @@ void slideshow_change_image(winwidget winwid, int change) filelist = feh_file_remove_from_list(filelist, last); last = NULL; } - s = slideshow_create_name(FEH_FILE(current_file->data)); - winwidget_rename(winwid, s); - free(s); + if (opt.on_last_slide == ON_LAST_SLIDE_HOLD && previous_file && + ((current_file == filelist && change == SLIDE_NEXT) || + (previous_file == filelist && change == SLIDE_PREV))) { + current_file = previous_file; + } - if ((winwidget_loadimage(winwid, FEH_FILE(current_file->data))) - != 0) { - success = 1; + if (winwidget_loadimage(winwid, FEH_FILE(current_file->data))) { + int w = gib_imlib_image_get_width(winwid->im); + int h = gib_imlib_image_get_height(winwid->im); + if (feh_should_ignore_image(winwid->im)) { + last = current_file; + continue; + } winwid->mode = MODE_NORMAL; winwid->file = current_file; - if ((winwid->im_w != gib_imlib_image_get_width(winwid->im)) - || (winwid->im_h != gib_imlib_image_get_height(winwid->im))) + if ((winwid->im_w != w) || (winwid->im_h != h)) winwid->had_resize = 1; winwidget_reset_image(winwid); - winwid->im_w = gib_imlib_image_get_width(winwid->im); - winwid->im_h = gib_imlib_image_get_height(winwid->im); - winwidget_render_image(winwid, 1, 1); + winwid->im_w = w; + winwid->im_h = h; + if (render) { + winwidget_render_image(winwid, 1, 0); + } break; } else last = current_file; @@ -273,9 +362,7 @@ void slideshow_change_image(winwidget winwid, int change) if (filelist_len == 0) eprintf("No more slides in show"); - if (opt.slideshow_delay >= 0.0) - feh_add_timer(cb_slide_timer, winwid, opt.slideshow_delay, "SLIDE_CHANGE"); - D_RETURN_(4); + return; } void slideshow_pause_toggle(winwidget w) @@ -289,148 +376,226 @@ void slideshow_pause_toggle(winwidget w) winwidget_rename(w, NULL); } -char *slideshow_create_name(feh_file * file) -{ - char *s = NULL; - int len = 0; - - D_ENTER(4); - - if (!opt.title) { - len = strlen(PACKAGE " [slideshow mode] - ") + strlen(file->filename) + 1; - s = emalloc(len); - snprintf(s, len, PACKAGE " [%d of %d] - %s", - gib_list_num(filelist, current_file) + 1, gib_list_length(filelist), file->filename); - } else { - s = estrdup(feh_printf(opt.title, file)); - } - - D_RETURN(4, s); -} - -void feh_action_run(feh_file * file, char *action) +void feh_action_run(feh_file * file, char *action, winwidget winwid) { - D_ENTER(4); if (action) { char *sys; - D(3, ("Running action %s\n", action)); - sys = feh_printf(action, file); + D(("Running action %s\n", action)); + sys = feh_printf(action, file, winwid); if (opt.verbose && !opt.list && !opt.customlist) fprintf(stderr, "Running action -->%s<--\n", sys); - system(sys); + if (system(sys) == -1) + perror("running action via system() failed"); + } + return; +} + +char *format_size(double size) +{ + static char ret[5]; + char units[] = {' ', 'k', 'M', 'G', 'T'}; + unsigned char postfix = 0; + while (size >= 1000) { + size /= 1000; + postfix++; } - D_RETURN_(4); + snprintf(ret, 5, "%3.0f%c", size, units[postfix]); + return ret; } -char *feh_printf(char *str, feh_file * file) +char *feh_printf(char *str, feh_file * file, winwidget winwid) { char *c; char buf[20]; static char ret[4096]; - - D_ENTER(4); + char *filelist_tmppath; ret[0] = '\0'; + filelist_tmppath = NULL; + gib_list *f; for (c = str; *c != '\0'; c++) { - if (*c == '%') { + 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) - strcat(ret, file->filename); + strncat(ret, file->filename, sizeof(ret) - strlen(ret) - 1); break; - case 'n': + case 'F': if (file) - strcat(ret, file->name); + strncat(ret, shell_escape(file->filename), sizeof(ret) - strlen(ret) - 1); break; - case 'w': - if (file) { - if (!file->info) - feh_file_info_load(file, NULL); - snprintf(buf, sizeof(buf), "%d", file->info->width); - strcat(ret, buf); + case 'g': + if (winwid) { + snprintf(buf, sizeof(buf), "%d,%d", winwid->w, winwid->h); + strncat(ret, buf, sizeof(ret) - strlen(ret) - 1); } break; case 'h': - if (file) { - if (!file->info) - feh_file_info_load(file, NULL); + if (file && (file->info || !feh_file_info_load(file, NULL))) { snprintf(buf, sizeof(buf), "%d", file->info->height); - strcat(ret, buf); + strncat(ret, buf, sizeof(ret) - strlen(ret) - 1); } break; - case 's': - if (file) { - if (!file->info) - feh_file_info_load(file, NULL); - snprintf(buf, sizeof(buf), "%d", file->info->size); - strcat(ret, buf); + case 'l': + snprintf(buf, sizeof(buf), "%d", gib_list_length(filelist)); + strncat(ret, buf, sizeof(ret) - strlen(ret) - 1); + break; + case 'L': + if (filelist_tmppath != NULL) { + strncat(ret, filelist_tmppath, sizeof(ret) - strlen(ret) - 1); + } else { + filelist_tmppath = feh_unique_filename("/tmp/","filelist"); + feh_write_filelist(filelist, filelist_tmppath); + strncat(ret, filelist_tmppath, sizeof(ret) - strlen(ret) - 1); + } + break; + case 'm': + strncat(ret, mode, sizeof(ret) - strlen(ret) - 1); + break; + case 'n': + if (file) + strncat(ret, file->name, sizeof(ret) - strlen(ret) - 1); + break; + case 'N': + if (file) + strncat(ret, shell_escape(file->name), sizeof(ret) - strlen(ret) - 1); + break; + case 'o': + if (winwid) { + snprintf(buf, sizeof(buf), "%d,%d", winwid->im_x, + winwid->im_y); + strncat(ret, buf, sizeof(ret) - strlen(ret) - 1); } break; case 'p': - if (file) { - if (!file->info) - feh_file_info_load(file, NULL); + if (file && (file->info || !feh_file_info_load(file, NULL))) { snprintf(buf, sizeof(buf), "%d", file->info->pixels); - strcat(ret, buf); + strncat(ret, buf, sizeof(ret) - strlen(ret) - 1); + } + break; + case 'P': + if (file && (file->info || !feh_file_info_load(file, NULL))) { + strncat(ret, format_size(file->info->pixels), sizeof(ret) - strlen(ret) - 1); + } + break; + case 'r': + if (winwid) { + snprintf(buf, sizeof(buf), "%.1f", winwid->im_angle); + strncat(ret, buf, sizeof(ret) - strlen(ret) - 1); + } + break; + case 's': + if (file && (file->size >= 0 || !feh_file_stat(file))) { + snprintf(buf, sizeof(buf), "%d", file->size); + strncat(ret, buf, sizeof(ret) - strlen(ret) - 1); + } + break; + case 'S': + if (file && (file->size >= 0 || !feh_file_stat(file))) { + strncat(ret, format_size(file->size), sizeof(ret) - strlen(ret) - 1); } break; case 't': - if (file) { - if (!file->info) - feh_file_info_load(file, NULL); - strcat(ret, file->info->format); + if (file && (file->info || !feh_file_info_load(file, NULL))) { + strncat(ret, file->info->format, sizeof(ret) - strlen(ret) - 1); } break; - case 'P': - strcat(ret, PACKAGE); + case 'u': + f = current_file ? current_file : gib_list_find_by_data(filelist, file); + snprintf(buf, sizeof(buf), "%d", f ? gib_list_num(filelist, f) + 1 : 0); + strncat(ret, buf, sizeof(ret) - strlen(ret) - 1); break; case 'v': - strcat(ret, VERSION); + strncat(ret, VERSION, sizeof(ret) - strlen(ret) - 1); break; - case 'm': - strcat(ret, mode); + case 'V': + snprintf(buf, sizeof(buf), "%d", getpid()); + strncat(ret, buf, sizeof(ret) - strlen(ret) - 1); break; - case 'l': - snprintf(buf, sizeof(buf), "%d", gib_list_length(filelist)); - strcat(ret, buf); + case 'w': + if (file && (file->info || !feh_file_info_load(file, NULL))) { + snprintf(buf, sizeof(buf), "%d", file->info->width); + strncat(ret, buf, sizeof(ret) - strlen(ret) - 1); + } break; - case 'u': - snprintf(buf, sizeof(buf), "%d", - current_file != NULL ? gib_list_num(filelist, current_file) - + 1 : 0); - strcat(ret, buf); + case 'W': + if (winwid) { + snprintf(buf, sizeof(buf), "%dx%d+%d+%d", winwid->w, winwid->h, winwid->x, winwid->y); + strncat(ret, buf, sizeof(ret) - strlen(ret) - 1); + } + break; + case 'z': + if (winwid) { + snprintf(buf, sizeof(buf), "%.2f", winwid->zoom); + strncat(ret, buf, sizeof(ret) - strlen(ret) - 1); + } else { + strncat(ret, "1.00", sizeof(ret) - strlen(ret) - 1); + } + break; + case 'Z': + if (winwid) { + snprintf(buf, sizeof(buf), "%f", winwid->zoom); + strncat(ret, buf, sizeof(ret) - strlen(ret) - 1); + } + break; + case '%': + strncat(ret, "%", sizeof(ret) - strlen(ret) - 1); break; default: - strncat(ret, c, 1); + weprintf("Unrecognized format specifier %%%c", *c); + if ((strlen(ret) + 3) < sizeof(ret)) + strncat(ret, c - 1, 2); break; } - } else if (*c == '\\') { + } else if ((*c == '\\') && (*(c+1) != '\0') && ((strlen(ret) + 3) < sizeof(ret))) { c++; switch (*c) { case 'n': strcat(ret, "\n"); break; default: - strncat(ret, c, 1); + strncat(ret, c - 1, 2); break; } - } else + } else if ((strlen(ret) + 2) < sizeof(ret)) strncat(ret, c, 1); } - D_RETURN(4, ret); + if (filelist_tmppath != NULL) + free(filelist_tmppath); + return(ret); } void feh_filelist_image_remove(winwidget winwid, char do_delete) { if (winwid->type == WIN_TYPE_SLIDESHOW) { - char *s; gib_list *doomed; doomed = current_file; - slideshow_change_image(winwid, SLIDE_NEXT); + /* + * work around feh_list_jump exiting if ON_LAST_SLIDE_QUIT is set + * and no further files are left (we need to delete first) + */ + if (opt.on_last_slide == ON_LAST_SLIDE_QUIT && ! doomed->next && do_delete) { + feh_file_rm_and_free(filelist, doomed); + exit(0); + } + if (doomed->next) { + slideshow_change_image(winwid, SLIDE_NEXT, 0); + } + else { + slideshow_change_image(winwid, SLIDE_PREV, 0); + } if (do_delete) filelist = feh_file_rm_and_free(filelist, doomed); else @@ -440,15 +605,14 @@ void feh_filelist_image_remove(winwidget winwid, char do_delete) winwidget_destroy(winwid); return; } - s = slideshow_create_name(FEH_FILE(winwid->file->data)); - winwidget_rename(winwid, s); - free(s); + winwidget_render_image(winwid, 1, 0); } else if ((winwid->type == WIN_TYPE_SINGLE) || (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER)) { if (do_delete) filelist = feh_file_rm_and_free(filelist, winwid->file); else filelist = feh_file_remove_from_list(filelist, winwid->file); + winwid->file = NULL; winwidget_destroy(winwid); } } @@ -457,28 +621,36 @@ void slideshow_save_image(winwidget win) { char *tmpname; Imlib_Load_Error err; + char *base_dir = ""; + if (opt.output_dir) { + base_dir = estrjoin("", opt.output_dir, "/", NULL); + } - D_ENTER(4); if (win->file) { - tmpname = feh_unique_filename("", FEH_FILE(win->file->data)->name); + tmpname = feh_unique_filename(base_dir, FEH_FILE(win->file->data)->name); } else if (mode) { char *tmp; tmp = estrjoin(".", mode, "png", NULL); - tmpname = feh_unique_filename("", tmp); + tmpname = feh_unique_filename(base_dir, tmp); free(tmp); } else { - tmpname = feh_unique_filename("", "noname.png"); + tmpname = feh_unique_filename(base_dir, "noname.png"); } - if (!opt.quiet) - printf("saving image to filename '%s'\n", tmpname); + if (opt.output_dir) { + free(base_dir); + } + + if (opt.verbose) + fprintf(stderr, "saving image to filename '%s'\n", tmpname); gib_imlib_save_image_with_error_return(win->im, tmpname, &err); + if (err) - weprintf("Can't save image %s:", tmpname); + feh_print_load_error(tmpname, win, err, LOAD_ERROR_IMLIB); free(tmpname); - D_RETURN_(4); + return; } gib_list *feh_list_jump(gib_list * root, gib_list * l, int direction, int num) @@ -498,10 +670,16 @@ gib_list *feh_list_jump(gib_list * root, gib_list * l, int direction, int num) if (ret->next) { ret = ret->next; } else { - if (opt.cycle_once) { + if (opt.on_last_slide == ON_LAST_SLIDE_QUIT) { exit(0); } - ret = root; + if (opt.randomize) { + /* Randomize the filename order */ + filelist = gib_list_randomize(filelist); + ret = filelist; + } else { + ret = root; + } } } else { if (ret->prev) diff --git a/src/structs.h b/src/structs.h index a2d3527..8438930 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1,6 +1,7 @@ /* structs.h Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2020 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -33,5 +34,7 @@ typedef struct __feh_file_info feh_file_info; typedef struct __winwidget _winwidget; typedef _winwidget *winwidget; typedef struct __fehoptions fehoptions; +typedef struct __fehkey fehkey; +typedef struct __fehkb fehkb; #endif diff --git a/src/strverscmp.c b/src/strverscmp.c new file mode 100644 index 0000000..ddc6b6d --- /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]-'1'<9U && r[dp]-'1'<9U) { + /* If we're looking at non-degenerate digit sequences starting + * with nonzero digits, 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/support.c b/src/support.c deleted file mode 100644 index 4688d6f..0000000 --- a/src/support.c +++ /dev/null @@ -1,610 +0,0 @@ -/* support.c - -Copyright (C) 1999-2003 Tom Gilbert. - -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 of the Software and its documentation and acknowledgment shall be -given in the documentation and software packages that this Software was -used. - -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 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. - -*/ - -#include "feh.h" -#include "filelist.h" -#include "options.h" -#include "support.h" -#include <limits.h> -Window ipc_win = None; -Window my_ipc_win = None; -Atom ipc_atom = None; -static unsigned char timeout = 0; - -/* - * This is a boolean indicating - * That while we seem to see E16 IPC - * it's actually E17 faking it - * -- richlowe 2005-06-22 - */ -static char e17_fake_ipc = 0; - -void feh_wm_set_bg_file(char *file, unsigned char bgmode) -{ - Imlib_Image im; - feh_file *fil; - - fil = feh_file_new(file); - if (fil) { - if (feh_load_image(&im, fil) == 0) - eprintf("Couldn't load image in order to set bg"); - switch (bgmode) { - case BG_MODE_SEAMLESS: - gib_imlib_image_tile(im); - feh_wm_set_bg(NULL, im, 0, 0, 0, 0, 1); - break; - case BG_MODE_TILE: - feh_wm_set_bg(file, im, 0, 0, 0, 0, 1); - break; - case BG_MODE_SCALE: - feh_wm_set_bg(file, im, 0, 1, 0, 0, 1); - break; - case BG_MODE_FILL: - feh_wm_set_bg(file, im, 0, 0, 1, 0, 1); - break; - default: - feh_wm_set_bg(file, im, 1, 0, 0, 0, 1); - break; - } - gib_imlib_free_image_and_decache(im); - feh_file_free(fil); - } -} - -void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled, - int filled, int desktop, int set) -{ - char bgname[20]; - int num = (int) rand(); - char bgfil[4096]; - char sendbuf[4096]; - - D_ENTER(4); - - snprintf(bgname, sizeof(bgname), "FEHBG_%d", num); - - if (!fil) { - snprintf(bgfil, sizeof(bgfil), "%s/.%s.png", getenv("HOME"), bgname); - imlib_context_set_image(im); - imlib_image_set_format("png"); - gib_imlib_save_image(im, bgfil); - D(3, ("bg saved as %s\n", bgfil)); - fil = bgfil; - } - D(3, ("Setting bg %s\n", fil)); - - if (feh_wm_get_wm_is_e() && (enl_ipc_get_win() != None)) { - snprintf(sendbuf, sizeof(sendbuf), "background %s bg.file %s", bgname, fil); - enl_ipc_send(sendbuf); - - if (scaled) { - snprintf(sendbuf, sizeof(sendbuf), "background %s bg.solid 0 0 0", bgname); - enl_ipc_send(sendbuf); - snprintf(sendbuf, sizeof(sendbuf), "background %s bg.tile 0", bgname); - enl_ipc_send(sendbuf); - snprintf(sendbuf, sizeof(sendbuf), "background %s bg.xjust 512", bgname); - enl_ipc_send(sendbuf); - snprintf(sendbuf, sizeof(sendbuf), "background %s bg.yjust 512", bgname); - enl_ipc_send(sendbuf); - snprintf(sendbuf, sizeof(sendbuf), "background %s bg.xperc 1024", bgname); - enl_ipc_send(sendbuf); - snprintf(sendbuf, sizeof(sendbuf), "background %s bg.yperc 1024", bgname); - enl_ipc_send(sendbuf); - } else if (centered) { - snprintf(sendbuf, sizeof(sendbuf), "background %s bg.solid 0 0 0", bgname); - enl_ipc_send(sendbuf); - snprintf(sendbuf, sizeof(sendbuf), "background %s bg.tile 0", bgname); - enl_ipc_send(sendbuf); - snprintf(sendbuf, sizeof(sendbuf), "background %s bg.xjust 512", bgname); - enl_ipc_send(sendbuf); - snprintf(sendbuf, sizeof(sendbuf), "background %s bg.yjust 512", bgname); - enl_ipc_send(sendbuf); - } else { - snprintf(sendbuf, sizeof(sendbuf), "background %s bg.tile 1", bgname); - enl_ipc_send(sendbuf); - } - - if (set) { - snprintf(sendbuf, sizeof(sendbuf), "use_bg %s %d", bgname, desktop); - enl_ipc_send(sendbuf); - } - enl_ipc_sync(); - } else { - Atom prop_root, prop_esetroot, type; - int format; - unsigned long length, after; - unsigned char *data_root, *data_esetroot; - Pixmap pmap_d1, pmap_d2; - /* string for sticking in ~/.fehbg */ - char *fehbg = NULL; - char *home; - char filbuf[PATH_MAX]; - - /* local display to set closedownmode on */ - Display *disp2; - Window root2; - int depth2; - XGCValues gcvalues; - GC gc; - int in, out, w, h; - - D(3, ("Falling back to XSetRootWindowPixmap\n")); - - /* Put the filename in filbuf between ' and escape ' in the filename */ - out = 0; - filbuf[out++] = '\''; - for (in = 0; fil[in] && out < (PATH_MAX - 4); in++) { - if (fil[in] == '\'') { - filbuf[out++] = '\\'; - } - filbuf[out++] = fil[in]; - } - filbuf[out++] = '\''; - filbuf[out++] = 0; - - if (scaled) { - w = scr->width; - h = scr->height; - -/* disable xinerama check for setting background */ -#if 0 -/* #ifdef HAVE_LIBXINERAMA */ - if (opt.xinerama && xinerama_screens) { - w = xinerama_screens[xinerama_screen].width; - h = xinerama_screens[xinerama_screen].height; - } -#endif /* HAVE_LIBXINERAMA */ - - pmap_d1 = XCreatePixmap(disp, root, w, h, depth); - gib_imlib_render_image_on_drawable_at_size(pmap_d1, im, 0, 0, w, h, 1, 0, 1); - fehbg = estrjoin(" ", "feh --bg-scale", filbuf, NULL); - } else if (centered) { - XGCValues gcval; - GC gc; - int x, y; - - D(3, ("centering\n")); - w = scr->width; - h = scr->height; - -/* disable xinerama check for setting background */ -#if 0 -/* #ifdef HAVE_LIBXINERAMA */ - if (opt.xinerama && xinerama_screens) { - w = xinerama_screens[xinerama_screen].width; - h = xinerama_screens[xinerama_screen].height; - } -#endif /* HAVE_LIBXINERAMA */ - - pmap_d1 = XCreatePixmap(disp, root, w, h, depth); - gcval.foreground = BlackPixel(disp, DefaultScreen(disp)); - gc = XCreateGC(disp, root, GCForeground, &gcval); - XFillRectangle(disp, pmap_d1, gc, 0, 0, w, h); - x = (w - gib_imlib_image_get_width(im)) >> 1; - y = (h - gib_imlib_image_get_height(im)) >> 1; - gib_imlib_render_image_on_drawable(pmap_d1, im, x, y, 1, 0, 0); - XFreeGC(disp, gc); - fehbg = estrjoin(" ", "feh --bg-center", filbuf, NULL); - } else if (filled) { - int x = scr->width; - int y = scr->height; - int u = gib_imlib_image_get_width(im); - int v = gib_imlib_image_get_height(im); - int s = 0; - int t = 0; - - if ((u * y) > (x * v)) { - h = y; - w = (y * u) / v; - s = (x - w) / 2; - } else { - h = (x * v) / u; - w = x; - t = (y - h) / 2; - } - pmap_d1 = XCreatePixmap(disp, root, w, h, depth); - gib_imlib_render_image_on_drawable_at_size(pmap_d1, im, s, t, - w, h, 1, 0, 1); - fehbg = estrjoin(" ", "feh --bg-fill", filbuf, NULL); - } else { - w = gib_imlib_image_get_width(im); - h = gib_imlib_image_get_height(im); - pmap_d1 = XCreatePixmap(disp, root, w, h, depth); - gib_imlib_render_image_on_drawable(pmap_d1, im, 0, 0, 1, 0, 0); - fehbg = estrjoin(" ", "feh --bg-tile", filbuf, NULL); - } - - if (fehbg) { - home = getenv("HOME"); - if (home) { - FILE *fp; - char *path; - path = estrjoin("/", home, ".fehbg", NULL); - if ((fp = fopen(path, "w")) == NULL) { - weprintf("Can't open %s for write", path); - } else { - fprintf(fp, "%s\n", fehbg); - fclose(fp); - } - free(path); - } - free(fehbg); - } - - /* create new display, copy pixmap to new display */ - disp2 = XOpenDisplay(NULL); - if (!disp2) - eprintf("Can't reopen X display."); - root2 = RootWindow(disp2, DefaultScreen(disp2)); - depth2 = DefaultDepth(disp2, DefaultScreen(disp2)); - XSync(disp, False); - pmap_d2 = XCreatePixmap(disp2, root2, scr->width, scr->height, depth2); - gcvalues.fill_style = FillTiled; - gcvalues.tile = pmap_d1; - gc = XCreateGC(disp2, pmap_d2, GCFillStyle | GCTile, &gcvalues); - XFillRectangle(disp2, pmap_d2, gc, 0, 0, scr->width, scr->height); - XFreeGC(disp2, gc); - XSync(disp2, False); - XSync(disp, False); - XFreePixmap(disp, pmap_d1); - - prop_root = XInternAtom(disp2, "_XROOTPMAP_ID", True); - prop_esetroot = XInternAtom(disp2, "ESETROOT_PMAP_ID", True); - - if (prop_root != None && prop_esetroot != None) { - XGetWindowProperty(disp2, root2, prop_root, 0L, 1L, - False, AnyPropertyType, &type, &format, &length, &after, &data_root); - if (type == XA_PIXMAP) { - XGetWindowProperty(disp2, root2, - prop_esetroot, 0L, 1L, - False, AnyPropertyType, - &type, &format, &length, &after, &data_esetroot); - if (data_root && data_esetroot) { - if (type == XA_PIXMAP && *((Pixmap *) data_root) == *((Pixmap *) data_esetroot)) { - XKillClient(disp2, *((Pixmap *) - data_root)); - } - } - } - } - /* This will locate the property, creating it if it doesn't exist */ - prop_root = XInternAtom(disp2, "_XROOTPMAP_ID", False); - prop_esetroot = XInternAtom(disp2, "ESETROOT_PMAP_ID", False); - - if (prop_root == None || prop_esetroot == None) - weprintf("creation of pixmap property failed."); - - XChangeProperty(disp2, root2, prop_root, XA_PIXMAP, 32, PropModeReplace, (unsigned char *) &pmap_d2, 1); - XChangeProperty(disp2, root2, prop_esetroot, XA_PIXMAP, 32, - PropModeReplace, (unsigned char *) &pmap_d2, 1); - - XSetWindowBackgroundPixmap(disp2, root2, pmap_d2); - XClearWindow(disp2, root2); - XFlush(disp2); - XSetCloseDownMode(disp2, RetainPermanent); - XCloseDisplay(disp2); - } - D_RETURN_(4); -} - -signed char feh_wm_get_wm_is_e(void) -{ - static signed char e = -1; - - D_ENTER(4); - - /* check if E is actually running */ - if (e == -1) { - /* XXX: This only covers E17 prior to 6/22/05 */ - if ((XInternAtom(disp, "ENLIGHTENMENT_COMMS", True) != None) - && (XInternAtom(disp, "ENLIGHTENMENT_VERSION", True) != None)) { - D(3, ("Enlightenment detected.\n")); - e = 1; - } else { - D(3, ("Enlightenment not detected.\n")); - e = 0; - } - } - D_RETURN(4, e); -} - -int feh_wm_get_num_desks(void) -{ - char *buf, *ptr; - int desks; - - D_ENTER(4); - - if (!feh_wm_get_wm_is_e()) - D_RETURN(4, -1); - - buf = enl_send_and_wait("num_desks ?"); - if (buf == IPC_FAKE) /* Fake E17 IPC */ - D_RETURN(4, -1); - D(3, ("Got from E IPC: %s\n", buf)); - ptr = buf; - while (ptr && !isdigit(*ptr)) - ptr++; - desks = atoi(ptr); - -D_RETURN(4, desks)} - -Window enl_ipc_get_win(void) -{ - - unsigned char *str = NULL; - Atom prop, prop2, ever; - unsigned long num, after; - int format; - Window dummy_win; - int dummy_int; - unsigned int dummy_uint; - - D_ENTER(4); - - D(3, ("Searching for IPC window.\n")); - - /* - * Shortcircuit this entire func - * if we already know it's an e17 fake - */ - if (e17_fake_ipc) - D_RETURN(4, ipc_win) - - prop = XInternAtom(disp, "ENLIGHTENMENT_COMMS", True); - if (prop == None) { - D(3, ("Enlightenment is not running.\n")); - D_RETURN(4, None); - } else { - /* XXX: This will only work with E17 prior to 6/22/2005 */ - ever = XInternAtom(disp, "ENLIGHTENMENT_VERSION", True); - if (ever == None) { - /* This is an E without ENLIGHTENMENT_VERSION */ - D(3, ("E16 IPC Protocol not supported")); - D_RETURN(4, None); - } - } - XGetWindowProperty(disp, root, prop, 0, 14, False, AnyPropertyType, &prop2, &format, &num, &after, &str); - if (str) { - sscanf((char *) str, "%*s %x", (unsigned int *) &ipc_win); - XFree(str); - } - if (ipc_win != None) { - if (!XGetGeometry - (disp, ipc_win, &dummy_win, &dummy_int, &dummy_int, - &dummy_uint, &dummy_uint, &dummy_uint, &dummy_uint)) { - D(3, (" -> IPC Window property is valid, but the window doesn't exist.\n")); - ipc_win = None; - } - str = NULL; - if (ipc_win != None) { - XGetWindowProperty(disp, ipc_win, prop, 0, 14, - False, AnyPropertyType, &prop2, &format, &num, &after, &str); - if (str) { - XFree(str); - } else { - D(3, (" -> IPC Window lacks the proper atom. I can't talk to fake IPC windows....\n")); - ipc_win = None; - } - } - } - if (ipc_win != None) { - - XGetWindowProperty(disp, ipc_win, ever, 0, 14, False, - AnyPropertyType, &prop2, &format, &num, &after, &str); - if (str) { - /* - * This is E17's way of telling us it's only pretending - * as a workaround for a bug related to the way java handles - * Window Managers. - * (Only valid after date of this comment) - * -- richlowe 2005-06-22 - */ - XFree(str); - D(3, (" -> Found a fake E17 IPC window, ignoring")); - ipc_win = None; - e17_fake_ipc = 1; - D_RETURN(4, ipc_win); - } - - D(3, - (" -> IPC Window found and verified as 0x%08x. Registering feh as an IPC client.\n", (int) ipc_win)); - XSelectInput(disp, ipc_win, StructureNotifyMask | SubstructureNotifyMask); - enl_ipc_send("set clientname " PACKAGE); - enl_ipc_send("set version " VERSION); - enl_ipc_send("set email tom@linuxbrit.co.uk"); - enl_ipc_send("set web http://www.linuxbrit.co.uk"); - enl_ipc_send("set info Feh - be pr0n or be dead"); - } - if (my_ipc_win == None) { - my_ipc_win = XCreateSimpleWindow(disp, root, -2, -2, 1, 1, 0, 0, 0); - } - D_RETURN(4, ipc_win); -} - -void enl_ipc_send(char *str) -{ - - static char *last_msg = NULL; - char buff[21]; - register unsigned short i; - register unsigned char j; - unsigned short len; - XEvent ev; - - D_ENTER(4); - if (str == NULL) { - if (last_msg == NULL) - eprintf("eeek"); - str = last_msg; - D(4, ("Resending last message \"%s\" to Enlightenment.\n", str)); - } else { - if (last_msg != NULL) { - free(last_msg); - } - last_msg = estrdup(str); - D(4, ("Sending \"%s\" to Enlightenment.\n", str)); - } - if (ipc_win == None) { - if ((ipc_win = enl_ipc_get_win()) == None) { - D(3, ("Hrm. Enlightenment doesn't seem to be running. No IPC window, no IPC.\n")); - D_RETURN_(4); - } - } - len = strlen(str); - ipc_atom = XInternAtom(disp, "ENL_MSG", False); - if (ipc_atom == None) { - D(3, ("IPC error: Unable to find/create ENL_MSG atom.\n")); - D_RETURN_(4); - } - for (; XCheckTypedWindowEvent(disp, my_ipc_win, ClientMessage, &ev);); /* Discard any out-of-sync messages */ - ev.xclient.type = ClientMessage; - ev.xclient.serial = 0; - ev.xclient.send_event = True; - ev.xclient.window = ipc_win; - ev.xclient.message_type = ipc_atom; - ev.xclient.format = 8; - - for (i = 0; i < len + 1; i += 12) { - sprintf(buff, "%8x", (int) my_ipc_win); - for (j = 0; j < 12; j++) { - buff[8 + j] = str[i + j]; - if (!str[i + j]) { - break; - } - } - buff[20] = 0; - for (j = 0; j < 20; j++) { - ev.xclient.data.b[j] = buff[j]; - } - XSendEvent(disp, ipc_win, False, 0, (XEvent *) & ev); - } - D_RETURN_(4); -} - -static sighandler_t *enl_ipc_timeout(int sig) -{ - timeout = 1; - D_RETURN(4, (sighandler_t *) sig); - sig = 0; -} - -char *enl_wait_for_reply(void) -{ - - XEvent ev; - static char msg_buffer[20]; - register unsigned char i; - - D_ENTER(4); - - alarm(2); - for (; !XCheckTypedWindowEvent(disp, my_ipc_win, ClientMessage, &ev) - && !timeout;); - alarm(0); - if (ev.xany.type != ClientMessage) { - D_RETURN(4, IPC_TIMEOUT); - } - for (i = 0; i < 20; i++) { - msg_buffer[i] = ev.xclient.data.b[i]; - } - D_RETURN(4, msg_buffer + 8); -} - -char *enl_ipc_get(const char *msg_data) -{ - - static char *message = NULL; - static unsigned short len = 0; - char buff[13], *ret_msg = NULL; - register unsigned char i; - unsigned char blen; - - D_ENTER(4); - - if (msg_data == IPC_TIMEOUT) { - D_RETURN(4, IPC_TIMEOUT); - } - for (i = 0; i < 12; i++) { - buff[i] = msg_data[i]; - } - buff[12] = 0; - blen = strlen(buff); - if (message != NULL) { - len += blen; - message = (char *) erealloc(message, len + 1); - strcat(message, buff); - } else { - len = blen; - message = (char *) emalloc(len + 1); - strcpy(message, buff); - } - if (blen < 12) { - ret_msg = message; - message = NULL; - D(4, ("Received complete reply: \"%s\"\n", ret_msg)); - } - D_RETURN(4, ret_msg); -} - -char *enl_send_and_wait(char *msg) -{ - char *reply = IPC_TIMEOUT; - sighandler_t old_alrm; - - D_ENTER(4); - - /* - * Shortcut this func and return IPC_FAKE - * If the IPC Window is the E17 fake - */ - if (e17_fake_ipc) - return IPC_FAKE; - - if (ipc_win == None) { - /* The IPC window is missing. Wait for it to return or feh to be killed. */ - /* Only called once in the E17 case */ - for (; enl_ipc_get_win() == None;) { - if (e17_fake_ipc) - return IPC_FAKE; - else - sleep(1); - } - } - old_alrm = (sighandler_t) signal(SIGALRM, (sighandler_t) enl_ipc_timeout); - for (; reply == IPC_TIMEOUT;) { - timeout = 0; - enl_ipc_send(msg); - for (; !(reply = enl_ipc_get(enl_wait_for_reply()));); - if (reply == IPC_TIMEOUT) { - /* We timed out. The IPC window must be AWOL. Reset and resend message. */ - D(3, ("IPC timed out. IPC window has gone. Clearing ipc_win.\n")); - XSelectInput(disp, ipc_win, None); - ipc_win = None; - } - } - signal(SIGALRM, old_alrm); - D_RETURN(4, reply); -} diff --git a/src/thumbnail.c b/src/thumbnail.c index 425fa8c..70af5e0 100644 --- a/src/thumbnail.c +++ b/src/thumbnail.c @@ -1,6 +1,7 @@ /* thumbnail.c Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2024 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -30,10 +31,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "thumbnail.h" #include "md5.h" #include "feh_png.h" +#include "index.h" +#include "signals.h" -static char *create_index_dimension_string(int w, int h); -static char *create_index_size_string(char *file); -static char *create_index_title_string(int num, int w, int h); static gib_list *thumbnails = NULL; static thumbmode_data td; @@ -55,22 +55,24 @@ void init_thumbnail_mode(void) int max_column_w = 0; */ + Imlib_Load_Error err; Imlib_Image im_temp; int ww = 0, hh = 0, www, hhh, xxx, yyy; + int orig_w, orig_h; int x = 0, y = 0; winwidget winwid = NULL; Imlib_Image im_thumb = NULL; unsigned char trans_bg = 0; int title_area_h = 0; int tw = 0, th = 0; - int fw_name, fw_size, fw_dim, fh; + int fw, fh; int thumbnailcount = 0; feh_file *file = NULL; gib_list *l, *last = NULL; - int lines; + int lineno; int index_image_width, index_image_height; - int x_offset_name = 0, x_offset_dim = 0, x_offset_size = 0; - char *s; + unsigned int thumb_counter = 0; + gib_list *line, *lines; /* initialize thumbnail mode data */ td.im_main = NULL; @@ -89,16 +91,23 @@ void init_thumbnail_mode(void) td.vertical = 0; td.max_column_w = 0; - D_ENTER(3); - + if (!opt.thumb_title) + opt.thumb_title = "%n"; mode = "thumbnail"; - td.font_main = gib_imlib_load_font(opt.font); + if (opt.font) + td.font_main = gib_imlib_load_font(opt.font); + + if (!td.font_main) + td.font_main = gib_imlib_load_font(DEFAULT_FONT); if (opt.title_font) { int fh, fw; td.font_title = gib_imlib_load_font(opt.title_font); + if (!td.font_title) + td.font_title = gib_imlib_load_font(DEFAULT_FONT_TITLE); + gib_imlib_get_text_size(td.font_title, "W", NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); title_area_h = fh + 4; @@ -111,9 +120,8 @@ void init_thumbnail_mode(void) /* Work out how tall the font is */ gib_imlib_get_text_size(td.font_main, "W", NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT); - /* For now, allow room for the right number of lines with small gaps */ - td.text_area_h = ((th + 2) * (opt.index_show_name + opt.index_show_size + - opt.index_show_dim)) + 5; + get_index_string_dim(NULL, td.font_main, &fw, &fh); + td.text_area_h = fh + 5; /* This includes the text area for index data */ td.thumb_tot_h = opt.thumb_h + td.text_area_h; @@ -124,7 +132,7 @@ void init_thumbnail_mode(void) trans_bg = 1; else { - D(3, ("Time to apply a background to blend onto\n")); + D(("Time to apply a background to blend onto\n")); if (feh_load_image_char(&td.im_bg, opt.bg_file) != 0) { td.bg_w = gib_imlib_image_get_width(td.im_bg); td.bg_h = gib_imlib_image_get_height(td.im_bg); @@ -137,11 +145,21 @@ void init_thumbnail_mode(void) index_image_width = td.w; index_image_height = td.h + title_area_h; + D(("imlib_create_image(%d, %d)\n", index_image_width, index_image_height)); td.im_main = imlib_create_image(index_image_width, index_image_height); - gib_imlib_image_set_has_alpha(td.im_main, 1); - if (!td.im_main) - eprintf("Imlib error creating index image, are you low on RAM?"); + if (!td.im_main) { + if (index_image_height >= 32768 || index_image_width >= 32768) { + eprintf("Failed to create %dx%d pixels (%d MB) index image.\n" + "This is probably due to Imlib2 issues when dealing with images larger than 32k x 32k pixels.", + index_image_width, index_image_height, index_image_width * index_image_height * 4 / (1024*1024)); + } else { + eprintf("Failed to create %dx%d pixels (%d MB) index image. Do you have enough RAM?", + index_image_width, index_image_height, index_image_width * index_image_height * 4 / (1024*1024)); + } + } + + gib_imlib_image_set_has_alpha(td.im_main, 1); if (td.im_bg) gib_imlib_blend_image_onto_image(td.im_main, td.im_bg, @@ -158,39 +176,63 @@ void init_thumbnail_mode(void) td.h + title_area_h, 0, 0, 0, 255); } - /* Create title now */ - - if (!opt.title) - s = estrdup(PACKAGE " [thumbnail mode]"); - else - s = estrdup(feh_printf(opt.title, NULL)); if (opt.display) { - winwid = winwidget_create_from_image(td.im_main, s, WIN_TYPE_THUMBNAIL); + winwid = winwidget_create_from_image(td.im_main, WIN_TYPE_THUMBNAIL); + winwidget_rename(winwid, PACKAGE " [thumbnail mode]"); winwidget_show(winwid); } - /* make sure we have an ~/.thumbnails/normal directory for storing - permanent thumbnails */ - td.cache_thumbnails = feh_thumbnail_setup_thumbnail_dir(); td.cache_thumbnails = opt.cache_thumbnails; + if (td.cache_thumbnails) { + if (opt.thumb_w > opt.thumb_h) + td.cache_dim = opt.thumb_w; + else + td.cache_dim = opt.thumb_h; + + if (td.cache_dim > 1024) { + /* Not specified by XDG thumbnail standard */ + td.cache_thumbnails = 0; + } else if (td.cache_dim > 512) { + td.cache_dim = 1024; + td.cache_dir = estrdup("xx-large"); + } else if (td.cache_dim > 256) { + td.cache_dim = 512; + td.cache_dir = estrdup("x-large"); + } else if (td.cache_dim > 128) { + td.cache_dim = 256; + td.cache_dir = estrdup("large"); + } else { + td.cache_dim = 128; + td.cache_dir = estrdup("normal"); + } + feh_thumbnail_setup_thumbnail_dir(); + } + for (l = filelist; l; l = l->next) { file = FEH_FILE(l->data); if (last) { filelist = feh_file_remove_from_list(filelist, last); last = NULL; } - D(4, ("About to load image %s\n", file->filename)); + D(("About to load image %s\n", file->filename)); /* if (feh_load_image(&im_temp, file) != 0) */ - if (feh_thumbnail_get_thumbnail(&im_temp, file) != 0) { + if (feh_thumbnail_get_thumbnail(&im_temp, file, &orig_w, &orig_h) + != 0) { if (opt.verbose) feh_display_status('.'); - D(4, ("Successfully loaded %s\n", file->filename)); + D(("Successfully loaded %s\n", file->filename)); www = opt.thumb_w; hhh = opt.thumb_h; ww = gib_imlib_image_get_width(im_temp); hh = gib_imlib_image_get_height(im_temp); + + if (!orig_w) { + orig_w = ww; + orig_h = hh; + } + thumbnailcount++; if (gib_imlib_image_has_alpha(im_temp)) imlib_context_set_blend(1); @@ -222,7 +264,7 @@ void init_thumbnail_mode(void) if (opt.alpha) { DATA8 atab[256]; - D(3, ("Applying alpha options\n")); + D(("Applying alpha options\n")); gib_imlib_image_set_has_alpha(im_thumb, 1); memset(atab, opt.alpha_level, sizeof(atab)); gib_imlib_apply_color_modifier_to_rectangle @@ -231,34 +273,18 @@ void init_thumbnail_mode(void) td.text_area_w = opt.thumb_w; /* Now draw on the info text */ - if (opt.index_show_name) { - gib_imlib_get_text_size(td.font_main, file->name, NULL, - &fw_name, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw_name > td.text_area_w) - td.text_area_w = fw_name; - } - if (opt.index_show_dim) { - gib_imlib_get_text_size(td.font_main, - create_index_dimension_string(ww, hh), - NULL, &fw_dim, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw_dim > td.text_area_w) - td.text_area_w = fw_dim; - } - if (opt.index_show_size) { - gib_imlib_get_text_size(td.font_main, - create_index_size_string(file->filename), - NULL, &fw_size, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw_size > td.text_area_w) - td.text_area_w = fw_size; + if (opt.index_info) { + get_index_string_dim(file, td.font_main, &fw, &fh); + if (fw > td.text_area_w) + td.text_area_w = fw; + if (fh > td.text_area_h) { + td.text_area_h = fh + 5; + td.thumb_tot_h = opt.thumb_h + td.text_area_h; + } } if (td.text_area_w > opt.thumb_w) td.text_area_w += 5; - /* offsets for centering text */ - x_offset_name = (td.text_area_w - fw_name) / 2; - x_offset_dim = (td.text_area_w - fw_dim) / 2; - x_offset_size = (td.text_area_w - fw_size) / 2; - if (td.vertical) { if (td.text_area_w > td.max_column_w) td.max_column_w = td.text_area_w; @@ -278,14 +304,12 @@ void init_thumbnail_mode(void) break; } - if (opt.aspect) { - xxx = x + ((opt.thumb_w - www) / 2); - yyy = y + ((opt.thumb_h - hhh) / 2); - } else { - /* Ignore the aspect ratio and squash the image in */ - xxx = x; - yyy = y; - } + /* center image relative to the text below it (if any) */ + xxx = x + ((td.text_area_w - www) / 2); + yyy = y; + + if (opt.aspect) + yyy += (opt.thumb_h - hhh) / 2; /* Draw now */ gib_imlib_blend_image_onto_image(td.im_main, @@ -301,28 +325,23 @@ void init_thumbnail_mode(void) gib_imlib_free_image_and_decache(im_thumb); - lines = 0; - if (opt.index_show_name) - gib_imlib_text_draw(td.im_main, - td.font_main, NULL, - x + x_offset_name, - y + opt.thumb_h + (lines++ * (th + 2)) + 2, - file->name, IMLIB_TEXT_TO_RIGHT, - 255, 255, 255, 255); - if (opt.index_show_dim) - gib_imlib_text_draw(td.im_main, - td.font_main, NULL, - x + x_offset_dim, - y + opt.thumb_h + (lines++ * (th + 2)) + 2, - create_index_dimension_string(ww, hh), - IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); - if (opt.index_show_size) - gib_imlib_text_draw(td.im_main, - td.font_main, NULL, - x + x_offset_size, - y + opt.thumb_h + (lines++ * (th + 2)) + 2, - create_index_size_string(file->filename), - IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); + lineno = 0; + if (opt.index_info) { + line = lines = feh_wrap_string(create_index_string(file), + opt.thumb_w * 3, td.font_main, NULL); + + while (line) { + gib_imlib_get_text_size(td.font_main, (char *) line -> data, + NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + gib_imlib_text_draw(td.im_main, td.font_main, NULL, + x + ((td.text_area_w - fw) >> 1), + y + opt.thumb_h + (lineno++ * (th + 2)) + 2, + (char *) line->data, + IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); + line = line->next; + } + gib_list_free_and_data(lines); + } if (td.vertical) y += td.thumb_tot_h; @@ -334,13 +353,21 @@ void init_thumbnail_mode(void) last = l; } if (opt.display) { - winwidget_render_image(winwid, 0, 0); + /* thumb_counter is unsigned, so no need to catch overflows */ + if (++thumb_counter == opt.thumb_redraw) { + winwidget_render_image(winwid, 0, 1); + thumb_counter = 0; + } if (!feh_main_iteration(0)) exit(0); } } + + if (thumb_counter != 0) + winwidget_render_image(winwid, 0, 1); + if (opt.verbose) - fprintf(stdout, "\n"); + putc('\n', stderr); if (opt.title_font) { int fw, fh, fx, fy; @@ -353,6 +380,9 @@ void init_thumbnail_mode(void) fy = index_image_height - fh - 2; gib_imlib_text_draw(td.im_main, td.font_title, NULL, fx, fy, s, IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); + + if (opt.display) + winwidget_render_image(winwid, 0, 1); } if (opt.output && opt.output_file) { @@ -360,16 +390,21 @@ void init_thumbnail_mode(void) if (opt.output_dir) snprintf(output_buf, 1024, "%s/%s", opt.output_dir, opt.output_file); - else - strncpy(output_buf, opt.output_file, 1024); - gib_imlib_save_image(td.im_main, output_buf); - if (opt.verbose) { + else { + strncpy(output_buf, opt.output_file, 1023); + output_buf[1023] = '\0'; + } + gib_imlib_save_image_with_error_return(td.im_main, output_buf, &err); + if (err) { + feh_print_load_error(output_buf, td.im_main, err, LOAD_ERROR_IMLIB); + } + else if (opt.verbose) { int tw, th; tw = gib_imlib_image_get_width(td.im_main); th = gib_imlib_image_get_height(td.im_main); - fprintf(stdout, PACKAGE " - File saved as %s\n", output_buf); - fprintf(stdout, + fprintf(stderr, PACKAGE " - File saved as %s\n", output_buf); + fprintf(stderr, " - Image is %dx%d pixels and contains %d thumbnails\n", tw, th, thumbnailcount); } @@ -377,54 +412,25 @@ void init_thumbnail_mode(void) if (!opt.display) gib_imlib_free_image_and_decache(td.im_main); - - free(s); - D_RETURN_(3); -} - -static char *create_index_size_string(char *file) -{ - static char str[50]; - int size = 0; - double kbs = 0.0; - struct stat st; - - D_ENTER(4); - if (stat(file, &st)) - kbs = 0.0; - else { - size = st.st_size; - kbs = (double) size / 1000; + 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; + } + } } - snprintf(str, sizeof(str), "%.2fKb", kbs); - D_RETURN(4, str); -} - -static char *create_index_dimension_string(int w, int h) -{ - static char str[50]; - D_ENTER(4); - snprintf(str, sizeof(str), "%dx%d", w, h); - D_RETURN(4, str); -} - -static char *create_index_title_string(int num, int w, int h) -{ - static char str[50]; - - D_ENTER(4); - snprintf(str, sizeof(str), PACKAGE " index - %d thumbnails, %d by %d pixels", num, w, h); - D_RETURN(4, str); + return; } feh_thumbnail *feh_thumbnail_new(feh_file * file, int x, int y, int w, int h) { feh_thumbnail *thumb; - D_ENTER(4); - thumb = (feh_thumbnail *) emalloc(sizeof(feh_thumbnail)); thumb->x = x; thumb->y = y; @@ -433,7 +439,7 @@ feh_thumbnail *feh_thumbnail_new(feh_file * file, int x, int y, int w, int h) thumb->file = file; thumb->exists = 1; - D_RETURN(4, thumb); + return(thumb); } feh_file *feh_thumbnail_get_file_from_coords(int x, int y) @@ -441,18 +447,16 @@ feh_file *feh_thumbnail_get_file_from_coords(int x, int y) gib_list *l; feh_thumbnail *thumb; - D_ENTER(4); - for (l = thumbnails; l; l = l->next) { thumb = FEH_THUMB(l->data); if (XY_IN_RECT(x, y, thumb->x, thumb->y, thumb->w, thumb->h)) { if (thumb->exists) { - D_RETURN(4, thumb->file); + return(thumb->file); } } } - D(4, ("No matching %d %d\n", x, y)); - D_RETURN(4, NULL); + D(("No matching %d %d\n", x, y)); + return(NULL); } feh_thumbnail *feh_thumbnail_get_thumbnail_from_coords(int x, int y) @@ -460,18 +464,16 @@ feh_thumbnail *feh_thumbnail_get_thumbnail_from_coords(int x, int y) gib_list *l; feh_thumbnail *thumb; - D_ENTER(4); - for (l = thumbnails; l; l = l->next) { thumb = FEH_THUMB(l->data); if (XY_IN_RECT(x, y, thumb->x, thumb->y, thumb->w, thumb->h)) { if (thumb->exists) { - D_RETURN(4, thumb); + return(thumb); } } } - D(4, ("No matching %d %d\n", x, y)); - D_RETURN(4, NULL); + D(("No matching %d %d\n", x, y)); + return(NULL); } feh_thumbnail *feh_thumbnail_get_from_file(feh_file * file) @@ -479,18 +481,16 @@ feh_thumbnail *feh_thumbnail_get_from_file(feh_file * file) gib_list *l; feh_thumbnail *thumb; - D_ENTER(4); - for (l = thumbnails; l; l = l->next) { thumb = FEH_THUMB(l->data); if (thumb->file == file) { if (thumb->exists) { - D_RETURN(4, thumb); + return(thumb); } } } - D(4, ("No match\n")); - D_RETURN(4, NULL); + D(("No match\n")); + return(NULL); } void feh_thumbnail_mark_removed(feh_file * file, int deleted) @@ -498,228 +498,97 @@ void feh_thumbnail_mark_removed(feh_file * file, int deleted) feh_thumbnail *thumb; winwidget w; - D_ENTER(4); - thumb = feh_thumbnail_get_from_file(file); if (thumb) { w = winwidget_get_first_window_of_type(WIN_TYPE_THUMBNAIL); if (w) { - td.font_main = imlib_load_font(DEFAULT_FONT_TITLE); + int tw, th; if (deleted) gib_imlib_image_fill_rectangle(w->im, thumb->x, thumb->y, thumb->w, thumb->h, 255, 0, 0, 150); else gib_imlib_image_fill_rectangle(w->im, thumb->x, thumb->y, thumb->w, thumb->h, 0, 0, 255, 150); - if (td.font_main) { - int tw, th; - - gib_imlib_get_text_size(td.font_main, "X", NULL, &tw, &th, - IMLIB_TEXT_TO_RIGHT); - gib_imlib_text_draw(w->im, td.font_main, NULL, - thumb->x + ((thumb->w - tw) / 2), - thumb->y + ((thumb->h - th) / 2), "X", - IMLIB_TEXT_TO_RIGHT, 205, 205, 50, 255); - } else - weprintf(DEFAULT_FONT_TITLE); + + gib_imlib_get_text_size(td.font_main, "X", NULL, &tw, &th, + IMLIB_TEXT_TO_RIGHT); + gib_imlib_text_draw(w->im, td.font_main, NULL, + thumb->x + ((thumb->w - tw) / 2), + thumb->y + ((thumb->h - th) / 2), "X", + IMLIB_TEXT_TO_RIGHT, 205, 205, 50, 255); winwidget_render_image(w, 0, 1); } thumb->exists = 0; } - D_RETURN_(4); + return; } void feh_thumbnail_calculate_geometry(void) { - gib_list *l; - feh_file *file; - - int x = 0, y = 0; - int fw, fh; - if (!opt.limit_w && !opt.limit_h) { if (td.im_bg) { - if (opt.verbose) - fprintf(stdout, - PACKAGE - " - No size restriction specified for index.\n" - " You did specify a background however, so the\n" - " index size has defaulted to the size of the image\n"); opt.limit_w = td.bg_w; opt.limit_h = td.bg_h; - } else { - if (opt.verbose) - fprintf(stdout, - PACKAGE - " - No size restriction specified for index.\n" - " Using defaults (width limited to 640)\n"); - opt.limit_w = 640; - } + } else + opt.limit_w = 800; } /* Here we need to whiz through the files, and look at the filenames and info in the selected font, work out how much space we need, and calculate the size of the image we will require */ - if (opt.limit_w && opt.limit_h) { - int rec_h = 0; - + if (opt.limit_w) { td.w = opt.limit_w; - td.h = opt.limit_h; - - /* Work out if this is big enough, and give a warning if not */ - - /* Pretend we are limiting width by that specified, loop through, and - see it we fit in the height specified. If not, continue the loop, - and recommend the final value instead. Carry on and make the index - anyway. */ - for (l = filelist; l; l = l->next) { - file = FEH_FILE(l->data); - td.text_area_w = opt.thumb_w; - if (opt.index_show_name) { - gib_imlib_get_text_size(td.font_main, file->name, NULL, - &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > td.text_area_w) - td.text_area_w = fw; - } - if (opt.index_show_dim) { - gib_imlib_get_text_size(td.font_main, - create_index_dimension_string(1000, 1000), - NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > td.text_area_w) - td.text_area_w = fw; - } - if (opt.index_show_size) { - gib_imlib_get_text_size(td.font_main, - create_index_size_string(file->filename), - NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > td.text_area_w) - td.text_area_w = fw; - } - if (td.text_area_w > opt.thumb_w) - td.text_area_w += 5; - - if ((x > td.w - td.text_area_w)) { - x = 0; - y += td.thumb_tot_h; - } - - x += td.text_area_w; - } - rec_h = y + td.thumb_tot_h; - - if (td.h < rec_h) { - weprintf - ("The image size you specified (%d by %d) is not large\n" - "enough to hold all the thumnails you specified (%d). To fit all\n" - "the thumnails, either decrease their size, choose a smaller font,\n" - "or use a larger image (may I recommend %d by %d?)", - opt.limit_w, opt.limit_h, filelist_len, opt.limit_w, rec_h); + index_calculate_height(td.font_main, td.w, &td.h, &td.thumb_tot_h); + + if (opt.limit_h) { + if (td.h> opt.limit_h) + weprintf( + "The image size you specified (%dx%d) is not large\n" + "enough to hold all %d thumbnails. To fit all\n" + "the thumnails, either decrease their size, choose a smaller font,\n" + "or use a larger image (like %dx%d)", + opt.limit_w, opt.limit_h, filelist_len, opt.limit_w, td.h); + td.h = opt.limit_h; } } else if (opt.limit_h) { td.vertical = 1; td.h = opt.limit_h; - /* calc w */ - for (l = filelist; l; l = l->next) { - file = FEH_FILE(l->data); - td.text_area_w = opt.thumb_w; - /* Calc width of text */ - if (opt.index_show_name) { - gib_imlib_get_text_size(td.font_main, file->name, NULL, - &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > td.text_area_w) - td.text_area_w = fw; - } - if (opt.index_show_dim) { - gib_imlib_get_text_size(td.font_main, - create_index_dimension_string(1000, 1000), - NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > td.text_area_w) - td.text_area_w = fw; - } - if (opt.index_show_size) { - gib_imlib_get_text_size(td.font_main, - create_index_size_string(file->filename), - NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > td.text_area_w) - td.text_area_w = fw; - } - if (td.text_area_w > opt.thumb_w) - td.text_area_w += 5; - - if (td.text_area_w > td.max_column_w) - td.max_column_w = td.text_area_w; - - if ((y > td.h - td.thumb_tot_h)) { - y = 0; - x += td.max_column_w; - td.max_column_w = 0; - } - - y += td.thumb_tot_h; - } - td.w = x + td.text_area_w; - td.max_column_w = 0; - } else if (opt.limit_w) { - td.w = opt.limit_w; - /* calc h */ - - for (l = filelist; l; l = l->next) { - file = FEH_FILE(l->data); - td.text_area_w = opt.thumb_w; - if (opt.index_show_name) { - gib_imlib_get_text_size(td.font_main, file->name, NULL, - &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > td.text_area_w) - td.text_area_w = fw; - } - if (opt.index_show_dim) { - gib_imlib_get_text_size(td.font_main, - create_index_dimension_string(1000, 1000), - NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > td.text_area_w) - td.text_area_w = fw; - } - if (opt.index_show_size) { - gib_imlib_get_text_size(td.font_main, - create_index_size_string(file->filename), - NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); - if (fw > td.text_area_w) - td.text_area_w = fw; - } - - if (td.text_area_w > opt.thumb_w) - td.text_area_w += 5; - - if ((x > td.w - td.text_area_w)) { - x = 0; - y += td.thumb_tot_h; - } - x += td.text_area_w; - } - td.h = y + td.thumb_tot_h; + index_calculate_width(td.font_main, &td.w, td.h, &td.thumb_tot_h); } } -int feh_thumbnail_get_thumbnail(Imlib_Image * image, feh_file * file) +int feh_thumbnail_get_thumbnail(Imlib_Image * image, feh_file * file, + int * orig_w, int * orig_h) { int status = 0; char *thumb_file = NULL, *uri = NULL; + *orig_w = 0; + *orig_h = 0; + if (!file || !file->filename) return (0); if (td.cache_thumbnails) { uri = feh_thumbnail_get_name_uri(file->filename); thumb_file = feh_thumbnail_get_name(uri); - status = feh_thumbnail_get_generated(image, file, thumb_file, uri); + + if (thumb_file == NULL) { + free(uri); + return feh_load_image(image, file); + } + + status = feh_thumbnail_get_generated(image, file, thumb_file, + orig_w, orig_h); if (!status) - status = feh_thumbnail_generate(image, file, thumb_file, uri); + status = feh_thumbnail_generate(image, file, thumb_file, uri, + orig_w, orig_h); - printf("uri is %s, thumb_file is %s\n", uri, thumb_file); + D(("uri is %s, thumb_file is %s\n", uri, thumb_file)); free(uri); free(thumb_file); } else @@ -728,21 +597,39 @@ int feh_thumbnail_get_thumbnail(Imlib_Image * image, feh_file * file) return status; } +static char *feh_thumbnail_get_prefix(void) +{ + char *dir = NULL, *home, *xdg_cache_home; + + // TODO: perhaps make sure that either of those paths aren't /-terminated + + xdg_cache_home = getenv("XDG_CACHE_HOME"); + if (xdg_cache_home && xdg_cache_home[0] == '/') { + dir = estrjoin("/", xdg_cache_home, "thumbnails", td.cache_dir, NULL); + } else { + home = getenv("HOME"); + if (home && home[0] == '/') { + dir = estrjoin("/", home, ".cache/thumbnails", td.cache_dir, NULL); + } + } + + return dir; +} + char *feh_thumbnail_get_name(char *uri) { - char *home = NULL, *thumb_file = NULL, *md5_name = NULL; + char *prefix, *thumb_file = NULL, *md5_name; /* FIXME: make sure original file isn't under ~/.thumbnails */ - md5_name = feh_thumbnail_get_name_md5(uri); - - home = getenv("HOME"); - if (home) { - thumb_file = estrjoin("/", home, ".thumbnails/normal", md5_name, NULL); + prefix = feh_thumbnail_get_prefix(); + if (prefix) { + md5_name = feh_thumbnail_get_name_md5(uri); + thumb_file = estrjoin("/", prefix, md5_name, NULL); + free(md5_name); + free(prefix); } - free(md5_name); - return thumb_file; } @@ -750,15 +637,16 @@ char *feh_thumbnail_get_name_uri(char *name) { char *cwd, *uri = NULL; - /* FIXME: what happends with http, https, and ftp? MTime etc */ - if ((strncmp(name, "http://", 7) != 0) && - (strncmp(name, "https://", 8) != 0) && (strncmp(name, "ftp://", 6) != 0) - && (strncmp(name, "file://", 7) != 0)) { + /* FIXME: what happens with http, https, and ftp? MTime etc */ + if (!path_is_url(name)) { - /* make sure it's an absoulte path */ + /* make sure it's an absolute path */ /* FIXME: add support for ~, need to investigate if it's expanded - somewhere else before adding (unecessary) code */ + somewhere else before adding (unnecessary) code */ if (name[0] != '/') { + /* work around /some/path/./image.ext */ + if ((strncmp(name, "./", 2)) == 0) + name += 2; cwd = getcwd(NULL, 0); uri = estrjoin("/", "file:/", cwd, name, NULL); free(cwd); @@ -784,7 +672,7 @@ char *feh_thumbnail_get_name_md5(char *uri) md5_finish(&pms, digest); /* print the md5 as hex to a string */ - md5_name = emalloc(32 + 4 + 1 * sizeof(char)); /* md5 + .png + '\0' */ + md5_name = emalloc(32 + 4 + 1); /* md5 + .png + '\0' */ for (i = 0, pos = md5_name; i < 16; i++, pos += 2) { sprintf(pos, "%02x", digest[i]); } @@ -794,24 +682,34 @@ char *feh_thumbnail_get_name_md5(char *uri) } int feh_thumbnail_generate(Imlib_Image * image, feh_file * file, - char *thumb_file, char *uri) + char *thumb_file, char *uri, int * orig_w, int * orig_h) { int w, h, thumb_w, thumb_h; Imlib_Image im_temp; struct stat sb; + char c_width[8], c_height[8]; + char *tmp_thumb_file, *prefix; + int tmp_fd; if (feh_load_image(&im_temp, file) != 0) { - w = gib_imlib_image_get_width(im_temp); - h = gib_imlib_image_get_height(im_temp); - thumb_w = 128; - thumb_h = 128; + *orig_w = w = gib_imlib_image_get_width(im_temp); + *orig_h = h = gib_imlib_image_get_height(im_temp); + thumb_w = td.cache_dim; + thumb_h = td.cache_dim; - if ((w > 128) || (h > 128)) { + if ((w > td.cache_dim) || (h > td.cache_dim)) { double ratio = (double) w / h; if (ratio > 1.0) - thumb_h = 128 / ratio; + thumb_h = td.cache_dim / ratio; else if (ratio != 1.0) - thumb_w = 128 * ratio; + thumb_w = td.cache_dim * ratio; + } else { + /* + * The image is smaller than the specified thumbnail size. + * Do not cache or transform it. + */ + *image = im_temp; + return 1; } *image = gib_imlib_create_cropped_scaled_image(im_temp, 0, 0, w, h, @@ -820,31 +718,57 @@ int feh_thumbnail_generate(Imlib_Image * image, feh_file * file, if (!stat(file->filename, &sb)) { char c_mtime[128]; sprintf(c_mtime, "%d", (int)sb.st_mtime); - feh_png_write_png(*image, thumb_file, "Thumb::URI", uri, - "Thumb::MTime", c_mtime); + snprintf(c_width, 8, "%d", w); + snprintf(c_height, 8, "%d", h); + prefix = feh_thumbnail_get_prefix(); + if (prefix == NULL) { + gib_imlib_free_image_and_decache(im_temp); + return 0; + } + tmp_thumb_file = estrjoin("/", prefix, ".feh_thumbnail_XXXXXX", NULL); + free(prefix); + tmp_fd = mkstemp(tmp_thumb_file); + if (!feh_png_write_png_fd(*image, tmp_fd, "Thumb::URI", uri, + "Thumb::MTime", c_mtime, + "Thumb::Image::Width", c_width, + "Thumb::Image::Height", c_height)) { + rename(tmp_thumb_file, thumb_file); + } else { + unlink(tmp_thumb_file); + } + close(tmp_fd); + free(tmp_thumb_file); } gib_imlib_free_image_and_decache(im_temp); - return (1); + return 1; } - return (0); + return 0; } -int feh_thumbnail_get_generated(Imlib_Image * image, feh_file * file, char *thumb_file, char *uri) +int feh_thumbnail_get_generated(Imlib_Image * image, feh_file * file, + char *thumb_file, int * orig_w, int * orig_h) { struct stat sb; char *c_mtime; + char *c_width, *c_height; time_t mtime = 0; gib_hash *hash; if (!stat(file->filename, &sb)) { hash = feh_png_read_comments(thumb_file); if (hash != NULL) { - c_mtime = (char *) gib_hash_get(hash, "Thumb::MTime"); + c_mtime = (char *) gib_hash_get(hash, "Thumb::MTime"); + c_width = (char *) gib_hash_get(hash, "Thumb::Image::Width"); + c_height = (char *) gib_hash_get(hash, "Thumb::Image::Height"); if (c_mtime != NULL) mtime = (time_t) strtol(c_mtime, NULL, 10); + if (c_width != NULL) + *orig_w = atoi(c_width); + if (c_height != NULL) + *orig_h = atoi(c_height); gib_hash_free_and_data(hash); } @@ -859,34 +783,165 @@ int feh_thumbnail_get_generated(Imlib_Image * image, feh_file * file, char *thum return (0); } +void feh_thumbnail_show_fullsize(feh_file *thumbfile) +{ + winwidget thumbwin = NULL; + gib_list *l; + + for (l = filelist; l; l = l->next) { + if (FEH_FILE(l->data) == thumbfile) { + break; + } + } + if (!l) { + eprintf("Cannot find %s in filelist, wtf", thumbfile->filename); + } + thumbwin = winwidget_get_first_window_of_type(WIN_TYPE_THUMBNAIL_VIEWER); + if (!thumbwin) { + thumbwin = winwidget_create_from_file( + l, + WIN_TYPE_THUMBNAIL_VIEWER); + if (thumbwin) + winwidget_show(thumbwin); + } else if (FEH_FILE(thumbwin->file->data) != thumbfile) { + thumbwin->file = l; +#ifdef HAVE_INOTIFY + winwidget_inotify_remove(thumbwin); +#endif + feh_reload_image(thumbwin, 1, 0); +#ifdef HAVE_INOTIFY + winwidget_inotify_add(thumbwin, thumbfile); +#endif + } +} + +void feh_thumbnail_select(winwidget winwid, feh_thumbnail *thumbnail) +{ + Imlib_Image origwin; + + if (thumbnail == td.selected) + return; + + if (thumbnail) { + origwin = winwid->im; + winwid->im = gib_imlib_clone_image(origwin); + gib_imlib_image_fill_rectangle(winwid->im, + thumbnail->x, thumbnail->y, thumbnail->w, + thumbnail->h, 50, 50, 255, 100); + gib_imlib_image_draw_rectangle(winwid->im, + thumbnail->x, thumbnail->y, thumbnail->w, + thumbnail->h, 255, 255, 255, 255); + gib_imlib_image_draw_rectangle(winwid->im, + thumbnail->x + 1, thumbnail->y + 1, + thumbnail->w - 2, thumbnail->h - 2, + 0, 0, 0, 255); + gib_imlib_image_draw_rectangle(winwid->im, + thumbnail->x + 2, thumbnail->y + 2, + thumbnail->w - 4, thumbnail->h - 4, + 255, 255, 255, 255); + winwidget_render_image(winwid, 0, 0); + gib_imlib_free_image_and_decache(winwid->im); + winwid->im = origwin; + } else + winwidget_render_image(winwid, 0, 0); + + td.selected = thumbnail; +} + +void feh_thumbnail_select_next(winwidget winwid, int jump) +{ + gib_list *l; + feh_thumbnail *thumb; + int len = 0, cur = 0, target = 0; + + for (l = thumbnails; l; l = l->next) { + thumb = FEH_THUMB(l->data); + if (thumb == td.selected) + cur = len; + len++; + } + + target = (cur + len - jump) % len; + + for (l = thumbnails; l; l = l->next) { + if (target-- == 0) { + feh_thumbnail_select(winwid, FEH_THUMB(l->data)); + } + } +} + +void feh_thumbnail_select_prev(winwidget winwid, int jump) +{ + gib_list *l; + feh_thumbnail *thumb; + int len = 0, cur = 0, target = 0; + + for (l = thumbnails; l; l = l->next) { + thumb = FEH_THUMB(l->data); + if (thumb == td.selected) + cur = len; + len++; + } + + target = (cur + jump) % len; + + for (l = thumbnails; l; l = l->next) { + if (target-- == 0) { + feh_thumbnail_select(winwid, FEH_THUMB(l->data)); + break; + } + } +} + +void feh_thumbnail_show_selected(void) +{ + if (td.selected && td.selected->file) + feh_thumbnail_show_fullsize(td.selected->file); +} + +feh_file* feh_thumbnail_get_selected_file(void) +{ + if (td.selected) + return td.selected->file; + return NULL; +} + int feh_thumbnail_setup_thumbnail_dir(void) { int status = 0; struct stat sb; - char *dir, *dir_thumbnails, *home; + char *dir, *p; - home = getenv("HOME"); - if (home != NULL) { - dir = estrjoin("/", home, ".thumbnails/normal", NULL); + dir = feh_thumbnail_get_prefix(); + if (dir) { if (!stat(dir, &sb)) { if (S_ISDIR(sb.st_mode)) status = 1; else weprintf("%s should be a directory", dir); } else { - dir_thumbnails = estrjoin("/", home, ".thumbnails", NULL); + for (p = dir + 1; *p; p++) { + if (*p != '/') { + continue; + } - if (stat(dir_thumbnails, &sb) != 0) { - if (mkdir(dir_thumbnails, 0700) == -1) - weprintf("unable to create %s directory", dir_thumbnails); + *p = 0; + if (stat(dir, &sb) != 0) { + if (mkdir(dir, 0700) == -1) { + weprintf("unable to create directory %s", dir); + } + } + *p = '/'; } - if (mkdir(dir, 0700) == -1) - weprintf("unable to create %s directory", dir); - else - status = 1; + if (stat(dir, &sb) != 0) { + if (mkdir(dir, 0700) == -1) { + weprintf("unable to create directory %s", dir); + } + } } + free(dir); } return status; diff --git a/src/thumbnail.h b/src/thumbnail.h index e096693..e69759f 100644 --- a/src/thumbnail.h +++ b/src/thumbnail.h @@ -1,6 +1,7 @@ /* thumbnail.h Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2020 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -43,22 +44,25 @@ typedef struct thumbnail { } feh_thumbnail; typedef struct thumbmode_data { - /* FIXME: text_area_h not really needed, remove? */ + Imlib_Image im_main; /* base image which all thumbnails are rendered on */ + Imlib_Image im_bg; /* background for the thumbnails */ - Imlib_Image im_main; /* base image which all thumbnails are rendered on */ - Imlib_Image im_bg; /* background for the thumbnails */ + Imlib_Font font_main; /* font used for file info */ + Imlib_Font font_title; /* font used for title */ - Imlib_Font font_main; /* font used for file info */ - Imlib_Font font_title; /* font used for title */ + int w, h, bg_w, bg_h; /* dimensions of the window and bg image */ - int w, h, bg_w, bg_h; /* dimensions of the window and bg image */ + int thumb_tot_h; /* total space needed for a thumbnail including description */ + int text_area_w, text_area_h; /* space needed for thumbnail description */ - int thumb_tot_h; /* total space needed for a thumbnail including description */ - int text_area_w, text_area_h; /* space needed for thumbnail description */ + int max_column_w; /* FIXME: description */ + int vertical; /* == !opt.limit_w && opt.limit_h */ + + int cache_thumbnails; /* use cached thumbnails from ~/.thumbnails */ + int cache_dim; /* 128 = 128x128 ("normal"), 256 = 256x256 ("large") */ + char *cache_dir; /* "normal"/"large" (.thumbnails/...) */ + feh_thumbnail *selected; /* currently selected thumbnail */ - int max_column_w; /* FIXME: description */ - int vertical; /* FIXME: vertical in what way? */ - int cache_thumbnails; /* use cached thumbnails from ~/.thumbnails/normal */ } thumbmode_data; feh_thumbnail *feh_thumbnail_new(feh_file * fil, int x, int y, int w, int h); @@ -69,12 +73,18 @@ void feh_thumbnail_mark_removed(feh_file * file, int deleted); void feh_thumbnail_calculate_geometry(void); -int feh_thumbnail_get_thumbnail(Imlib_Image * image, feh_file * file); -int feh_thumbnail_generate(Imlib_Image * image, feh_file * file, char *thumb_file, char *uri); -int feh_thumbnail_get_generated(Imlib_Image * image, feh_file * file, char *thumb_file, char *uri); +int feh_thumbnail_get_thumbnail(Imlib_Image * image, feh_file * file, int * orig_w, int * orig_h); +int feh_thumbnail_generate(Imlib_Image * image, feh_file * file, char *thumb_file, char *uri, int * orig_w, int * orig_h); +int feh_thumbnail_get_generated(Imlib_Image * image, feh_file * file, char * thumb_file, int * orig_w, int * orig_h); char *feh_thumbnail_get_name(char *uri); char *feh_thumbnail_get_name_uri(char *name); char *feh_thumbnail_get_name_md5(char *uri); +void feh_thumbnail_show_fullsize(feh_file *thumbfile); +void feh_thumbnail_select(winwidget winwid, feh_thumbnail *thumbnail); +void feh_thumbnail_select_next(winwidget winwid, int jump); +void feh_thumbnail_select_prev(winwidget winwid, int jump); +void feh_thumbnail_show_selected(void); +feh_file *feh_thumbnail_get_selected_file(void); int feh_thumbnail_setup_thumbnail_dir(void); diff --git a/src/timers.c b/src/timers.c index 710d2ab..8e42050 100644 --- a/src/timers.c +++ b/src/timers.c @@ -1,6 +1,7 @@ /* timers.c Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2020 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -33,45 +34,72 @@ void feh_handle_timer(void) { fehtimer ft; - D_ENTER(4); if (!first_timer) { - D(3, ("No timer to handle, returning\n")); - D_RETURN_(4); + D(("No timer to handle, returning\n")); + return; } ft = first_timer; first_timer = first_timer->next; - D(3, ("Executing timer function now\n")); + D(("Executing timer function now\n")); (*(ft->func)) (ft->data); - D(3, ("Freeing the timer\n")); + D(("Freeing the timer\n")); if (ft && ft->name) free(ft->name); if (ft) free(ft); - D_RETURN_(4); + return; } double feh_get_time(void) { struct timeval timev; - D_ENTER(4); gettimeofday(&timev, NULL); - D_RETURN(4, (double) timev.tv_sec + (((double) timev.tv_usec) / 1000000)); + 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_ENTER(4); - D(3, ("removing %s\n", name)); + D(("removing timer for %p\n", data)); pptr = NULL; ptr = first_timer; while (ptr) { - D(3, ("Stepping through event list\n")); + 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; + + D(("removing %s\n", name)); + pptr = NULL; + ptr = first_timer; + while (ptr) { + D(("Stepping through event list\n")); ft = ptr; if (!strcmp(ft->name, name)) { - D(3, ("Found it. Removing\n")); + D(("Found it. Removing\n")); if (pptr) pptr->next = ft->next; else @@ -82,36 +110,35 @@ void feh_remove_timer(char *name) free(ft->name); if (ft) free(ft); - D_RETURN_(4); + return; } pptr = ptr; ptr = ptr->next; } - D_RETURN_(4); + return; } + void feh_add_timer(void (*func) (void *data), void *data, double in, char *name) { fehtimer ft, ptr, pptr; double tally; - D_ENTER(4); - D(3, ("adding timer %s for %f seconds time\n", name, in)); + D(("adding timer %s for %f seconds time\n", name, in)); feh_remove_timer(name); - ft = malloc(sizeof(_fehtimer)); + ft = emalloc(sizeof(_fehtimer)); ft->next = NULL; ft->func = func; ft->data = data; ft->name = estrdup(name); ft->just_added = 1; ft->in = in; - D(3, ("ft->in = %f\n", ft->in)); - tally = 0.0; + D(("ft->in = %f\n", ft->in)); if (!first_timer) { - D(3, ("No first timer\n")); + D(("No first timer\n")); first_timer = ft; } else { - D(3, ("There is a first timer\n")); + D(("There is a first timer\n")); pptr = NULL; ptr = first_timer; tally = 0.0; @@ -127,7 +154,7 @@ void feh_add_timer(void (*func) (void *data), void *data, double in, char *name) ft->in -= tally; if (ft->next) ft->next->in -= ft->in; - D_RETURN_(4); + return; } pptr = ptr; ptr = ptr->next; @@ -138,8 +165,8 @@ void feh_add_timer(void (*func) (void *data), void *data, double in, char *name) first_timer = ft; ft->in -= tally; } - D(3, ("ft->in = %f\n", ft->in)); - D_RETURN_(4); + D(("ft->in = %f\n", ft->in)); + return; } void feh_add_unique_timer(void (*func) (void *data), void *data, double in) @@ -147,13 +174,12 @@ void feh_add_unique_timer(void (*func) (void *data), void *data, double in) static long i = 0; char evname[20]; - D_ENTER(4); snprintf(evname, sizeof(evname), "T_%ld", i); - D(4, ("adding timer with unique name %s\n", evname)); + D(("adding timer with unique name %s\n", evname)); feh_add_timer(func, data, in, evname); i++; /* Mega paranoia ;) */ if (i > 1000000) i = 0; - D_RETURN_(4); + return; } 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/ttfonts/black.style b/src/ttfonts/black.style deleted file mode 100644 index d2a86a9..0000000 --- a/src/ttfonts/black.style +++ /dev/null @@ -1,4 +0,0 @@ -#Style -#NAME Menu -255 255 255 64 2 2 -255 255 255 255 0 0 diff --git a/src/ttfonts/menu.style b/src/ttfonts/menu.style deleted file mode 100644 index 8557360..0000000 --- a/src/ttfonts/menu.style +++ /dev/null @@ -1,4 +0,0 @@ -#Style -#NAME Menu -0 0 0 64 2 2 -0 0 0 0 0 0 diff --git a/src/ttfonts/yudit.ttf b/src/ttfonts/yudit.ttf Binary files differdeleted file mode 100644 index 8099e63..0000000 --- a/src/ttfonts/yudit.ttf +++ /dev/null diff --git a/src/utils.c b/src/utils.c index b74bc8c..eb128a2 100644 --- a/src/utils.c +++ b/src/utils.c @@ -27,16 +27,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "debug.h" #include "options.h" -static char *feh_user_name = NULL; -static char *feh_tmp_dir = NULL; - /* eprintf: print error message and exit */ void eprintf(char *fmt, ...) { va_list args; fflush(stdout); - fprintf(stderr, "%s ERROR: ", PACKAGE); + fputs(PACKAGE " ERROR: ", stderr); va_start(args, fmt); vfprintf(stderr, fmt, args); @@ -44,7 +41,7 @@ void eprintf(char *fmt, ...) if (fmt[0] != '\0' && fmt[strlen(fmt) - 1] == ':') fprintf(stderr, " %s", strerror(errno)); - fprintf(stderr, "\n"); + fputs("\n", stderr); exit(2); } @@ -54,7 +51,7 @@ void weprintf(char *fmt, ...) va_list args; fflush(stdout); - fprintf(stderr, "%s WARNING: ", PACKAGE); + fputs(PACKAGE " WARNING: ", stderr); va_start(args, fmt); vfprintf(stderr, fmt, args); @@ -62,7 +59,7 @@ void weprintf(char *fmt, ...) if (fmt[0] != '\0' && fmt[strlen(fmt) - 1] == ':') fprintf(stderr, " %s", strerror(errno)); - fprintf(stderr, "\n"); + fputs("\n", stderr); } /* estrdup: duplicate a string, report if error */ @@ -125,7 +122,7 @@ char *estrjoin(const char *separator, ...) s = va_arg(args, char *); } va_end(args); - string = malloc(sizeof(char) * (len + 1)); + string = emalloc(sizeof(char) * (len + 1)); *string = 0; va_start(args, separator); @@ -146,29 +143,28 @@ char *estrjoin(const char *separator, ...) return string; } -char *stroflen(char c, int l) -{ - static char buf[1024]; - int i = 0; - - buf[0] = '\0'; - while (l--) - buf[i++] = c; - buf[i] = '\0'; - return buf; +char path_is_url(char *path) { + if ((!strncmp(path, "http://", 7)) + || (!strncmp(path, "https://", 8)) + || (!strncmp(path, "gopher://", 9)) + || (!strncmp(path, "gophers://", 10)) + || (!strncmp(path, "ftp://", 6)) + || (!strncmp(path, "file://", 7))) + return 1; + return 0; } +/* Note: path must end with a trailing / or be an empty string */ /* free the result please */ char *feh_unique_filename(char *path, char *basename) { char *tmpname; char num[10]; - char cppid[10]; + char cppid[12]; static long int i = 1; struct stat st; pid_t ppid; - D_ENTER(4); /* Massive paranoia ;) */ if (i > 999998) i = 1; @@ -176,13 +172,15 @@ char *feh_unique_filename(char *path, char *basename) ppid = getpid(); snprintf(cppid, sizeof(cppid), "%06ld", (long) ppid); + tmpname = NULL; /* make sure file doesn't exist */ do { snprintf(num, sizeof(num), "%06ld", i++); + free(tmpname); tmpname = estrjoin("", path, "feh_", cppid, "_", num, "_", basename, NULL); } while (stat(tmpname, &st) == 0); - D_RETURN(4, tmpname); + return(tmpname); } /* reads file into a string, but limits o 4095 chars and ensures a \0 */ @@ -190,14 +188,14 @@ char *ereadfile(char *path) { char buffer[4096]; FILE *fp; - int count; + size_t count; fp = fopen(path, "r"); if (!fp) return NULL; count = fread(buffer, sizeof(char), sizeof(buffer) - 1, fp); - if (buffer[count - 1] == '\n') + if (count > 0 && buffer[count - 1] == '\n') buffer[count - 1] = '\0'; else buffer[count] = '\0'; @@ -207,31 +205,25 @@ char *ereadfile(char *path) return estrdup(buffer); } -char *feh_get_tmp_dir(void) +char *shell_escape(char *input) { - char *tmp; - if (feh_tmp_dir) - return feh_tmp_dir; - tmp = getenv("TMPDIR"); - if (!tmp) - tmp = getenv("TMP"); - if (!tmp) - tmp = getenv("TEMP"); - if (!tmp) - tmp = "/tmp"; - feh_tmp_dir = estrdup(tmp); - return feh_tmp_dir; -} + static char ret[1024]; + unsigned int out = 0, in = 0; + + ret[out++] = '\''; + for (in = 0; input[in] && (out < (sizeof(ret) - 7)); in++) { + if (input[in] == '\'') { + ret[out++] = '\''; + ret[out++] = '"'; + ret[out++] = '\''; + ret[out++] = '"'; + ret[out++] = '\''; + } + else + ret[out++] = input[in]; + } + ret[out++] = '\''; + ret[out++] = '\0'; -char *feh_get_user_name(void) -{ - struct passwd *pw = NULL; - - if (feh_user_name) - return feh_user_name; - setpwent(); - pw = getpwuid(getuid()); - endpwent(); - feh_user_name = estrdup(pw->pw_name); - return feh_user_name; + return ret; } diff --git a/src/utils.h b/src/utils.h index d0d7665..22cbbf8 100644 --- a/src/utils.h +++ b/src/utils.h @@ -26,6 +26,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef UTILS_H #define UTILS_H +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + #ifndef __GNUC__ # define __attribute__(x) #endif @@ -36,11 +43,10 @@ char *_estrdup(char *s); void *_emalloc(size_t n); void *_erealloc(void *ptr, size_t n); char *estrjoin(const char *separator, ...); -char *stroflen(char, int); +char path_is_url(char *path); char *feh_unique_filename(char *path, char *basename); char *ereadfile(char *path); -char *feh_get_tmp_dir(void); -char *feh_get_user_name(void); +char *shell_escape(char *input); #define ESTRAPPEND(a,b) \ {\ diff --git a/src/wallpaper.c b/src/wallpaper.c new file mode 100644 index 0000000..e2fa67e --- /dev/null +++ b/src/wallpaper.c @@ -0,0 +1,918 @@ +/* wallpaper.c + +Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2020 Birte Kristina Friesel. + +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 of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +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 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. + +*/ + +#include <limits.h> +#include <sys/stat.h> + +#include "feh.h" +#include "filelist.h" +#include "options.h" +#include "wallpaper.h" + +Window ipc_win = None; +Window my_ipc_win = None; +Atom ipc_atom = None; +static unsigned char timeout = 0; + +/* + * This is a boolean indicating + * That while we seem to see E16 IPC + * it's actually E17 faking it + * -- richlowe 2005-06-22 + */ +static char e17_fake_ipc = 0; + +void feh_wm_set_bg_filelist(unsigned char bgmode) +{ + if (filelist_len == 0) + eprintf("No files specified for background setting"); + + switch (bgmode) { + case BG_MODE_TILE: + feh_wm_set_bg(NULL, NULL, 0, 0, 0, 0, 1); + break; + case BG_MODE_SCALE: + feh_wm_set_bg(NULL, NULL, 0, 1, 0, 0, 1); + break; + case BG_MODE_FILL: + feh_wm_set_bg(NULL, NULL, 0, 0, 1, 0, 1); + break; + case BG_MODE_MAX: + feh_wm_set_bg(NULL, NULL, 0, 0, 2, 0, 1); + break; + default: + feh_wm_set_bg(NULL, NULL, 1, 0, 0, 0, 1); + break; + } +} + +static void feh_wm_load_next(Imlib_Image *im) +{ + static gib_list *wpfile = NULL; + + if (wpfile == NULL) + wpfile = filelist; + + if (feh_load_image(im, FEH_FILE(wpfile->data)) == 0) + eprintf("Unable to load image %s", FEH_FILE(wpfile->data)->filename); + if (wpfile->next) + wpfile = wpfile->next; + + return; +} + +static void feh_wm_set_bg_scaled(Pixmap pmap, Imlib_Image im, int use_filelist, + int x, int y, int w, int h) +{ + if (use_filelist) + feh_wm_load_next(&im); + + gib_imlib_render_image_on_drawable_at_size(pmap, im, x, y, w, h, + 1, 1, !opt.force_aliasing); + + if (use_filelist) + gib_imlib_free_image_and_decache(im); + + return; +} + +static void feh_wm_set_bg_centered(Pixmap pmap, Imlib_Image im, int use_filelist, + int x, int y, int w, int h) +{ + int offset_x, offset_y; + + if (use_filelist) + feh_wm_load_next(&im); + + if(opt.geom_flags & XValue) + if(opt.geom_flags & XNegative) + offset_x = (w - gib_imlib_image_get_width(im)) + opt.geom_x; + else + offset_x = opt.geom_x; + else + offset_x = (w - gib_imlib_image_get_width(im)) >> 1; + + if(opt.geom_flags & YValue) + if(opt.geom_flags & YNegative) + offset_y = (h - gib_imlib_image_get_height(im)) + opt.geom_y; + else + offset_y = opt.geom_y; + else + offset_y = (h - gib_imlib_image_get_height(im)) >> 1; + + gib_imlib_render_image_part_on_drawable_at_size(pmap, im, + ((offset_x < 0) ? -offset_x : 0), + ((offset_y < 0) ? -offset_y : 0), + w, + h, + x + ((offset_x > 0) ? offset_x : 0), + y + ((offset_y > 0) ? offset_y : 0), + w, + h, + 1, 1, 0); + + if (use_filelist) + gib_imlib_free_image_and_decache(im); + + return; +} + +static void feh_wm_set_bg_filled(Pixmap pmap, Imlib_Image im, int use_filelist, + int x, int y, int w, int h) +{ + int img_w, img_h, cut_x; + int render_w, render_h, render_x, render_y; + + if (use_filelist) + feh_wm_load_next(&im); + + img_w = gib_imlib_image_get_width(im); + img_h = gib_imlib_image_get_height(im); + + cut_x = (((img_w * h) > (img_h * w)) ? 1 : 0); + + render_w = ( cut_x ? ((img_h * w) / h) : img_w); + render_h = ( !cut_x ? ((img_w * h) / w) : img_h); + + render_x = ( cut_x ? ((img_w - render_w) >> 1) : 0); + render_y = ( !cut_x ? ((img_h - render_h) >> 1) : 0); + + if ((opt.geom_flags & XValue) && cut_x) { + if (opt.geom_flags & XNegative) { + render_x = img_w - render_w + opt.geom_x; + } else { + render_x = opt.geom_x; + } + if (render_x < 0) { + render_x = 0; + } else if (render_x + render_w > img_w) { + render_x = img_w - render_w; + } + } + else if ((opt.geom_flags & YValue) && !cut_x) { + if (opt.geom_flags & YNegative) { + render_y = img_h - render_h + opt.geom_y; + } else { + render_y = opt.geom_y; + } + if (render_y < 0) { + render_y = 0; + } else if (render_y + render_h > img_h) { + render_y = img_h - render_h; + } + } + + gib_imlib_render_image_part_on_drawable_at_size(pmap, im, + render_x, render_y, + render_w, render_h, + x, y, w, h, + 1, 1, !opt.force_aliasing); + + if (use_filelist) + gib_imlib_free_image_and_decache(im); + + return; +} + +static void feh_wm_set_bg_maxed(Pixmap pmap, Imlib_Image im, int use_filelist, + int x, int y, int w, int h) +{ + int img_w, img_h, border_x; + int render_w, render_h, render_x, render_y; + int margin_x, margin_y; + + if (use_filelist) + feh_wm_load_next(&im); + + img_w = gib_imlib_image_get_width(im); + img_h = gib_imlib_image_get_height(im); + + border_x = (((img_w * h) > (img_h * w)) ? 0 : 1); + + render_w = ( border_x ? ((img_w * h) / img_h) : w); + render_h = ( !border_x ? ((img_h * w) / img_w) : h); + + if(opt.geom_flags & XValue) + if(opt.geom_flags & XNegative) + margin_x = (w - render_w) + opt.geom_x; + else + margin_x = opt.geom_x; + else + margin_x = (w - render_w) >> 1; + + if(opt.geom_flags & YValue) + if(opt.geom_flags & YNegative) + margin_y = (h - render_h) + opt.geom_y; + else + margin_y = opt.geom_y; + else + margin_y = (h - render_h) >> 1; + + render_x = x + ( border_x ? margin_x : 0); + render_y = y + ( !border_x ? margin_y : 0); + + gib_imlib_render_image_on_drawable_at_size(pmap, im, + render_x, render_y, + render_w, render_h, + 1, 1, !opt.force_aliasing); + + if (use_filelist) + gib_imlib_free_image_and_decache(im); + + 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) +{ + XGCValues gcvalues; + XGCValues gcval; + GC gc; + char bgname[20]; + int num = (int) random(); + char bgfil[4096]; + char sendbuf[4096]; + + /* + * TODO this re-implements mkstemp (badly). However, it is only needed + * for non-file images and enlightenment. Might be easier to just remove + * it. + */ + + snprintf(bgname, sizeof(bgname), "FEHBG_%d", num); + + if (!fil && im) { + if (getenv("HOME") == NULL) { + weprintf("Cannot save wallpaper to temporary file: You have no HOME"); + return; + } + snprintf(bgfil, sizeof(bgfil), "%s/.%s.png", getenv("HOME"), bgname); + imlib_context_set_image(im); + imlib_image_set_format("png"); + gib_imlib_save_image(im, bgfil); + D(("bg saved as %s\n", bgfil)); + fil = bgfil; + } + + if (feh_wm_get_wm_is_e() && (enl_ipc_get_win() != None)) { + if (use_filelist) { + feh_wm_load_next(&im); + fil = FEH_FILE(filelist->data)->filename; + } + if ((size_t) snprintf(sendbuf, sizeof(sendbuf), "background %s bg.file %s", bgname, fil) >= sizeof(sendbuf)) { + weprintf("Writing to IPC send buffer was truncated"); + return; + } + enl_ipc_send(sendbuf); + + if (scaled) { + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.solid 0 0 0", bgname); + enl_ipc_send(sendbuf); + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.tile 0", bgname); + enl_ipc_send(sendbuf); + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.xjust 512", bgname); + enl_ipc_send(sendbuf); + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.yjust 512", bgname); + enl_ipc_send(sendbuf); + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.xperc 1024", bgname); + enl_ipc_send(sendbuf); + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.yperc 1024", bgname); + enl_ipc_send(sendbuf); + } else if (centered) { + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.solid 0 0 0", bgname); + enl_ipc_send(sendbuf); + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.tile 0", bgname); + enl_ipc_send(sendbuf); + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.xjust 512", bgname); + enl_ipc_send(sendbuf); + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.yjust 512", bgname); + enl_ipc_send(sendbuf); + } else { + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.tile 1", bgname); + enl_ipc_send(sendbuf); + } + + snprintf(sendbuf, sizeof(sendbuf), "use_bg %s %d", bgname, desktop); + enl_ipc_send(sendbuf); + enl_ipc_sync(); + } else { + Atom prop_root, prop_esetroot, type; + int format, i; + unsigned long length, after; + unsigned char *data_root = NULL, *data_esetroot = NULL; + Pixmap pmap_d1, pmap_d2; + + /* local display to set closedownmode on */ + Display *disp2; + Window root2; + int depth2; + int w, h; + + D(("Falling back to XSetRootWindowPixmap\n")); + + XColor color; + Colormap cmap = DefaultColormap(disp, DefaultScreen(disp)); + if (opt.image_bg) + XAllocNamedColor(disp, cmap, (char*) opt.image_bg, &color, &color); + else + XAllocNamedColor(disp, cmap, "black", &color, &color); + + if (scaled) { + pmap_d1 = XCreatePixmap(disp, root, scr->width, scr->height, depth); + +#ifdef HAVE_LIBXINERAMA + if (opt.xinerama_index >= 0) { + gcval.foreground = color.pixel; + gc = XCreateGC(disp, root, GCForeground, &gcval); + XFillRectangle(disp, pmap_d1, gc, 0, 0, scr->width, scr->height); + XFreeGC(disp, gc); + } + + if (opt.xinerama && xinerama_screens) { + for (i = 0; i < num_xinerama_screens; i++) { + if (opt.xinerama_index < 0 || opt.xinerama_index == i) { + feh_wm_set_bg_scaled(pmap_d1, im, use_filelist, + xinerama_screens[i].x_org, xinerama_screens[i].y_org, + xinerama_screens[i].width, xinerama_screens[i].height); + } + } + } + else +#endif /* HAVE_LIBXINERAMA */ + feh_wm_set_bg_scaled(pmap_d1, im, use_filelist, + 0, 0, scr->width, scr->height); + } else if (centered) { + + D(("centering\n")); + + pmap_d1 = XCreatePixmap(disp, root, scr->width, scr->height, depth); + gcval.foreground = color.pixel; + gc = XCreateGC(disp, root, GCForeground, &gcval); + XFillRectangle(disp, pmap_d1, gc, 0, 0, scr->width, scr->height); + +#ifdef HAVE_LIBXINERAMA + if (opt.xinerama && xinerama_screens) { + for (i = 0; i < num_xinerama_screens; i++) { + if (opt.xinerama_index < 0 || opt.xinerama_index == i) { + feh_wm_set_bg_centered(pmap_d1, im, use_filelist, + xinerama_screens[i].x_org, xinerama_screens[i].y_org, + xinerama_screens[i].width, xinerama_screens[i].height); + } + } + } + else +#endif /* HAVE_LIBXINERAMA */ + feh_wm_set_bg_centered(pmap_d1, im, use_filelist, + 0, 0, scr->width, scr->height); + + XFreeGC(disp, gc); + + } else if (filled == 1) { + + pmap_d1 = XCreatePixmap(disp, root, scr->width, scr->height, depth); + +#ifdef HAVE_LIBXINERAMA + if (opt.xinerama_index >= 0) { + gcval.foreground = color.pixel; + gc = XCreateGC(disp, root, GCForeground, &gcval); + XFillRectangle(disp, pmap_d1, gc, 0, 0, scr->width, scr->height); + XFreeGC(disp, gc); + } + + if (opt.xinerama && xinerama_screens) { + for (i = 0; i < num_xinerama_screens; i++) { + if (opt.xinerama_index < 0 || opt.xinerama_index == i) { + feh_wm_set_bg_filled(pmap_d1, im, use_filelist, + xinerama_screens[i].x_org, xinerama_screens[i].y_org, + xinerama_screens[i].width, xinerama_screens[i].height); + } + } + } + else +#endif /* HAVE_LIBXINERAMA */ + feh_wm_set_bg_filled(pmap_d1, im, use_filelist + , 0, 0, scr->width, scr->height); + + } else if (filled == 2) { + + pmap_d1 = XCreatePixmap(disp, root, scr->width, scr->height, depth); + gcval.foreground = color.pixel; + gc = XCreateGC(disp, root, GCForeground, &gcval); + XFillRectangle(disp, pmap_d1, gc, 0, 0, scr->width, scr->height); + +#ifdef HAVE_LIBXINERAMA + if (opt.xinerama && xinerama_screens) { + for (i = 0; i < num_xinerama_screens; i++) { + if (opt.xinerama_index < 0 || opt.xinerama_index == i) { + feh_wm_set_bg_maxed(pmap_d1, im, use_filelist, + xinerama_screens[i].x_org, xinerama_screens[i].y_org, + xinerama_screens[i].width, xinerama_screens[i].height); + } + } + } + else +#endif /* HAVE_LIBXINERAMA */ + feh_wm_set_bg_maxed(pmap_d1, im, use_filelist, + 0, 0, scr->width, scr->height); + + XFreeGC(disp, gc); + + } else { + if (use_filelist) + feh_wm_load_next(&im); + w = gib_imlib_image_get_width(im); + h = gib_imlib_image_get_height(im); + pmap_d1 = XCreatePixmap(disp, root, w, h, depth); + gib_imlib_render_image_on_drawable(pmap_d1, im, 0, 0, 1, 1, 0); + } + + 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); + if (!disp2) + eprintf("Can't reopen X display."); + root2 = RootWindow(disp2, DefaultScreen(disp2)); + depth2 = DefaultDepth(disp2, DefaultScreen(disp2)); + XSync(disp, False); + pmap_d2 = XCreatePixmap(disp2, root2, scr->width, scr->height, depth2); + gcvalues.fill_style = FillTiled; + gcvalues.tile = pmap_d1; + gc = XCreateGC(disp2, pmap_d2, GCFillStyle | GCTile, &gcvalues); + XFillRectangle(disp2, pmap_d2, gc, 0, 0, scr->width, scr->height); + XFreeGC(disp2, gc); + XSync(disp2, False); + XSync(disp, False); + XFreePixmap(disp, pmap_d1); + + prop_root = XInternAtom(disp2, "_XROOTPMAP_ID", True); + prop_esetroot = XInternAtom(disp2, "ESETROOT_PMAP_ID", True); + + if (prop_root != None && prop_esetroot != None) { + XGetWindowProperty(disp2, root2, prop_root, 0L, 1L, + False, AnyPropertyType, &type, &format, &length, &after, &data_root); + if (type == XA_PIXMAP) { + XGetWindowProperty(disp2, root2, + prop_esetroot, 0L, 1L, + False, AnyPropertyType, + &type, &format, &length, &after, &data_esetroot); + if (data_root && data_esetroot) { + if (type == XA_PIXMAP && *((Pixmap *) data_root) == *((Pixmap *) data_esetroot)) { + XKillClient(disp2, *((Pixmap *) + data_root)); + } + } + } + } + + if (data_root) + XFree(data_root); + + if (data_esetroot) + XFree(data_esetroot); + + /* This will locate the property, creating it if it doesn't exist */ + prop_root = XInternAtom(disp2, "_XROOTPMAP_ID", False); + prop_esetroot = XInternAtom(disp2, "ESETROOT_PMAP_ID", False); + + if (prop_root == None || prop_esetroot == None) + eprintf("creation of pixmap property failed."); + + XChangeProperty(disp2, root2, prop_root, XA_PIXMAP, 32, PropModeReplace, (unsigned char *) &pmap_d2, 1); + XChangeProperty(disp2, root2, prop_esetroot, XA_PIXMAP, 32, + PropModeReplace, (unsigned char *) &pmap_d2, 1); + + XSetWindowBackgroundPixmap(disp2, root2, pmap_d2); + XClearWindow(disp2, root2); + XFlush(disp2); + XSetCloseDownMode(disp2, RetainPermanent); + XCloseDisplay(disp2); + } + return; +} + +signed char feh_wm_get_wm_is_e(void) +{ + static signed char e = -1; + + /* check if E is actually running */ + if (e == -1) { + /* XXX: This only covers E17 prior to 6/22/05 */ + if ((XInternAtom(disp, "ENLIGHTENMENT_COMMS", True) != None) + && (XInternAtom(disp, "ENLIGHTENMENT_VERSION", True) != None)) { + D(("Enlightenment detected.\n")); + e = 1; + } else { + D(("Enlightenment not detected.\n")); + e = 0; + } + } + return(e); +} + +int feh_wm_get_num_desks(void) +{ + char *buf, *ptr; + int desks; + + if (!feh_wm_get_wm_is_e()) + return(-1); + + buf = enl_send_and_wait("num_desks ?"); + if (buf == IPC_FAKE) /* Fake E17 IPC */ + return(-1); + D(("Got from E IPC: %s\n", buf)); + ptr = buf; + while (ptr && !isdigit(*ptr)) + ptr++; + desks = atoi(ptr); + + return(desks); +} + +Window enl_ipc_get_win(void) +{ + + unsigned char *str = NULL; + Atom prop, prop2, ever; + unsigned long num, after; + int format; + Window dummy_win; + int dummy_int; + unsigned int dummy_uint; + + D(("Searching for IPC window.\n")); + + /* + * Shortcircuit this entire func + * if we already know it's an e17 fake + */ + if (e17_fake_ipc) + return(ipc_win); + + prop = XInternAtom(disp, "ENLIGHTENMENT_COMMS", True); + if (prop == None) { + D(("Enlightenment is not running.\n")); + return(None); + } else { + /* XXX: This will only work with E17 prior to 6/22/2005 */ + ever = XInternAtom(disp, "ENLIGHTENMENT_VERSION", True); + if (ever == None) { + /* This is an E without ENLIGHTENMENT_VERSION */ + D(("E16 IPC Protocol not supported")); + return(None); + } + } + XGetWindowProperty(disp, root, prop, 0, 14, False, AnyPropertyType, &prop2, &format, &num, &after, &str); + if (str) { + sscanf((char *) str, "%*s %x", (unsigned int *) &ipc_win); + XFree(str); + } + if (ipc_win != None) { + if (!XGetGeometry + (disp, ipc_win, &dummy_win, &dummy_int, &dummy_int, + &dummy_uint, &dummy_uint, &dummy_uint, &dummy_uint)) { + D((" -> IPC Window property is valid, but the window doesn't exist.\n")); + ipc_win = None; + } + str = NULL; + if (ipc_win != None) { + XGetWindowProperty(disp, ipc_win, prop, 0, 14, + False, AnyPropertyType, &prop2, &format, &num, &after, &str); + if (str) { + XFree(str); + } else { + D((" -> IPC Window lacks the proper atom. I can't talk to fake IPC windows....\n")); + ipc_win = None; + } + } + } + if (ipc_win != None) { + + XGetWindowProperty(disp, ipc_win, ever, 0, 14, False, + AnyPropertyType, &prop2, &format, &num, &after, &str); + if (str) { + /* + * This is E17's way of telling us it's only pretending + * as a workaround for a bug related to the way java handles + * Window Managers. + * (Only valid after date of this comment) + * -- richlowe 2005-06-22 + */ + XFree(str); + D((" -> Found a fake E17 IPC window, ignoring")); + ipc_win = None; + e17_fake_ipc = 1; + return(ipc_win); + } + + D((" -> IPC Window found and verified as 0x%08x. Registering feh as an IPC client.\n", (int) ipc_win)); + XSelectInput(disp, ipc_win, StructureNotifyMask | SubstructureNotifyMask); + enl_ipc_send("set clientname " PACKAGE); + enl_ipc_send("set version " VERSION); + enl_ipc_send("set email tom@linuxbrit.co.uk"); + enl_ipc_send("set web http://www.linuxbrit.co.uk"); + enl_ipc_send("set info Feh - be pr0n or be dead"); + } + if (my_ipc_win == None) { + my_ipc_win = XCreateSimpleWindow(disp, root, -2, -2, 1, 1, 0, 0, 0); + } + return(ipc_win); +} + +void enl_ipc_send(char *str) +{ + + static char *last_msg = NULL; + char buff[21]; + register unsigned short i; + register unsigned char j; + unsigned short len; + XEvent ev; + + if (str == NULL) { + if (last_msg == NULL) + eprintf("eeek"); + str = last_msg; + D(("Resending last message \"%s\" to Enlightenment.\n", str)); + } else { + if (last_msg != NULL) { + free(last_msg); + } + last_msg = estrdup(str); + D(("Sending \"%s\" to Enlightenment.\n", str)); + } + if (ipc_win == None) { + if ((ipc_win = enl_ipc_get_win()) == None) { + D(("Hrm. Enlightenment doesn't seem to be running. No IPC window, no IPC.\n")); + return; + } + } + len = strlen(str); + ipc_atom = XInternAtom(disp, "ENL_MSG", False); + if (ipc_atom == None) { + D(("IPC error: Unable to find/create ENL_MSG atom.\n")); + return; + } + for (; XCheckTypedWindowEvent(disp, my_ipc_win, ClientMessage, &ev);); /* Discard any out-of-sync messages */ + ev.xclient.type = ClientMessage; + ev.xclient.serial = 0; + ev.xclient.send_event = True; + ev.xclient.window = ipc_win; + ev.xclient.message_type = ipc_atom; + ev.xclient.format = 8; + + for (i = 0; i < len + 1; i += 12) { + sprintf(buff, "%8x", (int) my_ipc_win); + for (j = 0; j < 12; j++) { + buff[8 + j] = str[i + j]; + if (!str[i + j]) { + break; + } + } + buff[20] = 0; + for (j = 0; j < 20; j++) { + ev.xclient.data.b[j] = buff[j]; + } + XSendEvent(disp, ipc_win, False, 0, (XEvent *) & ev); + } + return; +} + +void enl_ipc_timeout(int sig) +{ + if (sig == SIGALRM) + timeout = 1; + return; +} + +char *enl_wait_for_reply(void) +{ + + XEvent ev; + static char msg_buffer[20]; + register unsigned char i; + + alarm(2); + for (; !XCheckTypedWindowEvent(disp, my_ipc_win, ClientMessage, &ev) + && !timeout;); + alarm(0); + if (ev.xany.type != ClientMessage) { + return(IPC_TIMEOUT); + } + for (i = 0; i < 20; i++) { + msg_buffer[i] = ev.xclient.data.b[i]; + } + return(msg_buffer + 8); +} + +char *enl_ipc_get(const char *msg_data) +{ + + static char *message = NULL; + static size_t len = 0; + char buff[13], *ret_msg = NULL; + register unsigned char i; + unsigned char blen; + + if (msg_data == IPC_TIMEOUT) { + return(IPC_TIMEOUT); + } + for (i = 0; i < 12; i++) { + buff[i] = msg_data[i]; + } + buff[12] = 0; + blen = strlen(buff); + if (message != NULL) { + len += blen; + message = (char *) erealloc(message, len + 1); + strcat(message, buff); + } else { + len = blen; + message = (char *) emalloc(len + 1); + strcpy(message, buff); + } + if (blen < 12) { + ret_msg = message; + message = NULL; + D(("Received complete reply: \"%s\"\n", ret_msg)); + } + return(ret_msg); +} + +char *enl_send_and_wait(char *msg) +{ + char *reply = IPC_TIMEOUT; + struct sigaction e17_sh, feh_sh; + sigset_t e17_ss; + + /* + * Shortcut this func and return IPC_FAKE + * If the IPC Window is the E17 fake + */ + if (e17_fake_ipc) + return IPC_FAKE; + + if (ipc_win == None) { + /* The IPC window is missing. Wait for it to return or feh to be killed. */ + /* Only called once in the E17 case */ + for (; enl_ipc_get_win() == None;) { + if (e17_fake_ipc) + return IPC_FAKE; + else + sleep(1); + } + } + + if ((sigemptyset(&e17_ss) == -1) || sigaddset(&e17_ss, SIGALRM) == -1) { + weprintf("Failed to set up temporary E17 signal masks"); + return reply; + } + e17_sh.sa_handler = enl_ipc_timeout; + e17_sh.sa_mask = e17_ss; + e17_sh.sa_flags = 0; + if (sigaction(SIGALRM, &e17_sh, &feh_sh) == -1) { + weprintf("Failed to set up temporary E17 signal handler"); + return reply; + } + + for (; reply == IPC_TIMEOUT;) { + timeout = 0; + enl_ipc_send(msg); + for (; !(reply = enl_ipc_get(enl_wait_for_reply()));); + if (reply == IPC_TIMEOUT) { + /* We timed out. The IPC window must be AWOL. Reset and resend message. */ + D(("IPC timed out. IPC window has gone. Clearing ipc_win.\n")); + XSelectInput(disp, ipc_win, None); + ipc_win = None; + } + } + if (sigaction(SIGALRM, &feh_sh, NULL) == -1) { + weprintf("Failed to restore signal handler"); + } + return(reply); +} diff --git a/src/support.h b/src/wallpaper.h index bb17082..c836c0f 100644 --- a/src/support.h +++ b/src/wallpaper.h @@ -1,6 +1,7 @@ -/* support.h +/* wallpaper.h Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2020 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -23,8 +24,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef SUPPORT_H -#define SUPPORT_H +#ifndef WALLPAPER_H +#define WALLPAPER_H #include <X11/Xfuncproto.h> #include <X11/Intrinsic.h> /* Xlib, Xutil, Xresource, Xfuncproto */ @@ -41,16 +42,15 @@ 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 *); extern char *enl_send_and_wait(char *); extern void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled, - int fill, int desktop, int set); + int fill, int desktop, int for_screen); extern int feh_wm_get_num_desks(void); extern signed char feh_wm_get_wm_is_e(void); -void feh_wm_set_bg_file(char *file, unsigned char bgmode); +void feh_wm_set_bg_filelist(unsigned char bgmode); -_XFUNCPROTOEND #endif diff --git a/src/winwidget.c b/src/winwidget.c index 5ee7ace..809a0a6 100644 --- a/src/winwidget.c +++ b/src/winwidget.c @@ -1,6 +1,7 @@ /* winwidget.c Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2025 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -27,12 +28,17 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "filelist.h" #include "winwidget.h" #include "options.h" +#include "events.h" +#include "timers.h" + +#ifdef HAVE_INOTIFY +#include <sys/inotify.h> +#endif static void winwidget_unregister(winwidget win); static void winwidget_register(winwidget win); static winwidget winwidget_allocate(void); -static char bm_no_data[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; int window_num = 0; /* For window list */ winwidget *windows = NULL; /* List of windows to loop though */ @@ -41,8 +47,8 @@ static winwidget winwidget_allocate(void) { winwidget ret = NULL; - D_ENTER(4); ret = emalloc(sizeof(_winwidget)); + memset(ret, 0, sizeof(_winwidget)); ret->win = 0; ret->w = 0; @@ -56,9 +62,11 @@ static winwidget winwidget_allocate(void) ret->im = NULL; ret->name = NULL; ret->file = NULL; + ret->errstr = NULL; ret->type = WIN_TYPE_UNSET; ret->visible = 0; ret->caption_entry = 0; + ret->force_aliasing = opt.force_aliasing; /* Zoom stuff */ ret->mode = MODE_NORMAL; @@ -69,24 +77,25 @@ static winwidget winwidget_allocate(void) ret->im_x = 0; ret->im_y = 0; ret->zoom = 1.0; + ret->old_zoom = 1.0; ret->click_offset_x = 0; ret->click_offset_y = 0; - ret->im_click_offset_x = 0; - ret->im_click_offset_y = 0; ret->has_rotated = 0; - D_RETURN(4, ret); +#ifdef HAVE_INOTIFY + ret->inotify_wd = -1; +#endif + + return(ret); } -winwidget winwidget_create_from_image(Imlib_Image im, char *name, char type) +winwidget winwidget_create_from_image(Imlib_Image im, char type) { winwidget ret = NULL; - D_ENTER(4); - if (im == NULL) - D_RETURN(4, NULL); + return(NULL); ret = winwidget_allocate(); ret->type = type; @@ -95,66 +104,74 @@ winwidget winwidget_create_from_image(Imlib_Image im, char *name, char type) ret->w = ret->im_w = gib_imlib_image_get_width(ret->im); ret->h = ret->im_h = gib_imlib_image_get_height(ret->im); - if (name) - ret->name = estrdup(name); - else - ret->name = estrdup(PACKAGE); - - if (opt.full_screen) + if (opt.full_screen && (type != WIN_TYPE_THUMBNAIL)) { ret->full_screen = True; + } else if (opt.default_zoom) { + ret->zoom = 0.01 * opt.default_zoom; + ret->w *= ret->zoom; + ret->h *= ret->zoom; + } winwidget_create_window(ret, ret->w, ret->h); - winwidget_render_image(ret, 1, 1); + winwidget_render_image(ret, 1, 0); - D_RETURN(4, ret); + return(ret); } -winwidget winwidget_create_from_file(gib_list * list, char *name, char type) +winwidget winwidget_create_from_file(gib_list * list, char type) { winwidget ret = NULL; feh_file *file = FEH_FILE(list->data); - D_ENTER(4); - if (!file || !file->filename) - D_RETURN(4, NULL); + return(NULL); ret = winwidget_allocate(); ret->file = list; ret->type = type; - if (name) - ret->name = estrdup(name); - else - ret->name = estrdup(file->filename); - if (winwidget_loadimage(ret, file) == 0) { + if ((winwidget_loadimage(ret, file) == 0) || feh_should_ignore_image(ret->im)) { winwidget_destroy(ret); - D_RETURN(4, NULL); + return(NULL); } if (!ret->win) { ret->w = ret->im_w = gib_imlib_image_get_width(ret->im); ret->h = ret->im_h = gib_imlib_image_get_height(ret->im); - D(3, ("image is %dx%d pixels, format %s\n", ret->w, ret->h, gib_imlib_image_format(ret->im))); - if (opt.full_screen) + D(("image is %dx%d pixels, format %s\n", ret->w, ret->h, gib_imlib_image_format(ret->im))); + if (opt.full_screen) { ret->full_screen = True; + } else if (opt.default_zoom) { + ret->zoom = 0.01 * opt.default_zoom; + ret->w *= ret->zoom; + ret->h *= ret->zoom; + } winwidget_create_window(ret, ret->w, ret->h); - winwidget_render_image(ret, 1, 1); + winwidget_render_image(ret, 1, 0); } - D_RETURN(4, ret); + return(ret); } void winwidget_create_window(winwidget ret, int w, int h) { + XWindowAttributes window_attr; XSetWindowAttributes attr; XEvent ev; XClassHint *xch; MWMHints mwmhints; Atom prop = None; + pid_t pid; int x = 0; int y = 0; + char *tmpname; +#ifdef HOST_NAME_MAX + char hostname[HOST_NAME_MAX]; +#else /* ! HOST_NAME_MAX */ + long host_name_max; + char *hostname; +#endif /* HOST_NAME_MAX */ - D_ENTER(4); + D(("winwidget_create_window %dx%d\n", w, h)); if (ret->full_screen) { w = scr->width; @@ -164,6 +181,8 @@ void winwidget_create_window(winwidget ret, int w, int h) if (opt.xinerama && xinerama_screens) { w = xinerama_screens[xinerama_screen].width; h = xinerama_screens[xinerama_screen].height; + x = xinerama_screens[xinerama_screen].x_org; + y = xinerama_screens[xinerama_screen].y_org; } #endif /* HAVE_LIBXINERAMA */ } else if (opt.geom_flags) { @@ -203,6 +222,12 @@ void winwidget_create_window(winwidget ret, int w, int h) #endif /* HAVE_LIBXINERAMA */ } + if (opt.paused) { + tmpname = estrjoin(" ", ret->name, "[Paused]", NULL); + free(ret->name); + ret->name = tmpname; + } + ret->x = x; ret->y = y; ret->w = w; @@ -221,6 +246,7 @@ void winwidget_create_window(winwidget ret, int w, int h) KeyPressMask | KeyReleaseMask | ButtonMotionMask | ExposureMask | FocusChangeMask | PropertyChangeMask | VisibilityChangeMask; + memset(&mwmhints, 0, sizeof(mwmhints)); if (opt.borderless || ret->full_screen) { prop = XInternAtom(disp, "_MOTIF_WM_HINTS", True); if (prop == None) { @@ -233,14 +259,33 @@ void winwidget_create_window(winwidget ret, int w, int h) mwmhints.flags = MWM_HINTS_DECORATIONS; mwmhints.decorations = 0; } - } else - mwmhints.flags = 0; + } - 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, @@ -256,18 +301,43 @@ void winwidget_create_window(winwidget ret, int w, int h) ev.xclient.display = disp; ev.xclient.window = ret->win; ev.xclient.format = 32; - ev.xclient.data.l[0] = (ret->full_screen ? 1 : 0); + ev.xclient.data.l[0] = 1; ev.xclient.data.l[1] = prop_fs; XChangeProperty(disp, ret->win, prop_state, XA_ATOM, 32, PropModeReplace, (unsigned char *) &prop_fs, 1); } + pid = getpid(); + prop = XInternAtom(disp, "_NET_WM_PID", False); + XChangeProperty(disp, ret->win, prop, XA_CARDINAL, sizeof(pid_t) * 8, + PropModeReplace, (const unsigned char *)&pid, 1); + +#ifdef HOST_NAME_MAX + if (gethostname(hostname, HOST_NAME_MAX) == 0) { + hostname[HOST_NAME_MAX-1] = '\0'; + prop = XInternAtom(disp, "WM_CLIENT_MACHINE", False); + XChangeProperty(disp, ret->win, prop, XA_STRING, sizeof(char) * 8, + PropModeReplace, (unsigned char *)hostname, strlen(hostname)); + } +#else /* ! HOST_NAME_MAX */ + if ((host_name_max = sysconf(_SC_HOST_NAME_MAX)) != -1 ) { + if ((hostname = calloc(1, host_name_max + 1)) != NULL ) { + if (gethostname(hostname, host_name_max) == 0) { + prop = XInternAtom(disp, "WM_CLIENT_MACHINE", False); + XChangeProperty(disp, ret->win, prop, XA_STRING, sizeof(char) * 8, + PropModeReplace, (unsigned char *)hostname, strlen(hostname)); + } + free(hostname); + } + } +#endif /* HOST_NAME_MAX */ + XSetWMProtocols(disp, ret->win, &wmDeleteWindow, 1); winwidget_update_title(ret); xch = XAllocClassHint(); xch->res_name = "feh"; - xch->res_class = "feh"; + xch->res_class = opt.x11_class ? opt.x11_class : "feh"; XSetClassHint(disp, ret->win, xch); XFree(xch); @@ -281,16 +351,8 @@ void winwidget_create_window(winwidget ret, int w, int h) XSetWMNormalHints(disp, ret->win, &xsz); XMoveWindow(disp, ret->win, x, y); } - if (ret->full_screen && opt.hide_pointer) { - Cursor no_ptr; - XColor black, dummy; - Pixmap bm_no; - bm_no = XCreateBitmapFromData(disp, ret->win, bm_no_data, 8, 8); - XAllocNamedColor(disp, DefaultColormapOfScreen(DefaultScreenOfDisplay(disp)), "black", &black, &dummy); - - no_ptr = XCreatePixmapCursor(disp, bm_no, bm_no, &black, &black, 0, 0); - XDefineCursor(disp, ret->win, no_ptr); - } + if (opt.hide_pointer) + winwidget_set_pointer(ret, 0); /* set the icon name property */ XSetIconName(disp, ret->win, "feh"); @@ -298,40 +360,89 @@ void winwidget_create_window(winwidget ret, int w, int h) XSetCommand(disp, ret->win, cmdargv, cmdargc); winwidget_register(ret); - D_RETURN_(4); + + /* do not scale down a thumbnail list window, only those created from it */ + if (opt.geom_enabled && (ret->type != WIN_TYPE_THUMBNAIL)) { + opt.geom_w = w; + opt.geom_h = h; + opt.geom_flags |= WidthValue | HeightValue; + } + + return; } void winwidget_update_title(winwidget ret) { char *name; + Atom prop_name = XInternAtom(disp, "_NET_WM_NAME", False); + Atom prop_icon = XInternAtom(disp, "_NET_WM_ICON_NAME", False); + Atom prop_utf8 = XInternAtom(disp, "UTF8_STRING", False); - D_ENTER(4); - D(4, ("winwid->name = %s\n", ret->name)); + D(("winwid->name = %s\n", ret->name)); name = ret->name ? ret->name : "feh"; XStoreName(disp, ret->win, name); XSetIconName(disp, ret->win, name); - /* XFlush(disp); */ - D_RETURN_(4); + + XChangeProperty(disp, ret->win, prop_name, prop_utf8, 8, + PropModeReplace, (const unsigned char *)name, strlen(name)); + + XChangeProperty(disp, ret->win, prop_icon, prop_utf8, 8, + PropModeReplace, (const unsigned char *)name, strlen(name)); + + return; } -void winwidget_setup_pixmaps(winwidget winwid) +void winwidget_update_caption(winwidget winwid) { - D_ENTER(4); + if (opt.caption_path) { + /* TODO: Does someone understand the caching here. Is this the right + * approach now that I have broken this out into a separate function. -lsmith */ + + /* cache bg pixmap. during caption entry, multiple redraws are done + * because the caption overlay changes - the image doesn't though, so re- + * rendering that is a waste of time */ + if (winwid->caption_entry) { + GC gc; + if (winwid->bg_pmap_cache) + XFreePixmap(disp, winwid->bg_pmap_cache); + winwid->bg_pmap_cache = XCreatePixmap(disp, winwid->win, winwid->w, winwid->h, depth); + gc = XCreateGC(disp, winwid->win, 0, NULL); + XCopyArea(disp, winwid->bg_pmap, winwid->bg_pmap_cache, gc, 0, 0, winwid->w, winwid->h, 0, 0); + XFreeGC(disp, gc); + } + feh_draw_caption(winwid); + } + return; +} +void winwidget_setup_pixmaps(winwidget winwid) +{ if (winwid->full_screen) { if (!(winwid->bg_pmap)) { if (winwid->gc == None) { XGCValues gcval; - gcval.foreground = BlackPixel(disp, DefaultScreen(disp)); - winwid->gc = XCreateGC(disp, winwid->win, GCForeground, &gcval); + if (!opt.image_bg || !strcmp(opt.image_bg, "default")) { + gcval.foreground = BlackPixel(disp, DefaultScreen(disp)); + winwid->gc = XCreateGC(disp, winwid->win, GCForeground, &gcval); + } else if (!strcmp(opt.image_bg, "checks")) { + gcval.tile = feh_create_checks(); + gcval.fill_style = FillTiled; + winwid->gc = XCreateGC(disp, winwid->win, GCTile | GCFillStyle, &gcval); + } else { + XColor color; + Colormap cmap = DefaultColormap(disp, DefaultScreen(disp)); + XAllocNamedColor(disp, cmap, (char*) opt.image_bg, &color, &color); + gcval.foreground = color.pixel; + winwid->gc = XCreateGC(disp, winwid->win, GCForeground, &gcval); + } } winwid->bg_pmap = XCreatePixmap(disp, winwid->win, scr->width, scr->height, depth); } XFillRectangle(disp, winwid->bg_pmap, winwid->gc, 0, 0, scr->width, scr->height); } else { if (!winwid->bg_pmap || winwid->had_resize) { - D(4, ("recreating background pixmap (%dx%d)\n", winwid->w, winwid->h)); + D(("recreating background pixmap (%dx%d)\n", winwid->w, winwid->h)); if (winwid->bg_pmap) XFreePixmap(disp, winwid->bg_pmap); @@ -343,130 +454,79 @@ void winwidget_setup_pixmaps(winwidget winwid) winwid->had_resize = 0; } } - D_RETURN_(4); + return; } -void winwidget_render_image(winwidget winwid, int resize, int alias) +void winwidget_render_image(winwidget winwid, int resize, int force_alias) { int sx, sy, sw, sh, dx, dy, dw, dh; int calc_w, calc_h; - - D_ENTER(4); + int antialias = 0; if (!winwid->full_screen && resize) { - winwidget_resize(winwid, winwid->im_w, winwid->im_h); + if (opt.default_zoom) { + winwid->zoom = 0.01 * opt.default_zoom; + winwidget_resize(winwid, winwid->im_w * winwid->zoom, winwid->im_h * winwid->zoom, 0); + } else { + winwidget_resize(winwid, winwid->im_w, winwid->im_h, 0); + } winwidget_reset_image(winwid); } - /* bounds checks for panning */ - if (winwid->im_x > winwid->w) - winwid->im_x = winwid->w; - if (winwid->im_y > winwid->h) - winwid->im_y = winwid->h; + D(("winwidget_render_image resize %d force_alias %d im %dx%d\n", + resize, force_alias, winwid->im_w, winwid->im_h)); - winwidget_setup_pixmaps(winwid); + /* winwidget_setup_pixmaps(winwid) resets the winwid->had_resize flag */ + int had_resize = winwid->had_resize || resize; - if (!winwid->full_screen && ((gib_imlib_image_has_alpha(winwid->im)) || (opt.geom_flags) - || (winwid->im_x || winwid->im_y) || (winwid->zoom != 1.0) - || (winwid->w > winwid->im_w || winwid->h > winwid->im_h) - || (winwid->has_rotated))) - feh_draw_checks(winwid); + winwidget_setup_pixmaps(winwid); - if (!winwid->full_screen && opt.scale_down && ((winwid->w < winwid->im_w) - || (winwid->h < winwid->im_h))) { - D(2, ("scaling down image\n")); + if (had_resize && !opt.keep_zoom_vp && (winwid->type != WIN_TYPE_THUMBNAIL)) { + double required_zoom = 1.0; + feh_calc_needed_zoom(&required_zoom, winwid->im_w, winwid->im_h, winwid->w, winwid->h); - feh_calc_needed_zoom(&(winwid->zoom), winwid->im_w, winwid->im_h, winwid->w, winwid->h); - winwidget_resize(winwid, winwid->im_w * winwid->zoom, winwid->im_h * winwid->zoom); - } + winwid->zoom = opt.default_zoom ? (0.01 * opt.default_zoom) : 1.0; - if (resize && (winwid->full_screen || opt.geom_flags)) { - int smaller; /* Is the image smaller than screen? */ - int max_w = 0, max_h = 0; + if ((opt.scale_down || (winwid->full_screen && !opt.default_zoom)) + && winwid->zoom > required_zoom) + winwid->zoom = required_zoom; + else if ((opt.zoom_mode && required_zoom > 1) + && (!opt.default_zoom || required_zoom < winwid->zoom)) + winwid->zoom = required_zoom; - if (winwid->full_screen) { - max_w = scr->width; - max_h = scr->height; -#ifdef HAVE_LIBXINERAMA - if (opt.xinerama && xinerama_screens) { - max_w = xinerama_screens[xinerama_screen].width; - max_h = xinerama_screens[xinerama_screen].height; + if (opt.offset_flags & XValue) { + if (opt.offset_flags & XNegative) { + winwid->im_x = winwid->w - (winwid->im_w * winwid->zoom) - opt.offset_x; + } else { + winwid->im_x = - opt.offset_x * winwid->zoom; } -#endif /* HAVE_LIBXINERAMA */ } else { - if (opt.geom_flags & WidthValue) { - max_w = opt.geom_w; - } - if (opt.geom_flags & HeightValue) { - max_h = opt.geom_h; - } + winwid->im_x = (int) (winwid->w - (winwid->im_w * winwid->zoom)) >> 1; } - - D(4, ("Calculating for fullscreen/fixed geom render\n")); - smaller = ((winwid->im_w < max_w) - && (winwid->im_h < max_h)); - - if (!smaller || opt.auto_zoom) { - double ratio = 0.0; - - /* Image is larger than the screen (so wants shrinking), or it's - smaller but wants expanding to fill it */ - ratio = feh_calc_needed_zoom(&(winwid->zoom), winwid->im_w, winwid->im_h, max_w, max_h); - - /* contributed by Jens Laas <jens.laas@data.slu.se> - * What it does: - * zooms images by a fixed amount but never larger than the screen. - * - * Why: - * This is nice if you got a collection of images where some - * are small and can stand a small zoom. Large images are unaffected. - * - * When does it work, and how? - * You have to be in fullscreen mode _and_ have auto-zoom turned on. - * "feh -FZ --zoom 130 imagefile" will do the trick. - * -zoom percent - the new switch. - * 100 = orignal size, - * 130 is 30% larger. - */ - if (opt.default_zoom) { - double old_zoom = winwid->zoom; - - winwid->zoom = 0.01 * opt.default_zoom; - if ((winwid->im_h * winwid->zoom) > max_h) - winwid->zoom = old_zoom; - if ((winwid->im_w * winwid->zoom) > max_w) - winwid->zoom = old_zoom; - - winwid->im_x = ((int) - (max_w - (winwid->im_w * winwid->zoom))) >> 1; - winwid->im_y = ((int) - (max_h - (winwid->im_h * winwid->zoom))) >> 1; + if (opt.offset_flags & YValue) { + if (opt.offset_flags & YNegative) { + winwid->im_y = winwid->h - (winwid->im_h * winwid->zoom) - opt.offset_y; } else { - if (ratio > 1.0) { - /* height is the factor */ - winwid->im_x = 0; - winwid->im_y = ((int) - (max_h - (winwid->im_h * winwid->zoom))) >> 1; - } else { - /* width is the factor */ - winwid->im_x = ((int) - (max_w - (winwid->im_w * winwid->zoom))) >> 1; - winwid->im_y = 0; - } + winwid->im_y = - opt.offset_y * winwid->zoom; } } else { - /* my modification to jens hack, allow --zoom without auto-zoom mode */ - if (opt.default_zoom) { - winwid->zoom = 0.01 * opt.default_zoom; - } else { - winwid->zoom = 1.0; - } - /* Just center the image in the window */ - winwid->im_x = (int) (max_w - (winwid->im_w * winwid->zoom)) >> 1; - winwid->im_y = (int) (max_h - (winwid->im_h * winwid->zoom)) >> 1; + winwid->im_y = (int) (winwid->h - (winwid->im_h * winwid->zoom)) >> 1; } } + winwid->had_resize = 0; + + if (opt.keep_zoom_vp) + winwidget_sanitise_offsets(winwid); + + if (!winwid->full_screen && ((gib_imlib_image_has_alpha(winwid->im)) + || (opt.geom_flags & (WidthValue | HeightValue)) + || (winwid->im_x || winwid->im_y) + || (winwid->w > winwid->im_w * winwid->zoom) + || (winwid->h > winwid->im_h * winwid->zoom) + || (winwid->has_rotated))) + feh_draw_checks(winwid); + /* Now we ensure only to render the area we're looking at */ dx = winwid->im_x; dy = winwid->im_y; @@ -476,71 +536,85 @@ void winwidget_render_image(winwidget winwid, int resize, int alias) dy = 0; if (winwid->im_x < 0) - sx = 0 - (winwid->im_x / winwid->zoom); + sx = 0 - lround(winwid->im_x / winwid->zoom); else sx = 0; if (winwid->im_y < 0) - sy = 0 - (winwid->im_y / winwid->zoom); + sy = 0 - lround(winwid->im_y / winwid->zoom); else sy = 0; - calc_w = winwid->im_w * winwid->zoom; - calc_h = winwid->im_h * winwid->zoom; + calc_w = lround(winwid->im_w * winwid->zoom); + calc_h = lround(winwid->im_h * winwid->zoom); dw = (winwid->w - winwid->im_x); dh = (winwid->h - winwid->im_y); - if (calc_w < dw) + + D(("sx: %4d sy: %4d sw: sh: dx: %4d dy: %4d dw: %4d dh: %4d zoom: %f\n", + sx, sy, sw, sh, dx, dy, dw, dh, winwid->zoom)); + + if (calc_w < dw) { dw = calc_w; - if (calc_h < dh) + } + if (calc_h < dh) { dh = calc_h; + } if (dw > winwid->w) dw = winwid->w; if (dh > winwid->h) dh = winwid->h; - sw = dw / winwid->zoom; - sh = dh / winwid->zoom; + sw = lround(dw / winwid->zoom); + sh = lround(dh / winwid->zoom); - D(5, - ("sx: %d sy: %d sw: %d sh: %d dx: %d dy: %d dw: %d dh: %d zoom: %f\n", + D(("sx: %4d sy: %4d sw: %4d sh: %4d dx: %4d dy: %4d dw: %4d dh: %4d zoom: %f\n", sx, sy, sw, sh, dx, dy, dw, dh, winwid->zoom)); - D(5, ("winwidget_render(): winwid->im_angle = %f\n", winwid->im_angle)); + if ((winwid->zoom != 1.0 || winwid->has_rotated) && !force_alias && !winwid->force_aliasing) + antialias = 1; + + D(("winwidget_render(): winwid->im_angle = %f\n", winwid->im_angle)); if (winwid->has_rotated) gib_imlib_render_image_part_on_drawable_at_size_with_rotation - (winwid->bg_pmap, winwid->im, sx, sy, sw, sh, dx, dy, dw, dh, winwid->im_angle, 1, 1, alias); + (winwid->bg_pmap, winwid->im, sx, sy, sw, sh, dx, dy, dw, dh, + winwid->im_angle, 1, 1, antialias); else gib_imlib_render_image_part_on_drawable_at_size(winwid->bg_pmap, winwid->im, sx, sy, sw, sh, dx, dy, dw, dh, 1, - gib_imlib_image_has_alpha(winwid->im), alias); - if (opt.caption_path) { - /* cache bg pixmap. during caption entry, multiple redraws are done - * because the caption overlay changes - the image doesn't though, so re- - * rendering that is a waste of time */ - if (winwid->caption_entry) { - GC gc; - if (winwid->bg_pmap_cache) - XFreePixmap(disp, winwid->bg_pmap_cache); - winwid->bg_pmap_cache = XCreatePixmap(disp, winwid->win, winwid->w, winwid->h, depth); - gc = XCreateGC(disp, winwid->win, 0, NULL); - XCopyArea(disp, winwid->bg_pmap, winwid->bg_pmap_cache, gc, 0, 0, winwid->w, winwid->h, 0, 0); - XFreeGC(disp, gc); + gib_imlib_image_has_alpha(winwid->im), + antialias); + + if (opt.mode == MODE_NORMAL) { + if (opt.caption_path) + winwidget_update_caption(winwid); + if (opt.draw_filename) + feh_draw_filename(winwid); +#ifdef HAVE_LIBEXIF + if (opt.draw_exif) + feh_draw_exif(winwid); +#endif + if (opt.draw_actions) + feh_draw_actions(winwid); + if (opt.draw_info && opt.info_cmd) + feh_draw_info(winwid); + if (winwid->errstr) + feh_draw_errstr(winwid); + if (winwid->file != NULL) { + if (opt.title && winwid->type != WIN_TYPE_THUMBNAIL_VIEWER) { + winwidget_rename(winwid, feh_printf(opt.title, FEH_FILE(winwid->file->data), winwid)); + } else if (opt.thumb_title && winwid->type == WIN_TYPE_THUMBNAIL_VIEWER) { + winwidget_rename(winwid, feh_printf(opt.thumb_title, FEH_FILE(winwid->file->data), winwid)); + } } - feh_draw_caption(winwid); - } - - if (opt.draw_filename) - feh_draw_filename(winwid); - if (opt.draw_actions) - feh_draw_actions(winwid); - if ((opt.mode == MODE_ZOOM) && !alias) + } else if ((opt.mode == MODE_ZOOM) && !antialias) feh_draw_zoom(winwid); + XSetWindowBackgroundPixmap(disp, winwid->win, winwid->bg_pmap); XClearWindow(disp, winwid->win); - D_RETURN_(4); + return; } void winwidget_render_image_cached(winwidget winwid) @@ -558,6 +632,8 @@ void winwidget_render_image_cached(winwidget winwid) feh_draw_filename(winwid); if (opt.draw_actions) feh_draw_actions(winwid); + if (opt.draw_info && opt.info_cmd) + feh_draw_info(winwid); XSetWindowBackgroundPixmap(disp, winwid->win, winwid->bg_pmap); XClearWindow(disp, winwid->win); } @@ -566,16 +642,17 @@ double feh_calc_needed_zoom(double *zoom, int orig_w, int orig_h, int dest_w, in { double ratio = 0.0; - D_ENTER(4); - ratio = ((double) orig_w / orig_h) / ((double) dest_w / dest_h); + if (opt.zoom_mode == ZOOM_MODE_FILL) + ratio = 1.0 / ratio; + if (ratio > 1.0) *zoom = ((double) dest_w / orig_w); else *zoom = ((double) dest_h / orig_h); - D_RETURN(4, ratio); + return(ratio); } Pixmap feh_create_checks(void) @@ -583,47 +660,28 @@ Pixmap feh_create_checks(void) static Pixmap checks_pmap = None; Imlib_Image checks = NULL; - D_ENTER(4); if (checks_pmap == None) { - int onoff, x, y; - checks = imlib_create_image(16, 16); if (!checks) eprintf("Unable to create a teeny weeny imlib image. I detect problems"); - if (strncmp(opt.image_bg, "white", 5) == 0) - gib_imlib_image_fill_rectangle(checks, 0, 0, 16, 16, 255, 255, 255, 255); - else if (strncmp(opt.image_bg, "black", 5) == 0) - gib_imlib_image_fill_rectangle(checks, 0, 0, 16, 16, 0, 0, 0, 255); - else { - for (y = 0; y < 16; y += 8) { - onoff = (y / 8) & 0x1; - for (x = 0; x < 16; x += 8) { - if (onoff) - gib_imlib_image_fill_rectangle(checks, x, y, 8, 8, 144, 144, 144, 255); - else - gib_imlib_image_fill_rectangle(checks, x, y, 8, 8, 100, 100, 100, 255); - onoff++; - if (onoff == 2) - onoff = 0; - } - } + if (!opt.image_bg || !strcmp(opt.image_bg, "default") || !strcmp(opt.image_bg, "checks")) { + gib_imlib_image_fill_rectangle(checks, 0, 0, 16, 16, 144, 144, 144, 255); + gib_imlib_image_fill_rectangle(checks, 0, 0, 8, 8, 100, 100, 100, 255); + gib_imlib_image_fill_rectangle(checks, 8, 8, 8, 8, 100, 100, 100, 255); + } else { + XColor color; + Colormap cmap = DefaultColormap(disp, DefaultScreen(disp)); + XAllocNamedColor(disp, cmap, (char*) opt.image_bg, &color, &color); + gib_imlib_image_fill_rectangle(checks, 0, 0, 16, 16, color.red, color.green, color.blue, 255); } checks_pmap = XCreatePixmap(disp, root, 16, 16, depth); gib_imlib_render_image_on_drawable(checks_pmap, checks, 0, 0, 1, 0, 0); gib_imlib_free_image_and_decache(checks); } - D_RETURN(4, checks_pmap); -} - -void winwidget_clear_background(winwidget w) -{ - D_ENTER(4); - XSetWindowBackgroundPixmap(disp, w->win, feh_create_checks()); - /* XClearWindow(disp, w->win); */ - D_RETURN_(4); + return(checks_pmap); } void feh_draw_checks(winwidget win) @@ -631,19 +689,17 @@ void feh_draw_checks(winwidget win) static GC gc = None; XGCValues gcval; - D_ENTER(4); if (gc == None) { gcval.tile = feh_create_checks(); gcval.fill_style = FillTiled; gc = XCreateGC(disp, win->win, GCTile | GCFillStyle, &gcval); } XFillRectangle(disp, win->bg_pmap, gc, 0, 0, win->w, win->h); - D_RETURN_(4); + return; } void winwidget_destroy_xwin(winwidget winwid) { - D_ENTER(4); if (winwid->win) { winwidget_unregister(winwid); XDestroyWindow(disp, winwid->win); @@ -652,177 +708,295 @@ void winwidget_destroy_xwin(winwidget winwid) XFreePixmap(disp, winwid->bg_pmap); winwid->bg_pmap = None; } - D_RETURN_(4); + return; } void winwidget_destroy(winwidget winwid) { - D_ENTER(4); +#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); - if ((winwid->type == WIN_TYPE_ABOUT) && winwid->file) { - feh_file_free(FEH_FILE(winwid->file->data)); - free(winwid->file); - } if (winwid->gc) XFreeGC(disp, winwid->gc); if (winwid->im) gib_imlib_free_image_and_decache(winwid->im); free(winwid); - D_RETURN_(4); + return; } +#ifdef HAVE_INOTIFY +void winwidget_inotify_remove(winwidget winwid) +{ + if (winwid->inotify_wd >= 0) { + D(("Removing inotify watch\n")); + if (inotify_rm_watch(opt.inotify_fd, winwid->inotify_wd)) + weprintf("inotify_rm_watch failed:"); + winwid->inotify_wd = -1; + } +} +#endif + +#ifdef HAVE_INOTIFY +void winwidget_inotify_add(winwidget winwid, feh_file * file) +{ + if (opt.auto_reload && !path_is_url(file->filename)) { + D(("Adding inotify watch for %s\n", file->filename)); + char dir[PATH_MAX]; + feh_file_dirname(dir, file, PATH_MAX); + + /* + * Handle files without directory part, e.g. "feh somefile.jpg". + * These always reside in the current directory. + */ + if (dir[0] == '\0') { + dir[0] = '.'; + dir[1] = '\0'; + } + winwid->inotify_wd = inotify_add_watch(opt.inotify_fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO); + if (winwid->inotify_wd < 0) + weprintf("inotify_add_watch failed:"); + } +} +#endif + +#ifdef HAVE_INOTIFY +#define INOTIFY_BUFFER_LEN (1024 * (sizeof (struct inotify_event)) + 16) +void feh_event_handle_inotify(void) +{ + D(("Received inotify events\n")); + char buf[INOTIFY_BUFFER_LEN]; + int i = 0; + int len = read (opt.inotify_fd, buf, INOTIFY_BUFFER_LEN); + if (len < 0) { + if (errno != EINTR) + eprintf("inotify event read failed"); + } else if (!len) + eprintf("inotify event read failed"); + while (i < len) { + struct inotify_event *event; + event = (struct inotify_event *) &buf[i]; + for (int j = 0; j < window_num; j++) { + if(windows[j]->inotify_wd == event->wd) { + if (event->mask & IN_IGNORED) { + D(("inotify watch was implicitly removed\n")); + windows[j]->inotify_wd = -1; + } else if (event->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)) { + if (strcmp(event->name, FEH_FILE(windows[j]->file->data)->name) == 0) { + D(("inotify says file changed\n")); + feh_reload_image(windows[j], 0, 0); + } + } + break; + } + } + i += sizeof(struct inotify_event) + event->len; + } +} +#endif + void winwidget_destroy_all(void) { int i; - D_ENTER(4); /* Have to DESCEND the list here, 'cos of the way _unregister works */ for (i = window_num - 1; i >= 0; i--) winwidget_destroy(windows[i]); - D_RETURN_(4); + return; } -void winwidget_rerender_all(int resize, int alias) +void winwidget_rerender_all(int resize) { int i; - D_ENTER(4); /* Have to DESCEND the list here, 'cos of the way _unregister works */ for (i = window_num - 1; i >= 0; i--) - winwidget_render_image(windows[i], resize, alias); - D_RETURN_(4); + winwidget_render_image(windows[i], resize, 0); + return; } winwidget winwidget_get_first_window_of_type(unsigned int type) { int i; - D_ENTER(4); for (i = 0; i < window_num; i++) if (windows[i]->type == type) - D_RETURN(4, windows[i]); - D_RETURN(4, NULL); + return(windows[i]); + return(NULL); } int winwidget_loadimage(winwidget winwid, feh_file * file) { - D_ENTER(4); - D(4, ("filename %s\n", file->filename)); - D_RETURN(4, feh_load_image(&(winwid->im), file)); + D(("filename %s\n", file->filename)); +#ifdef HAVE_INOTIFY + winwidget_inotify_remove(winwid); +#endif + int res = feh_load_image(&(winwid->im), file); +#ifdef HAVE_INOTIFY + if (res) { + winwidget_inotify_add(winwid, file); + } +#endif + return(res); } void winwidget_show(winwidget winwid) { XEvent ev; - D_ENTER(4); - /* feh_debug_print_winwid(winwid); */ if (!winwid->visible) { XMapWindow(disp, winwid->win); if (opt.full_screen) XMoveWindow(disp, winwid->win, 0, 0); /* wait for the window to map */ - D(4, ("Waiting for window to map\n")); + D(("Waiting for window to map\n")); XMaskEvent(disp, StructureNotifyMask, &ev); - D(4, ("Window mapped\n")); + winwidget_get_geometry(winwid, NULL); + + /* Unfortunately, StructureNotifyMask does not only mask + * the events of type MapNotify (which we want to mask here) + * but also such of type ConfigureNotify (and others, see + * https://tronche.com/gui/x/xlib/events/processing-overview.html), + * which should be handled, especially on tiling wm's. To + * remedy this, the handler is executed explicitly: + */ + if (ev.type == ConfigureNotify) + feh_event_handle_ConfigureNotify(&ev); + D(("Window mapped\n")); winwid->visible = 1; } - D_RETURN_(4); -} - -int winwidget_count(void) -{ - D_ENTER(4); - D_RETURN(4, window_num); + return; } void winwidget_move(winwidget winwid, int x, int y) { - D_ENTER(4); if (winwid && ((winwid->x != x) || (winwid->y != y))) { - winwid->x = x; - winwid->y = y; winwid->x = (x > scr->width) ? scr->width : x; winwid->y = (y > scr->height) ? scr->height : y; XMoveWindow(disp, winwid->win, winwid->x, winwid->y); XFlush(disp); } else { - D(4, ("No move actually needed\n")); + D(("No move actually needed\n")); } - D_RETURN_(4); + return; } -void winwidget_resize(winwidget winwid, int w, int h) +void winwidget_resize(winwidget winwid, int w, int h, int force_resize) { - Window ignored_window; XWindowAttributes attributes; - int tc_x, tc_y; + int tc_x, tc_y, px, py; + int scr_width = scr->width; + int scr_height = scr->height; + + /* discarded */ + Window dw; + int di, i; + unsigned int du; + + XGetWindowAttributes(disp, winwid->win, &attributes); - D_ENTER(4); - if (opt.geom_flags) { +#ifdef HAVE_LIBXINERAMA + if (opt.xinerama && xinerama_screens) { + xinerama_screen = 0; + XQueryPointer(disp, root, &dw, &dw, &px, &py, &di, &di, &du); + for (i = 0; i < num_xinerama_screens; i++) { + if (XY_IN_RECT(px, py, + xinerama_screens[i].x_org, + xinerama_screens[i].y_org, + xinerama_screens[i].width, + xinerama_screens[i].height)) { + xinerama_screen = i; + break; + } + + } + if (opt.xinerama_index >= 0) + xinerama_screen = opt.xinerama_index; + + scr_width = xinerama_screens[xinerama_screen].width; + scr_height = xinerama_screens[xinerama_screen].height; + } +#endif + + + D((" x %d y %d w %d h %d\n", attributes.x, attributes.y, winwid->w, + winwid->h)); + + if ((opt.geom_flags & (WidthValue | HeightValue)) && !force_resize) { winwid->had_resize = 1; return; } if (winwid && ((winwid->w != w) || (winwid->h != h))) { - D(4, ("Really doing a resize\n")); - /* winwidget_clear_background(winwid); */ if (opt.screen_clip) { - winwid->w = (w > scr->width) ? scr->width : w; - winwid->h = (h > scr->height) ? scr->height : h; + double required_zoom = winwid->zoom; + if (opt.scale_down && !opt.keep_zoom_vp) { + int max_w = (w > scr_width) ? scr_width : w; + int max_h = (h > scr_height) ? scr_height : h; + feh_calc_needed_zoom(&required_zoom, winwid->im_w, winwid->im_h, max_w, max_h); + } + int desired_w = winwid->im_w * required_zoom; + int desired_h = winwid->im_h * required_zoom; + winwid->w = (desired_w > scr_width) ? scr_width : desired_w; + winwid->h = (desired_h > scr_height) ? scr_height : desired_h; } - /* XResizeWindow(disp, winwid->win, winwid->w, winwid->h); */ - XGetWindowAttributes(disp, winwid->win, &attributes); - XTranslateCoordinates(disp, winwid->win, attributes.root, - -attributes.border_width - - attributes.x, - -attributes.border_width - attributes.y, &tc_x, &tc_y, &ignored_window); - winwid->x = tc_x; - winwid->y = tc_y; - XMoveResizeWindow(disp, winwid->win, tc_x, tc_y, winwid->w, winwid->h); + if (winwid->full_screen) { + XTranslateCoordinates(disp, winwid->win, attributes.root, + -attributes.border_width - attributes.x, + -attributes.border_width - attributes.y, &tc_x, &tc_y, &dw); + winwid->x = tc_x; + winwid->y = tc_y; + XMoveResizeWindow(disp, winwid->win, tc_x, tc_y, winwid->w, winwid->h); + } else + XResizeWindow(disp, winwid->win, winwid->w, winwid->h); winwid->had_resize = 1; XFlush(disp); -#ifdef HAVE_LIBXINERAMA - if (opt.xinerama && xinerama_screens) { - int i; - - for (i = 0; i < num_xinerama_screens; i++) { - xinerama_screen = 0; - if (XY_IN_RECT(winwid->x, winwid->y, - xinerama_screens[i].x_org, - xinerama_screens[i].y_org, - xinerama_screens[i].width, xinerama_screens[i].height)) { - xinerama_screen = i; - break; - } - - } + /* + * Note: + * While calling winwidget_get_geometry(winwid, NULL); at this point + * would help alleviate flashing issues that can occur when feh has + * to render a window two times in a row, or renders the initial image + * with a resolution that differs from the one that is needed to + * accomodate the resize. + * + * However, it would also break --scale-down in floating setups. As + * flashing is less annoying, we do not call winwidget_get_geometry. + * here. + */ + + if (force_resize && (opt.geom_flags & (WidthValue | HeightValue)) + && (winwid->type != WIN_TYPE_THUMBNAIL)) { + opt.geom_w = winwid->w; + opt.geom_h = winwid->h; } -#endif /* HAVE_LIBXINERAMA */ + + D(("-> x %d y %d w %d h %d\n", winwid->x, winwid->y, winwid->w, + winwid->h)); } else { - D(4, ("No resize actually needed\n")); + D(("No resize actually needed\n")); } - D_RETURN_(4); + return; } void winwidget_hide(winwidget winwid) { - D_ENTER(4); XUnmapWindow(disp, winwid->win); winwid->visible = 0; - D_RETURN_(4); + return; } static void winwidget_register(winwidget win) { - D_ENTER(4); - D(5, ("window %p\n", win)); + D(("window %p\n", win)); window_num++; if (windows) windows = erealloc(windows, window_num * sizeof(winwidget)); @@ -831,14 +1005,13 @@ static void winwidget_register(winwidget win) windows[window_num - 1] = win; XSaveContext(disp, win->win, xid_context, (XPointer) win); - D_RETURN_(4); + return; } static void winwidget_unregister(winwidget win) { int i, j; - D_ENTER(4); for (i = 0; i < window_num; i++) { if (windows[i] == win) { for (j = i; j < window_num - 1; j++) @@ -853,17 +1026,16 @@ static void winwidget_unregister(winwidget win) } } XDeleteContext(disp, win->win, xid_context); - D_RETURN_(4); + return; } winwidget winwidget_get_from_window(Window win) { winwidget ret = NULL; - D_ENTER(4); if (XFindContext(disp, win, xid_context, (XPointer *) & ret) != XCNOENT) - D_RETURN(4, ret); - D_RETURN(4, NULL); + return(ret); + return(NULL); } void winwidget_rename(winwidget winwid, char *newname) @@ -871,7 +1043,6 @@ void winwidget_rename(winwidget winwid, char *newname) /* newname == NULL -> update current title */ char *p_str; - D_ENTER(4); if (newname == NULL) newname = estrdup(winwid->name ? winwid->name : ""); if (winwid->name) @@ -891,18 +1062,18 @@ void winwidget_rename(winwidget winwid, char *newname) *p_str = '\0'; winwidget_update_title(winwid); - D_RETURN_(4); + return; } void winwidget_free_image(winwidget w) { - D_ENTER(4); - if (w->im) - gib_imlib_free_image_and_decache(w->im); + if (w->im) { + gib_imlib_free_image(w->im); + } w->im = NULL; w->im_w = 0; w->im_h = 0; - D_RETURN_(4); + return; } void feh_debug_print_winwid(winwidget w) @@ -911,25 +1082,55 @@ void feh_debug_print_winwid(winwidget w) "h = %d\n" "im_w = %d\n" "im_h = %d\n" "im_angle = %f\n" "type = %d\n" "had_resize = %d\n" "im = %p\n" "GC = %p\n" "pixmap = %ld\n" "name = %s\n" "file = %p\n" "mode = %d\n" - "im_x = %d\n" "im_y = %d\n" "zoom = %f\n" + "im_x = %d\n" "im_y = %d\n" "zoom = %f\n" "old_zoom = %f\n" "click_offset_x = %d\n" "click_offset_y = %d\n" - "im_click_offset_x = %d\n" "im_click_offset_y = %d\n" "has_rotated = %d\n", (void *)w, w->win, w->w, w->h, w->im_w, w->im_h, w->im_angle, w->type, w->had_resize, w->im, (void *)w->gc, w->bg_pmap, w->name, (void *)w->file, w->mode, w->im_x, w->im_y, - w->zoom, w->click_offset_x, w->click_offset_y, - w->im_click_offset_x, w->im_click_offset_y, w->has_rotated); + w->zoom, w->old_zoom, w->click_offset_x, w->click_offset_y, + w->has_rotated); } void winwidget_reset_image(winwidget winwid) { - D_ENTER(4); - winwid->zoom = 1.0; - winwid->im_x = 0; - winwid->im_y = 0; + if (!opt.keep_zoom_vp) { + winwid->zoom = 1.0; + winwid->old_zoom = 1.0; + winwid->im_x = 0; + winwid->im_y = 0; + } winwid->im_angle = 0.0; winwid->has_rotated = 0; - D_RETURN_(4); + return; +} + +void winwidget_center_image(winwidget winwid) +{ + int scr_width, scr_height; + + scr_width = scr->width; + scr_height = scr->height; + +#ifdef HAVE_LIBXINERAMA + if (opt.xinerama && xinerama_screens) { + scr_width = xinerama_screens[xinerama_screen].width; + scr_height = xinerama_screens[xinerama_screen].height; + } +#endif /* HAVE_LIBXINERAMA */ + + if (winwid->full_screen) { + winwid->im_x = (scr_width - lround(winwid->im_w * winwid->zoom)) >> 1; + winwid->im_y = (scr_height - lround(winwid->im_h * winwid->zoom)) >> 1; + } else { + if (opt.geom_flags & WidthValue) + winwid->im_x = ((int)opt.geom_w - lround(winwid->im_w * winwid->zoom)) >> 1; + else + winwid->im_x = (int) (winwid->w - lround(winwid->im_w * winwid->zoom)) >> 1; + if (opt.geom_flags & HeightValue) + winwid->im_y = ((int)opt.geom_h - lround(winwid->im_h * winwid->zoom)) >> 1; + else + winwid->im_y = (int) (winwid->h - lround(winwid->im_h * winwid->zoom)) >> 1; + } } void winwidget_sanitise_offsets(winwidget winwid) @@ -937,8 +1138,6 @@ void winwidget_sanitise_offsets(winwidget winwid) int far_left, far_top; int min_x, max_x, max_y, min_y; - D_ENTER(4); - far_left = winwid->w - (winwid->im_w * winwid->zoom); far_top = winwid->h - (winwid->im_h * winwid->zoom); @@ -965,41 +1164,58 @@ void winwidget_sanitise_offsets(winwidget winwid) if (winwid->im_y < min_y) winwid->im_y = min_y; - D_RETURN_(4); + return; } void winwidget_size_to_image(winwidget winwid) { - D_ENTER(4); - winwidget_resize(winwid, winwid->im_w * winwid->zoom, winwid->im_h * winwid->zoom); + winwidget_resize(winwid, winwid->im_w * winwid->zoom, winwid->im_h * winwid->zoom, 1); winwid->im_x = winwid->im_y = 0; - winwidget_render_image(winwid, 0, 1); - D_RETURN_(4); + winwidget_render_image(winwid, 0, 0); + return; +} + +void winwidget_set_pointer(winwidget winwid, int visible) +{ + Cursor no_ptr; + XColor black, dummy; + Pixmap bm_no; + static char bm_no_data[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + if (visible) + XUndefineCursor(disp, winwid->win); + else { + bm_no = XCreateBitmapFromData(disp, winwid->win, bm_no_data, 8, 8); + XAllocNamedColor(disp, DefaultColormapOfScreen(DefaultScreenOfDisplay(disp)), "black", &black, &dummy); + + no_ptr = XCreatePixmapCursor(disp, bm_no, bm_no, &black, &black, 0, 0); + XDefineCursor(disp, winwid->win, no_ptr); + } } int winwidget_get_width(winwidget winwid) { int rect[4]; - D_ENTER(4); winwidget_get_geometry(winwid, rect); - D_RETURN(4, rect[2]); + return(rect[2]); } int winwidget_get_height(winwidget winwid) { int rect[4]; - D_ENTER(4); winwidget_get_geometry(winwid, rect); - D_RETURN(4, rect[3]); + return(rect[3]); } void winwidget_get_geometry(winwidget winwid, int *rect) { unsigned int bw, bp; Window child; - D_ENTER(4); + + int inner_rect[4]; + if (!rect) - return; + rect = inner_rect; XGetGeometry(disp, winwid->win, &root, &(rect[0]), &(rect[1]), (unsigned int *)&(rect[2]), (unsigned int *)&(rect[3]), &bw, &bp); @@ -1011,7 +1227,7 @@ void winwidget_get_geometry(winwidget winwid, int *rect) winwid->y = rect[1]; winwid->w = rect[2]; winwid->h = rect[3]; - D_RETURN_(4); + return; } void winwidget_show_menu(winwidget winwid) @@ -1021,11 +1237,7 @@ void winwidget_show_menu(winwidget winwid) Window r; XQueryPointer(disp, winwid->win, &r, &r, &x, &y, &b, &b, &c); - if (winwid->type == WIN_TYPE_ABOUT) { - if (!menu_about_win) - feh_menu_init_about_win(); - feh_menu_show_at_xy(menu_about_win, winwid, x, y); - } else if (winwid->type == WIN_TYPE_SINGLE) { + if (winwid->type == WIN_TYPE_SINGLE) { if (!menu_single_win) feh_menu_init_single_win(); feh_menu_show_at_xy(menu_single_win, winwid, x, y); diff --git a/src/winwidget.h b/src/winwidget.h index 47ebfcc..0894b5a 100644 --- a/src/winwidget.h +++ b/src/winwidget.h @@ -1,6 +1,7 @@ /* winwidget.h Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2020 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -60,16 +61,15 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* Motif window hints */ typedef struct _mwmhints { - CARD32 flags; - CARD32 functions; - CARD32 decorations; - INT32 input_mode; - CARD32 status; + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long input_mode; + unsigned long status; } MWMHints; enum win_type { WIN_TYPE_UNSET, WIN_TYPE_SLIDESHOW, WIN_TYPE_SINGLE, - WIN_TYPE_ABOUT, WIN_TYPE_THUMBNAIL, WIN_TYPE_THUMBNAIL_VIEWER }; @@ -81,6 +81,7 @@ struct __winwidget { int h; int im_w; int im_h; + int force_aliasing; double im_angle; enum win_type type; unsigned char had_resize, full_screen; @@ -91,51 +92,69 @@ struct __winwidget { char *name; gib_list *file; unsigned char visible; + char *errstr; - /* Stuff for zooming */ + /* panning, zooming, etc. */ unsigned char mode; - unsigned char caption_entry; /* are we in caption entry mode? */ + unsigned char caption_entry; + + /* image offset from window top left */ + int im_x; + int im_y; + + /* From 0 (not visible) to 1.00 (actual size) + * all the way up to INT_MAX (eww) + */ + double zoom; + double old_zoom; - /* New stuff */ - int im_x; /* image offset from window top left */ - int im_y; /* image offset from window top left */ - double zoom; /* From 0 (not visible) to 100 (actual size) - all the way up to INT_MAX (ouch) */ int click_offset_x; int click_offset_y; int im_click_offset_x; int im_click_offset_y; + time_t click_start_time; unsigned char has_rotated; + +#ifdef HAVE_INOTIFY + int inotify_wd; +#endif }; +#ifdef HAVE_INOTIFY +void winwidget_inotify_remove(winwidget winwid); +void winwidget_inotify_add(winwidget winwid, feh_file * file); +#endif + int winwidget_loadimage(winwidget winwid, feh_file * filename); void winwidget_show(winwidget winwid); void winwidget_show_menu(winwidget winwid); void winwidget_hide(winwidget winwid); void winwidget_destroy_all(void); void winwidget_free_image(winwidget w); -void winwidget_render_image(winwidget winwid, int resize, int alias); +void winwidget_center_image(winwidget w); +void winwidget_render_image(winwidget winwid, int resize, int force_alias); void winwidget_rotate_image(winwidget winid, double angle); void winwidget_move(winwidget winwid, int x, int y); -void winwidget_resize(winwidget winwid, int w, int h); +void winwidget_resize(winwidget winwid, int w, int h, int force_resize); void winwidget_setup_pixmaps(winwidget winwid); void winwidget_update_title(winwidget ret); -void winwidget_rerender_all(int resize, int alias); +void winwidget_update_caption(winwidget winwid); +void winwidget_rerender_all(int resize); void winwidget_destroy_xwin(winwidget winwid); -int winwidget_count(void); + +void winwidget_set_pointer(winwidget winwid, int visible); void winwidget_get_geometry(winwidget winwid, int *rect); int winwidget_get_width(winwidget winwid); int winwidget_get_height(winwidget winwid); winwidget winwidget_get_from_window(Window win); -winwidget winwidget_create_from_file(gib_list * filename, char *name, char type); -winwidget winwidget_create_from_image(Imlib_Image im, char *name, char type); +winwidget winwidget_create_from_file(gib_list * filename, char type); +winwidget winwidget_create_from_image(Imlib_Image im, char type); void winwidget_rename(winwidget winwid, char *newname); void winwidget_destroy(winwidget winwid); void winwidget_create_window(winwidget ret, int w, int h); -void winwidget_clear_background(winwidget w); Pixmap feh_create_checks(void); double feh_calc_needed_zoom(double *zoom, int orig_w, int orig_h, int dest_w, int dest_h); void feh_debug_print_winwid(winwidget winwid); |