summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile41
-rw-r--r--src/collage.c210
-rw-r--r--src/debug.h1
-rw-r--r--src/deps.mk41
-rw-r--r--src/events.c552
-rw-r--r--src/events.h2
-rw-r--r--src/exif.c439
-rw-r--r--src/exif.h47
-rw-r--r--src/exif_canon.c61
-rw-r--r--src/exif_canon.h34
-rw-r--r--src/exif_cfg.h71
-rw-r--r--src/exif_nikon.c542
-rw-r--r--src/exif_nikon.h34
-rw-r--r--src/feh.h85
-rw-r--r--src/feh_png.c40
-rw-r--r--src/feh_png.h6
-rw-r--r--src/fehrc.raw74
-rw-r--r--src/filelist.c431
-rw-r--r--src/filelist.h43
-rw-r--r--src/getopt.c949
-rw-r--r--src/getopt.h130
-rw-r--r--src/getopt1.c173
-rw-r--r--src/gib_hash.c149
-rw-r--r--src/gib_hash.h75
-rw-r--r--src/gib_imlib.c732
-rw-r--r--src/gib_imlib.h194
-rw-r--r--src/gib_list.c579
-rw-r--r--src/gib_list.h105
-rw-r--r--src/gib_style.c114
-rw-r--r--src/gib_style.h69
-rw-r--r--src/help.raw175
-rw-r--r--src/imlib.c1678
-rw-r--r--src/index.c423
-rw-r--r--src/index.h35
-rw-r--r--src/keyevents.c856
-rw-r--r--src/list.c48
-rw-r--r--src/main.c153
-rw-r--r--src/menu.c578
-rw-r--r--src/menu.h16
-rw-r--r--src/multiwindow.c22
-rw-r--r--src/options.c1068
-rw-r--r--src/options.h270
-rw-r--r--src/signals.c52
-rw-r--r--src/signals.h7
-rw-r--r--src/slideshow.c539
-rw-r--r--src/structs.h3
-rw-r--r--src/strverscmp.c57
-rw-r--r--src/thumbnail.c673
-rw-r--r--src/thumbnail.h18
-rw-r--r--src/timers.c37
-rw-r--r--src/timers.h2
-rw-r--r--src/utils.c85
-rw-r--r--src/utils.h12
-rw-r--r--src/wallpaper.c (renamed from src/support.c)599
-rw-r--r--src/wallpaper.h (renamed from src/support.h)14
-rw-r--r--src/winwidget.c619
-rw-r--r--src/winwidget.h35
57 files changed, 9276 insertions, 4821 deletions
diff --git a/src/Makefile b/src/Makefile
index 84ee7ab..2968671 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,6 +1,41 @@
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}
I_SRCS = ${shell echo *.raw}
@@ -17,9 +52,9 @@ include deps.mk
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} -MM ${TARGETS} > $@
+ ${CC} ${CFLAGS} -MM ${TARGETS} > $@
clean:
rm -f feh *.o *.inc
diff --git a/src/collage.c b/src/collage.c
deleted file mode 100644
index 782d5f8..0000000
--- a/src/collage.c
+++ /dev/null
@@ -1,210 +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;
-
- 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(("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(("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(("About to load image %s\n", file->filename));
- if (feh_load_image(&im_temp, file) != 0) {
- D(("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(("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(("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);
-
- return;
-}
diff --git a/src/debug.h b/src/debug.h
index 93cb6bf..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
diff --git a/src/deps.mk b/src/deps.mk
deleted file mode 100644
index 36b264f..0000000
--- a/src/deps.mk
+++ /dev/null
@@ -1,41 +0,0 @@
-collage.o: collage.c feh.h structs.h menu.h utils.h getopt.h debug.h \
- winwidget.h filelist.h options.h
-events.o: events.c feh.h structs.h menu.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 utils.h getopt.h \
- debug.h
-filelist.o: filelist.c feh.h structs.h menu.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 utils.h getopt.h debug.h \
- filelist.h winwidget.h options.h
-index.o: index.c feh.h structs.h menu.h utils.h getopt.h debug.h \
- filelist.h winwidget.h options.h
-keyevents.o: keyevents.c feh.h structs.h menu.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 utils.h getopt.h debug.h filelist.h \
- options.h
-main.o: main.c feh.h structs.h menu.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 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 utils.h getopt.h \
- debug.h winwidget.h timers.h filelist.h options.h
-options.o: options.c feh.h structs.h menu.h utils.h getopt.h debug.h \
- filelist.h options.h help.inc fehrc.inc
-signals.o: signals.c feh.h structs.h menu.h utils.h getopt.h debug.h \
- winwidget.h
-slideshow.o: slideshow.c feh.h structs.h menu.h utils.h getopt.h debug.h \
- filelist.h timers.h winwidget.h options.h signals.h
-support.o: support.c feh.h structs.h menu.h utils.h getopt.h debug.h \
- filelist.h options.h support.h
-thumbnail.o: thumbnail.c feh.h structs.h menu.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 utils.h getopt.h debug.h \
- options.h timers.h
-utils.o: utils.c feh.h structs.h menu.h utils.h getopt.h debug.h \
- options.h
-winwidget.o: winwidget.c feh.h structs.h menu.h utils.h getopt.h debug.h \
- filelist.h winwidget.h options.h
diff --git a/src/events.c b/src/events.c
index 36379b1..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,15 +32,147 @@ 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_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;
@@ -61,6 +194,7 @@ void feh_event_init(void)
static void feh_event_handle_ButtonPress(XEvent * ev)
{
winwidget winwid = NULL;
+ unsigned int state, button;
/* get the heck out if it's a mouse-click on the
cover, we'll hide the menus on release */
@@ -69,74 +203,126 @@ static void feh_event_handle_ButtonPress(XEvent * ev)
}
winwid = winwidget_get_from_window(ev->xbutton.window);
- if (winwid && winwid->caption_entry) {
+ if (winwid == NULL || winwid->caption_entry) {
return;
}
- if (!opt.no_menus && EV_IS_MENU_BUTTON(ev)) {
+ 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"));
- 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(("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(("blur starting at %d, %d\n", ev->xbutton.x, ev->xbutton.y));
- }
- } else if (ev->xbutton.button == opt.pan_button) {
- D(("Pan Button Press event\n"));
- if (winwid != NULL) {
- 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;
- }
- } else if (ev->xbutton.button == opt.zoom_button) {
+ 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"));
- if (winwid != NULL) {
- D(("Zoom mode baby!\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 (ev->xbutton.button == opt.reload_button) {
+ 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"));
- if (winwid != NULL)
- feh_reload_image(winwid, 0, 0);
- } else if (ev->xbutton.button == opt.prev_button) {
+ feh_reload_image(winwid, 0, 1);
+
+ } else if (feh_is_bb(EVENT_prev_img, button, state)) {
D(("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) {
+ 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 != NULL)
- && (winwid->type == WIN_TYPE_SLIDESHOW))
- slideshow_change_image(winwid, SLIDE_NEXT);
+ if (winwid->type == WIN_TYPE_SLIDESHOW)
+ slideshow_change_image(winwid, SLIDE_NEXT, 1);
+
} else {
D(("Received other ButtonPress event\n"));
+ feh_event_handle_generic(winwid, state, NoSymbol, button);
}
return;
}
@@ -144,6 +330,8 @@ static void feh_event_handle_ButtonPress(XEvent * ev)
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;
if (menu_root) {
/* if menus are open, close them, and execute action if needed */
@@ -164,39 +352,28 @@ static void feh_event_handle_ButtonRelease(XEvent * ev)
}
winwid = winwidget_get_from_window(ev->xbutton.window);
- if (winwid && winwid->caption_entry) {
+ 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(("Disabling pan mode\n"));
- opt.mode = MODE_NORMAL;
- winwid->mode = MODE_NORMAL;
- winwidget_sanitise_offsets(winwid);
- winwidget_render_image(winwid, 0, 1);
- }
+ 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;
@@ -206,63 +383,45 @@ 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);
- if (thumbwin)
- 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 (winwid != NULL) {
- opt.mode = MODE_NORMAL;
- winwid->mode = MODE_NORMAL;
- }
- }
- } else if ((ev->xbutton.button == opt.rotate_button)
- || (ev->xbutton.button == opt.zoom_button)) {
- D(("Mode-based Button Release event\n"));
- if (winwid != NULL) {
- D(("Disabling mode\n"));
opt.mode = MODE_NORMAL;
winwid->mode = MODE_NORMAL;
+ }
- if ((ev->xbutton.button == opt.zoom_button)
- && (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);
+ } 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, 1);
- }
- } else if ((ev->xbutton.button == opt.blur_button)
- && ((opt.no_blur_ctrl_mask)
- || (ev->xbutton.state & ControlMask))) {
- D(("Blur Button Release event\n"));
- if (winwid != NULL) {
- D(("Disabling Blur mode\n"));
- opt.mode = MODE_NORMAL;
- winwid->mode = MODE_NORMAL;
- }
+ 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;
}
return;
}
-static void feh_event_handle_ConfigureNotify(XEvent * ev)
+void feh_event_handle_ConfigureNotify(XEvent * ev)
{
while (XCheckTypedWindowEvent(disp, ev->xconfigure.window, ConfigureNotify, ev));
if (!menu_root) {
@@ -280,7 +439,7 @@ 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);
}
}
}
@@ -362,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);
}
@@ -374,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);
}
@@ -387,15 +550,15 @@ static void feh_event_handle_MotionNotify(XEvent * ev)
winwid->zoom = winwid->old_zoom + (
((double) ev->xmotion.x - (double) winwid->click_offset_x)
/ 128.0);
- /*/ ((double) (winwid->click_offset_x + 1)));*/
else
winwid->zoom = winwid->old_zoom - (
((double) winwid->click_offset_x - (double) ev->xmotion.x)
/ 128.0);
- /*/ ((double) (winwid->click_offset_x + 1)));*/
- 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;
/* center around click_offset */
winwid->im_x = winwid->click_offset_x
@@ -403,7 +566,7 @@ static void feh_event_handle_MotionNotify(XEvent * ev)
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;
@@ -412,8 +575,14 @@ 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(("Panning\n"));
orig_x = winwid->im_x;
@@ -424,36 +593,38 @@ static void feh_event_handle_MotionNotify(XEvent * ev)
winwidget_sanitise_offsets(winwid);
- D(("im_x %d, im_w %d, off %d, mx %d\n", winwid->im_x,
- winwid->im_w, winwid->click_offset_x, ev->xmotion.x));
+ 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->click_offset_x >= winwid->w - 4))
+ 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;
}
- else if ((ev->xmotion.x <= 1) && (winwid->click_offset_x
- <= (winwid->im_w * winwid->zoom) - winwid->w - 3))
+ // 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->click_offset_y >= winwid->h - 4))
+ && (winwid->im_y < 0))
{
XWarpPointer(disp, None, winwid->win, 0, 0, 0, 0,
ev->xmotion.x, 3);
winwid->click_offset_y -= winwid->h - 4;
}
- else if ((ev->xmotion.y <= 1) && (winwid->click_offset_y
- <= (winwid->im_h * winwid->zoom) - winwid->h - 3))
+ // 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);
@@ -462,7 +633,7 @@ static void feh_event_handle_MotionNotify(XEvent * ev)
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));
@@ -473,16 +644,18 @@ static void feh_event_handle_MotionNotify(XEvent * ev)
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(("angle: %f\n", winwid->im_angle));
- winwidget_render_image(winwid, 0, 0);
+ winwidget_render_image(winwid, 0, 1);
}
} else if (opt.mode == MODE_BLUR) {
while (XCheckTypedWindowEvent(disp, ev->xmotion.window, MotionNotify, ev));
@@ -494,71 +667,34 @@ static void feh_event_handle_MotionNotify(XEvent * ev)
D(("Blurring\n"));
temp = gib_imlib_clone_image(winwid->im);
- 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, 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);
}
}
}
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/exif_canon.h b/src/exif_canon.h
new file mode 100644
index 0000000..58ecc0e
--- /dev/null
+++ b/src/exif_canon.h
@@ -0,0 +1,34 @@
+/* exif_canon.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_CANON_H
+#define EXIF_CANON_H
+
+#include <libexif/exif-data.h>
+
+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
diff --git a/src/feh.h b/src/feh.h
index f627a78..54e78ea 100644
--- a/src/feh.h
+++ b/src/feh.h
@@ -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,15 +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 "utils.h"
-#include "getopt.h"
#include "debug.h"
@@ -92,11 +104,31 @@ enum bgmode_type { BG_MODE_NONE = 0, BG_MODE_TILE, BG_MODE_CENTER,
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);
int feh_main_iteration(int block);
@@ -108,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);
+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);
+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);
+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;
@@ -170,4 +222,9 @@ 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/fehrc.raw b/src/fehrc.raw
deleted file mode 100644
index 154a6e1..0000000
--- a/src/fehrc.raw
+++ /dev/null
@@ -1,74 +0,0 @@
-# Feh configuration file.
-# Lines starting with # are comments. Don't use comments mid-line.
-
-# Feh expects to find this as ~/.fehrc or /etc/fehrc
-# If both are available, ~/.fehrc will be used
-
-# Options are defined in theme_name/options pairs.
-# Separate themename and options by whitespace.
-
-# There are two ways of specifying the theme. Either use feh -Tthemename,
-# or use a symbolic link to feh with the name of the theme. eg
-# ln -s `which feh` ~/bin/mkindex
-# Now when you run 'mkindex', feh will load the config specified for the
-# mkindex theme.
-
-# Multiple options can of course be used. If they are too long for one line,
-# you can (usually) use a \\ to make them continue on the next one:
-# imagemap -rV --quiet -W 400 -H 300 \\
-# --thumb-width 40 --thumb-height 30
-
-# ====================
-# A few default themes
-# ====================
-
-# Webcam mode, simply specify the url(s).
-# e.g. feh -Twebcam http://cam1 http://cam2
-webcam --multiwindow --reload 20
-
-# Create an index of the current directory. This version uses . as the
-# current dir, so you don't even need any commandline arguments.
-mkindex -iVO index.jpg .
-
-# More ambitious version...
-imgidx --index --output-only .fehindex.jpg --limit-width 1024 \\
- --thumb-width 128 --thumb-height 128 --verbose --quiet
-
-# Show a presentation
-present --full-screen --sort name --hide-pointer
-
-# Booth mode ;-)
-booth --full-screen --hide-pointer --slideshow-delay 20
-
-# Screw xscreensaver, use feh =)
-screensave --recursive --full-screen --randomize --slideshow-delay 10 --hide-pointer
-
-# Add <img> tags to your html with ease :-)
-newimg -q -L \"<img src=\\\"%f\\\" alt=\\\"%n\\\" border=\\\"0\\\" width=\\\"%w\\\" height=\\\"%h\\\">\"
-
-# Different menus
-brushed --menu-bg " PREFIX "/share/feh/images/menubg_brushed.png
-aluminium --menu-bg " PREFIX "/share/feh/images/menubg_aluminium.png
-aqua --menu-bg " PREFIX "/share/feh/images/menubg_aqua.png
-sky --menu-bg " PREFIX "/share/feh/images/menubg_sky.png
-black --menu-bg " PREFIX "/share/feh/images/menubg_black.png \\
- --menu-style " PREFIX "/share/feh/fonts/black.style
-
-# Some more examples, used by the feh developer
-
-rfs --full-screen --hide-pointer --auto-zoom --randomize
-fs --full-screen --hide-pointer --auto-zoom --sort filename
-
-thumb_s --thumbnails --cache-thumbnails --thumb-width 128 --thumb-height 128 \\
- --limit-width 1024 --sort filename \\
- --fontpath /usr/share/fonts/truetype/ttf-dejavu/ --font DejaVuSans/8
-
-thumb_b --thumbnails --cache-thumbnails --thumb-width 256 --thumb-height 256 \\
- --limit-width 1024 --sort filename \\
- --fontpath /usr/share/fonts/truetype/ttf-dejavu/ --font DejaVuSans/8
-
-thumb_s_nt --thumbnails --cache-thumbnails --thumb-width 128 --thumb-height 128 \\
- --limit-width 1024 --sort filename --index-name 0
-
-thumb_b_nt --thumbnails --cache-thumbnails --thumb-width 256 --thumb-height 256 \\
- --limit-width 1024 --sort filename --index-name 0
diff --git a/src/filelist.c b/src/filelist.c
index 5c0777d..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;
@@ -47,7 +53,12 @@ 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;
+#ifdef HAVE_LIBEXIF
+ newfile->ed = NULL;
+#endif
return(newfile);
}
@@ -63,6 +74,10 @@ 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);
return;
}
@@ -76,7 +91,6 @@ feh_file_info *feh_file_info_new(void)
info->width = 0;
info->height = 0;
- info->size = 0;
info->pixels = 0;
info->has_alpha = 0;
info->format = NULL;
@@ -111,13 +125,81 @@ gib_list *feh_file_remove_from_list(gib_list * list, gib_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;
- if (!origpath)
+ if (!origpath || *origpath == '\0')
return;
path = estrdup(origpath);
@@ -131,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 */
+ 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);
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);
@@ -150,33 +234,15 @@ 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);
return;
}
if ((S_ISDIR(st.st_mode)) && (level != FILELIST_LAST)) {
- struct dirent *de;
+ struct dirent **de;
DIR *dir;
+ int cnt, n;
D(("It is a directory\n"));
@@ -186,24 +252,35 @@ void add_file_to_filelist_recursively(char *origpath, unsigned char level)
free(path);
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);
}
- de = readdir(dir);
+ } 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]);
+ }
+ free(de);
}
closedir(dir);
} else if (S_ISREG(st.st_mode)) {
@@ -229,32 +306,49 @@ void delete_rm_files(void)
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;
- if (opt.verbose)
- fprintf(stdout, PACKAGE " - preloading...\n");
-
for (l = list; l; l = l->next) {
file = FEH_FILE(l->data);
D(("file %p, file->next %p, file->name %s\n", l, l->next, file->name));
- 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 (opt.verbose)
- feh_display_status('.');
+ 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);
}
@@ -262,48 +356,39 @@ gib_list *feh_file_info_preload(gib_list * 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;
+ if (feh_file_stat(file))
+ return(1);
+
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;
- }
- return(1);
- }
-
if (im)
im1 = im;
- else if (!feh_load_image(&im1, file))
- return(1);
-
- if (!im1)
+ else if (!feh_load_image(&im1, file) || !im1)
return(1);
file->info = feh_file_info_new();
@@ -317,21 +402,59 @@ 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);
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)
{
- return(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)
{
- return(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)
@@ -351,7 +474,7 @@ int feh_cmp_pixels(void *file1, void *file2)
int feh_cmp_size(void *file1, void *file2)
{
- return((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)
@@ -361,10 +484,25 @@ int feh_cmp_format(void *file1, void *file2)
void feh_prepare_filelist(void)
{
- 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();
}
@@ -386,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;
@@ -419,7 +563,7 @@ int feh_write_filelist(gib_list * list, char *filename)
FILE *fp;
gib_list *l;
- if (!list || !filename)
+ if (!list || !filename || !strcmp(filename, "/dev/stdin"))
return(0);
errno = 0;
@@ -441,26 +585,39 @@ gib_list *feh_read_filelist(char *filename)
FILE *fp;
gib_list *list = NULL;
char s[1024], s1[1024];
- Imlib_Image im1;
+ Imlib_Load_Error err = IMLIB_LOAD_ERROR_NONE;
+ Imlib_Image tmp_im;
+ struct stat st;
+ signed short tmp_conversion_timeout;
if (!filename)
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;
- return(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. */
return(NULL);
@@ -476,7 +633,8 @@ gib_list *feh_read_filelist(char *filename)
/* 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);
return(list);
}
@@ -490,17 +648,19 @@ char *feh_absolute_path(char *path)
if (!path)
return(NULL);
- if (path[0] == '/')
+ 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(("Need to convert %s to an absolute form\n", path));
- /* I SHOULD be able to just use a simple realpath() here, but dumb *
+ /* 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 {
@@ -510,16 +670,49 @@ char *feh_absolute_path(char *path)
return(ret);
}
-void feh_save_filelist()
+void feh_save_filelist(void)
{
char *tmpname;
+ char *base_dir = "";
- tmpname = feh_unique_filename("", "filelist");
+ if (opt.output_dir) {
+ base_dir = estrjoin("", opt.output_dir, "/", NULL);
+ }
- if (!opt.quiet)
- printf("saving filelist to filename '%s'\n", tmpname);
+ tmpname = feh_unique_filename(base_dir, "filelist");
+
+ 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);
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
index e0ea432..0e99c68 100644
--- a/src/help.raw
+++ b/src/help.raw
@@ -2,7 +2,7 @@
Usage : " PACKAGE " [options] <files or directories ...>
- This is just a short option summary. Please consult the manual for details.
+ This is just a short option summary. Please see \"man " PACKAGE "\" for details.
OPTIONS
-h, --help Show help and exit
@@ -10,34 +10,42 @@ OPTIONS
-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
- -_, --rcfile FILE Use FILE to parse themes and options from
-r, --recursive Recursively expand any directories in FILE to
- the content of those directories. (Take it easy)
+ 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 STRING Limit the window size to STRING, like \"640x480\"
+ -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 POSITION Start at POSITION in the filelist
- -p, --preload Remove unlaodable files from the internal 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 the screen
- size
+ -., --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 full screen mode
+ -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.
+ 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
- --cycle-once Exit after one loop through the slideshow
+ --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
- -Q, --builtin Use builtin http client instead of wget
-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
@@ -45,15 +53,18 @@ OPTIONS
-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, width, height, pixels, size or format
+ name, filename, mtime, width, height, pixels, size,
+ or format
-n, --reverse Reverse sort order
- -A, --action ACTION Specify action to perform when pressing <return>.
+ --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>
- --action-hold-slide Do not change to next image after running an action
-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
- -c, --collage Montage mode with randomly distributed thumbnails
-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
@@ -62,9 +73,7 @@ OPTIONS
-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-name BOOL Show/Don't show filename in index/thumbnail mode
- --index-size BOOL Show/Don't show filesize in index/thumbnail mode
- --index-dim BOOL Show/Don't show dimensions in index/thumbnail mode
+ --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
@@ -76,41 +85,25 @@ OPTIONS
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.
- --menu-style FILE Use FILE as the style descriptor for menu text.
- -), --menu-bg BG Use BG for the background image in menus.
-B, --image-bg STYLE Set background for transparent images and the like.
- Accepted values: white, black, default
+ 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.
- -0, --reload-button B Use button B to reload the image (defaults to 0)
- -1, --pan-button B Use button B pan the image (hold button down, move
- the mouse to move the image around. Advances to the
- next image when the mouse is not moved (defaults to
- 1, usually the left button).
- -2, --zoom-button B Use button B to zoom the current image in any
- mode (defaults to 2, usually the middle button).
- -3, --menu-button B Click button B to activate the menu in any
- mode. Set to 0 for any button. This option
- is disabled if the -N or --no-menus option is set
- (defaults to 3, usually the right button).
- --menu-ctrl-mask Require CTRL+Button for menu activation
- -4, --prev-button B Use button B to switch to the previous image
- (defaults to 4, which usually is <mousewheel up>).
- -5, --next-button B Use button B to switch to the next image
- (defaults to 5, which usually is <mousewheel down>).
- -8, --rotate-button B Use CTRL+Button B to rotate the current image in
- any mode (default=2).
- --no-rotate-ctrl-mask Don't require CTRL+Button for rotation in
- any mode -- just use the button (default=off).
- -9, --blur-button B Use CTRL+Button B to blur the current image in
- any mode (default=1).
- --no-blur-ctrl-mask Don't require CTRL+Button for blurring in
- any mode -- just use the button (default=off).
--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
@@ -135,60 +128,84 @@ INDEX MODE OPTIONS
font is specified, a title will not be printed
FORMAT SPECIFIERS
+ %a information about slideshow state (playing/paused)
%f image path/filename
+ %F image path/filename (shell-escaped)
+ %g window dimensions (\"width,height\") in pixels
+ %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
- %s image size (bytes)
+ %N image name (shell-escaped)
+ %o offset of top-left image corner to window (\"x,y\") in pixels
%p image pixel size
- %w image width
- %h image height
+ %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
- %P " PACKAGE "
- %v " PACKAGE " version
- %m current mode (slideshow, multiwindow...)
- %l total number of files in the filelist
%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
+ \\n newline
-KEYS
- p, <BACKSPACE>, <LEFT> Go to previous slide
- n, <SPACE>, <RIGHT> Go to next slide
- r Reload image
- v Toggle fullscreen
- m Show menu
- c Enable caption entry mode
- w Resize window to current image dimensions
- h Pause/Continue the slideshow
- z Jump to a random position in the current filelist
+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
- f Save current filelist to unique filename
- <, > In place editing, rotate 90 degrees right/left
+ 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
- <ESCAPE> Quit the slideshow
- + Increase reload delay
- - Decrease reload delay
+ <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
- x Close current window
- q Quit " PACKAGE "
+ 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 +> Zoom in
- <KEYPAD -> Zoom out
+ <KEYPAD +>, <UP> Zoom in
+ <KEYPAD ->, <DOWN> Zoom out
<KEYPAD *> Zoom to 100%
<KEYPAD /> Zoom to fit the window
- <ENTER>, 0 Run action specified by --action option
- 1-9 Run action 1-9 specified by --action[1-9] options
This program is free software, see the file COPYING for licensing info.
-Copyright Tom Gilbert (and various contributors) 1999-2003
+Copyright Tom Gilbert (and various contributors) 1999-2003.
+Copyright Birte Kristina Friesel (and various contributors) 2010-2020.
-Homepage: https://derf.homelinux.org/p/feh
-Email bugs to <derf@finalrewind.org>
+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 8196340..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,17 +66,45 @@ 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;
- if (getenv("XINERAMA_SCREEN"))
- xinerama_screen = atoi(getenv("XINERAMA_SCREEN"));
- else
- 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 */
@@ -98,12 +142,23 @@ 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);
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)
{
feh_file *file;
@@ -115,421 +170,852 @@ int feh_load_image_char(Imlib_Image * im, char *filename)
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(("filename is %s, image is %p\n", file->filename, im));
-
- if (!file || !file->filename)
- return(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)
- return(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(("Load *failed*\n"));
- return(0);
}
+}
- D(("Loaded ok\n"));
- return(1);
+#ifdef HAVE_LIBMAGIC
+void uninit_magic(void)
+{
+ if (!magic) {
+ return;
+ }
+
+ 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 *basename;
- char *path = NULL;
+ const char * mime_type = NULL;
- if (opt.keep_http) {
- if (opt.output_dir)
- path = opt.output_dir;
- else
- path = "";
- } else
- path = "/tmp/";
+ if (!magic) {
+ return 1;
+ }
- basename = strrchr(url, '/') + 1;
- tmpname = feh_unique_filename(path, basename);
-
- 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(("using builtin http collection\n"));
- fp = fopen(tmpname, "w");
- if (!fp) {
- weprintf("couldn't write to file %s:", tmpname);
- free(tmpname);
- return(NULL);
- }
+ magic_setflags(magic, MAGIC_MIME_TYPE | MAGIC_SYMLINK | magic_flags);
+ mime_type = magic_file(magic, file->filename);
- hostname = feh_strip_hostname(url);
- if (!hostname) {
- weprintf("couldn't work out hostname from %s:", url);
- fclose(fp);
- unlink(tmpname);
- free(tmpname);
- return(NULL);
- }
+ if (!mime_type) {
+ return 0;
+ }
- D(("trying hostname %s\n", hostname));
+ D(("file %s has mime type: %s\n", file->filename, mime_type));
- if (!(hptr = feh_gethostbyname(hostname))) {
- weprintf("error resolving host %s:", hostname);
- fclose(fp);
- unlink(tmpname);
- free(hostname);
- free(tmpname);
- return(NULL);
+ if (strncmp(mime_type, "image/", 6) == 0) {
+ return 1;
+ }
+
+ /* no infinite loop on compressed content, please */
+ if (magic_flags) {
+ return 0;
+ }
+
+ /* 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);
+ }
+
+ return 0;
+}
+#else
+int feh_is_image(__attribute__((unused)) feh_file * file, __attribute__((unused)) int magic_flags)
+{
+ return 1;
+}
+#endif
+
+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);
- return(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);
- return(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(url, '/') + 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);
- weprintf("error sending over socket:");
- return(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);
-
- 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);
+ }
- switch (body) {
+ /*
+ * 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);
+ }
+ }
- case IN_BODY:
- fwrite(buf + i, 1, size - i, fp);
- i = size;
- break;
+ 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 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;
+ D(("Loaded ok\n"));
+ return(1);
+}
- 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;
+void feh_reload_image(winwidget w, int resize, int force_new)
+{
+ char *new_title;
+ int len;
+ Imlib_Image tmp;
+ int old_w, old_h;
- } /* switch */
- } /* for i */
- }
- } /* while read */
- close(sockno);
- fclose(fp);
+ if (!w->file) {
+ im_weprintf(w, "couldn't reload, this image has no file associated with it.");
+ winwidget_render_image(w, 0, 0);
+ return;
+ }
+
+ 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 {
+ 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);
+
+ return;
+}
+
+static int feh_file_is_raw(char *filename)
+{
+ childpid = fork();
+ if (childpid == -1) {
+ perror("fork");
+ return 0;
+ }
+
+ 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 pid;
int status;
+ do {
+ waitpid(childpid, &status, WUNTRACED);
+ if (WIFEXITED(status)) {
+ return !WEXITSTATUS(status);
+ }
+ } while (!WIFEXITED(status) && !WIFSIGNALED(status));
+ }
- if ((pid = fork()) < 0) {
- weprintf("open url: fork failed:");
- free(tmpname);
- return(NULL);
- } else if (pid == 0) {
- char *quiet = NULL;
+ return 0;
+}
- if (!opt.verbose)
- quiet = estrdup("-q");
+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;
+ }
- execlp("wget", "wget", "--cache=off", "-O", tmpname, url, quiet, NULL);
- eprintf("url: Is 'wget' installed? Failed to exec wget:");
+ 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);
+ 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);
+ }
+ }
+ close(fd);
+ childpid = 0;
+ }
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- weprintf("url: wget failed to load URL %s\n", url);
- unlink(tmpname);
- free(tmpname);
- return(NULL);
+ 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);
}
}
+ closedir(dir);
}
- return(tmpname);
+ free(argv_fn);
+
+ if ((sfn != NULL) && opt.use_conversion_cache)
+ gib_hash_set(conversion_cache, filename, sfn);
+
+ return sfn;
}
-struct hostent *feh_gethostbyname(const char *name)
+#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
{
- struct hostent *hp;
- unsigned long addr;
+ // 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;
+}
- addr = (unsigned long) inet_addr(name);
- if ((int) addr != -1)
- hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
- else
- hp = gethostbyname(name);
- return(hp);
+static char *feh_http_load_image(char *url)
+{
+ CURL *curl;
+ CURLcode res;
+ char *sfn;
+ FILE *sfp;
+ int fd = -1;
+ char *ebuff;
+ char *tmpname;
+ char *basename;
+ char *path = NULL;
+
+ 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;
+}
- start = strchr(url, '/');
- if (!start)
- return(NULL);
+#endif /* HAVE_LIBCURL */
- start += 2;
+void feh_imlib_image_fill_text_bg(Imlib_Image im, int w, int h)
+{
+ gib_imlib_image_set_has_alpha(im, 1);
- finish = strchr(start, '/');
- if (!finish)
- return(NULL);
+ imlib_context_set_blend(0);
- len = finish - start;
+ 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);
- ret = emalloc(len + 1);
- strncpy(ret, start, len);
- ret[len] = '\0';
- return(ret);
+ imlib_context_set_blend(1);
}
-void feh_draw_zoom(winwidget w)
+static Imlib_Font feh_load_font(winwidget w)
{
static Imlib_Font fn = NULL;
- int tw = 0, th = 0;
- Imlib_Image im = NULL;
- char buf[100];
- static DATA8 atab[256];
-
- if (!w->im)
- return;
if (opt.font)
fn = gib_imlib_load_font(opt.font);
if (!fn) {
- fn = gib_imlib_load_font(DEFAULT_FONT);
+ if (w && w->full_screen)
+ fn = gib_imlib_load_font(DEFAULT_FONT_BIG);
+ else
+ fn = gib_imlib_load_font(DEFAULT_FONT);
}
if (!fn) {
- weprintf("Couldn't load font for zoom printing");
- return;
+ eprintf("Couldn't load font to draw a message");
}
- memset(atab, 0, sizeof(atab));
+ 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];
+
+ if (!w->im)
+ return;
+
+ 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));
@@ -543,9 +1029,7 @@ 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);
@@ -554,12 +1038,65 @@ void feh_draw_zoom(winwidget w)
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;
@@ -567,49 +1104,43 @@ void feh_draw_filename(winwidget w)
|| (!FEH_FILE(w->file->data)->filename))
return;
- if (opt.font)
- fn = gib_imlib_load_font(opt.font);
+ fn = feh_load_font(w);
- if (!fn) {
- if (w->full_screen)
- fn = gib_imlib_load_font(DEFAULT_FONT_BIG);
- else
- fn = gib_imlib_load_font(DEFAULT_FONT);
- }
+ /* 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 (!fn) {
- weprintf("Couldn't load font for filename printing");
- return;
- }
+ 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
+ snprintf(s, len, "%d of %d", gib_list_num(filelist, current_file) +
+ 1, gib_list_length(filelist));
- memset(atab, 0, sizeof(atab));
+ 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 have >=2 files */
- if (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);
@@ -621,81 +1152,188 @@ void feh_draw_filename(winwidget w)
return;
}
-void feh_draw_info(winwidget w)
+#ifdef HAVE_LIBEXIF
+void feh_draw_exif(winwidget w)
{
static Imlib_Font fn = NULL;
- int tw = 0, th = 0;
+ int width = 0, height = 0, line_width = 0, line_height = 0;
Imlib_Image im = NULL;
- static DATA8 atab[256];
- int no_lines = 0;
- char *info_cmd;
- char info_buf[256];
- FILE *info_pipe;
-
- if ((!w->file) || (!FEH_FILE(w->file->data))
- || (!FEH_FILE(w->file->data)->filename))
+ 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;
+ }
- if (opt.font)
- fn = gib_imlib_load_font(opt.font);
- if (!fn) {
- if (w->full_screen)
- fn = gib_imlib_load_font(DEFAULT_FONT_BIG);
- else
- fn = gib_imlib_load_font(DEFAULT_FONT);
- }
+ buffer[0] = '\0';
+ exif_get_info(FEH_FILE(w->file->data)->ed, buffer, EXIF_MAX_DATA);
- if (!fn) {
- weprintf("Couldn't load font for filename printing");
- return;
+ 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;
+ }
- info_cmd = feh_printf(opt.info_cmd, FEH_FILE(w->file->data));
+ pos++;
+ pos2++;
+ }
+ info_line[pos2] = '\0';
- memset(atab, 0, sizeof(atab));
+ gib_imlib_get_text_size(fn, info_line, NULL, &line_width,
+ &line_height, IMLIB_TEXT_TO_RIGHT);
- gib_imlib_get_text_size(fn, "w", NULL, &tw, &th, 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);
- info_pipe = popen(info_cmd, "r");
+ no_lines++;
+ }
+ }
+
+ if (no_lines == 0)
+ return;
- im = imlib_create_image(290 * tw, 20 * th);
+ height *= no_lines;
+ width += 4;
+
+ im = imlib_create_image(width, height);
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, 290 * tw, 20 * th, NULL, NULL, NULL, atab);
- gib_imlib_image_fill_rectangle(im, 0, 0, 290 * tw, 20 * th, 0, 0, 0, 0);
+ 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) {
- gib_imlib_text_draw(im, fn, NULL, 2, 2,
- "Error runnig info command", IMLIB_TEXT_TO_RIGHT,
- 255, 0, 0, 255);
- gib_imlib_get_text_size(fn, "Error running info command", NULL, &tw, &th,
- IMLIB_TEXT_TO_RIGHT);
+ 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 < 20) && fgets(info_buf, 256, info_pipe)) {
- info_buf[strlen(info_buf)-1] = '\0';
- gib_imlib_text_draw(im, fn, NULL, 2, (no_lines*th)+2, info_buf,
- IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
- gib_imlib_text_draw(im, fn, NULL, 1, (no_lines*th)+1, info_buf,
- IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
+ 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, 2,
- w->h - (th * no_lines) - 2, 1, 1, 0);
+ 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)
+char *build_caption_filename(feh_file * file, short create_dir)
{
char *caption_filename;
char *s, *dir, *caption_dir;
@@ -714,6 +1352,8 @@ char *build_caption_filename(feh_file * file)
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))
@@ -733,7 +1373,6 @@ 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;
@@ -749,9 +1388,12 @@ void feh_draw_caption(winwidget w)
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);
}
@@ -776,22 +1418,7 @@ void feh_draw_caption(winwidget w)
caption_style->bits = gib_list_add_front(caption_style->bits,
gib_style_bit_new(1, 1, 0, 0, 0, 255));
- if (opt.font)
- fn = gib_imlib_load_font(opt.font);
-
- if (!fn) {
- if (w->full_screen)
- fn = gib_imlib_load_font(DEFAULT_FONT_BIG);
- else
- fn = gib_imlib_load_font(DEFAULT_FONT);
- }
-
- if (!fn) {
- weprintf("Couldn't load font for caption printing");
- return;
- }
-
- memset(atab, 0, sizeof(atab));
+ fn = feh_load_font(w);
if (*(file->caption) == '\0') {
p = estrdup("Caption entry mode - Hit ESC to cancel");
@@ -826,12 +1453,9 @@ 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;
@@ -867,6 +1491,13 @@ void feh_display_status(char stat)
D(("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);
@@ -874,49 +1505,106 @@ 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 = gib_list_length(filelist);
- fprintf(stdout, " %5d/%d (%d)\n[%3d%%] ",
+ fprintf(stderr, " %5d/%d (%d)\n[%3d%%] ",
i, init_len, len, ((int) ((float) i / init_len * 100)));
} 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++;
return;
}
-void feh_edit_inplace_orient(winwidget w, int orientation)
+void feh_edit_inplace(winwidget w, int op)
{
- int ret;
- Imlib_Image old;
+ 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)
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);
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);
}
return;
@@ -1007,7 +1695,6 @@ gib_list *feh_wrap_string(char *text, int wrap_width, Imlib_Font fn, gib_style *
list = gib_list_add_end(list, estrdup(line));
free(line);
line = NULL;
- line_width = 0;
}
gib_list_free_and_data(words);
}
@@ -1020,38 +1707,79 @@ gib_list *feh_wrap_string(char *text, int wrap_width, Imlib_Font fn, gib_style *
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:");
+ im_weprintf(w, "lossless %s: fork failed:", op_name);
+ free(file_str);
return;
- } else if (pid == 0) {
+ }
+ 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: Is 'jpegtran' installed? Failed to exec:");
- } 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);
+ 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)
@@ -1062,11 +1790,10 @@ 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[2];
+ char index[3];
char *line;
/* Count number of defined actions. This method sucks a bit since it needs
@@ -1085,22 +1812,7 @@ void feh_draw_actions(winwidget w)
|| (!FEH_FILE(w->file->data)->filename))
return;
- if (opt.font)
- fn = gib_imlib_load_font(opt.font);
-
- if (!fn) {
- if (w->full_screen)
- fn = gib_imlib_load_font(DEFAULT_FONT_BIG);
- else
- fn = gib_imlib_load_font(DEFAULT_FONT);
- }
-
- if (!fn) {
- weprintf("Couldn't load font for actions printing");
- return;
- }
-
- memset(atab, 0, sizeof(atab));
+ 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 */
@@ -1108,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)
@@ -1134,21 +1846,19 @@ void feh_draw_actions(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, "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,
diff --git a/src/index.c b/src/index.c
index 15dcea1..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,17 +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;
+ gib_list *line, *lines;
if (opt.montage) {
mode = "montage";
@@ -75,11 +74,11 @@ void init_index_mode(void)
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
@@ -90,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;
@@ -111,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,
@@ -292,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);
}
@@ -354,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;
@@ -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,37 +348,133 @@ void init_index_mode(void)
if (!opt.display)
gib_imlib_free_image_and_decache(im_main);
- free(s);
return;
}
-static char *create_index_size_string(char *file)
+void index_calculate_height(Imlib_Font fn, int w, int *h, int *tot_thumb_h)
{
- static char str[50];
- int size = 0;
- double kbs = 0.0;
- struct stat st;
-
- if (stat(file, &st))
- kbs = 0.0;
- else {
- size = st.st_size;
- kbs = (double) size / 1000;
+ 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;
+}
- snprintf(str, sizeof(str), "%.2fKb", kbs);
- return(str);
+void index_calculate_width(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;
+ 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_dimension_string(int w, int h)
+void get_index_string_dim(feh_file *file, Imlib_Font fn, int *fw, int *fh)
{
- static char str[50];
+ 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;
+ }
- snprintf(str, sizeof(str), "%dx%d", w, h);
- return(str);
+ /* 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();
+ }
+
+ 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;
+}
+
+char *create_index_string(feh_file * file)
+{
+ 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];
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/keyevents.c b/src/keyevents.c
index 7940973..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,37 +29,349 @@ 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>
+
+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)
{
+ struct stat st;
if (opt.actions[action]) {
if (opt.slideshow) {
- feh_action_run(FEH_FILE(winwid->file->data), opt.actions[action]);
- winwidget_update_caption(winwid);
+ feh_action_run(FEH_FILE(winwid->file->data), opt.actions[action], winwid);
- if (! opt.hold_actions[action])
- slideshow_change_image(winwid, SLIDE_NEXT);
+ 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), opt.actions[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);
+ }
}
}
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;
@@ -70,45 +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:
+ else if (feh_is_kp(EVENT_menu_parent, state, keysym, 0))
feh_menu_select_parent(selected_menu);
- break;
- case XK_Down:
+ 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:
+ else if (feh_is_kp(EVENT_menu_child, state, keysym, 0))
feh_menu_select_submenu(selected_menu);
- break;
- case XK_Return:
- case XK_space:
+ else if (feh_is_kp(EVENT_menu_select, state, keysym, 0))
feh_menu_item_activate(selected_menu, selected_item);
- break;
- default:
- break;
- }
-
return;
}
if (winwid == NULL)
return;
- if (winwid->caption_entry) {
+ feh_event_handle_generic(winwid, state, keysym, 0);
+}
+
+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;
+}
+
+void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysym, unsigned int button) {
+ int curr_screen = 0;
+
+ 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);
@@ -116,14 +437,15 @@ 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);
+ eprintf("couldn't write to captions file %s:", caption_filename);
return;
}
fprintf(fp, "%s", FEH_FILE(winwid->file->data)->caption);
@@ -156,258 +478,280 @@ void feh_event_handle_keypress(XEvent * ev)
return;
}
- switch (keysym) {
- case XK_Left:
- if (kev->state & ControlMask) {
- winwid->im_x += 10;
- winwidget_render_image(winwid, 0, 0);
- }
- else if (opt.slideshow)
- slideshow_change_image(winwid, SLIDE_PREV);
- break;
- case XK_Right:
- if (kev->state & ControlMask) {
- winwid->im_x -= 10;
- winwidget_render_image(winwid, 0, 0);
- }
- else if (opt.slideshow)
- slideshow_change_image(winwid, SLIDE_NEXT);
- break;
- case XK_Page_Up:
- case XK_KP_Page_Up:
+ if (feh_is_kp(EVENT_next_img, 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_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_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_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, 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_FIRST);
- break;
- case XK_End:
- case XK_KP_End:
+ 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_LAST);
- break;
- case XK_Return:
- case XK_KP_Enter:
- case XK_0:
- case XK_KP_0:
+ 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_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);
- break;
- case XK_1:
- case XK_KP_1:
+ }
+ else if (feh_is_kp(EVENT_action_1, state, keysym, button)) {
feh_event_invoke_action(winwid, 1);
- break;
- case XK_2:
- case XK_KP_2:
+ }
+ else if (feh_is_kp(EVENT_action_2, state, keysym, button)) {
feh_event_invoke_action(winwid, 2);
- break;
- case XK_3:
- case XK_KP_3:
+ }
+ else if (feh_is_kp(EVENT_action_3, state, keysym, button)) {
feh_event_invoke_action(winwid, 3);
- break;
- case XK_4:
- case XK_KP_4:
+ }
+ else if (feh_is_kp(EVENT_action_4, state, keysym, button)) {
feh_event_invoke_action(winwid, 4);
- break;
- case XK_5:
- case XK_KP_5:
+ }
+ else if (feh_is_kp(EVENT_action_5, state, keysym, button)) {
feh_event_invoke_action(winwid, 5);
- break;
- case XK_6:
- case XK_KP_6:
+ }
+ else if (feh_is_kp(EVENT_action_6, state, keysym, button)) {
feh_event_invoke_action(winwid, 6);
- break;
- case XK_7:
- case XK_KP_7:
+ }
+ else if (feh_is_kp(EVENT_action_7, state, keysym, button)) {
feh_event_invoke_action(winwid, 7);
- break;
- case XK_8:
- case XK_KP_8:
+ }
+ else if (feh_is_kp(EVENT_action_8, state, keysym, button)) {
feh_event_invoke_action(winwid, 8);
- break;
- case XK_9:
- case XK_KP_9:
+ }
+ else if (feh_is_kp(EVENT_action_9, state, keysym, button)) {
feh_event_invoke_action(winwid, 9);
- break;
- case XK_KP_Left:
- winwid->im_x += 10;
- winwidget_render_image(winwid, 0, 0);
- break;
- case XK_KP_Right:
- winwid->im_x -= 10;
+ }
+ 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_Up:
- winwid->im_y += 10;
+ }
+ 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_Down:
- winwid->im_y -= 10;
+ }
+ 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_Add:
- case XK_Up:
- if (kev->state & ControlMask) {
- winwid->im_y += 10;
- winwidget_render_image(winwid, 0, 0);
- }
- else {
- /* erroneously recognized as '+' in the *kbuf switch. Work around this. */
- len = 0;
- winwid->old_zoom = winwid->zoom;
- winwid->zoom = winwid->zoom * 1.25;
- 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, 1);
- }
- break;
- case XK_KP_Subtract:
- case XK_Down:
- if (kev->state & ControlMask) {
- winwid->im_y -= 10;
- winwidget_render_image(winwid, 0, 0);
- }
- else {
- len = 0;
- winwid->old_zoom = winwid->zoom;
- winwid->zoom = winwid->zoom * 0.75;
- 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, 1);
- }
- 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_center_image(winwid);
- winwidget_render_image(winwid, 0, 1);
- break;
- case XK_KP_Begin:
- winwidget_render_image(winwid, 0, 1);
- break;
- default:
- break;
+ winwidget_render_image(winwid, 0, 0);
+ opt.zoom_mode = save_zoom;
}
-
- if (len <= 0 || len > (int) sizeof(kbuf))
- return;
- kbuf[len] = '\0';
-
- switch (*kbuf) {
- 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':
+ 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 ' ':
- if (opt.slideshow)
- slideshow_change_image(winwid, SLIDE_NEXT);
- break;
- case 'o':
+ 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;
- break;
- case 'p':
- case '\b':
- if (opt.slideshow)
- slideshow_change_image(winwid, SLIDE_PREV);
- break;
- case 'z':
- if (opt.slideshow)
- slideshow_change_image(winwid, SLIDE_RAND);
- break;
- case 'q':
- winwidget_destroy_all();
- break;
- case 'c':
- if (opt.caption_path)
+ }
+ 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':
+ }
+ 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':
+ }
+ else if (feh_is_kp(EVENT_toggle_pause, state, keysym, button)) {
slideshow_pause_toggle(winwid);
- break;
- 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':
+ }
+ 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();
- break;
- case 'w':
+ }
+ else if (feh_is_kp(EVENT_size_to_image, state, keysym, button)) {
winwidget_size_to_image(winwid);
- break;
- case 'm':
+ }
+ else if (!opt.no_menus && feh_is_kp(EVENT_toggle_menu, state, keysym, button)) {
winwidget_show_menu(winwid);
- break;
- 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':
+ }
+ 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,
@@ -417,15 +761,14 @@ void feh_event_handle_keypress(XEvent * ev)
break;
}
}
- if (getenv("XINERAMA_SCREEN"))
- curr_screen = xinerama_screen =
- atoi(getenv("XINERAMA_SCREEN"));
+ 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
@@ -436,20 +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 '+':
+ }
+ 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 '-':
+ 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;
+ }
+ 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;
}
diff --git a/src/list.c b/src/list.c
index 989cfcf..0fdc6a5 100644
--- a/src/list.c
+++ b/src/list.c
@@ -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
@@ -36,20 +37,24 @@ void init_list_mode(void)
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);
}
@@ -72,6 +77,7 @@ void real_loadables_mode(int loadable)
{
feh_file *file;
gib_list *l;
+ char ret = 0;
opt.quiet = 1;
@@ -83,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);
}
diff --git a/src/main.c b/src/main.c
index 8905508..2503773 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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,7 +30,13 @@ 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;
@@ -39,19 +46,38 @@ int main(int argc, char **argv)
{
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)
@@ -63,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));
- return(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. */
@@ -92,15 +143,32 @@ int feh_main_iteration(int block)
double t1 = 0.0, t2 = 0.0;
fehtimer ft;
- if (window_num == 0)
+ if (window_num == 0 || sig_exit != 0)
return(0);
+ if (sig_received) {
+ feh_process_signal();
+ }
+
if (first) {
/* Only need to set these up the first time */
xfd = ConnectionNumber(disp);
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 */
@@ -112,8 +180,12 @@ int feh_main_iteration(int block)
if (ev_handler[ev.type])
(*(ev_handler[ev.type])) (&ev);
- if (window_num == 0)
+ if (window_num == 0 || sig_exit != 0)
return(0);
+
+ if (sig_received) {
+ feh_process_signal();
+ }
}
XFlush(disp);
@@ -121,6 +193,16 @@ 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;
@@ -158,12 +240,23 @@ int feh_main_iteration(int block)
&& ((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 ;-) */
@@ -175,11 +268,21 @@ int feh_main_iteration(int block)
&& ((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)
+ if (window_num == 0 || sig_exit != 0)
return(0);
-
+
+ if (sig_received) {
+ feh_process_signal();
+ }
+
return(1);
}
@@ -187,6 +290,32 @@ void feh_clean_exit(void)
{
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);
diff --git a/src/menu.c b/src/menu.c
index c50f015..96173a4 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -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,7 +35,6 @@ 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;
@@ -45,18 +45,45 @@ 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, void *data);
+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_ABOUT = 1, CB_CLOSE, 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_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_OPT_AUTO_ZOOM
+ 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)
@@ -99,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;
@@ -109,7 +136,7 @@ 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 = border.right = border.top = border.bottom
= 4;
@@ -141,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)
@@ -241,7 +266,6 @@ 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;
}
@@ -439,14 +463,12 @@ void feh_menu_show_at_submenu(feh_menu * m, feh_menu * parent_m, feh_menu_item *
void feh_menu_move(feh_menu * m, int x, int y)
{
- int dx, dy;
-
if (!m)
return;
- dx = x - m->x;
- dy = y - m->y;
+
if (m->visible)
XMoveWindow(disp, m->win, x, y);
+
m->x = x;
m->y = y;
return;
@@ -515,26 +537,25 @@ void feh_menu_show(feh_menu * m)
}
feh_menu_item *feh_menu_add_toggle_entry(feh_menu * m, char *text,
- Imlib_Image icon, char *submenu, int action,
- 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;
- mi = feh_menu_add_entry(m, text, icon, submenu, action, 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);
return(mi);
}
-feh_menu_item *feh_menu_add_entry(feh_menu * m, char *text, Imlib_Image icon,
- char *submenu, int action, 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;
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);
@@ -566,12 +587,12 @@ feh_menu_item *feh_menu_add_entry(feh_menu * m, char *text, Imlib_Image icon,
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;
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 {
@@ -580,15 +601,14 @@ void feh_menu_entry_get_size(feh_menu * m, feh_menu_item * i, int *w, int *h)
}
return;
- m = NULL;
}
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;
+ 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;
@@ -597,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)
@@ -615,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)
@@ -654,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)
@@ -704,42 +701,9 @@ void feh_menu_draw_item(feh_menu_item * i, Imlib_Image im, int ox, int oy)
}
/* 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(("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(("submenu item\n"));
feh_menu_draw_submenu_at(i->x + i->sub_x,
@@ -861,27 +825,16 @@ void feh_menu_draw_toggle_at(int x, int y, int w, int h, Imlib_Image dst, int ox
void feh_menu_draw_submenu_at(int x, int y, Imlib_Image dst, int ox, int oy)
{
- ImlibPolygon poly;
-
- 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);
+
+ for (int i= 0; i <= 3; i++) {
+ imlib_image_draw_line(x+i, y+3+i, x+i, y+9-i, 0);
+ }
return;
}
@@ -896,7 +849,7 @@ void feh_menu_item_draw_at(int x, int y, int w, int h, Imlib_Image dst, int ox,
{
imlib_context_set_image(dst);
if (selected)
- gib_imlib_image_fill_rectangle(dst, x - ox, y - oy, w, h, 255, 255, 255, 178);
+ gib_imlib_image_fill_rectangle(dst, x - ox, y - oy, w, h, 127, 127, 127, 178);
return;
}
@@ -944,39 +897,42 @@ void feh_menu_init_main(void)
menu_main = feh_menu_new();
menu_main->name = estrdup("MAIN");
- feh_menu_add_entry(menu_main, "File", NULL, "FILE", 0, 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", 0, NULL, NULL);
- mi = feh_menu_add_entry(menu_main, "Image Info", NULL, "INFO", 0, 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, 0, NULL, NULL);
+ feh_menu_add_entry(menu_main, NULL, NULL, 0, 0, NULL);
}
- mi = feh_menu_add_entry(menu_main, "Options", NULL, "OPTIONS", 0, 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, CB_ABOUT, NULL, NULL);
if (opt.multiwindow)
- feh_menu_add_entry(menu_main, "Close", NULL, NULL, CB_CLOSE, NULL, NULL);
- feh_menu_add_entry(menu_main, "Exit", NULL, NULL, 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, CB_RESET, NULL, NULL);
- feh_menu_add_entry(m, "Resize Window", NULL, NULL, CB_FIT, NULL, NULL);
- feh_menu_add_entry(m, "Reload", NULL, NULL, CB_RELOAD, NULL, NULL);
- feh_menu_add_entry(m, "Save Image", NULL, NULL, CB_SAVE_IMAGE, NULL, NULL);
- feh_menu_add_entry(m, "Save List", NULL, NULL, CB_SAVE_FILELIST, NULL, NULL);
- feh_menu_add_entry(m, "Edit in Place", NULL, "EDIT", 0, NULL, NULL);
- feh_menu_add_entry(m, "Background", NULL, "BACKGROUND", 0, NULL, NULL);
- feh_menu_add_entry(m, NULL, NULL, NULL, 0, NULL, NULL);
- feh_menu_add_entry(m, "Hide", NULL, NULL, CB_REMOVE, NULL, NULL);
- feh_menu_add_entry(m, "Delete", NULL, "CONFIRM", 0, 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);
+ 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];
@@ -986,57 +942,53 @@ void feh_menu_init_common()
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, CB_SORT_FILENAME, NULL, NULL);
- feh_menu_add_entry(m, "By Image Name", NULL, NULL, CB_SORT_IMAGENAME, NULL, NULL);
- if (opt.preload || (opt.sort > SORT_FILENAME))
- feh_menu_add_entry(m, "By File Size", NULL, NULL, CB_SORT_FILESIZE, NULL, NULL);
- feh_menu_add_entry(m, "Randomize", NULL, NULL, 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, 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, CB_EDIT_ROTATE, (void *) 1, NULL);
- feh_menu_add_entry(m, "Rotate 180", NULL, NULL, CB_EDIT_ROTATE, (void *) 2, NULL);
- feh_menu_add_entry(m, "Rotate 90 CCW", NULL, NULL, 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", 0, NULL, NULL);
- feh_menu_add_entry(menu_bg, "Set Scaled", NULL, "SCALED", 0, NULL, NULL);
- feh_menu_add_entry(menu_bg, "Set Centered", NULL, "CENTERED", 0, NULL, NULL);
- feh_menu_add_entry(menu_bg, "Set Filled", NULL, "FILLED", 0, 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, CB_BG_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, CB_BG_TILED_NOFILE,
- (void *) i, NULL);
+ feh_menu_add_entry(m, buf, NULL, CB_BG_TILED_NOFILE,
+ i, NULL);
}
m = feh_menu_new();
@@ -1045,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, CB_BG_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, CB_BG_SCALED_NOFILE,
- (void *) i, NULL);
+ feh_menu_add_entry(m, buf, NULL, CB_BG_SCALED_NOFILE,
+ i, NULL);
}
m = feh_menu_new();
@@ -1057,11 +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,
- CB_BG_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,
- CB_BG_CENTERED_NOFILE, (void *) i, NULL);
+ feh_menu_add_entry(m, buf, NULL,
+ CB_BG_CENTERED_NOFILE, i, NULL);
}
m = feh_menu_new();
@@ -1069,33 +1021,33 @@ 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_add_entry(m, buf, NULL,
CB_BG_FILLED,
- (void *) i, NULL);
+ i, NULL);
else
- feh_menu_add_entry(m, buf, NULL, NULL,
+ feh_menu_add_entry(m, buf, NULL,
CB_BG_FILLED_NOFILE,
- (void *) i, NULL);
+ i, NULL);
}
} else {
if (opt.slideshow || opt.multiwindow) {
- feh_menu_add_entry(menu_bg, "Set Tiled", NULL,
- NULL, CB_BG_TILED, NULL, NULL);
- feh_menu_add_entry(menu_bg, "Set Scaled", NULL,
- NULL, CB_BG_SCALED, NULL, NULL);
- feh_menu_add_entry(menu_bg, "Set Centered", NULL,
- NULL, CB_BG_CENTERED, NULL, NULL);
- feh_menu_add_entry(menu_bg, "Set Filled", NULL,
- NULL, CB_BG_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, CB_BG_TILED_NOFILE, NULL, NULL);
- feh_menu_add_entry(menu_bg, "Set Scaled", NULL,
- NULL, CB_BG_SCALED_NOFILE, NULL, NULL);
- feh_menu_add_entry(menu_bg, "Set Centered", NULL,
- NULL, CB_BG_CENTERED_NOFILE, NULL, NULL);
- feh_menu_add_entry(menu_bg, "Set Filled", NULL,
- NULL, CB_BG_FILLED_NOFILE, 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;
@@ -1103,17 +1055,6 @@ void feh_menu_init_common()
return;
}
-void feh_menu_init_about_win(void)
-{
- menu_about_win = feh_menu_new();
- menu_about_win->name = estrdup("ABOUTWIN");
-
- feh_menu_add_entry(menu_about_win, "Close", NULL, NULL, CB_CLOSE, NULL, NULL);
- feh_menu_add_entry(menu_about_win, "Exit", NULL, NULL, CB_EXIT, NULL, NULL);
-
- return;
-}
-
void feh_menu_init_single_win(void)
{
feh_menu *m;
@@ -1125,31 +1066,29 @@ void feh_menu_init_single_win(void)
menu_single_win = feh_menu_new();
menu_single_win->name = estrdup("SINGLEWIN");
- feh_menu_add_entry(menu_single_win, "File", NULL, "SINGLEWIN_FILE", 0, 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, CB_RESET, NULL, NULL);
- feh_menu_add_entry(m, "Resize Window", NULL, NULL, CB_FIT, NULL, NULL);
- feh_menu_add_entry(m, "Reload", NULL, NULL, CB_RELOAD, NULL, NULL);
- feh_menu_add_entry(m, "Save Image", NULL, NULL, CB_SAVE_IMAGE, NULL, NULL);
- feh_menu_add_entry(m, "Save List", NULL, NULL, CB_SAVE_FILELIST, NULL, NULL);
- feh_menu_add_entry(m, "Edit in Place", NULL, "EDIT", 0, NULL, NULL);
- feh_menu_add_entry(m, "Background", NULL, "BACKGROUND", 0, 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, 0, NULL, NULL);
- feh_menu_add_entry(m, "Hide", NULL, NULL, CB_REMOVE, NULL, NULL);
- feh_menu_add_entry(m, "Delete", NULL, "CONFIRM", 0, 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", 0, 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, 0, NULL, NULL);
- mi = feh_menu_add_entry(menu_single_win, "Options", NULL, "OPTIONS", 0, 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,
- CB_ABOUT, NULL, NULL);
- feh_menu_add_entry(menu_single_win, "Close", NULL, NULL, CB_CLOSE, NULL, NULL);
- feh_menu_add_entry(menu_single_win, "Exit", NULL, NULL, 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);
return;
}
@@ -1165,21 +1104,19 @@ void feh_menu_init_thumbnail_win(void)
menu_thumbnail_win = feh_menu_new();
menu_thumbnail_win->name = estrdup("THUMBWIN");
- feh_menu_add_entry(menu_thumbnail_win, "File", NULL, "THUMBWIN_FILE", 0, 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, CB_RESET, NULL, NULL);
- feh_menu_add_entry(m, "Resize Window", NULL, NULL, CB_FIT, NULL, NULL);
- feh_menu_add_entry(m, "Save Image", NULL, NULL, CB_SAVE_IMAGE, NULL, NULL);
- feh_menu_add_entry(m, "Save List", NULL, NULL, CB_SAVE_FILELIST, NULL, NULL);
- feh_menu_add_entry(m, "Background", NULL, "BACKGROUND", 0, NULL, NULL);
- feh_menu_add_entry(menu_thumbnail_win, NULL, NULL, NULL, 0, NULL, NULL);
- mi = feh_menu_add_entry(menu_thumbnail_win, "Options", NULL, "OPTIONS", 0, 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,
- CB_ABOUT, NULL, NULL);
- feh_menu_add_entry(menu_thumbnail_win, "Close", NULL, NULL, CB_CLOSE, NULL, NULL);
- feh_menu_add_entry(menu_thumbnail_win, "Exit", NULL, NULL, CB_EXIT, NULL, NULL);
+ 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;
}
@@ -1194,34 +1131,32 @@ void feh_menu_init_thumbnail_viewer(void)
menu_thumbnail_viewer = feh_menu_new();
menu_thumbnail_viewer->name = estrdup("THUMBVIEW");
- feh_menu_add_entry(menu_thumbnail_viewer, "File", NULL, "THUMBVIEW_FILE",
- 0, 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, CB_RESET, NULL, NULL);
- feh_menu_add_entry(m, "Resize Window", NULL, NULL, CB_FIT, NULL, NULL);
- feh_menu_add_entry(m, "Reload", NULL, NULL, CB_RELOAD, NULL, NULL);
- feh_menu_add_entry(m, "Save Image", NULL, NULL, CB_SAVE_IMAGE, NULL, NULL);
- feh_menu_add_entry(m, "Save List", NULL, NULL, CB_SAVE_FILELIST, NULL, NULL);
- feh_menu_add_entry(m, "Edit in Place", NULL, "EDIT", 0, NULL, NULL);
- feh_menu_add_entry(m, "Background", NULL, "BACKGROUND", 0, NULL, NULL);
- feh_menu_add_entry(m, NULL, NULL, NULL, 0, NULL, NULL);
- feh_menu_add_entry(m, "Hide", NULL, NULL, CB_REMOVE_THUMB, NULL, NULL);
- feh_menu_add_entry(m, "Delete", NULL, "THUMBVIEW_CONFIRM", 0, NULL, NULL);
- mi = feh_menu_add_entry(menu_thumbnail_viewer, "Image Info", NULL,
- "INFO", 0, 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, 0, NULL, NULL);
- mi = feh_menu_add_entry(menu_thumbnail_viewer, "Options", NULL,
- "OPTIONS", 0, 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,
- CB_ABOUT, NULL, NULL);
- feh_menu_add_entry(menu_thumbnail_viewer, "Close", NULL, NULL, CB_CLOSE, NULL, NULL);
- feh_menu_add_entry(menu_thumbnail_viewer, "Exit", NULL, NULL, 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, CB_DELETE_THUMB, NULL, NULL);
+ feh_menu_add_entry(m, "Confirm", NULL, CB_DELETE_THUMB, 0, NULL);
return;
}
@@ -1240,15 +1175,8 @@ void feh_menu_cb_opt_fullscreen(feh_menu * m, feh_menu_item * i)
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,
@@ -1258,16 +1186,15 @@ void feh_menu_cb_opt_fullscreen(feh_menu * m, feh_menu_item * i)
}
}
- if (getenv("XINERAMA_SCREEN"))
- curr_screen = xinerama_screen =
- atoi(getenv("XINERAMA_SCREEN"));
+ if (opt.xinerama_index >= 0)
+ curr_screen = xinerama_screen = opt.xinerama_index;
}
#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_render_image(m->fehwin, 1, 0);
winwidget_show(m->fehwin);
#ifdef HAVE_LIBXINERAMA
@@ -1280,54 +1207,38 @@ void feh_menu_cb_opt_fullscreen(feh_menu * m, feh_menu_item * i)
#endif /* HAVE_LIBXINERAMA */
}
-void feh_menu_cb(feh_menu * m, feh_menu_item * i, int action, void *data)
+void feh_menu_cb(feh_menu * m, feh_menu_item * i, int action, unsigned short data)
{
char *path;
- Imlib_Image im;
- winwidget winwid;
switch (action) {
case CB_BG_TILED:
- 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);
+ 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_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);
+ 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_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);
+ 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_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);
+ 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, (int) data, 1);
+ 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, (int) data, 1);
+ 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, (int) data, 1);
+ 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, (int) data, 1);
- break;
- case CB_ABOUT:
- 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);
- }
+ feh_wm_set_bg(NULL, m->fehwin->im, 0, 0, 1, data, 0);
break;
case CB_CLOSE:
winwidget_destroy(m->fehwin);
@@ -1339,13 +1250,13 @@ void feh_menu_cb(feh_menu * m, feh_menu_item * i, int action, void *data)
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);
+ winwidget_resize(m->fehwin, m->fehwin->im_w, m->fehwin->im_h, 0);
}
winwidget_reset_image(m->fehwin);
- winwidget_render_image(m->fehwin, 1, 1);
+ winwidget_render_image(m->fehwin, 1, 0);
break;
case CB_RELOAD:
- feh_reload_image(m->fehwin, 0, 0);
+ feh_reload_image(m->fehwin, 0, 1);
break;
case CB_REMOVE:
feh_filelist_image_remove(m->fehwin, 0);
@@ -1364,32 +1275,50 @@ void feh_menu_cb(feh_menu * m, feh_menu_item * i, int action, void *data)
case CB_SORT_FILENAME:
filelist = gib_list_sort(filelist, feh_cmp_filename);
if (opt.jump_on_resort) {
- slideshow_change_image(m->fehwin, SLIDE_FIRST);
+ 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);
+ 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);
+ 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);
+ 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_orient(m->fehwin, (int) data);
+ 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);
@@ -1403,7 +1332,7 @@ void feh_menu_cb(feh_menu * m, feh_menu_item * i, int action, void *data)
opt.draw_filename = TRUE;
else
opt.draw_filename = FALSE;
- winwidget_rerender_all(0, 1);
+ winwidget_rerender_all(0);
break;
case CB_OPT_DRAW_ACTIONS:
MENU_ITEM_TOGGLE(i);
@@ -1411,7 +1340,7 @@ void feh_menu_cb(feh_menu * m, feh_menu_item * i, int action, void *data)
opt.draw_actions = TRUE;
else
opt.draw_actions = FALSE;
- winwidget_rerender_all(0, 1);
+ winwidget_rerender_all(0);
break;
case CB_OPT_KEEP_HTTP:
MENU_ITEM_TOGGLE(i);
@@ -1435,8 +1364,18 @@ void feh_menu_cb(feh_menu * m, feh_menu_item * i, int action, void *data)
break;
case CB_OPT_AUTO_ZOOM:
MENU_ITEM_TOGGLE(i);
- opt.auto_zoom = MENU_ITEM_IS_ON(i) ? 1 : 0;
- winwidget_rerender_all(1, 1);
+ 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;
@@ -1458,16 +1397,16 @@ static feh_menu *feh_menu_func_gen_info(feh_menu * m)
mm = feh_menu_new();
mm->name = estrdup("INFO");
snprintf(buffer, sizeof(buffer), "Filename: %s", file->name);
- feh_menu_add_entry(mm, buffer, NULL, NULL, 0, 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, 0, 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, 0, 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, 0, NULL, NULL);
+ feh_menu_add_entry(mm, buffer, NULL, 0, 0, NULL);
}
mm->func_free = feh_menu_func_free_info;
@@ -1487,20 +1426,23 @@ static feh_menu *feh_menu_func_gen_options(feh_menu * m)
mm = feh_menu_new();
mm->name = estrdup("OPTIONS");
mm->fehwin = m->fehwin;
- feh_menu_add_toggle_entry(mm, "Auto-Zoom", NULL, NULL, CB_OPT_AUTO_ZOOM, NULL, NULL, opt.auto_zoom);
- feh_menu_add_toggle_entry(mm, "Freeze Window Size", NULL, NULL,
- CB_OPT_FREEZE_WINDOW, NULL, NULL, opt.geom_flags);
- feh_menu_add_toggle_entry(mm, "Fullscreen", NULL, NULL,
- CB_OPT_FULLSCREEN, NULL, NULL, m->fehwin->full_screen);
-
- feh_menu_add_entry(mm, NULL, NULL, NULL, 0, NULL, NULL);
-
- feh_menu_add_toggle_entry(mm, "Draw Filename", NULL, NULL,
- CB_OPT_DRAW_FILENAME, NULL, NULL, opt.draw_filename);
- feh_menu_add_toggle_entry(mm, "Draw Actions", NULL, NULL,
- CB_OPT_DRAW_ACTIONS, NULL, NULL, opt.draw_actions);
- feh_menu_add_toggle_entry(mm, "Keep HTTP Files", NULL, NULL,
- 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;
return(mm);
}
diff --git a/src/menu.h b/src/menu.h
index 9e8cd21..5bad00c 100644
--- a/src/menu.h
+++ b/src/menu.h
@@ -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
@@ -85,12 +86,11 @@ struct _feh_menu_list {
struct _feh_menu_item {
int state;
- Imlib_Image icon;
char *text;
char *submenu;
int action;
void (*func_free) (void *data);
- void *data;
+ unsigned short data;
feh_menu_item *next;
feh_menu_item *prev;
unsigned char is_toggle;
@@ -130,12 +130,12 @@ 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,
- int action, 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,
- int action, 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_item * i, Imlib_Image im, int ox, int oy);
void feh_menu_redraw(feh_menu * m);
@@ -143,7 +143,6 @@ 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);
@@ -168,7 +167,6 @@ 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/multiwindow.c b/src/multiwindow.c
index c46e453..abbf6c9 100644
--- a/src/multiwindow.c
+++ b/src/multiwindow.c
@@ -28,30 +28,20 @@ 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;
+
+ 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);
@@ -61,7 +51,7 @@ void init_multiwindow_mode(void)
D(("EEEK. Couldn't load image in multiwindow mode. "
"I 'm not sure if this is a problem\n"));
}
- free(s);
}
+
return;
}
diff --git a/src/options.c b/src/options.c
index 2206f3e..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,15 +24,15 @@ 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_create_default_config(char *rcfile);
-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);
@@ -53,52 +54,40 @@ void init_parse_options(int argc, char **argv)
opt.display = 1;
opt.aspect = 1;
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 = NULL;
- 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.max_height = opt.max_width = UINT_MAX;
- 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.draw_actions = 0;
-
- opt.rotate_button = 2;
- opt.no_rotate_ctrl_mask = 0;
- opt.blur_button = 1;
- opt.no_blur_ctrl_mask = 0;
+ opt.zoom_rate = 1.25;
opt.start_list_at = NULL;
opt.jump_on_resort = 1;
- opt.builtin_http = 0;
-
- opt.xinerama = 0;
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;
+
+ feh_getopt_theme(argc, argv);
- D(("About to parse env options (if any)\n"));
- /* Check for and parse any options in FEH_OPTIONS */
- feh_parse_environment_options();
+ D(("About to check for theme configuration\n"));
+ feh_check_theme_options(argv);
D(("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(argc, argv);
+ feh_parse_option_array(argc, argv, 1);
/* If we have a filelist to read, do it now */
if (opt.filelistfile) {
@@ -111,9 +100,6 @@ void init_parse_options(int argc, char **argv)
D(("Options parsed\n"));
- if (opt.bgmode)
- return;
-
filelist_len = gib_list_length(filelist);
if (!filelist_len)
show_mini_usage();
@@ -124,7 +110,7 @@ void init_parse_options(int argc, char **argv)
return;
}
-static void feh_check_theme_options(int arg, char **argv)
+static void feh_check_theme_options(char **argv)
{
if (!theme) {
/* This prevents screw up when running src/feh or ./feh */
@@ -141,53 +127,65 @@ static void feh_check_theme_options(int arg, char **argv)
free(theme);
return;
- arg = 0;
}
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 (opt.rcfile) {
- if ((fp = fopen(opt.rcfile, "r")) == NULL) {
- weprintf("couldn't load the specified rcfile %s\n", opt.rcfile);
- return;
- }
- } 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(("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)
- return;
- }
-
- free(rcpath);
+ 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;
}
+ oldrcpath = estrjoin("/", home, ".fehrc", NULL);
+
+ fp = fopen(rcpath, "r");
+
+ 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';
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;
@@ -221,67 +219,52 @@ static void feh_load_options_for_theme(char *theme)
return;
}
-static void feh_parse_environment_options(void)
-{
- char *opts;
-
- if ((opts = getenv("FEH_OPTIONS")) == NULL)
- return;
-
- weprintf
- ("The FEH_OPTIONS configuration method is depreciated and will soon die.\n"
- "Use the .fehrc configuration file instead.");
-
- /* We definitely have some options to parse */
- feh_parse_options_from_string(opts);
- 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;
/* 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);
return;
}
@@ -301,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
@@ -308,7 +294,7 @@ 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';
@@ -317,232 +303,249 @@ char *feh_string_normalize(char *str)
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[] = "-T:";
+ static struct option lopts[] = {
+ {"theme", 1, 0, 'T'},
+ {0, 0, 0, 0}
+ };
+ int optch = 0, cmdx = 0;
+
+ 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:cC:dD:e:E:f:Fg:GhH:iIj:J:kK:lL:mM:nNo:O:pPqQrR:sS:tT:uUvVwW:xXy:YzZ"
- "0:1:2:4:5:8:9:.@:^:~:):|:_:+:";
+ "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[] = {
- {"help" , 0, 0, 'h'},
- {"version" , 0, 0, 'v'},
- {"montage" , 0, 0, 'm'},
- {"collage" , 0, 0, 'c'},
- {"index" , 0, 0, 'i'},
- {"fullindex" , 0, 0, 'I'},
- {"verbose" , 0, 0, 'V'},
- {"borderless" , 0, 0, 'x'},
- {"keep-http" , 0, 0, 'k'},
- {"stretch" , 0, 0, 's'},
- {"multiwindow" , 0, 0, 'w'},
- {"recursive" , 0, 0, 'r'},
- {"randomize" , 0, 0, 'z'},
- {"list" , 0, 0, 'l'},
- {"quiet" , 0, 0, 'q'},
- {"loadable" , 0, 0, 'U'},
- {"unloadable" , 0, 0, 'u'},
- {"no-menus" , 0, 0, 'N'},
- {"full-screen" , 0, 0, 'F'}, /* deprecated */
- {"fullscreen" , 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'},
- {"builtin" , 0, 0, 'Q'},
- {"scale-down" , 0, 0, '.'},
- {"no-jump-on-resort", 0, 0, 220},
- {"hide-pointer" , 0, 0, 'Y'},
- {"draw-actions" , 0, 0, 'G'},
- {"cache-thumbnails", 0, 0, 'P'},
- {"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},
-
- {"output" , 1, 0, 'o'},
- {"output-only" , 1, 0, 'O'},
- {"action" , 1, 0, 'A'},
- {"limit-width" , 1, 0, 'W'},
- {"limit-height" , 1, 0, 'H'},
- {"reload" , 1, 0, 'R'},
- {"alpha" , 1, 0, 'a'},
- {"sort" , 1, 0, 'S'},
- {"theme" , 1, 0, 'T'},
- {"filelist" , 1, 0, 'f'},
- {"customlist" , 1, 0, 'L'},
- {"geometry" , 1, 0, 'g'},
- {"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" , 0, 0, '+'},
- {"output-dir" , 1, 0, 'j'},
- {"bg-tile" , 1, 0, 200},
- {"bg-center" , 1, 0, 201},
- {"bg-scale" , 1, 0, 202},
- {"menu-style" , 1, 0, 204},
- {"zoom" , 1, 0, 205},
- {"no-screen-clip", 0, 0, 206},
- {"caption-path" , 1, 0, 'K'},
- {"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},
- {"bg-max" , 1, 0, 219},
- {"index-name" , 1, 0, 230},
- {"index-size" , 1, 0, 231},
- {"index-dim" , 1, 0, 232},
- {"thumb-redraw" , 1, 0, 'J'},
- {"info" , 1, 0, 234},
-
+ {"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;
- int i = 0;
- /* Now to pass some optionarinos */
while ((optch = getopt_long(argc, argv, stropts, lopts, &cmdx)) != EOF) {
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 'Q':
- opt.builtin_http = 1;
- break;
- case 'L':
- opt.customlist = estrdup(optarg);
- opt.display = 0;
- break;
- case 'M':
- free(opt.menu_font);
- opt.menu_font = estrdup(optarg);
- break;
- case '+':
- opt.debug = 1;
- 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;
- opt.display = 0;
+ 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"))
@@ -558,219 +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(("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);
- if (opt.slideshow_delay < 0.0) {
- opt.slideshow_delay *= (-1);
- opt.paused = 1;
- }
+ 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 'f':
- opt.filelistfile = estrdup(optarg);
+ case OPTION_output:
+ opt.output = 1;
+ opt.output_file = estrdup(optarg);
break;
- case '0':
- opt.reload_button = atoi(optarg);
+ case OPTION_preload:
+ opt.preload = 1;
break;
- case '1':
- opt.pan_button = atoi(optarg);
+ case OPTION_quiet:
+ opt.quiet = 1;
break;
- case '2':
- opt.zoom_button = atoi(optarg);
+ case OPTION_recursive:
+ opt.recursive = 1;
break;
- case '3':
- opt.menu_button = atoi(optarg);
+ case OPTION_stretch:
+ opt.stretch = 1;
break;
- case '4':
- opt.prev_button = atoi(optarg);
+ case OPTION_thumbnails:
+ opt.thumbs = 1;
+ opt.index_info = estrdup("%n");
break;
- case '5':
- opt.next_button = atoi(optarg);
+ case OPTION_unloadable:
+ opt.unloadables = 1;
+ opt.display = 0;
break;
- case '8':
- opt.rotate_button = atoi(optarg);
+ case OPTION_version:
+ show_version();
break;
- case '9':
- opt.blur_button = atoi(optarg);
+ case OPTION_multiwindow:
+ opt.multiwindow = 1;
break;
- case '|':
- opt.start_list_at = estrdup(optarg);
+ case OPTION_borderless:
+ opt.borderless = 1;
break;
- case 't':
- opt.thumbs = 1;
- opt.index_show_name = 1;
- opt.index_show_size = 0;
- opt.index_show_dim = 0;
+ case OPTION_thumb_width:
+ opt.thumb_w = atoi(optarg);
break;
- case 'j':
- opt.output_dir = estrdup(optarg);
+ 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 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 218:
- opt.bgmode = BG_MODE_FILL;
- opt.output_file = estrdup(optarg);
- break;
- case 219:
- opt.bgmode = BG_MODE_MAX;
- opt.output_file = estrdup(optarg);
break;
- case 204:
- free(opt.menu_style);
- opt.menu_style = 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 205:
- opt.default_zoom = atoi(optarg);
- break;
- case 206:
+ case OPTION_no_screen_clip:
opt.screen_clip = 0;
break;
- case 'K':
- opt.caption_path = estrdup(optarg);
+ case OPTION_index_info:
+ opt.index_info = estrdup(optarg);
break;
- case 209:
+ case OPTION_magick_timeout:
+ weprintf("--magick-timeout is deprecated, please use --conversion-timeout instead");
+ opt.conversion_timeout = atoi(optarg);
+ break;
+ 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.jump_on_resort = 0;
+ case OPTION_bg_fill:
+ opt.bgmode = BG_MODE_FILL;
break;
- case 'Y':
- opt.hide_pointer = 1;
+ case OPTION_bg_max:
+ opt.bgmode = BG_MODE_MAX;
break;
- case 'G':
- opt.draw_actions = 1;
+ case OPTION_no_jump_on_resort:
+ opt.jump_on_resort = 0;
break;
- case 'P':
- opt.cache_thumbnails = 1;
+ case OPTION_edit:
+ opt.edit = 1;
+ break;
+#ifdef HAVE_LIBEXIF
+ case OPTION_draw_exif:
+ opt.draw_exif = 1;
break;
- case 224:
- opt.cycle_once = 1;
+ 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;
- case 225:
+#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 227:
- opt.no_blur_ctrl_mask = 1;
+ 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 228:
- opt.menu_ctrl_mask = 1;
+ case OPTION_tap_zones:
+ opt.tap_zones = 1;
break;
- case 230:
- opt.index_show_name = atoi(optarg);
+ case OPTION_force_aliasing:
+ opt.force_aliasing = 1;
break;
- case 231:
- opt.index_show_size = atoi(optarg);
+ case OPTION_no_fehbg:
+ opt.no_fehbg = 1;
break;
- case 232:
- opt.index_show_dim = atoi(optarg);
+ case OPTION_keep_zoom_vp:
+ opt.keep_zoom_vp = 1;
break;
- case 'J':
- opt.thumb_redraw = atoi(optarg);
+ case OPTION_scroll_step:
+ opt.scroll_step = atoi(optarg);
break;
- case 234:
- opt.info_cmd = estrdup(optarg);
+ case OPTION_xinerama_index:
+ opt.xinerama_index = atoi(optarg);
+ break;
+ case OPTION_insecure:
+ opt.insecure_ssl = 1;
+ break;
+ case OPTION_no_recursive:
+ opt.recursive = 0;
+ break;
+ 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 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 OPTION_window_id:
+ opt.x11_windowid = strtol(optarg, NULL, 0);
+ break;
+ 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;
@@ -780,62 +868,90 @@ 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);
}
}
-
- 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];
+ 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;
+ optind = 0;
return;
}
static void check_options(void)
{
- 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;
- }
-
- 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;
+ eprintf("You cannot combine --loadable with --unloadable");
}
return;
@@ -843,38 +959,70 @@ static void check_options(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(stderr, PACKAGE " - No loadable images specified.\n"
- "Use " PACKAGE " --help for detailed usage information\n");
+ 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);
}
static void show_usage(void)
{
fputs(
+#ifdef INCLUDE_HELP
#include "help.inc"
+#else
+ "See 'man " PACKAGE "'\n"
+#endif
, stdout);
exit(0);
}
-
-static void feh_create_default_config(char *rcfile)
-{
- FILE *fp;
-
- if ((fp = fopen(rcfile, "w")) == NULL) {
- weprintf("Unable to create default config file %s\n", rcfile);
- return;
- }
-
- fputs(
-#include "fehrc.inc"
- , fp);
- fclose(fp);
-
- return;
-}
diff --git a/src/options.h b/src/options.h
index e99c14c..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 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,76 +71,279 @@ struct __fehoptions {
unsigned char reverse;
unsigned char no_menus;
unsigned char scale_down;
- unsigned char builtin_http;
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;
unsigned int thumb_redraw;
- int reload;
+ double reload;
int sort;
+ 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 zoom_mode;
+ double zoom_rate;
unsigned char adjust_reload;
+ int xinerama_index;
+ char *x11_class;
+ unsigned long int x11_windowid;
+
+ /* signed in case someone wants to invert scrolling real quick */
+ int scroll_step;
+
+ // 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);
diff --git a/src/signals.c b/src/signals.c
index b3e118a..058b8c9 100644
--- a/src/signals.c
+++ b/src/signals.c
@@ -1,6 +1,6 @@
/* signals.c
-Copyright (C) 2010 by Daniel Friesel
+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
@@ -24,20 +24,29 @@ 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 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, 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 mask, SIGUSR1/2 won't work");
+ weprintf("Failed to set up signal masks");
return;
}
@@ -47,9 +56,14 @@ void setup_signal_handlers()
if (
(sigaction(SIGUSR1, &feh_sh, NULL) == -1) ||
- (sigaction(SIGUSR2, &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, SIGUSR1/2 won't work");
+ weprintf("Failed to set up signal handler");
return;
}
@@ -58,15 +72,23 @@ void setup_signal_handlers()
void feh_handle_signal(int signo)
{
- winwidget winwid
- = winwidget_get_first_window_of_type(WIN_TYPE_SLIDESHOW);
-
- if (winwid) {
- if (signo == SIGUSR1)
- slideshow_change_image(winwid, SLIDE_NEXT);
- else if (signo == SIGUSR2)
- slideshow_change_image(winwid, SLIDE_PREV);
+ 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;
}
- return;
+ sig_received = signo;
}
diff --git a/src/signals.h b/src/signals.h
index 526285d..3d78b67 100644
--- a/src/signals.h
+++ b/src/signals.h
@@ -1,6 +1,6 @@
/* signals.h
-Copyright (C) 2010 by Daniel Friesel
+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
@@ -26,6 +26,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef SIGNALS_H
#define SIGNALS_H
-void setup_signal_handlers();
-
+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 de10300..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
@@ -34,158 +35,179 @@ void init_slideshow_mode(void)
{
winwidget w = NULL;
int success = 0;
- char *s = NULL;
gib_list *l = filelist, *last = NULL;
- feh_file *file = NULL;
- for (l = filelist; l && opt.start_list_at; l = l->next) {
+ /*
+ * 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;
}
}
+ /*
+ * 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) {
+ 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)
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();
- setup_signal_handlers();
-
return;
}
void cb_slide_timer(void *data)
{
- slideshow_change_image((winwidget) data, SLIDE_NEXT);
+ 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;
- feh_reload_image(w, 0, 0);
- feh_add_unique_timer(cb_reload_timer, w, opt.reload);
- return;
-}
+ 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);
- if (!w->file) {
- weprintf("couldn't reload, this image has no file associated with it.");
- return;
- }
+ 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));
+ }
- free(FEH_FILE(w->file->data)->caption);
- FEH_FILE(w->file->data)->caption = NULL;
+ if (!(filelist_len = gib_list_length(filelist))) {
+ eprintf("No files found to reload.");
+ }
- 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);
+ feh_prepare_filelist();
- /* force imlib2 not to cache */
- if (force_new) {
- winwidget_free_image(w);
- }
+ /* 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;
+ }
- /* 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);
- return;
- }
- 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;
- }
+ free(current_filename);
- 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);
+ if (!current_file)
+ current_file = filelist;
+ w->file = current_file;
}
- winwidget_render_image(w, resize, 1);
-
- winwidget_rename(w, title);
- free(title);
- free(new_title);
+ 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;
/* We can't use filelist_len in the for loop, since that changes when we
* encounter invalid images.
*/
int our_filelist_len = filelist_len;
- char *s;
+
+ 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)
+ 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 *
@@ -195,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 < 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);
@@ -211,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)
@@ -242,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;
@@ -251,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;
@@ -278,8 +362,6 @@ 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");
return;
}
@@ -294,143 +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;
-
- 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));
- }
-
- return(s);
-}
-
-void feh_action_run(feh_file * file, char *action)
+void feh_action_run(feh_file * file, char *action, winwidget winwid)
{
if (action) {
char *sys;
D(("Running action %s\n", action));
- sys = feh_printf(action, file);
+ 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 *feh_printf(char *str, feh_file * file)
+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++;
+ }
+ snprintf(ret, 5, "%3.0f%c", size, units[postfix]);
+ return ret;
+}
+
+char *feh_printf(char *str, feh_file * file, winwidget winwid)
{
char *c;
char buf[20];
static char ret[4096];
+ 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);
}
+ 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,24 +621,33 @@ 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);
+ }
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);
return;
@@ -497,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/thumbnail.c b/src/thumbnail.c
index ed4e0fb..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,23 +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;
@@ -90,6 +91,8 @@ void init_thumbnail_mode(void)
td.vertical = 0;
td.max_column_w = 0;
+ if (!opt.thumb_title)
+ opt.thumb_title = "%n";
mode = "thumbnail";
if (opt.font)
@@ -102,6 +105,9 @@ void init_thumbnail_mode(void)
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;
@@ -114,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;
@@ -140,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,
@@ -161,20 +176,13 @@ 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 = opt.cache_thumbnails;
if (td.cache_thumbnails) {
@@ -183,9 +191,15 @@ void init_thumbnail_mode(void)
else
td.cache_dim = opt.thumb_h;
- if (td.cache_dim > 256) {
- /* No caching as specified by standard. Sort of. */
+ 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");
@@ -204,7 +218,8 @@ void init_thumbnail_mode(void)
}
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(("Successfully loaded %s\n", file->filename));
@@ -212,6 +227,12 @@ void init_thumbnail_mode(void)
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);
@@ -252,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;
@@ -320,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;
@@ -355,7 +355,7 @@ void init_thumbnail_mode(void)
if (opt.display) {
/* thumb_counter is unsigned, so no need to catch overflows */
if (++thumb_counter == opt.thumb_redraw) {
- winwidget_render_image(winwid, 0, 0);
+ winwidget_render_image(winwid, 0, 1);
thumb_counter = 0;
}
if (!feh_main_iteration(0))
@@ -364,10 +364,10 @@ void init_thumbnail_mode(void)
}
if (thumb_counter != 0)
- winwidget_render_image(winwid, 0, 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;
@@ -380,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) {
@@ -387,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);
}
@@ -404,43 +412,19 @@ void init_thumbnail_mode(void)
if (!opt.display)
gib_imlib_free_image_and_decache(td.im_main);
-
- free(s);
- return;
-}
-
-static char *create_index_size_string(char *file)
-{
- static char str[50];
- int size = 0;
- double kbs = 0.0;
- struct stat st;
-
- 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);
- return(str);
-}
-
-static char *create_index_dimension_string(int w, int h)
-{
- static char str[50];
-
- snprintf(str, sizeof(str), "%dx%d", w, h);
- return(str);
-}
-
-static char *create_index_title_string(int num, int w, int h)
-{
- static char str[50];
- snprintf(str, sizeof(str), PACKAGE " index - %d thumbnails, %d by %d pixels", num, w, h);
- return(str);
+ return;
}
feh_thumbnail *feh_thumbnail_new(feh_file * file, int x, int y, int w, int h)
@@ -518,24 +502,20 @@ void feh_thumbnail_mark_removed(feh_file * file, int deleted)
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;
@@ -545,193 +525,68 @@ void feh_thumbnail_mark_removed(feh_file * file, int deleted)
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);
+
+ 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);
D(("uri is %s, thumb_file is %s\n", uri, thumb_file));
free(uri);
@@ -742,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", td.cache_dir, 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;
}
@@ -765,14 +638,15 @@ char *feh_thumbnail_get_name_uri(char *name)
char *cwd, *uri = NULL;
/* FIXME: what happens 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)) {
+ 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);
@@ -798,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]);
}
@@ -808,15 +682,18 @@ 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);
+ *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;
@@ -826,6 +703,13 @@ int feh_thumbnail_generate(Imlib_Image * image, feh_file * file,
thumb_h = td.cache_dim / ratio;
else if (ratio != 1.0)
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,
@@ -834,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)
+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);
}
@@ -873,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", td.cache_dir, 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 a384d11..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,8 +44,6 @@ 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 */
@@ -57,11 +56,12 @@ typedef struct thumbmode_data {
int text_area_w, text_area_h; /* space needed for thumbnail description */
int max_column_w; /* FIXME: description */
- int vertical; /* FIXME: vertical in what way? */
+ 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 */
} thumbmode_data;
@@ -73,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);
+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 cba0716..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
@@ -57,7 +58,37 @@ double feh_get_time(void)
return((double) timev.tv_sec + (((double) timev.tv_usec) / 1000000));
}
-void feh_remove_timer(char *name)
+void feh_remove_timer_by_data(void *data)
+{
+ fehtimer ft, ptr, pptr;
+
+ D(("removing timer for %p\n", data));
+ pptr = NULL;
+ ptr = first_timer;
+ while (ptr) {
+ D(("Stepping through event list\n"));
+ ft = ptr;
+ if (ft->data == data) {
+ D(("Found it. Removing\n"));
+ if (pptr)
+ pptr->next = ft->next;
+ else
+ first_timer = ft->next;
+ if (ft->next)
+ ft->next->in += ft->in;
+ if (ft->name)
+ free(ft->name);
+ if (ft)
+ free(ft);
+ return;
+ }
+ pptr = ptr;
+ ptr = ptr->next;
+ }
+ return;
+}
+
+static void feh_remove_timer(char *name)
{
fehtimer ft, ptr, pptr;
@@ -87,6 +118,7 @@ void feh_remove_timer(char *name)
return;
}
+
void feh_add_timer(void (*func) (void *data), void *data, double in, char *name)
{
fehtimer ft, ptr, pptr;
@@ -94,7 +126,7 @@ void feh_add_timer(void (*func) (void *data), void *data, double in, char *name)
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;
@@ -102,7 +134,6 @@ void feh_add_timer(void (*func) (void *data), void *data, double in, char *name)
ft->just_added = 1;
ft->in = in;
D(("ft->in = %f\n", ft->in));
- tally = 0.0;
if (!first_timer) {
D(("No first timer\n"));
first_timer = ft;
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/utils.c b/src/utils.c
index 5259af8..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,24 +143,24 @@ 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;
@@ -175,9 +172,11 @@ 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);
@@ -189,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';
@@ -206,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/support.c b/src/wallpaper.c
index afb941b..e2fa67e 100644
--- a/src/support.c
+++ b/src/wallpaper.c
@@ -1,6 +1,7 @@
-/* support.c
+/* 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
@@ -23,11 +24,14 @@ 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 "support.h"
-#include <limits.h>
+#include "wallpaper.h"
+
Window ipc_win = None;
Window my_ipc_win = None;
Atom ipc_atom = None;
@@ -41,48 +45,330 @@ static unsigned char timeout = 0;
*/
static char e17_fake_ipc = 0;
-void feh_wm_set_bg_file(char *file, unsigned char bgmode)
+void feh_wm_set_bg_filelist(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) {
+ if (filelist_len == 0)
+ eprintf("No files specified for background setting");
+
+ switch (bgmode) {
case BG_MODE_TILE:
- feh_wm_set_bg(file, im, 0, 0, 0, 0, 1);
+ feh_wm_set_bg(NULL, NULL, 0, 0, 0, 0, 1);
break;
case BG_MODE_SCALE:
- feh_wm_set_bg(file, im, 0, 1, 0, 0, 1);
+ feh_wm_set_bg(NULL, NULL, 0, 1, 0, 0, 1);
break;
case BG_MODE_FILL:
- feh_wm_set_bg(file, im, 0, 0, 1, 0, 1);
+ feh_wm_set_bg(NULL, NULL, 0, 0, 1, 0, 1);
break;
case BG_MODE_MAX:
- feh_wm_set_bg(file, im, 0, 0, 2, 0, 1);
+ feh_wm_set_bg(NULL, NULL, 0, 0, 2, 0, 1);
break;
default:
- feh_wm_set_bg(file, im, 1, 0, 0, 0, 1);
+ 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);
- feh_file_free(fil);
+
+ 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 set)
+ int filled, int desktop, int use_filelist)
{
+ XGCValues gcvalues;
+ XGCValues gcval;
+ GC gc;
char bgname[20];
- int num = (int) rand();
+ 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) {
+ 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");
@@ -90,10 +376,16 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
D(("bg saved as %s\n", bgfil));
fil = bgfil;
}
- D(("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);
+ 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) {
@@ -123,171 +415,142 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
enl_ipc_send(sendbuf);
}
- if (set) {
- snprintf(sendbuf, sizeof(sendbuf), "use_bg %s %d", bgname, desktop);
- 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;
+ int format, i;
unsigned long length, after;
- unsigned char *data_root, *data_esetroot;
+ unsigned char *data_root = NULL, *data_esetroot = NULL;
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;
+ int w, h;
D(("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;
+ 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) {
- w = scr->width;
- h = scr->height;
+ 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);
+ }
-/* 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;
+ 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);
+ }
+ }
}
-#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
+#endif /* HAVE_LIBXINERAMA */
+ feh_wm_set_bg_scaled(pmap_d1, im, use_filelist,
+ 0, 0, scr->width, scr->height);
} else if (centered) {
- XGCValues gcval;
- GC gc;
- int x, y;
D(("centering\n"));
- w = scr->width;
- h = scr->height;
-/* disable xinerama check for setting background */
-#if 0
-/* #ifdef HAVE_LIBXINERAMA */
+ 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) {
- w = xinerama_screens[xinerama_screen].width;
- h = xinerama_screens[xinerama_screen].height;
+ 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);
- 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 == 1) {
- int scr_w = scr->width;
- int scr_h = scr->height;
- int img_w = gib_imlib_image_get_width(im);
- int img_h = gib_imlib_image_get_height(im);
- int render_x = 0;
- int render_y = 0;
-
- if ((img_w * scr_h) > (scr_w * img_h)) {
- h = scr_h;
- w = (scr_h * img_w) / img_h;
- render_x = (scr_w - w) / 2;
- } else {
- h = (scr_w * img_h) / img_w;
- w = scr_w;
- render_y = (scr_h - h) / 2;
+
+ 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);
}
- pmap_d1 = XCreatePixmap(disp, root, w, h, depth);
- gib_imlib_render_image_on_drawable_at_size(pmap_d1, im,
- render_x, render_y, w, h, 1, 0, 1);
- fehbg = estrjoin(" ", "feh --bg-fill", filbuf, NULL);
- } else if (filled == 2) {
- int scr_w = scr->width;
- int scr_h = scr->height;
- int img_w = gib_imlib_image_get_width(im);
- int img_h = gib_imlib_image_get_height(im);
- int render_x = 0;
- int render_y = 0;
- XGCValues gcval;
-
- if (img_w > img_h) {
- w = scr_w;
- h = (((scr_w * 100) / img_w) * img_h) / 100;
- render_y = (scr_h - h) / 2;
- if (h > scr_h) {
- w = (((scr_h * 100) / h) * w) / 100;
- h = scr_h;
- render_x = (scr_w - w) / 2;
- render_y = 0;
- }
- } else {
- h = scr_h;
- w = (((scr_h * 100) / img_h) * img_w) / 100;
- render_x = (scr_w - w) / 2;
- if (w > scr_w) {
- h = (((scr_w * 100) / w) * h) / 100;
- w = scr_w;
- render_x = 0;
- render_y = (scr_h - h) / 2;
+
+ 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_w, scr_h, depth);
- gcval.foreground = BlackPixel(disp, DefaultScreen(disp));
+ 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_w, scr_h);
- gib_imlib_render_image_on_drawable_at_size(pmap_d1, im,
- render_x, render_y, w, h, 1, 0, 1);
+ 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);
- fehbg = estrjoin(" ", "feh --bg-max", filbuf, NULL);
+
} 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, 0, 0);
- fehbg = estrjoin(" ", "feh --bg-tile", filbuf, NULL);
+ gib_imlib_render_image_on_drawable(pmap_d1, im, 0, 0, 1, 1, 0);
}
- 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);
- }
+ 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);
@@ -325,12 +588,19 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
}
}
}
+
+ 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)
- weprintf("creation of pixmap property failed.");
+ 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,
@@ -533,11 +803,11 @@ void enl_ipc_send(char *str)
return;
}
-static sighandler_t *enl_ipc_timeout(int sig)
+void enl_ipc_timeout(int sig)
{
- timeout = 1;
- return((sighandler_t *) sig);
- sig = 0;
+ if (sig == SIGALRM)
+ timeout = 1;
+ return;
}
char *enl_wait_for_reply(void)
@@ -564,7 +834,7 @@ char *enl_ipc_get(const char *msg_data)
{
static char *message = NULL;
- static unsigned short len = 0;
+ static size_t len = 0;
char buff[13], *ret_msg = NULL;
register unsigned char i;
unsigned char blen;
@@ -597,7 +867,8 @@ char *enl_ipc_get(const char *msg_data)
char *enl_send_and_wait(char *msg)
{
char *reply = IPC_TIMEOUT;
- sighandler_t old_alrm;
+ struct sigaction e17_sh, feh_sh;
+ sigset_t e17_ss;
/*
* Shortcut this func and return IPC_FAKE
@@ -616,7 +887,19 @@ char *enl_send_and_wait(char *msg)
sleep(1);
}
}
- old_alrm = (sighandler_t) signal(SIGALRM, (sighandler_t) enl_ipc_timeout);
+
+ 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);
@@ -628,6 +911,8 @@ char *enl_send_and_wait(char *msg)
ipc_win = None;
}
}
- signal(SIGALRM, old_alrm);
+ 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 26a43e2..3b90158 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,6 +28,12 @@ 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);
@@ -41,6 +48,7 @@ static winwidget winwidget_allocate(void)
winwidget ret = NULL;
ret = emalloc(sizeof(_winwidget));
+ memset(ret, 0, sizeof(_winwidget));
ret->win = 0;
ret->w = 0;
@@ -54,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;
@@ -73,10 +83,14 @@ static winwidget winwidget_allocate(void)
ret->click_offset_y = 0;
ret->has_rotated = 0;
+#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;
@@ -90,20 +104,20 @@ 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 && (type != WIN_TYPE_THUMBNAIL))
+ 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);
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);
@@ -114,12 +128,8 @@ winwidget winwidget_create_from_file(gib_list * list, char *name, char type)
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);
return(NULL);
}
@@ -128,10 +138,15 @@ winwidget winwidget_create_from_file(gib_list * list, 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);
D(("image is %dx%d pixels, format %s\n", ret->w, ret->h, gib_imlib_image_format(ret->im)));
- if (opt.full_screen)
+ 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);
}
return(ret);
@@ -139,14 +154,22 @@ winwidget winwidget_create_from_file(gib_list * list, char *name, char type)
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(("winwidget_create_window %dx%d\n", w, h));
@@ -200,7 +223,6 @@ void winwidget_create_window(winwidget ret, int w, int h)
}
if (opt.paused) {
- printf("name %s\n", ret->name);
tmpname = estrjoin(" ", ret->name, "[Paused]", NULL);
free(ret->name);
ret->name = tmpname;
@@ -224,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) {
@@ -236,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,
@@ -259,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);
@@ -293,18 +360,35 @@ void winwidget_create_window(winwidget ret, int w, int h)
XSetCommand(disp, ret->win, cmdargv, cmdargc);
winwidget_register(ret);
+
+ /* 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(("winwid->name = %s\n", ret->name));
name = ret->name ? ret->name : "feh";
XStoreName(disp, ret->win, name);
XSetIconName(disp, ret->win, name);
- /* XFlush(disp); */
+
+ 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;
}
@@ -338,8 +422,20 @@ void winwidget_setup_pixmaps(winwidget winwid)
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);
}
@@ -361,130 +457,76 @@ void winwidget_setup_pixmaps(winwidget winwid)
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;
+ 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));
- D(("winwidget_render_image resize %d alias %d im %dx%d\n",
- resize, alias, winwid->im_w, winwid->im_h));
+ /* winwidget_setup_pixmaps(winwid) resets the winwid->had_resize flag */
+ int had_resize = winwid->had_resize || resize;
winwidget_setup_pixmaps(winwid);
- if (!winwid->full_screen && opt.scale_down && ((winwid->w < winwid->im_w)
- || (winwid->h < winwid->im_h))) {
- D(("scaling down image %dx%d\n", winwid->w, winwid->h));
+ 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);
- if (resize)
- winwidget_resize(winwid, winwid->im_w * winwid->zoom, winwid->im_h * winwid->zoom);
- D(("after scaling down image %dx%d\n", winwid->w, winwid->h));
- }
+ winwid->zoom = opt.default_zoom ? (0.01 * opt.default_zoom) : 1.0;
- 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);
+ 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 (resize && (winwid->full_screen || opt.geom_flags)) {
- int smaller; /* Is the image smaller than screen? */
- int max_w = 0, max_h = 0;
-
- 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(("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;
@@ -507,10 +549,18 @@ void winwidget_render_image(winwidget winwid, int resize, int alias)
calc_h = lround(winwid->im_h * winwid->zoom);
dw = (winwid->w - winwid->im_x);
dh = (winwid->h - winwid->im_y);
- if (calc_w < dw)
+ if (calc_w < dw) {
dw = calc_w;
- if (calc_h < dh)
+ if (!winwid->full_screen) {
+ dx = 0;
+ }
+ }
+ if (calc_h < dh) {
dh = calc_h;
+ if (!winwid->full_screen) {
+ dy = 0;
+ }
+ }
if (dw > winwid->w)
dw = winwid->w;
if (dh > winwid->h)
@@ -522,28 +572,46 @@ void winwidget_render_image(winwidget winwid, int resize, int alias)
D(("sx: %d sy: %d sw: %d sh: %d dx: %d dy: %d dw: %d dh: %d zoom: %f\n",
sx, sy, sw, sh, dx, dy, dw, dh, winwid->zoom));
+ 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);
+ 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.info_cmd)
+ if (opt.draw_info && opt.info_cmd)
feh_draw_info(winwid);
- } else if ((opt.mode == MODE_ZOOM) && !alias)
+ 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));
+ }
+ }
+ } else if ((opt.mode == MODE_ZOOM) && !antialias)
feh_draw_zoom(winwid);
XSetWindowBackgroundPixmap(disp, winwid->win, winwid->bg_pmap);
@@ -566,7 +634,7 @@ void winwidget_render_image_cached(winwidget winwid)
feh_draw_filename(winwid);
if (opt.draw_actions)
feh_draw_actions(winwid);
- if (opt.info_cmd)
+ if (opt.draw_info && opt.info_cmd)
feh_draw_info(winwid);
XSetWindowBackgroundPixmap(disp, winwid->win, winwid->bg_pmap);
XClearWindow(disp, winwid->win);
@@ -578,6 +646,9 @@ double feh_calc_needed_zoom(double *zoom, int orig_w, int orig_h, int dest_w, in
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
@@ -592,30 +663,20 @@ Pixmap feh_create_checks(void)
Imlib_Image checks = NULL;
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);
@@ -625,13 +686,6 @@ Pixmap feh_create_checks(void)
return(checks_pmap);
}
-void winwidget_clear_background(winwidget w)
-{
- XSetWindowBackgroundPixmap(disp, w->win, feh_create_checks());
- /* XClearWindow(disp, w->win); */
- return;
-}
-
void feh_draw_checks(winwidget win)
{
static GC gc = None;
@@ -661,13 +715,15 @@ void winwidget_destroy_xwin(winwidget winwid)
void winwidget_destroy(winwidget winwid)
{
+#ifdef HAVE_INOTIFY
+ winwidget_inotify_remove(winwid);
+#endif
+ if (opt.reload > 0 && opt.multiwindow) {
+ feh_remove_timer_by_data(winwid);
+ }
winwidget_destroy_xwin(winwid);
if (winwid->name)
free(winwid->name);
- 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)
@@ -676,6 +732,76 @@ void winwidget_destroy(winwidget winwid)
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;
@@ -686,13 +812,13 @@ void winwidget_destroy_all(void)
return;
}
-void winwidget_rerender_all(int resize, int alias)
+void winwidget_rerender_all(int resize)
{
int i;
/* 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);
+ winwidget_render_image(windows[i], resize, 0);
return;
}
@@ -709,7 +835,16 @@ winwidget winwidget_get_first_window_of_type(unsigned int type)
int winwidget_loadimage(winwidget winwid, feh_file * file)
{
D(("filename %s\n", file->filename));
- return(feh_load_image(&(winwid->im), file));
+#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)
@@ -724,6 +859,17 @@ void winwidget_show(winwidget winwid)
/* wait for the window to map */
D(("Waiting for window to map\n"));
XMaskEvent(disp, StructureNotifyMask, &ev);
+ 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;
}
@@ -733,8 +879,6 @@ void winwidget_show(winwidget winwid)
void winwidget_move(winwidget winwid, int x, int y)
{
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);
@@ -745,55 +889,87 @@ void winwidget_move(winwidget winwid, int x, int y)
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);
- 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(("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;
- }
+ winwidget_get_geometry(winwid, NULL);
- }
- if (getenv("XINERAMA_SCREEN"))
- xinerama_screen = atoi(getenv("XINERAMA_SCREEN"));
+ 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(("No resize actually needed\n"));
@@ -882,8 +1058,9 @@ void winwidget_rename(winwidget winwid, char *newname)
void winwidget_free_image(winwidget w)
{
- 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;
@@ -907,9 +1084,12 @@ void feh_debug_print_winwid(winwidget w)
void winwidget_reset_image(winwidget winwid)
{
- 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;
return;
@@ -934,11 +1114,11 @@ void winwidget_center_image(winwidget winwid)
winwid->im_y = (scr_height - lround(winwid->im_h * winwid->zoom)) >> 1;
} else {
if (opt.geom_flags & WidthValue)
- winwid->im_x = (opt.geom_w - lround(winwid->im_w * winwid->zoom)) >> 1;
+ winwid->im_x = ((int)opt.geom_w - lround(winwid->im_w * winwid->zoom)) >> 1;
else
winwid->im_x = 0;
if (opt.geom_flags & HeightValue)
- winwid->im_y = (opt.geom_h - lround(winwid->im_h * winwid->zoom)) >> 1;
+ winwid->im_y = ((int)opt.geom_h - lround(winwid->im_h * winwid->zoom)) >> 1;
else
winwid->im_y = 0;
}
@@ -980,9 +1160,9 @@ void winwidget_sanitise_offsets(winwidget winwid)
void winwidget_size_to_image(winwidget winwid)
{
- 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);
+ winwidget_render_image(winwid, 0, 0);
return;
}
@@ -1022,8 +1202,11 @@ void winwidget_get_geometry(winwidget winwid, int *rect)
{
unsigned int bw, bp;
Window child;
+
+ 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);
@@ -1045,11 +1228,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 012d78f..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,6 +92,7 @@ struct __winwidget {
char *name;
gib_list *file;
unsigned char visible;
+ char *errstr;
/* panning, zooming, etc. */
unsigned char mode;
@@ -111,10 +113,20 @@ struct __winwidget {
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);
@@ -122,14 +134,14 @@ void winwidget_hide(winwidget winwid);
void winwidget_destroy_all(void);
void winwidget_free_image(winwidget w);
void winwidget_center_image(winwidget w);
-void winwidget_render_image(winwidget winwid, int resize, int alias);
+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_update_caption(winwidget winwid);
-void winwidget_rerender_all(int resize, int alias);
+void winwidget_rerender_all(int resize);
void winwidget_destroy_xwin(winwidget winwid);
void winwidget_set_pointer(winwidget winwid, int visible);
@@ -138,12 +150,11 @@ 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);