summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile11
-rw-r--r--src/collage.c216
-rw-r--r--src/debug.h2
-rw-r--r--src/events.c86
-rw-r--r--src/exif.c577
-rw-r--r--src/exif.h17
-rw-r--r--src/exif_canon.c30
-rw-r--r--src/exif_canon.h3
-rw-r--r--src/exif_cfg.h85
-rw-r--r--src/exif_nikon.c796
-rw-r--r--src/exif_nikon.h3
-rw-r--r--src/feh.h40
-rw-r--r--src/feh_png.c9
-rw-r--r--src/feh_png.h4
-rw-r--r--src/filelist.c196
-rw-r--r--src/filelist.h14
-rw-r--r--src/getopt.c949
-rw-r--r--src/getopt.h130
-rw-r--r--src/getopt1.c173
-rw-r--r--src/gib_hash.c5
-rw-r--r--src/gib_hash.h2
-rw-r--r--src/gib_imlib.c17
-rw-r--r--src/gib_imlib.h4
-rw-r--r--src/gib_list.c5
-rw-r--r--src/help.raw51
-rw-r--r--src/imlib.c672
-rw-r--r--src/index.c31
-rw-r--r--src/index.h2
-rw-r--r--src/keyevents.c61
-rw-r--r--src/list.c6
-rw-r--r--src/main.c109
-rw-r--r--src/menu.c41
-rw-r--r--src/menu.h2
-rw-r--r--src/multiwindow.c20
-rw-r--r--src/options.c577
-rw-r--r--src/options.h144
-rw-r--r--src/signals.c27
-rw-r--r--src/signals.h7
-rw-r--r--src/slideshow.c401
-rw-r--r--src/structs.h2
-rw-r--r--src/strverscmp.c57
-rw-r--r--src/thumbnail.c104
-rw-r--r--src/thumbnail.h6
-rw-r--r--src/timers.c35
-rw-r--r--src/timers.h2
-rw-r--r--src/utils.c3
-rw-r--r--src/wallpaper.c231
-rw-r--r--src/wallpaper.h5
-rw-r--r--src/winwidget.c445
-rw-r--r--src/winwidget.h16
50 files changed, 3169 insertions, 3262 deletions
diff --git a/src/Makefile b/src/Makefile
index 2f6185a..2968671 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,12 +1,9 @@
include ../config.mk
TARGETS = \
- collage.c \
events.c \
feh_png.c \
filelist.c \
- getopt.c \
- getopt1.c \
gib_hash.c \
gib_imlib.c \
gib_list.c \
@@ -35,6 +32,10 @@ ifeq (${exif},1)
exif_nikon.c
endif
+ifneq (${verscmp},1)
+ TARGETS += strverscmp.c
+endif
+
OBJECTS = ${TARGETS:.c=.o}
I_SRCS = ${shell echo *.raw}
@@ -51,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} ${CPPFLAGS} -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 b975136..0000000
--- a/src/collage.c
+++ /dev/null
@@ -1,216 +0,0 @@
-/* collage.c
-
-Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to
-deal in the Software without restriction, including without limitation the
-rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies of the Software and its documentation and acknowledgment shall be
-given in the documentation and software packages that this Software was
-used.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "feh.h"
-#include "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";
-
- weprintf("the --collage option (aka collage mode) is deprecated\n"
- "and will be removed soon\n");
-
- /* 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)
- fputs(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",
- stderr);
- opt.limit_w = bg_w;
- opt.limit_h = bg_h;
- } else {
- if (opt.verbose)
- fputs(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",
- stderr);
- 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, 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)
- fputs("\n", stderr);
-
- 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, 1023);
- output_buf[1023] = '\0';
- }
- 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(stderr, PACKAGE ": File saved as %s\n", output_buf);
- fprintf(stderr,
- " - 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 eb929e3..0ff1447 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -1,7 +1,7 @@
/* debug.h
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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/events.c b/src/events.c
index 947e69f..bafc517 100644
--- a/src/events.c
+++ b/src/events.c
@@ -1,7 +1,7 @@
/* events.c
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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
@@ -85,6 +85,15 @@ static void feh_set_parse_bb_partial(fehkey *button, char *binding)
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;
+ }
}
/*
@@ -255,7 +264,7 @@ static void feh_event_handle_ButtonPress(XEvent * ev)
- winwid->im_y) / winwid->old_zoom;
/* copied from zoom_in, keyevents.c */
- winwid->zoom = winwid->zoom * 1.25;
+ winwid->zoom = winwid->zoom * opt.zoom_rate;
if (winwid->zoom > ZOOM_MAX)
winwid->zoom = ZOOM_MAX;
@@ -283,7 +292,7 @@ static void feh_event_handle_ButtonPress(XEvent * ev)
- winwid->im_y) / winwid->old_zoom;
/* copied from zoom_out, keyevents.c */
- winwid->zoom = winwid->zoom * 0.80;
+ winwid->zoom = winwid->zoom / opt.zoom_rate;
if (winwid->zoom < ZOOM_MIN)
winwid->zoom = ZOOM_MIN;
@@ -357,9 +366,12 @@ static void feh_event_handle_ButtonRelease(XEvent * ev)
} else if (opt.mode == MODE_NEXT) {
opt.mode = MODE_NORMAL;
winwid->mode = MODE_NORMAL;
- if (winwid->type == WIN_TYPE_SLIDESHOW)
- slideshow_change_image(winwid, SLIDE_NEXT, 1);
- else if (winwid->type == WIN_TYPE_THUMBNAIL) {
+ 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;
int x, y;
@@ -509,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);
}
@@ -521,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);
}
@@ -628,12 +644,14 @@ 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, 0);
- 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));
@@ -649,29 +667,35 @@ 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, 1);
- 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) && (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);
- feh_thumbnail_select(winwid, thumbnail);
+ if (winwid != 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);
+ feh_thumbnail_select(winwid, thumbnail);
+ } else {
+ feh_event_handle_generic(winwid, ev->xmotion.state | Mod3Mask, NoSymbol, 0);
+ }
}
}
return;
diff --git a/src/exif.c b/src/exif.c
index 9ad9dae..6b0719d 100644
--- a/src/exif.c
+++ b/src/exif.c
@@ -1,6 +1,7 @@
/* 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
@@ -42,199 +43,302 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/* 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';
+ 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)
+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);
- }
- }
- }
+ 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)
+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);
- }
- }
- }
+ 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)
+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);
- }
- }
- }
- }
- }
+ 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;
- }
+ 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 *exif_get_data(char *path)
{
- ExifData *ed = NULL;
+ 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));
- }
+ /* 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);
+ return (ed);
}
@@ -243,76 +347,93 @@ ExifData * exif_get_data(char *path)
/* 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;
- }
- else
- {
- /* show normal exif tags. list must be defined in exif_cfg.h */
- while ( (i < USHRT_MAX) && (Exif_tag_list[i].ifd != EXIF_IFD_COUNT) )
- {
- exif_get_tag(ed, Exif_tag_list[i].ifd, Exif_tag_list[i].tag, buffer + strlen(buffer), maxsize - strlen(buffer));
- i++;
- }
-
- /* 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));
-
- }
-
+ 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
index 79187f4..41769c0 100644
--- a/src/exif.h
+++ b/src/exif.h
@@ -32,11 +32,16 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#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);
+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
index 8801899..ee72164 100644
--- a/src/exif_canon.c
+++ b/src/exif_canon.c
@@ -36,26 +36,26 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/* 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)
+void exc_get_mnote_canon_tags(ExifData * ed, unsigned int tag,
+ char *buffer, unsigned int maxsize)
{
- /* char buf[EXIF_STD_BUF_LEN];
+ /* 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); */
+ 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;
- }
+ switch (tag) {
+ default:
+ {
+ /* normal makernote tags without special treatment */
+ exif_get_mnote_tag(ed, tag, buffer, maxsize);
+ }
+ break;
+ }
- return;
+ return;
}
#endif
diff --git a/src/exif_canon.h b/src/exif_canon.h
index d8682c3..58ecc0e 100644
--- a/src/exif_canon.h
+++ b/src/exif_canon.h
@@ -28,6 +28,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <libexif/exif-data.h>
-extern void exc_get_mnote_canon_tags(ExifData *ed, unsigned int tag, char * buffer, unsigned int maxsize);
+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
index a961147..0a13fa3 100644
--- a/src/exif_cfg.h
+++ b/src/exif_cfg.h
@@ -28,78 +28,43 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <libexif/exif-data.h>
-typedef struct
-{
- ExifIfd ifd; /* section */
- ExifTag tag; /* tag */
-} t_EXIF_INFO;
-
-
-/* show these standard tags. section must be given first, than the tag itself */
-/* definition: http://libexif.sourceforge.net/api/exif-tag_8h.html */
-const t_EXIF_INFO Exif_tag_list [] =
-{
- {EXIF_IFD_0, EXIF_TAG_MAKE},
- {EXIF_IFD_0, EXIF_TAG_MODEL},
- {EXIF_IFD_0, EXIF_TAG_IMAGE_DESCRIPTION},
- {EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_ORIGINAL},
- {EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_TIME},
- {EXIF_IFD_EXIF, EXIF_TAG_SHUTTER_SPEED_VALUE},
- {EXIF_IFD_EXIF, EXIF_TAG_FNUMBER},
- {EXIF_IFD_EXIF, EXIF_TAG_APERTURE_VALUE},
- {EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_BIAS_VALUE},
- {EXIF_IFD_EXIF, EXIF_TAG_ISO_SPEED_RATINGS},
- {EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH},
- {EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH_IN_35MM_FILM},
- {EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_MODE},
- {EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_PROGRAM},
- {EXIF_IFD_EXIF, EXIF_TAG_SCENE_CAPTURE_TYPE},
- {EXIF_IFD_EXIF, EXIF_TAG_FLASH},
-
- {EXIF_IFD_COUNT, 0} /* end marker */
-};
+/* 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[] = {
-/* Nikon */
+ 6,
+ 8, /* Flash Setting */
+ 9, /* Flash Mode */
+ 135, /* Flash used */
+ 18, /* Flash Exposure Comp */
+ 168, /* Flash info: control mode */
-#define EXIF_NIKON_MAKERNOTE_END 0 /* end marker: if 0 used as a tag we must find something else */
+ 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 */
-/* 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 */
+ 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 */
+#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 */
+const unsigned int Exif_makernote_canon_tag_list[] = {
+ 8, /* Image Number */
+ 9, /* Owner Name */
+
+ EXIF_CANON_MAKERNOTE_END /* end marker */
};
diff --git a/src/exif_nikon.c b/src/exif_nikon.c
index c058d8c..a81f290 100644
--- a/src/exif_nikon.c
+++ b/src/exif_nikon.c
@@ -37,12 +37,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/* 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 */
- };
+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
@@ -50,280 +51,302 @@ char *EXN_NikonFlashControlModeValues[EXN_FLASH_CONTROL_MODES_MAX] = {"Off",
/* 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"};
+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)"};
+ "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)"};
+ "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)"};
+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"};
-
+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"};
+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"};
+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"};
+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);
+ 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)
+ 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;
-
- }
-
+
+ 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)
+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);
- }
- }
+
+ 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)
+static void exn_get_mnote_nikon_18(ExifData * ed, char *buffer,
+ unsigned int maxsize)
{
- char buf[EXIF_STD_BUF_LEN];
- float data = 0;
+ char buf[EXIF_STD_BUF_LEN];
+ float data = 0;
- buf[0] = '\0';
- exif_get_mnote_tag(ed, 18, buf, sizeof(buf));
+ buf[0] = '\0';
+ exif_get_mnote_tag(ed, 18, buf, sizeof(buf));
- sscanf(buf, "Flash Exposure Compensation: %f", &data); /* libexif buggy here. fix conversion */
+ 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 );
+ 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)
+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);
-
+ 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)
+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);
- }
+ 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);
+ }
}
@@ -331,180 +354,189 @@ static void exn_get_mnote_nikon_35(ExifData *ed, char * buffer, unsigned int max
/* get nikon Flash info: control mode (168) info */
-static void exn_get_mnote_nikon_168(ExifData *ed, char * buffer, unsigned int maxsize)
+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 */
-
- }
+ 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)
+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
- );
- }
-
- }
+ 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)
+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;
+ 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
index 16e8fb9..49d14b6 100644
--- a/src/exif_nikon.h
+++ b/src/exif_nikon.h
@@ -28,6 +28,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <libexif/exif-data.h>
-extern void exn_get_mnote_nikon_tags(ExifData *ed, unsigned int tag, char * buffer, unsigned int maxsize);
+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 a4a0a7b..54e78ea 100644
--- a/src/feh.h
+++ b/src/feh.h
@@ -1,7 +1,7 @@
/* feh.h
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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,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>
@@ -55,6 +63,7 @@ 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 "gib_hash.h"
@@ -66,7 +75,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "menu.h"
#include "utils.h"
-#include "getopt.h"
#include "debug.h"
@@ -107,7 +115,13 @@ enum slide_change { SLIDE_NEXT, SLIDE_PREV, SLIDE_RAND, SLIDE_FIRST, SLIDE_LAST,
SLIDE_JUMP_PREV_DIR
};
-enum image_bg { IMAGE_BG_CHECKS = 1, IMAGE_BG_BLACK, IMAGE_BG_WHITE };
+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
@@ -126,29 +140,31 @@ 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, int render);
void slideshow_pause_toggle(winwidget w);
-char *slideshow_create_name(feh_file * file, winwidget winwid);
-char *thumbnail_create_name(feh_file * file, winwidget winwid);
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_event_handle_stdin();
+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(int size);
+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);
@@ -168,13 +184,19 @@ void feh_display_status(char stat);
void real_loadables_mode(int loadable);
void feh_reload_image(winwidget w, int resize, int force_new);
void feh_filelist_image_remove(winwidget winwid, char do_delete);
-void feh_imlib_print_load_error(char *file, winwidget w, Imlib_Load_Error err);
+void feh_print_load_error(char *file, winwidget w, Imlib_Load_Error err, enum feh_load_error feh_err);
void slideshow_save_image(winwidget win);
void feh_edit_inplace(winwidget w, int orientation);
void feh_edit_inplace_lossless(winwidget w, int orientation);
gib_list *feh_wrap_string(char *text, int wrap_width, Imlib_Font fn, gib_style * style);
char *build_caption_filename(feh_file * file, short create_dir);
gib_list *feh_list_jump(gib_list * root, gib_list * l, int direction, int num);
+#ifdef HAVE_INOTIFY
+void feh_event_handle_inotify(void);
+#endif
+#ifndef HAVE_STRVERSCMP
+int strverscmp(const char *l0, const char *r0);
+#endif
/* Imlib stuff */
extern Display *disp;
diff --git a/src/feh_png.c b/src/feh_png.c
index d27df01..8f5b94d 100644
--- a/src/feh_png.c
+++ b/src/feh_png.c
@@ -23,13 +23,13 @@ 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 4
@@ -197,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 ac3375f..035d36a 100644
--- a/src/feh_png.h
+++ b/src/feh_png.h
@@ -26,11 +26,11 @@ 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_fd(Imlib_Image image, int fd, ...);
diff --git a/src/filelist.c b/src/filelist.c
index b569b8a..3d9bcef 100644
--- a/src/filelist.c
+++ b/src/filelist.c
@@ -1,7 +1,7 @@
/* filelist.c
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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,19 +24,19 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-#ifdef HAVE_LIBEXIF
-#include <libexif/exif-data.h>
-#endif
-
#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;
@@ -53,6 +53,8 @@ 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;
@@ -75,7 +77,7 @@ void feh_file_free(feh_file * file)
#ifdef HAVE_LIBEXIF
if (file->ed)
exif_data_unref(file->ed);
-#endif
+#endif
free(file);
return;
}
@@ -89,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;
@@ -155,7 +156,7 @@ static void feh_print_stat_error(char *path)
}
}
-static void add_stdin_to_filelist()
+static void add_stdin_to_filelist(void)
{
char buf[1024];
size_t readsize;
@@ -180,6 +181,7 @@ static void add_stdin_to_filelist()
while ((readsize = fread(buf, sizeof(char), sizeof(buf), stdin)) > 0) {
if (fwrite(buf, sizeof(char), readsize, outfile) < readsize) {
free(sfn);
+ fclose(outfile);
return;
}
}
@@ -197,7 +199,7 @@ 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);
@@ -304,7 +306,7 @@ 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;
@@ -313,20 +315,31 @@ gib_list *feh_file_info_preload(gib_list * list)
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 (((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('.');
+ 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)
feh_display_status(0);
@@ -343,23 +356,36 @@ 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)) {
- feh_print_stat_error(file->filename);
- return(1);
- }
-
if (im)
im1 = im;
else if (!feh_load_image(&im1, file) || !im1)
@@ -376,8 +402,6 @@ 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)
gib_imlib_free_image_and_decache(im1);
return(0);
@@ -393,18 +417,26 @@ void feh_file_dirname(char *dst, feh_file * f, int maxlen)
return;
}
- strncpy(dst, f->filename, n);
+ 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)
@@ -413,7 +445,7 @@ int feh_cmp_dirname(void *file1, void *file2)
int cmp;
feh_file_dirname(dir1, FEH_FILE(file1), PATH_MAX);
feh_file_dirname(dir2, FEH_FILE(file2), PATH_MAX);
- if ((cmp = strcmp(dir1, dir2)) != 0)
+ if ((cmp = strcmp_or_strverscmp(dir1, dir2)) != 0)
return(cmp);
return(feh_cmp_name(file1, file2));
}
@@ -421,20 +453,8 @@ int feh_cmp_dirname(void *file1, void *file2)
/* Return -1 if file1 is _newer_ than file2 */
int feh_cmp_mtime(void *file1, void *file2)
{
- struct stat s1, s2;
-
- if (stat(FEH_FILE(file1)->filename, &s1)) {
- feh_print_stat_error(FEH_FILE(file1)->filename);
- return(-1);
- }
-
- if (stat(FEH_FILE(file2)->filename, &s2)) {
- feh_print_stat_error(FEH_FILE(file2)->filename);
- return(-1);
- }
-
/* gib_list_sort is not stable, so explicitly return 0 as -1 */
- return(s1.st_mtime >= s2.st_mtime ? -1 : 1);
+ return(FEH_FILE(file1)->mtime >= FEH_FILE(file2)->mtime ? -1 : 1);
}
int feh_cmp_width(void *file1, void *file2)
@@ -454,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)
@@ -464,11 +484,25 @@ int feh_cmp_format(void *file1, void *file2)
void feh_prepare_filelist(void)
{
- if (opt.list || opt.customlist || (opt.sort > SORT_MTIME)
- || opt.preload || opt.min_width || opt.min_height
- || (opt.max_width != UINT_MAX) || (opt.max_height != UINT_MAX)) {
+ /*
+ * 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();
}
@@ -554,7 +588,7 @@ gib_list *feh_read_filelist(char *filename)
Imlib_Load_Error err = IMLIB_LOAD_ERROR_NONE;
Imlib_Image tmp_im;
struct stat st;
- signed short tmp_magick_timeout;
+ signed short tmp_conversion_timeout;
if (!filename)
return(NULL);
@@ -562,8 +596,8 @@ gib_list *feh_read_filelist(char *filename)
/*
* feh_load_image will fail horribly if filename is not seekable
*/
- tmp_magick_timeout = opt.magick_timeout;
- opt.magick_timeout = -1;
+ 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) {
@@ -574,7 +608,7 @@ gib_list *feh_read_filelist(char *filename)
return NULL;
}
}
- opt.magick_timeout = tmp_magick_timeout;
+ opt.conversion_timeout = tmp_conversion_timeout;
errno = 0;
@@ -620,12 +654,13 @@ char *feh_absolute_path(char *path)
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... */
if (getcwd(cwd, sizeof(cwd)) == NULL)
eprintf("Cannot determine working directory:");
- snprintf(temp, sizeof(temp), "%s/%s", cwd, path);
+ 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 {
@@ -635,11 +670,20 @@ 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);
+ }
+
+ 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);
@@ -648,3 +692,27 @@ void feh_save_filelist()
free(tmpname);
return;
}
+
+#ifdef HAVE_LIBCURL
+
+char *feh_http_unescape(char *url)
+{
+ CURL *curl = curl_easy_init();
+ if (!curl) {
+ return NULL;
+ }
+ char *tmp_url = curl_easy_unescape(curl, url, 0, NULL);
+ char *new_url = estrdup(tmp_url);
+ curl_free(tmp_url);
+ curl_easy_cleanup(curl);
+ return new_url;
+}
+
+#else
+
+char *feh_http_unescape(char *url)
+{
+ return NULL;
+}
+
+#endif
diff --git a/src/filelist.h b/src/filelist.h
index e24a6a6..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
@@ -36,6 +37,8 @@ struct __feh_file {
char *name;
/* info stuff */
+ time_t mtime;
+ int size;
feh_file_info *info; /* only set when needed */
#ifdef HAVE_LIBEXIF
ExifData *ed;
@@ -45,7 +48,6 @@ struct __feh_file {
struct __feh_file_info {
int width;
int height;
- int size;
int pixels;
unsigned char has_alpha;
char *format;
@@ -71,11 +73,11 @@ enum sort_type {
SORT_NAME,
SORT_FILENAME,
SORT_DIRNAME,
+ SORT_SIZE, // everything after SORT_SIZE requires stat(2) information on the filelist
SORT_MTIME,
- SORT_WIDTH,
+ SORT_WIDTH, // everything after SORT_WIDTH requires preloading the images in the filelist
SORT_HEIGHT,
SORT_PIXELS,
- SORT_SIZE,
SORT_FORMAT
};
@@ -88,7 +90,8 @@ 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);
@@ -96,7 +99,8 @@ 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);
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 0a985b0..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., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, 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
index a378b9c..9497d04 100644
--- a/src/gib_hash.c
+++ b/src/gib_hash.c
@@ -22,6 +22,7 @@ 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"
@@ -52,7 +53,7 @@ void gib_hash_node_free_and_data(gib_hash_node *node)
return;
}
-gib_hash *gib_hash_new()
+gib_hash *gib_hash_new(void)
{
gib_hash *hash = emalloc(sizeof(gib_hash));
hash->base = gib_hash_node_new("__gib_hash_new",NULL);
@@ -88,7 +89,7 @@ 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 simliar keys like key1 and key11 clobber eachother */
+ /* strncasecmp causes similar keys like key1 and key11 clobber each other */
return !strcasecmp(node->key, key);
}
diff --git a/src/gib_hash.h b/src/gib_hash.h
index 58506c8..125e280 100644
--- a/src/gib_hash.h
+++ b/src/gib_hash.h
@@ -55,7 +55,7 @@ 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();
+gib_hash *gib_hash_new(void);
void gib_hash_free(gib_hash *hash);
void gib_hash_free_and_data(gib_hash *hash);
diff --git a/src/gib_imlib.c b/src/gib_imlib.c
index 8f401aa..39d0081 100644
--- a/src/gib_imlib.c
+++ b/src/gib_imlib.c
@@ -27,6 +27,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "utils.h"
#include "debug.h"
+/*
int
gib_imlib_load_image(Imlib_Image * im, char *filename)
{
@@ -38,7 +39,6 @@ gib_imlib_load_image(Imlib_Image * im, char *filename)
*im = imlib_load_image_with_error_return(filename, &err);
if ((err) || (!im))
{
- /* Check error code */
switch (err)
{
case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST:
@@ -91,6 +91,7 @@ gib_imlib_load_image(Imlib_Image * im, char *filename)
}
return (1);
}
+*/
int
gib_imlib_image_get_width(Imlib_Image im)
@@ -322,7 +323,7 @@ gib_imlib_text_draw(Imlib_Image im, Imlib_Font fn, gib_style * s, int x,
gib_style_bit *bb;
gib_list *l;
- /* here we shift the draw to accomodate bits with negative offsets,
+ /* here we shift the draw to accommodate bits with negative offsets,
* which would be drawn at negative coords otherwise */
l = s->bits;
while (l)
@@ -717,3 +718,15 @@ 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
index 6a16a0c..6a35e7e 100644
--- a/src/gib_imlib.h
+++ b/src/gib_imlib.h
@@ -47,7 +47,9 @@ 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);
@@ -181,6 +183,8 @@ 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
}
diff --git a/src/gib_list.c b/src/gib_list.c
index 281f528..e7710bc 100644
--- a/src/gib_list.c
+++ b/src/gib_list.c
@@ -360,10 +360,9 @@ gib_list_randomize(gib_list * list)
{
farray[i] = f;
}
- srand(getpid() * time(NULL) % ((unsigned int) -1));
for (i = 0; i < len - 1; i++)
{
- r = i + rand() / (RAND_MAX / (len - i) + 1 );
+ r = i + random() / (RAND_MAX / (len - i) + 1 );
t = farray[r];
farray[r] = farray[i];
farray[i] = t;
@@ -521,7 +520,7 @@ gib_list_find(gib_list *root, unsigned char (*find_func)(gib_list *node, void *d
for (i=root; i; i=i->next)
if (find_func(i,data))
return i;
-
+
return NULL;
}
diff --git a/src/help.raw b/src/help.raw
index 067e35f..0e99c68 100644
--- a/src/help.raw
+++ b/src/help.raw
@@ -20,7 +20,7 @@ OPTIONS
-g, --geometry WxH[+X+Y] Limit the window size to DIMENSION[+OFFSET]
-f, --filelist FILE Load/save images from/to the FILE filelist
-|, --start-at FILENAME Start at FILENAME in the filelist
- -p, --preload Remove unlaodable files from the internal filelist
+ -p, --preload Remove unloadable files from the internal filelist
before attempting to display anything
-., --scale-down Automatically scale down images to fit screen size
-F, --fullscreen Make the window full screen
@@ -29,16 +29,20 @@ OPTIONS
mode or when window geometry is fixed. If combined
with --auto-zoom, zooming will be limited to the
the size. Also support \"max\" and \"fill\"
+ --zoom-step PERCENT Zoom images in and out by PERCENT (default: 25)
+ when using the zoom keys / buttons
--keep-zoom-vp Keep viewport zoom and settings while changing images
-w, --multiwindow Open all files at once, one window per image
-x, --borderless Create borderless windows
-d, --draw-filename Show the filename in the image window
--draw-tinted Show overlay texts on semi-transparent background
--draw-exif Show some Exif information (if compiled with exif=1)
+ --edit Make flip/rotation keys flip/rotate the underlying file
--auto-rotate Rotate images according to Exif info (if compiled with exif=1)
-^, --title TITLE Set window title (see FORMAT SPECIFIERS)
-D, --slideshow-delay NUM Set delay between automatically changing slides
- --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
-k, --keep-http Keep local copies when viewing HTTP/FTP files
--insecure Disable peer/host verification when using HTTPS.
@@ -52,11 +56,13 @@ OPTIONS
name, filename, mtime, width, height, pixels, size,
or format
-n, --reverse Reverse sort order
+ --version-sort Natural sort of (version) numbers within text
-A, --action [;]ACTION Specify action to perform when pressing <return>.
Executed by /bin/sh, may contain FORMAT SPECIFIERS
reloads image with \";\", switches to next otherwise
--action[1-9] Extra actions triggered by pressing keys <1>to <9>
-G, --draw-actions Show the defined actions in the image window
+ --tap-zones Enable tap zones for previous/next file in slide show mode
--force-aliasing Disable antialiasing
-m, --montage Enable montage mode
-i, --index Create an index print of all images
@@ -84,16 +90,20 @@ OPTIONS
can be used multiple times to add multiple paths.
-M, --menu-font FONT Use FONT for the font in menus.
-B, --image-bg STYLE Set background for transparent images and the like.
- Accepted values: 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.
--no-xinerama Disable Xinerama support
--no-screen-clip Do not limit window size to screen size
-Y, --hide-pointer Hide the pointer
- --magick-timeout INT Load unknown files with ImageMagick, timeout after
- INT seconds (0: no timeout)
+ --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
@@ -118,15 +128,17 @@ 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
%N image name (shell-escaped)
- %o x,y offset of top-left image corner to window in pixels
+ %o offset of top-left image corner to window (\"x,y\") in pixels
%p image pixel size
%P image pixel size in kilo-/megapixels
%r image rotation. half right turn == 3.1415 (pi)
@@ -137,34 +149,39 @@ FORMAT SPECIFIERS
%w image width
%v " PACKAGE " version
%V process ID
- %z current image zoom
+ %z current image zoom, rounded to two decimal places
+ %Z current image zoom, high precision
%% %
\\n newline
-KEYS
+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 Save current filelist to unique filename
+ f Toggle fullscreen
+ g Toggle fixed geometry mode
h pause/continue slideshow
i Toggle --info display
k Toggle zoom/viewport freeze when switching images
- m Show menu
+ 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
- v Toggle fullscreen
w Resize window to current image dimensions
x Close current window
z Jump to a random position in the current filelist
- <, > In place editing, rotate 90 degrees right/left
- _ In place editing, vertical flip
- | In place editing, horizontal flip
+ 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
@@ -181,14 +198,14 @@ KEYS
<KEYPAD UP> Move the image up
<KEYPAD DOWN> Move the image down
<KEYPAD BEGIN> Antialias the image
- <KEYPAD +> Zoom in
- <KEYPAD -> Zoom out
+ <KEYPAD +>, <UP> Zoom in
+ <KEYPAD ->, <DOWN> Zoom out
<KEYPAD *> Zoom to 100%
<KEYPAD /> Zoom to fit the window
This program is free software, see the file COPYING for licensing info.
Copyright Tom Gilbert (and various contributors) 1999-2003.
-Copyright Daniel Friesel (and various contributors) 2010-2016.
+Copyright Birte Kristina Friesel (and various contributors) 2010-2020.
Homepage: http://feh.finalrewind.org
Report bugs to <derf+feh@finalrewind.org> or #feh on irc.oftc.net.
diff --git a/src/imlib.c b/src/imlib.c
index 5b96e8a..d2352fd 100644
--- a/src/imlib.c
+++ b/src/imlib.c
@@ -1,7 +1,7 @@
/* imlib.c
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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,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"
@@ -43,6 +44,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "exif.h"
#endif
+#ifdef HAVE_LIBMAGIC
+#include <magic.h>
+
+magic_t magic = NULL;
+#endif
+
Display *disp = NULL;
Visual *vis = NULL;
Screen *scr = NULL;
@@ -59,9 +66,13 @@ 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
@@ -131,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;
@@ -148,11 +170,31 @@ int feh_load_image_char(Imlib_Image * im, char *filename)
return(i);
}
-void feh_imlib_print_load_error(char *file, winwidget w, Imlib_Load_Error err)
+void feh_print_load_error(char *file, winwidget w, Imlib_Load_Error err, enum feh_load_error feh_err)
{
if (err == IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS)
eprintf("%s - Out of file descriptors while loading", file);
else if (!opt.quiet || w) {
+ switch (feh_err) {
+ case LOAD_ERROR_IMLIB:
+ // handled in the next switch/case statement
+ break;
+ case LOAD_ERROR_IMAGEMAGICK:
+ im_weprintf(w, "%s - No ImageMagick loader for that file format", file);
+ break;
+ case LOAD_ERROR_CURL:
+ im_weprintf(w, "%s - libcurl was unable to retrieve the file", file);
+ break;
+ case LOAD_ERROR_DCRAW:
+ im_weprintf(w, "%s - Unable to open preview via dcraw", file);
+ break;
+ case LOAD_ERROR_MAGICBYTES:
+ im_weprintf(w, "%s - Does not look like an image (magic bytes missing)", file);
+ break;
+ }
+ if (feh_err != LOAD_ERROR_IMLIB) {
+ return;
+ }
switch (err) {
case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST:
im_weprintf(w, "%s - File does not exist", file);
@@ -191,6 +233,14 @@ void feh_imlib_print_load_error(char *file, winwidget w, Imlib_Load_Error err)
case IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE:
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:
im_weprintf(w, "While loading %s - Unknown error (%d)",
file, err);
@@ -199,11 +249,88 @@ void feh_imlib_print_load_error(char *file, winwidget w, Imlib_Load_Error err)
}
}
+#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;
+ }
+
+ 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)
+{
+ const char * mime_type = NULL;
+
+ if (!magic) {
+ return 1;
+ }
+
+ magic_setflags(magic, MAGIC_MIME_TYPE | MAGIC_SYMLINK | magic_flags);
+ mime_type = magic_file(magic, file->filename);
+
+ if (!mime_type) {
+ return 0;
+ }
+
+ D(("file %s has mime type: %s\n", file->filename, mime_type));
+
+ 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 { SRC_IMLIB, SRC_HTTP, SRC_MAGICK } image_source =
- SRC_IMLIB;
+ 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;
@@ -215,33 +342,89 @@ int feh_load_image(Imlib_Image * im, feh_file * file)
if (path_is_url(file->filename)) {
image_source = SRC_HTTP;
- if ((tmpname = feh_http_load_image(file->filename)) == NULL)
+ if ((tmpname = feh_http_load_image(file->filename)) == NULL) {
+ feh_err = LOAD_ERROR_CURL;
err = IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST;
+ }
+ }
+ else {
+ if (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;
+ }
}
- else
- *im = imlib_load_image_with_error_return(file->filename, &err);
- if ((err == IMLIB_LOAD_ERROR_UNKNOWN)
- || (err == IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT)) {
- image_source = SRC_MAGICK;
- tmpname = feh_magick_load_image(file->filename);
+ if (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;
+ }
+ }
}
- if ((image_source != SRC_IMLIB) && tmpname) {
+ if (tmpname) {
*im = imlib_load_image_with_error_return(tmpname, &err);
- if (im) {
+ if (!err && im) {
real_filename = file->filename;
file->filename = tmpname;
+
+ /*
+ * 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
- file->ed = exif_get_data(tmpname);
+ /*
+ * if we're called from within feh_reload_image, file->ed is already
+ * populated.
+ */
+ if (file->ed) {
+ exif_data_unref(file->ed);
+ }
+ file->ed = exif_data_new_from_file(tmpname);
#endif
}
- if ((image_source == SRC_MAGICK) || !opt.keep_http)
+ if (!opt.use_conversion_cache && ((image_source != SRC_HTTP) || !opt.keep_http))
unlink(tmpname);
-
- free(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);
+ } else if (im) {
+#ifdef HAVE_LIBEXIF
+ /*
+ * if we're called from within feh_reload_image, file->ed is already
+ * populated.
+ */
+ if (file->ed) {
+ exif_data_unref(file->ed);
+ }
+ file->ed = exif_data_new_from_file(file->filename);
+#endif
}
if ((err) || (!im)) {
@@ -249,26 +432,47 @@ int feh_load_image(Imlib_Image * im, feh_file * file)
fputs("\n", stderr);
reset_output = 1;
}
- feh_imlib_print_load_error(file->filename, NULL, err);
+ feh_print_load_error(file->filename, NULL, err, feh_err);
D(("Load *failed*\n"));
return(0);
}
+ /*
+ * 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;
- ExifData *exifData = exif_data_new_from_file(file->filename);
- if (exifData) {
- ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
- ExifEntry *exifEntry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION);
- if (exifEntry && opt.auto_rotate)
+ if (file->ed) {
+ ExifByteOrder byteOrder = exif_data_get_byte_order(file->ed);
+ ExifEntry *exifEntry = exif_data_get_entry(file->ed, EXIF_TAG_ORIENTATION);
+ if (exifEntry && opt.auto_rotate) {
orientation = exif_get_short(exifEntry->data, byteOrder);
+ }
}
- file->ed = exifData;
- if (orientation == 3)
+ 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
@@ -277,17 +481,201 @@ int feh_load_image(Imlib_Image * im, feh_file * file)
return(1);
}
+void feh_reload_image(winwidget w, int resize, int force_new)
+{
+ char *new_title;
+ int len;
+ Imlib_Image tmp;
+ int old_w, old_h;
+
+ 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 status;
+ do {
+ waitpid(childpid, &status, WUNTRACED);
+ if (WIFEXITED(status)) {
+ return !WEXITSTATUS(status);
+ }
+ } while (!WIFEXITED(status) && !WIFSIGNALED(status));
+ }
+
+ return 0;
+}
+
+static char *feh_dcraw_load_image(char *filename)
+{
+ char *basename;
+ char *tmpname;
+ char *sfn;
+ int fd = -1;
+
+ if (opt.use_conversion_cache) {
+ if (!conversion_cache)
+ conversion_cache = gib_hash_new();
+ if ((sfn = gib_hash_get(conversion_cache, filename)) != NULL)
+ return sfn;
+ }
+
+ basename = strrchr(filename, '/');
+
+ if (basename == NULL)
+ basename = filename;
+ else
+ basename++;
+
+ tmpname = feh_unique_filename("/tmp/", basename);
+
+ if (strlen(tmpname) > (NAME_MAX-6))
+ tmpname[NAME_MAX-7] = '\0';
+
+ sfn = estrjoin("_", tmpname, "XXXXXX", NULL);
+ free(tmpname);
+
+ fd = mkstemp(sfn);
+
+ if (fd == -1) {
+ free(sfn);
+ return NULL;
+ }
+
+ childpid = fork();
+ if (childpid == -1) {
+ weprintf("%s: Can't load with dcraw. Fork failed:", filename);
+ unlink(sfn);
+ free(sfn);
+ close(fd);
+ return NULL;
+ } else if (childpid == 0) {
+ dup2(fd, STDOUT_FILENO);
+ close(fd);
+
+ alarm(opt.conversion_timeout);
+ execlp("dcraw", "dcraw", "-c", "-e", filename, NULL);
+ _exit(1);
+ }
+
+ int status;
+ waitpid(-1, &status, 0);
+ if (WIFSIGNALED(status)) {
+ unlink(sfn);
+ free(sfn);
+ sfn = NULL;
+ if (!opt.quiet)
+ weprintf("%s - Conversion took too long, skipping", filename);
+ }
+
+ if ((sfn != NULL) && opt.use_conversion_cache)
+ gib_hash_set(conversion_cache, filename, sfn);
+
+ return sfn;
+}
+
static char *feh_magick_load_image(char *filename)
{
char *argv_fn;
char *basename;
char *tmpname;
char *sfn;
+ char tempdir[] = "/tmp/.feh-magick-tmp-XXXXXX";
int fd = -1, devnull = -1;
int status;
+ char created_tempdir = 0;
- if (opt.magick_timeout < 0)
- return NULL;
+ 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, '/');
@@ -318,6 +706,22 @@ static char *feh_magick_load_image(char *filename)
*/
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 {
+ created_tempdir = 1;
+ }
+ }
+
if ((childpid = fork()) < 0) {
weprintf("%s: Can't load with imagemagick. Fork failed:", filename);
unlink(sfn);
@@ -339,45 +743,89 @@ static char *feh_magick_load_image(char *filename)
*/
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.magick_timeout);
+ alarm(opt.conversion_timeout);
waitpid(childpid, &status, 0);
- alarm(0);
- if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) {
- close(fd);
+ kill(childpid, SIGKILL);
+ if (opt.conversion_timeout > 0 && !alarm(0)) {
unlink(sfn);
free(sfn);
sfn = NULL;
if (!opt.quiet) {
- if (WIFSIGNALED(status))
- weprintf("%s - Conversion took too long, skipping",
- filename);
+ weprintf("%s: Conversion took too long, skipping", filename);
}
-
- /*
- * Reap child. The previous waitpid call was interrupted by
- * alarm, but convert doesn't terminate immediately.
- * XXX
- * normally, if (WIFSIGNALED(status)) waitpid(childpid, &status, 0);
- * would suffice. However, as soon as feh has its own window,
- * this doesn't work anymore and the following workaround is
- * required. Hm.
- */
- waitpid(-1, &status, 0);
}
+ close(fd);
childpid = 0;
}
+ 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);
+ }
+
free(argv_fn);
+
+ if ((sfn != NULL) && opt.use_conversion_cache)
+ gib_hash_set(conversion_cache, filename, sfn);
+
return sfn;
}
#ifdef HAVE_LIBCURL
+#if LIBCURL_VERSION_NUM >= 0x072000 /* 07.32.0 */
+static int curl_quit_function(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
+#else
+static int curl_quit_function(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
+#endif
+{
+ // ignore "unused parameter" warnings
+ (void)clientp;
+ (void)dltotal;
+ (void)dlnow;
+ (void)ultotal;
+ (void)ulnow;
+ if (sig_exit) {
+ /*
+ * The user wants to quit feh. Tell libcurl to abort the transfer and
+ * return control to the main loop, where we can quit gracefully.
+ */
+ return 1;
+ }
+ return 0;
+}
+
static char *feh_http_load_image(char *url)
{
CURL *curl;
@@ -390,6 +838,13 @@ static char *feh_http_load_image(char *url)
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;
@@ -405,27 +860,58 @@ static char *feh_http_load_image(char *url)
}
basename = strrchr(url, '/') + 1;
- tmpname = feh_unique_filename(path, basename);
- if (strlen(tmpname) > (NAME_MAX-6))
- tmpname[NAME_MAX-7] = '\0';
+#ifdef HAVE_MKSTEMPS
+ tmpname = estrjoin("_", "feh_curl_XXXXXX", basename, NULL);
- sfn = estrjoin("_", tmpname, "XXXXXX", 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);
@@ -438,7 +924,9 @@ static char *feh_http_load_image(char *url)
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (res != CURLE_OK) {
- weprintf("open url: %s", ebuff);
+ if (res != CURLE_ABORTED_BY_CALLBACK) {
+ weprintf("open url: %s", ebuff);
+ }
unlink(sfn);
close(fd);
free(sfn);
@@ -447,6 +935,8 @@ static char *feh_http_load_image(char *url)
free(ebuff);
fclose(sfp);
+ if (opt.use_conversion_cache)
+ gib_hash_set(conversion_cache, url, sfn);
return sfn;
} else {
weprintf("open url: fdopen failed:");
@@ -455,7 +945,11 @@ static char *feh_http_load_image(char *url)
close(fd);
}
} else {
+#ifdef HAVE_MKSTEMPS
+ weprintf("open url: mkstemps failed:");
+#else
weprintf("open url: mkstemp failed:");
+#endif
free(sfn);
}
curl_easy_cleanup(curl);
@@ -685,7 +1179,7 @@ void feh_draw_exif(winwidget w)
if (buffer[0] == '\0')
{
- snprintf(buffer, EXIF_MAX_DATA, "%s", estrdup("Failed to run exif command"));
+ 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++;
@@ -697,29 +1191,28 @@ void feh_draw_exif(winwidget w)
{
/* max 128 lines */
pos2 = 0;
- while ( pos2 < 256 ) /* max 256 chars per line */
+ 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 */
- info_line[pos2] = '\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;
}
- else
- {
- info_line[pos2] = '\0'; /* line finished, continue with next line*/
-
- pos++;
- break;
- }
- pos++;
- pos2++;
+ pos++;
+ pos2++;
}
+ info_line[pos2] = '\0';
gib_imlib_get_text_size(fn, info_line, NULL, &line_width,
&line_height, IMLIB_TEXT_TO_RIGHT);
@@ -754,6 +1247,7 @@ void feh_draw_exif(winwidget w)
info_buf[i], IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
gib_imlib_text_draw(im, fn, NULL, 1, (i * line_height) + 1,
info_buf[i], IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
+ free(info_buf[i]);
}
@@ -1041,8 +1535,32 @@ void feh_edit_inplace(winwidget w, int op)
if (!w->file || !w->file->data || !FEH_FILE(w->file->data)->filename)
return;
- if (!strcmp(gib_imlib_image_format(w->im), "jpeg") &&
- !path_is_url(FEH_FILE(w->file->data)->filename)) {
+ 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;
@@ -1062,8 +1580,8 @@ void feh_edit_inplace(winwidget w, int op)
FEH_FILE(w->file->data)->filename, &err);
gib_imlib_free_image(old);
if (err)
- feh_imlib_print_load_error(FEH_FILE(w->file->data)->filename,
- w, err);
+ feh_print_load_error(FEH_FILE(w->file->data)->filename,
+ w, err, LOAD_ERROR_IMLIB);
feh_reload_image(w, 1, 1);
} else {
/*
@@ -1078,8 +1596,12 @@ void feh_edit_inplace(winwidget w, int op)
else {
imlib_image_orientate(op);
tmp = w->im_w;
- FEH_FILE(w->file->data)->info->width = w->im_w = w->im_h;
- FEH_FILE(w->file->data)->info->height = w->im_h = tmp;
+ 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);
diff --git a/src/index.c b/src/index.c
index fbc25b8..b0b6923 100644
--- a/src/index.c
+++ b/src/index.c
@@ -1,7 +1,7 @@
/* index.c
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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
@@ -59,7 +59,6 @@ void init_index_mode(void)
int lineno;
unsigned char trans_bg = 0;
int index_image_width, index_image_height;
- char *s;
gib_list *line, *lines;
if (opt.montage) {
@@ -132,7 +131,7 @@ void init_index_mode(void)
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, choos e asmaller font,\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;
@@ -148,9 +147,16 @@ void init_index_mode(void)
index_image_height = h + title_area_h;
im_main = imlib_create_image(index_image_width, index_image_height);
- if (!im_main)
- 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 (!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,
@@ -164,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, 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);
}
@@ -332,7 +332,7 @@ void init_index_mode(void)
gib_imlib_save_image_with_error_return(im_main, output_buf, &err);
if (err) {
- feh_imlib_print_load_error(output_buf, im_main, err);
+ feh_print_load_error(output_buf, im_main, err, LOAD_ERROR_IMLIB);
}
else if (opt.verbose) {
int tw, th;
@@ -348,7 +348,6 @@ void init_index_mode(void)
if (!opt.display)
gib_imlib_free_image_and_decache(im_main);
- free(s);
return;
}
diff --git a/src/index.h b/src/index.h
index 08ab337..b805cc0 100644
--- a/src/index.h
+++ b/src/index.h
@@ -1,6 +1,6 @@
/* index.h
-Copyright (C) 2011 Daniel Friesel.
+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
diff --git a/src/keyevents.c b/src/keyevents.c
index f082a78..2f9b1d6 100644
--- a/src/keyevents.c
+++ b/src/keyevents.c
@@ -1,7 +1,7 @@
/* keyevents.c
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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
@@ -35,7 +35,7 @@ struct __fehkey keys[EVENT_LIST_END];
struct termios old_term_settings;
unsigned char control_via_stdin = 0;
-void setup_stdin() {
+void setup_stdin(void) {
struct termios ctrl;
control_via_stdin = 1;
@@ -55,7 +55,7 @@ void setup_stdin() {
eprintf("tcsetattr failed");
}
-void restore_stdin() {
+void restore_stdin(void) {
if (tcsetattr(STDIN_FILENO, TCSANOW, &old_term_settings) == -1)
eprintf("tcsetattr failed");
}
@@ -185,6 +185,7 @@ void init_keyevents(void) {
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
@@ -194,10 +195,10 @@ void init_keyevents(void) {
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_v, 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_f, 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);
@@ -313,11 +314,12 @@ void feh_event_invoke_action(winwidget winwid, unsigned char action)
return;
}
-void feh_event_handle_stdin()
+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) == -1) {
+ if (read(STDIN_FILENO, &stdin_buf, 1) <= 0) {
control_via_stdin = 0;
if (isatty(STDIN_FILENO) && getpgrp() == (tcgetpgrp(STDIN_FILENO))) {
weprintf("reading a command from stdin failed - disabling control via stdin");
@@ -327,17 +329,40 @@ void feh_event_handle_stdin()
}
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)
- feh_event_handle_generic(windows[0], 0, keysym, 0);
+ 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)
@@ -578,7 +603,7 @@ void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysy
}
else if (feh_is_kp(EVENT_zoom_in, state, keysym, button)) {
winwid->old_zoom = winwid->zoom;
- winwid->zoom = winwid->zoom * 1.25;
+ winwid->zoom = winwid->zoom * opt.zoom_rate;
if (winwid->zoom > ZOOM_MAX)
winwid->zoom = ZOOM_MAX;
@@ -592,7 +617,7 @@ void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysy
}
else if (feh_is_kp(EVENT_zoom_out, state, keysym, button)) {
winwid->old_zoom = winwid->zoom;
- winwid->zoom = winwid->zoom * 0.80;
+ winwid->zoom = winwid->zoom / opt.zoom_rate;
if (winwid->zoom < ZOOM_MIN)
winwid->zoom = ZOOM_MIN;
@@ -637,6 +662,10 @@ void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysy
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);
@@ -657,12 +686,15 @@ void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysy
}
else if (feh_is_kp(EVENT_jump_random, state, keysym, button)) {
if (winwid->type == WIN_TYPE_THUMBNAIL)
- feh_thumbnail_select_next(winwid, rand() % (filelist_len - 1));
+ 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) {
+ 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...
@@ -678,6 +710,8 @@ void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysy
}
else if (feh_is_kp(EVENT_toggle_pause, state, keysym, button)) {
slideshow_pause_toggle(winwid);
+ /* We need to re-render the image to update the info string immediately. */
+ winwidget_render_image(winwid, 0, 0);
}
else if (feh_is_kp(EVENT_save_image, state, keysym, button)) {
slideshow_save_image(winwid);
@@ -769,6 +803,7 @@ void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysy
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 6f317c4..0fdc6a5 100644
--- a/src/list.c
+++ b/src/list.c
@@ -1,7 +1,7 @@
/* list.c
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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
@@ -50,7 +50,7 @@ void init_list_mode(void)
file->info->height,
format_size(file->info->pixels));
printf("\t%s\t%c\t%s\n",
- format_size(file->info->size),
+ format_size(file->size),
file->info->has_alpha ? 'X' : '-', file->filename);
}
@@ -92,6 +92,7 @@ void real_loadables_mode(int loadable)
if (opt.verbose)
feh_display_status('.');
puts(file->filename);
+ fflush(stdout);
feh_action_run(file, opt.actions[0], NULL);
}
else {
@@ -106,6 +107,7 @@ void real_loadables_mode(int loadable)
if (opt.verbose)
feh_display_status('.');
puts(file->filename);
+ fflush(stdout);
feh_action_run(file, opt.actions[0], NULL);
}
else {
diff --git a/src/main.c b/src/main.c
index 840919f..2503773 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,7 +1,7 @@
/* main.c
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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
@@ -34,6 +34,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "wallpaper.h"
#include <termios.h>
+#ifdef HAVE_INOTIFY
+#include <sys/inotify.h>
+#endif
+
char **cmdargv = NULL;
int cmdargc = 0;
char *mode = NULL;
@@ -42,7 +46,14 @@ 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();
@@ -51,14 +62,22 @@ int main(int argc, char **argv)
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)
@@ -85,7 +104,29 @@ int main(int argc, char **argv)
/* 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. */
@@ -102,9 +143,13 @@ 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);
@@ -135,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);
@@ -144,8 +193,16 @@ int feh_main_iteration(int block)
FD_ZERO(&fdset);
FD_SET(xfd, &fdset);
- if (control_via_stdin)
+ 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;
@@ -183,14 +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();
}
- else if (count && (FD_ISSET(0, &fdset)))
+ /*
+ * 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 ;-) */
@@ -202,13 +268,21 @@ int feh_main_iteration(int block)
&& ((errno == ENOMEM) || (errno == EINVAL)
|| (errno == EBADF)))
eprintf("Connection to X display lost");
- else if (count && (FD_ISSET(0, &fdset)))
+ 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);
}
@@ -216,12 +290,21 @@ void feh_clean_exit(void)
{
delete_rm_files();
- free(opt.menu_bg);
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
diff --git a/src/menu.c b/src/menu.c
index ddb2db1..96173a4 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -1,7 +1,7 @@
/* menu.c
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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
@@ -80,6 +80,8 @@ enum {
CB_OPT_FREEZE_WINDOW,
CB_OPT_FULLSCREEN,
CB_EDIT_ROTATE,
+ CB_EDIT_MIRROR,
+ CB_EDIT_FLIP,
CB_OPT_AUTO_ZOOM,
CB_OPT_KEEP_ZOOM_VP
};
@@ -134,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;
@@ -823,20 +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, 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;
}
@@ -920,7 +918,12 @@ void feh_menu_init_main(void)
feh_menu_add_entry(m, "Reload", NULL, CB_RELOAD, 0, NULL);
feh_menu_add_entry(m, "Save Image", NULL, CB_SAVE_IMAGE, 0, NULL);
feh_menu_add_entry(m, "Save List", NULL, CB_SAVE_FILELIST, 0, NULL);
- feh_menu_add_entry(m, "Edit in Place", "EDIT", 0, 0, NULL);
+ if (opt.edit) {
+ feh_menu_add_entry(m, "Edit in Place", "EDIT", 0, 0, NULL);
+ }
+ else {
+ feh_menu_add_entry(m, "Change View", "EDIT", 0, 0, NULL);
+ }
feh_menu_add_entry(m, "Background", "BACKGROUND", 0, 0, NULL);
feh_menu_add_entry(m, NULL, NULL, 0, 0, NULL);
feh_menu_add_entry(m, "Hide", NULL, CB_REMOVE, 0, NULL);
@@ -929,7 +932,7 @@ void feh_menu_init_main(void)
return;
}
-void feh_menu_init_common()
+void feh_menu_init_common(void)
{
int num_desks, i;
char buf[30];
@@ -963,6 +966,8 @@ void feh_menu_init_common()
feh_menu_add_entry(m, "Rotate 90 CW", NULL, CB_EDIT_ROTATE, 1, NULL);
feh_menu_add_entry(m, "Rotate 180", NULL, CB_EDIT_ROTATE, 2, NULL);
feh_menu_add_entry(m, "Rotate 90 CCW", NULL, CB_EDIT_ROTATE, 3, NULL);
+ feh_menu_add_entry(m, "Mirror", NULL, CB_EDIT_MIRROR, 0, NULL);
+ feh_menu_add_entry(m, "Flip", NULL, CB_EDIT_FLIP, 0, NULL);
menu_bg = feh_menu_new();
menu_bg->name = estrdup("BACKGROUND");
@@ -1309,6 +1314,12 @@ void feh_menu_cb(feh_menu * m, feh_menu_item * i, int action, unsigned short dat
case CB_EDIT_ROTATE:
feh_edit_inplace(m->fehwin, data);
break;
+ case CB_EDIT_MIRROR:
+ feh_edit_inplace(m->fehwin, INPLACE_EDIT_MIRROR);
+ break;
+ case CB_EDIT_FLIP:
+ feh_edit_inplace(m->fehwin, INPLACE_EDIT_FLIP);
+ break;
case CB_SAVE_IMAGE:
slideshow_save_image(m->fehwin);
break;
@@ -1390,7 +1401,7 @@ static feh_menu *feh_menu_func_gen_info(feh_menu * m)
if (!file->info)
feh_file_info_load(file, im);
if (file->info) {
- snprintf(buffer, sizeof(buffer), "Size: %dKb", file->info->size / 1024);
+ 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, 0, 0, NULL);
diff --git a/src/menu.h b/src/menu.h
index 403728f..5bad00c 100644
--- a/src/menu.h
+++ b/src/menu.h
@@ -1,7 +1,7 @@
/* menu.h
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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/multiwindow.c b/src/multiwindow.c
index 13cff90..abbf6c9 100644
--- a/src/multiwindow.c
+++ b/src/multiwindow.c
@@ -34,25 +34,14 @@ 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, w));
- }
-
- 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);
@@ -62,7 +51,6 @@ 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 4f87685..d38ce45 100644
--- a/src/options.c
+++ b/src/options.c
@@ -1,7 +1,7 @@
/* options.c
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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,6 +24,7 @@ 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"
@@ -53,25 +54,31 @@ void init_parse_options(int argc, char **argv)
opt.display = 1;
opt.aspect = 1;
opt.slideshow_delay = 0.0;
- opt.magick_timeout = -1;
+ 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.menu_bg = estrdup(PREFIX "/share/feh/images/menubg_default.png");
opt.max_height = opt.max_width = UINT_MAX;
+ opt.zoom_rate = 1.25;
+
opt.start_list_at = NULL;
opt.jump_on_resort = 1;
opt.screen_clip = 1;
+ opt.cache_size = 4;
#ifdef HAVE_LIBXINERAMA
/* if we're using xinerama, then enable it by default */
opt.xinerama = 1;
opt.xinerama_index = -1;
#endif /* HAVE_LIBXINERAMA */
+#ifdef HAVE_INOTIFY
+ opt.auto_reload = 1;
+#endif /* HAVE_INOTIFY */
+ opt.use_conversion_cache = 1;
feh_getopt_theme(argc, argv);
@@ -129,6 +136,7 @@ static void feh_load_options_for_theme(char *theme)
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;
@@ -165,11 +173,19 @@ static void feh_load_options_for_theme(char *theme)
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;
@@ -211,7 +227,7 @@ static void feh_parse_options_from_string(char *opts)
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
@@ -226,7 +242,7 @@ static void feh_parse_options_from_string(char *opts)
eprintf(PACKAGE " does not support more than 64 words per "
"theme definition.\n Please shorten your lines.");
- if ((*t == ' ') && !(inquote)) {
+ if ((*t == ' ') && !inquote) {
*t = '\0';
num++;
@@ -237,8 +253,10 @@ static void feh_parse_options_from_string(char *opts)
list[num - 1] = feh_string_normalize(s);
break;
- } else if (((*t == '\"') || (*t == '\'')) && last != '\\')
- inquote = !(inquote);
+ } else if ((*t == inquote) && (last != '\\')) {
+ inquote = 0;
+ } else if (((*t == '\"') || (*t == '\'')) && (last != '\\') && !inquote)
+ inquote = *t;
last = *t;
}
@@ -309,106 +327,117 @@ 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:pPqrR:sS:tT:uUvVwW:xXy:YzZ"
- ".@:^:~:):|:+:<:>:";
+ "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[] = {
- {"menu-bg" , 1, 0, ')'},
- {"debug" , 0, 0, '+'},
- {"scale-down" , 0, 0, '.'},
- {"max-dimension" , 1, 0, '<'},
- {"min-dimension" , 1, 0, '>'},
- {"title-font" , 1, 0, '@'},
- {"action" , 1, 0, 'A'},
- {"image-bg" , 1, 0, 'B'},
- {"fontpath" , 1, 0, 'C'},
- {"slideshow-delay",1, 0, 'D'},
- {"thumb-height" , 1, 0, 'E'},
- {"full-screen" , 0, 0, 'F'}, /* deprecated */
- {"fullscreen" , 0, 0, 'F'},
- {"draw-actions" , 0, 0, 'G'},
- {"limit-height" , 1, 0, 'H'},
- {"fullindex" , 0, 0, 'I'},
- {"thumb-redraw" , 1, 0, 'J'},
- {"caption-path" , 1, 0, 'K'},
- {"customlist" , 1, 0, 'L'},
- {"menu-font" , 1, 0, 'M'},
- {"no-menus" , 0, 0, 'N'},
- {"output-only" , 1, 0, 'O'},
- {"cache-thumbnails", 0, 0, 'P'},
- {"reload" , 1, 0, 'R'},
- {"sort" , 1, 0, 'S'},
- {"theme" , 1, 0, 'T'},
- {"loadable" , 0, 0, 'U'},
- {"verbose" , 0, 0, 'V'},
- {"limit-width" , 1, 0, 'W'},
- {"ignore-aspect" , 0, 0, 'X'},
- {"hide-pointer" , 0, 0, 'Y'},
- {"auto-zoom" , 0, 0, 'Z'},
- {"title" , 1, 0, '^'},
- {"alpha" , 1, 0, 'a'},
- {"bg" , 1, 0, 'b'},
- {"collage" , 0, 0, 'c'},
- {"draw-filename" , 0, 0, 'd'},
- {"font" , 1, 0, 'e'},
- {"filelist" , 1, 0, 'f'},
- {"geometry" , 1, 0, 'g'},
- {"help" , 0, 0, 'h'},
- {"index" , 0, 0, 'i'},
- {"output-dir" , 1, 0, 'j'},
- {"keep-http" , 0, 0, 'k'},
- {"list" , 0, 0, 'l'},
- {"montage" , 0, 0, 'm'},
- {"reverse" , 0, 0, 'n'},
- {"output" , 1, 0, 'o'},
- {"preload" , 0, 0, 'p'},
- {"quiet" , 0, 0, 'q'},
- {"recursive" , 0, 0, 'r'},
- {"stretch" , 0, 0, 's'},
- {"thumbnails" , 0, 0, 't'},
- {"unloadable" , 0, 0, 'u'},
- {"version" , 0, 0, 'v'},
- {"multiwindow" , 0, 0, 'w'},
- {"borderless" , 0, 0, 'x'},
- {"thumb-width" , 1, 0, 'y'},
- {"randomize" , 0, 0, 'z'},
- {"start-at" , 1, 0, '|'},
- {"thumb-title" , 1, 0, '~'},
- {"bg-tile" , 0, 0, 200},
- {"bg-center" , 0, 0, 201},
- {"bg-scale" , 0, 0, 202},
- {"zoom" , 1, 0, 205},
- {"no-screen-clip", 0, 0, 206},
- {"index-info" , 1, 0, 207},
- {"magick-timeout", 1, 0, 208},
- {"action1" , 1, 0, 209},
- {"action2" , 1, 0, 210},
- {"action3" , 1, 0, 211},
- {"action4" , 1, 0, 212},
- {"action5" , 1, 0, 213},
- {"action6" , 1, 0, 214},
- {"action7" , 1, 0, 215},
- {"action8" , 1, 0, 216},
- {"action9" , 1, 0, 217},
- {"bg-fill" , 0, 0, 218},
- {"bg-max" , 0, 0, 219},
- {"no-jump-on-resort", 0, 0, 220},
+ {"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, 223},
- {"auto-rotate" , 0, 0, 242},
+ {"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
- {"cycle-once" , 0, 0, 224},
- {"no-xinerama" , 0, 0, 225},
- {"draw-tinted" , 0, 0, 229},
- {"info" , 1, 0, 234},
- {"force-aliasing", 0, 0, 235},
- {"no-fehbg" , 0, 0, 236},
- {"keep-zoom-vp" , 0, 0, 237},
- {"scroll-step" , 1, 0, 238},
- {"xinerama-index", 1, 0, 239},
- {"insecure" , 0, 0, 240},
- {"no-recursive" , 0, 0, 241},
+ {"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;
@@ -418,101 +447,99 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun)
switch (optch) {
case 0:
break;
- case ')':
- free(opt.menu_bg);
- opt.menu_bg = estrdup(optarg);
- weprintf("The --menu-bg option is deprecated and will be removed by 2012");
- break;
- case '+':
+ case OPTION_debug:
opt.debug = 1;
break;
- case '<':
+ 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 '>':
+ 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 '@':
+ case OPTION_title_font:
opt.title_font = estrdup(optarg);
break;
- case 'A':
+ case OPTION_action:
opt.actions[0] = estrdup(optarg);
break;
- case 'B':
- if (!strcmp(optarg, "checks"))
- opt.image_bg = IMAGE_BG_CHECKS;
- else if (!strcmp(optarg, "white"))
- opt.image_bg = IMAGE_BG_WHITE;
- else if (!strcmp(optarg, "black"))
- opt.image_bg = IMAGE_BG_BLACK;
- else
- weprintf("Unknown argument to --image-bg: %s", optarg);
+ case OPTION_image_bg:
+ opt.image_bg = estrdup(optarg);
break;
- case 'C':
+ case OPTION_fontpath:
D(("adding fontpath %s\n", optarg));
imlib_add_path_to_font_path(optarg);
break;
- case 'D':
+ 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 'E':
+ case OPTION_thumb_height:
opt.thumb_h = atoi(optarg);
break;
- case 'F':
+ case OPTION_fullscreen:
opt.full_screen = 1;
break;
- case 'G':
+ case OPTION_draw_actions:
opt.draw_actions = 1;
break;
- case 'H':
+ case OPTION_limit_height:
opt.limit_h = atoi(optarg);
break;
- case 'I':
+ case OPTION_fullindex:
opt.index = 1;
opt.index_info = estrdup("%n\n%S\n%wx%h");
break;
- case 'J':
+ case OPTION_thumb_redraw:
opt.thumb_redraw = atoi(optarg);
break;
- case 'K':
+ case OPTION_caption_path:
opt.caption_path = estrdup(optarg);
break;
- case 'L':
+ case OPTION_customlist:
opt.customlist = estrdup(optarg);
opt.display = 0;
break;
- case 'M':
+ case OPTION_menu_font:
free(opt.menu_font);
opt.menu_font = estrdup(optarg);
break;
- case 'N':
+ case OPTION_no_menus:
opt.no_menus = 1;
break;
- case 'O':
+ case OPTION_output_only:
opt.output = 1;
opt.output_file = estrdup(optarg);
opt.display = 0;
break;
- case 'P':
+ case OPTION_cache_thumbnails:
opt.cache_thumbnails = 1;
break;
- case 'R':
+ 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"))
@@ -540,118 +567,116 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun)
opt.randomize = 0;
}
break;
- case 'T':
+ case OPTION_theme:
theme = estrdup(optarg);
break;
- case 'U':
+ case OPTION_loadable:
opt.loadables = 1;
opt.display = 0;
break;
- case 'V':
+ case OPTION_verbose:
opt.verbose = 1;
break;
- case 'W':
+ case OPTION_limit_width:
opt.limit_w = atoi(optarg);
break;
- case 'X':
+ case OPTION_ignore_aspect:
opt.aspect = 0;
break;
- case 'Y':
+ case OPTION_hide_pointer:
opt.hide_pointer = 1;
break;
- case 'Z':
+ case OPTION_auto_zoom:
opt.zoom_mode = ZOOM_MODE_MAX;
break;
- case '^':
+ case OPTION_title:
opt.title = estrdup(optarg);
break;
- case 'a':
+ 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 'c':
- opt.collage = 1;
- break;
- case 'd':
+ case OPTION_draw_filename:
opt.draw_filename = 1;
break;
- case 'e':
+ case OPTION_font:
opt.font = estrdup(optarg);
break;
- case 'f':
+ case OPTION_filelist:
if (!strcmp(optarg, "-"))
opt.filelistfile = estrdup("/dev/stdin");
else
opt.filelistfile = estrdup(optarg);
break;
- case 'g':
+ 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 'h':
+ case OPTION_help:
show_usage();
break;
- case 'i':
+ case OPTION_index:
opt.index = 1;
opt.index_info = estrdup("%n");
break;
- case 'j':
+ case OPTION_output_dir:
opt.output_dir = estrdup(optarg);
break;
- case 'k':
+ case OPTION_keep_http:
opt.keep_http = 1;
break;
- case 'l':
+ case OPTION_list:
opt.list = 1;
opt.display = 0;
break;
- case 'm':
+ case OPTION_montage:
opt.index = 1;
break;
- case 'n':
+ case OPTION_reverse:
opt.reverse = 1;
break;
- case 'o':
+ case OPTION_output:
opt.output = 1;
opt.output_file = estrdup(optarg);
break;
- case 'p':
+ case OPTION_preload:
opt.preload = 1;
break;
- case 'q':
+ case OPTION_quiet:
opt.quiet = 1;
break;
- case 'r':
+ case OPTION_recursive:
opt.recursive = 1;
break;
- case 's':
+ case OPTION_stretch:
opt.stretch = 1;
break;
- case 't':
+ case OPTION_thumbnails:
opt.thumbs = 1;
opt.index_info = estrdup("%n");
break;
- case 'u':
+ case OPTION_unloadable:
opt.unloadables = 1;
opt.display = 0;
break;
- case 'v':
+ case OPTION_version:
show_version();
break;
- case 'w':
+ case OPTION_multiwindow:
opt.multiwindow = 1;
break;
- case 'x':
+ case OPTION_borderless:
opt.borderless = 1;
break;
- case 'y':
+ case OPTION_thumb_width:
opt.thumb_w = atoi(optarg);
break;
- case 'z':
+ case OPTION_randomize:
opt.randomize = 1;
if (opt.sort != SORT_NONE) {
weprintf("commandline contains --sort and --randomize. "
@@ -659,22 +684,22 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun)
opt.sort = SORT_NONE;
}
break;
- case '|':
+ case OPTION_start_at:
opt.start_list_at = estrdup(optarg);
break;
- case '~':
+ case OPTION_thumb_title:
opt.thumb_title = estrdup(optarg);
break;
- case 200:
+ case OPTION_bg_title:
opt.bgmode = BG_MODE_TILE;
break;
- case 201:
+ case OPTION_bg_center:
opt.bgmode = BG_MODE_CENTER;
break;
- case 202:
+ case OPTION_bg_scale:
opt.bgmode = BG_MODE_SCALE;
break;
- case 205:
+ case OPTION_zoom:
if (!strcmp("fill", optarg))
opt.zoom_mode = ZOOM_MODE_FILL;
else if (!strcmp("max", optarg))
@@ -682,95 +707,159 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun)
else
opt.default_zoom = atoi(optarg);
break;
- case 206:
+ case OPTION_no_screen_clip:
opt.screen_clip = 0;
break;
- case 207:
+ case OPTION_index_info:
opt.index_info = estrdup(optarg);
break;
- case 208:
- opt.magick_timeout = atoi(optarg);
+ case OPTION_magick_timeout:
+ weprintf("--magick-timeout is deprecated, please use --conversion-timeout instead");
+ opt.conversion_timeout = atoi(optarg);
break;
- case 209:
+ case OPTION_action1:
opt.actions[1] = estrdup(optarg);
break;
- case 210:
+ case OPTION_action2:
opt.actions[2] = estrdup(optarg);
break;
- case 211:
+ case OPTION_action3:
opt.actions[3] = estrdup(optarg);
break;
- case 212:
+ case OPTION_action4:
opt.actions[4] = estrdup(optarg);
break;
- case 213:
+ case OPTION_action5:
opt.actions[5] = estrdup(optarg);
break;
- case 214:
+ case OPTION_action6:
opt.actions[6] = estrdup(optarg);
break;
- case 215:
+ case OPTION_action7:
opt.actions[7] = estrdup(optarg);
break;
- case 216:
+ case OPTION_action8:
opt.actions[8] = estrdup(optarg);
break;
- case 217:
+ case OPTION_action9:
opt.actions[9] = estrdup(optarg);
break;
- case 218:
+ case OPTION_bg_fill:
opt.bgmode = BG_MODE_FILL;
break;
- case 219:
+ case OPTION_bg_max:
opt.bgmode = BG_MODE_MAX;
break;
- case 220:
+ case OPTION_no_jump_on_resort:
opt.jump_on_resort = 0;
break;
+ case OPTION_edit:
+ opt.edit = 1;
+ break;
#ifdef HAVE_LIBEXIF
- case 223:
+ case OPTION_draw_exif:
opt.draw_exif = 1;
break;
- case 242:
+ 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;
- break;
#endif
- case 224:
- opt.cycle_once = 1;
break;
- case 225:
+#endif
+ case OPTION_no_xinerama:
opt.xinerama = 0;
break;
- case 229:
+ case OPTION_draw_tinted:
opt.text_bg = TEXT_BG_TINTED;
break;
- case 234:
+ case OPTION_info:
opt.info_cmd = estrdup(optarg);
- if (opt.info_cmd[0] == ';')
+ if (opt.info_cmd[0] == ';') {
+ opt.draw_info = 0;
opt.info_cmd++;
- else
+ } else {
opt.draw_info = 1;
+ }
break;
- case 235:
+ case OPTION_tap_zones:
+ opt.tap_zones = 1;
+ break;
+ case OPTION_force_aliasing:
opt.force_aliasing = 1;
break;
- case 236:
+ case OPTION_no_fehbg:
opt.no_fehbg = 1;
break;
- case 237:
+ case OPTION_keep_zoom_vp:
opt.keep_zoom_vp = 1;
break;
- case 238:
+ case OPTION_scroll_step:
opt.scroll_step = atoi(optarg);
break;
- case 239:
+ case OPTION_xinerama_index:
opt.xinerama_index = atoi(optarg);
break;
- case 240:
+ case OPTION_insecure:
opt.insecure_ssl = 1;
break;
- case 241:
+ 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;
}
@@ -786,8 +875,46 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun)
add_file_to_filelist_recursively(argv[optind++], FILELIST_FIRST);
}
}
- else if (finalrun && !opt.filelistfile && !opt.bgmode)
- add_file_to_filelist_recursively(".", FILELIST_FIRST);
+ else if (finalrun && !opt.filelistfile && !opt.bgmode) {
+ /*
+ * if --start-at is a non-local URL (i.e., does not start with file:///),
+ * behave as if "feh URL" was called (there is no directory we can load)
+ */
+ if (opt.start_list_at && path_is_url(opt.start_list_at) && (strlen(opt.start_list_at) <= 8 || strncmp(opt.start_list_at, "file:///", 8) != 0)) {
+ add_file_to_filelist_recursively(opt.start_list_at, FILELIST_FIRST);
+ /*
+ * Otherwise, make "feh --start-at dir/file.jpg" behave like
+ * "feh --start-at dir/file.jpg dir".
+ */
+ } else if (opt.start_list_at && strrchr(opt.start_list_at, '/')) {
+ /*
+ * feh can't candle urlencoded path components ("some%20dir" etc).
+ * Use libcurl to unescape them if --start-at is file://...
+ */
+ if (strlen(opt.start_list_at) > 8 && strncmp(opt.start_list_at, "file:///", 8) == 0) {
+ char *unescaped_path = feh_http_unescape(opt.start_list_at);
+ if (unescaped_path != NULL) {
+ free(opt.start_list_at);
+ opt.start_list_at = estrdup(unescaped_path + 7);
+ free(unescaped_path);
+ } else {
+ char *new_path = estrdup(opt.start_list_at + 7);
+ free(opt.start_list_at);
+ opt.start_list_at = new_path;
+ }
+ }
+ char *target_directory = estrdup(opt.start_list_at);
+ char *filename_start = strrchr(target_directory, '/');
+ if (filename_start) {
+ *filename_start = '\0';
+ }
+ add_file_to_filelist_recursively(target_directory, FILELIST_FIRST);
+ original_file_items = gib_list_add_front(original_file_items, estrdup(target_directory));
+ free(target_directory);
+ } else {
+ add_file_to_filelist_recursively(".", FILELIST_FIRST);
+ }
+ }
/* So that we can safely be called again */
optind = 0;
@@ -815,17 +942,11 @@ 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;
- }
-
if (opt.full_screen && opt.multiwindow) {
eprintf("You cannot combine --fullscreen with --multiwindow");
}
- if (opt.list && (opt.multiwindow || opt.index || opt.collage)) {
+ if (opt.list && (opt.multiwindow || opt.index)) {
eprintf("You cannot combine --list with other modes");
}
@@ -853,14 +974,26 @@ static void show_version(void)
"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
diff --git a/src/options.h b/src/options.h
index 4e2703e..74c12cd 100644
--- a/src/options.h
+++ b/src/options.h
@@ -1,7 +1,7 @@
/* options.h
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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
@@ -27,10 +27,15 @@ 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 thumbs;
unsigned char slideshow;
@@ -44,6 +49,7 @@ 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;
@@ -53,6 +59,10 @@ struct __fehoptions {
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;
@@ -68,17 +78,19 @@ struct __fehoptions {
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 image_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;
@@ -89,13 +101,13 @@ struct __fehoptions {
char *filelistfile;
char *menu_font;
char *customlist;
- char *menu_bg;
char *caption_path;
char *start_list_at;
char *info_cmd;
char *index_info;
int force_aliasing;
+ int tap_zones;
int thumb_w;
int thumb_h;
int limit_w;
@@ -103,20 +115,31 @@ struct __fehoptions {
unsigned int thumb_redraw;
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;
@@ -124,11 +147,119 @@ struct __fehoptions {
double slideshow_delay;
- signed short magick_timeout;
+ 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];
@@ -184,6 +315,7 @@ enum key_action {
EVENT_render,
EVENT_toggle_actions,
EVENT_toggle_aliasing,
+ EVENT_toggle_auto_zoom,
#ifdef HAVE_LIBEXIF
EVENT_toggle_exif,
#endif
diff --git a/src/signals.c b/src/signals.c
index 8a3a8f7..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,12 +24,15 @@ 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;
@@ -69,9 +72,6 @@ void setup_signal_handlers()
void feh_handle_signal(int signo)
{
- winwidget winwid;
- int i;
-
switch (signo) {
case SIGALRM:
if (childpid)
@@ -86,20 +86,9 @@ void feh_handle_signal(int signo)
case SIGQUIT:
if (childpid)
killpg(childpid, SIGINT);
- exit(128 + signo);
- }
-
- winwid = winwidget_get_first_window_of_type(WIN_TYPE_SLIDESHOW);
-
- if (winwid) {
- if (signo == SIGUSR1)
- slideshow_change_image(winwid, SLIDE_NEXT, 1);
- else if (signo == SIGUSR2)
- slideshow_change_image(winwid, SLIDE_PREV, 1);
- } else if (opt.multiwindow) {
- for (i = window_num - 1; i >= 0; i--)
- feh_reload_image(windows[i], 0, 0);
+ 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 db389d5..266cb2e 100644
--- a/src/slideshow.c
+++ b/src/slideshow.c
@@ -1,7 +1,7 @@
/* slideshow.c
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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
@@ -35,32 +35,78 @@ void init_slideshow_mode(void)
{
winwidget w = NULL;
int success = 0;
- char *s = NULL;
gib_list *l = filelist, *last = NULL;
- feh_file *file = NULL;
+ /*
+ * In theory, --start-at FILENAME is simple: Look for a file called
+ * FILENAME, start the filelist there, done.
+ *
+ * In practice, there are cases where this isn't sufficient. For instance,
+ * a user running 'feh --start-at hello.jpg /tmp' will expect feh to start
+ * at /tmp/hello.jpg, as if they had used
+ * 'feh --start-at /tmp/hello.jpg /tmp'. Similarly, XDG Desktop files
+ * may lead to the invocation 'feh --start-at /tmp/hello.jpg .' in /tmp,
+ * expecting the behaviour of 'feh --start-at ./hello.jpg .'.
+ *
+ * Since a good user experience is not about being technically correct, but
+ * about delivering the expected behaviour, we do some fuzzy matching
+ * here. In the worst case, this will cause --start-at to start at the
+ * wrong file.
+ */
+
+ // Try finding an exact filename match first
for (; l && opt.start_list_at; l = l->next) {
if (!strcmp(opt.start_list_at, FEH_FILE(l->data)->filename)) {
+ free(opt.start_list_at);
opt.start_list_at = NULL;
break;
}
}
+ /*
+ * 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, NULL);
- 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)
@@ -69,7 +115,6 @@ void init_slideshow_mode(void)
feh_add_unique_timer(cb_reload_timer, w, opt.reload);
break;
} else {
- free(s);
last = l;
}
}
@@ -92,177 +137,77 @@ void cb_reload_timer(void *data)
winwidget w = (winwidget) data;
- /* save the current filename for refinding it in new list */
- current_filename = estrdup(FEH_FILE(current_file->data)->filename);
-
- 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 (!(filelist_len = gib_list_length(filelist))) {
- eprintf("No files found to reload.");
- }
-
- feh_prepare_filelist();
-
- /* find the previously current file */
- for (l = filelist; l; l = l->next)
- if (strcmp(FEH_FILE(l->data)->filename, current_filename) == 0) {
- current_file = l;
- break;
- }
-
- free(current_filename);
-
- if (!current_file)
- current_file = filelist;
- w->file = current_file;
-
- /* reset window name in case of current file order,
- * filename, or filelist_length has changed.
- */
- current_filename = slideshow_create_name(FEH_FILE(current_file->data), w);
- winwidget_rename(w, current_filename);
- free(current_filename);
-
- feh_reload_image(w, 1, 0);
- feh_add_unique_timer(cb_reload_timer, w, opt.reload);
- return;
-}
-
-void feh_reload_image(winwidget w, int resize, int force_new)
-{
- char *title, *new_title;
- int len;
- Imlib_Image tmp;
- int old_w, old_h;
-
- unsigned char tmode =0;
- int tim_x =0;
- int tim_y =0;
- double tzoom =0;
-
- tmode = w->mode;
- tim_x = w->im_x;
- tim_y = w->im_y;
- tzoom = w->zoom;
-
- 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);
- title = estrdup(w->name);
- winwidget_rename(w, 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.
+ * 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 (force_new)
- winwidget_free_image(w);
+ if (current_file != NULL) {
+ /* save the current filename for refinding it in new list */
+ current_filename = estrdup(FEH_FILE(current_file->data)->filename);
- 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);
+ 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));
}
- winwidget_rename(w, title);
- free(title);
- free(new_title);
- return;
- }
- if (!resize && ((old_w != gib_imlib_image_get_width(tmp)) ||
- (old_h != gib_imlib_image_get_height(tmp))))
- resize = 1;
+ if (!(filelist_len = gib_list_length(filelist))) {
+ eprintf("No files found to reload.");
+ }
- if (!force_new)
- winwidget_free_image(w);
+ feh_prepare_filelist();
- w->im = tmp;
- winwidget_reset_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;
+ }
- 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;
+ free(current_filename);
- 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 (opt.keep_zoom_vp) {
- /* put back in: */
- w->mode = tmode;
- w->im_x = tim_x;
- w->im_y = tim_y;
- w->zoom = tzoom;
- winwidget_render_image(w, 0, 0);
- } else {
- winwidget_render_image(w, resize, 0);
+ if (!current_file)
+ current_file = filelist;
+ w->file = current_file;
}
- 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, int render)
{
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;
- unsigned char tmode =0;
- int tim_x =0;
- int tim_y =0;
- double tzoom =0;
+ 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 *
@@ -272,14 +217,28 @@ void slideshow_change_image(winwidget winwid, int change, int render)
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);
@@ -290,7 +249,7 @@ void slideshow_change_image(winwidget winwid, int change, int render)
case SLIDE_RAND:
if (filelist_len > 1) {
current_file = feh_list_jump(filelist, current_file, FORWARD,
- (rand() % (filelist_len - 1)) + 1);
+ (random() % (filelist_len - 1)) + 1);
change = SLIDE_NEXT;
}
break;
@@ -370,43 +329,29 @@ void slideshow_change_image(winwidget winwid, int change, int render)
last = NULL;
}
- if (opt.keep_zoom_vp) {
- /* pre loadimage - record settings */
- tmode = winwid->mode;
- tim_x = winwid->im_x;
- tim_y = winwid->im_y;
- tzoom = winwid->zoom;
+ 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) {
+ 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);
- if (opt.keep_zoom_vp) {
- /* put back in: */
- winwid->mode = tmode;
- winwid->im_x = tim_x;
- winwid->im_y = tim_y;
- winwid->zoom = tzoom;
- }
+ winwid->im_w = w;
+ winwid->im_h = h;
if (render) {
- if (opt.keep_zoom_vp) {
- winwidget_render_image(winwid, 0, 0);
- } else {
- winwidget_render_image(winwid, 1, 0);
- }
+ winwidget_render_image(winwid, 1, 0);
}
-
- s = slideshow_create_name(FEH_FILE(current_file->data), winwid);
- winwidget_rename(winwid, s);
- free(s);
-
break;
} else
last = current_file;
@@ -417,8 +362,6 @@ void slideshow_change_image(winwidget winwid, int change, int render)
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;
}
@@ -433,23 +376,6 @@ void slideshow_pause_toggle(winwidget w)
winwidget_rename(w, NULL);
}
-char *slideshow_create_name(feh_file * file, winwidget winwid)
-{
- 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, winwid));
- }
-
- return(s);
-}
-
void feh_action_run(feh_file * file, char *action, winwidget winwid)
{
if (action) {
@@ -459,12 +385,13 @@ void feh_action_run(feh_file * file, char *action, winwidget winwid)
if (opt.verbose && !opt.list && !opt.customlist)
fprintf(stderr, "Running action -->%s<--\n", sys);
- system(sys);
+ if (system(sys) == -1)
+ perror("running action via system() failed");
}
return;
}
-char *format_size(int size)
+char *format_size(double size)
{
static char ret[5];
char units[] = {' ', 'k', 'M', 'G', 'T'};
@@ -473,7 +400,7 @@ char *format_size(int size)
size /= 1000;
postfix++;
}
- snprintf(ret, 5, "%3d%c", size, units[postfix]);
+ snprintf(ret, 5, "%3.0f%c", size, units[postfix]);
return ret;
}
@@ -486,11 +413,20 @@ char *feh_printf(char *str, feh_file * file, winwidget winwid)
ret[0] = '\0';
filelist_tmppath = NULL;
+ gib_list *f;
for (c = str; *c != '\0'; 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)
strncat(ret, file->filename, sizeof(ret) - strlen(ret) - 1);
@@ -499,6 +435,12 @@ char *feh_printf(char *str, feh_file * file, winwidget winwid)
if (file)
strncat(ret, shell_escape(file->filename), sizeof(ret) - strlen(ret) - 1);
break;
+ 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 && (file->info || !feh_file_info_load(file, NULL))) {
snprintf(buf, sizeof(buf), "%d", file->info->height);
@@ -554,14 +496,14 @@ char *feh_printf(char *str, feh_file * file, winwidget winwid)
}
break;
case 's':
- if (file && (file->info || !feh_file_info_load(file, NULL))) {
- snprintf(buf, sizeof(buf), "%d", file->info->size);
+ 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->info || !feh_file_info_load(file, NULL))) {
- strncat(ret, format_size(file->info->size), sizeof(ret) - strlen(ret) - 1);
+ if (file && (file->size >= 0 || !feh_file_stat(file))) {
+ strncat(ret, format_size(file->size), sizeof(ret) - strlen(ret) - 1);
}
break;
case 't':
@@ -570,9 +512,8 @@ char *feh_printf(char *str, feh_file * file, winwidget winwid)
}
break;
case 'u':
- snprintf(buf, sizeof(buf), "%d",
- current_file != NULL ? gib_list_num(filelist, current_file)
- + 1 : 0);
+ 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':
@@ -588,6 +529,12 @@ char *feh_printf(char *str, feh_file * file, winwidget winwid)
strncat(ret, buf, sizeof(ret) - strlen(ret) - 1);
}
break;
+ 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);
@@ -596,6 +543,12 @@ char *feh_printf(char *str, feh_file * file, winwidget winwid)
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;
@@ -626,15 +579,14 @@ char *feh_printf(char *str, feh_file * file, winwidget winwid)
void feh_filelist_image_remove(winwidget winwid, char do_delete)
{
if (winwid->type == WIN_TYPE_SLIDESHOW) {
- char *s;
gib_list *doomed;
doomed = current_file;
/*
- * work around feh_list_jump exiting if cycle_once is enabled
+ * 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.cycle_once && ! doomed->next && do_delete) {
+ if (opt.on_last_slide == ON_LAST_SLIDE_QUIT && ! doomed->next && do_delete) {
feh_file_rm_and_free(filelist, doomed);
exit(0);
}
@@ -653,9 +605,6 @@ void feh_filelist_image_remove(winwidget winwid, char do_delete)
winwidget_destroy(winwid);
return;
}
- s = slideshow_create_name(FEH_FILE(winwid->file->data), winwid);
- 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)) {
@@ -672,16 +621,24 @@ 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.output_dir) {
+ free(base_dir);
}
if (opt.verbose)
@@ -690,7 +647,7 @@ void slideshow_save_image(winwidget win)
gib_imlib_save_image_with_error_return(win->im, tmpname, &err);
if (err)
- feh_imlib_print_load_error(tmpname, win, err);
+ feh_print_load_error(tmpname, win, err, LOAD_ERROR_IMLIB);
free(tmpname);
return;
@@ -713,7 +670,7 @@ 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);
}
if (opt.randomize) {
diff --git a/src/structs.h b/src/structs.h
index 3942bc0..8438930 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1,7 +1,7 @@
/* structs.h
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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/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 edf0f4f..70af5e0 100644
--- a/src/thumbnail.c
+++ b/src/thumbnail.c
@@ -1,7 +1,7 @@
/* thumbnail.c
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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
@@ -71,7 +71,6 @@ void init_thumbnail_mode(void)
gib_list *l, *last = NULL;
int lineno;
int index_image_width, index_image_height;
- char *s;
unsigned int thumb_counter = 0;
gib_list *line, *lines;
@@ -92,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)
@@ -147,9 +148,16 @@ void init_thumbnail_mode(void)
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);
- if (!td.im_main)
- 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 (!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);
@@ -168,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, 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) {
@@ -190,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");
@@ -389,7 +396,7 @@ void init_thumbnail_mode(void)
}
gib_imlib_save_image_with_error_return(td.im_main, output_buf, &err);
if (err) {
- feh_imlib_print_load_error(output_buf, td.im_main, err);
+ feh_print_load_error(output_buf, td.im_main, err, LOAD_ERROR_IMLIB);
}
else if (opt.verbose) {
int tw, th;
@@ -408,6 +415,7 @@ void init_thumbnail_mode(void)
else if (opt.start_list_at) {
for (l = thumbnails; l; l = l->next) {
if (!strcmp(opt.start_list_at, FEH_THUMB(l->data)->file->filename)) {
+ free(opt.start_list_at);
opt.start_list_at = NULL;
feh_thumbnail_select(winwid, FEH_THUMB(l->data));
break;
@@ -416,7 +424,6 @@ void init_thumbnail_mode(void)
}
- free(s);
return;
}
@@ -590,7 +597,7 @@ int feh_thumbnail_get_thumbnail(Imlib_Image * image, feh_file * file,
return status;
}
-static char *feh_thumbnail_get_prefix()
+static char *feh_thumbnail_get_prefix(void)
{
char *dir = NULL, *home, *xdg_cache_home;
@@ -633,9 +640,9 @@ char *feh_thumbnail_get_name_uri(char *name)
/* FIXME: what happens with http, https, and ftp? MTime etc */
if (!path_is_url(name)) {
- /* make sure it's an absoulte path */
+ /* make sure it's an absolute path */
/* FIXME: add support for ~, need to investigate if it's expanded
- somewhere else before adding (unecessary) code */
+ somewhere else before adding (unnecessary) code */
if (name[0] != '/') {
/* work around /some/path/./image.ext */
if ((strncmp(name, "./", 2)) == 0)
@@ -696,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,
@@ -772,25 +786,32 @@ int feh_thumbnail_get_generated(Imlib_Image * image, feh_file * file,
void feh_thumbnail_show_fullsize(feh_file *thumbfile)
{
winwidget thumbwin = NULL;
- char *s;
+ gib_list *l;
- if (!opt.thumb_title)
- s = thumbfile->name;
- else
- s = feh_printf(opt.thumb_title, thumbfile, NULL);
-
+ 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(
- gib_list_add_front(NULL, thumbfile),
- s, WIN_TYPE_THUMBNAIL_VIEWER);
+ l,
+ 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, 1);
+ 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
}
}
@@ -872,13 +893,13 @@ void feh_thumbnail_select_prev(winwidget winwid, int jump)
}
}
-void feh_thumbnail_show_selected()
+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()
+feh_file* feh_thumbnail_get_selected_file(void)
{
if (td.selected)
return td.selected->file;
@@ -925,16 +946,3 @@ int feh_thumbnail_setup_thumbnail_dir(void)
return status;
}
-
-char *thumbnail_create_name(feh_file * file, winwidget winwid)
-{
- char *s = NULL;
-
- if (!opt.thumb_title) {
- s = estrdup(file->filename);
- } else {
- s = estrdup(feh_printf(opt.thumb_title, file, winwid));
- }
-
- return(s);
-}
diff --git a/src/thumbnail.h b/src/thumbnail.h
index f22ff77..e69759f 100644
--- a/src/thumbnail.h
+++ b/src/thumbnail.h
@@ -1,7 +1,7 @@
/* thumbnail.h
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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
@@ -83,8 +83,8 @@ 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();
-feh_file *feh_thumbnail_get_selected_file();
+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 1cac94b..8e42050 100644
--- a/src/timers.c
+++ b/src/timers.c
@@ -1,7 +1,7 @@
/* timers.c
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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
@@ -58,7 +58,37 @@ double feh_get_time(void)
return((double) timev.tv_sec + (((double) timev.tv_usec) / 1000000));
}
-void feh_remove_timer(char *name)
+void feh_remove_timer_by_data(void *data)
+{
+ fehtimer ft, ptr, pptr;
+
+ D(("removing timer for %p\n", data));
+ pptr = NULL;
+ ptr = first_timer;
+ while (ptr) {
+ D(("Stepping through event list\n"));
+ ft = ptr;
+ if (ft->data == data) {
+ D(("Found it. Removing\n"));
+ if (pptr)
+ pptr->next = ft->next;
+ else
+ first_timer = ft->next;
+ if (ft->next)
+ ft->next->in += ft->in;
+ if (ft->name)
+ free(ft->name);
+ if (ft)
+ free(ft);
+ return;
+ }
+ pptr = ptr;
+ ptr = ptr->next;
+ }
+ return;
+}
+
+static void feh_remove_timer(char *name)
{
fehtimer ft, ptr, pptr;
@@ -88,6 +118,7 @@ void feh_remove_timer(char *name)
return;
}
+
void feh_add_timer(void (*func) (void *data), void *data, double in, char *name)
{
fehtimer ft, ptr, pptr;
diff --git a/src/timers.h b/src/timers.h
index a4243ca..e95d9b5 100644
--- a/src/timers.h
+++ b/src/timers.h
@@ -37,7 +37,7 @@ struct __fehtimer {
void feh_handle_timer(void);
double feh_get_time(void);
-void feh_remove_timer(char *name);
+void feh_remove_timer_by_data(void *data);
void feh_add_timer(void (*func) (void *data), void *data, double in, char *name);
void feh_add_unique_timer(void (*func) (void *data), void *data, double in);
diff --git a/src/utils.c b/src/utils.c
index ec30d4a..eb128a2 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -146,12 +146,15 @@ char *estrjoin(const char *separator, ...)
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)
{
diff --git a/src/wallpaper.c b/src/wallpaper.c
index c9a3a05..e2fa67e 100644
--- a/src/wallpaper.c
+++ b/src/wallpaper.c
@@ -1,7 +1,7 @@
/* wallpaper.c
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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
@@ -24,12 +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 "wallpaper.h"
-#include <limits.h>
-#include <sys/stat.h>
+
Window ipc_win = None;
Window my_ipc_win = None;
Atom ipc_atom = None;
@@ -89,7 +91,7 @@ static void feh_wm_set_bg_scaled(Pixmap pmap, Imlib_Image im, int use_filelist,
feh_wm_load_next(&im);
gib_imlib_render_image_on_drawable_at_size(pmap, im, x, y, w, h,
- 1, 0, !opt.force_aliasing);
+ 1, 1, !opt.force_aliasing);
if (use_filelist)
gib_imlib_free_image_and_decache(im);
@@ -130,7 +132,7 @@ static void feh_wm_set_bg_centered(Pixmap pmap, Imlib_Image im, int use_filelist
y + ((offset_y > 0) ? offset_y : 0),
w,
h,
- 1, 0, 0);
+ 1, 1, 0);
if (use_filelist)
gib_imlib_free_image_and_decache(im);
@@ -158,11 +160,36 @@ static void feh_wm_set_bg_filled(Pixmap pmap, Imlib_Image im, int use_filelist,
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, 0, !opt.force_aliasing);
+ 1, 1, !opt.force_aliasing);
if (use_filelist)
gib_imlib_free_image_and_decache(im);
@@ -210,7 +237,7 @@ static void feh_wm_set_bg_maxed(Pixmap pmap, Imlib_Image im, int use_filelist,
gib_imlib_render_image_on_drawable_at_size(pmap, im,
render_x, render_y,
render_w, render_h,
- 1, 0, !opt.force_aliasing);
+ 1, 1, !opt.force_aliasing);
if (use_filelist)
gib_imlib_free_image_and_decache(im);
@@ -218,6 +245,106 @@ static void feh_wm_set_bg_maxed(Pixmap pmap, Imlib_Image im, int use_filelist,
return;
}
+/*
+** Creates a script that can be used to create the same background
+** as the last time the program was called.
+*/
+void feh_wm_gen_bg_script(char* fil, int centered, int scaled, int filled, int use_filelist) {
+ char * home = getenv("HOME");
+
+ if (!home)
+ return;
+
+ FILE *fp;
+ int fd;
+ char *path;
+ char *exec_method;
+ char *absolute_path;
+ struct stat s;
+ gib_list *filelist_pos = filelist;
+
+ if (strchr(cmdargv[0], '/'))
+ exec_method = feh_absolute_path(cmdargv[0]);
+ else
+ exec_method = cmdargv[0];
+
+ path = estrjoin("/", home, ".fehbg", NULL);
+
+ if ((fp = fopen(path, "w")) == NULL) {
+ weprintf("Can't write to %s", path);
+ } else {
+ fputs("#!/bin/sh\n", fp);
+ fputs(exec_method, fp);
+ fputs(" --no-fehbg --bg-", fp);
+ if (centered)
+ fputs("center", fp);
+ else if (scaled)
+ fputs("scale", fp);
+ else if (filled == 1)
+ fputs("fill", fp);
+ else if (filled == 2)
+ fputs("max", fp);
+ else
+ fputs("tile", fp);
+ if (opt.image_bg) {
+ fputs(" --image-bg ", fp);
+ fputs(shell_escape(opt.image_bg), fp);
+ }
+#ifdef HAVE_LIBXINERAMA
+ if (opt.xinerama) {
+ if (opt.xinerama_index >= 0) {
+ fprintf(fp, " --xinerama-index %d", opt.xinerama_index);
+ }
+ }
+ else {
+ fputs(" --no-xinerama", fp);
+ }
+#endif /* HAVE_LIBXINERAMA */
+ if (opt.geom_flags & XValue) {
+ fprintf(fp, " --geometry %c%d",
+ opt.geom_flags & XNegative ? '-' : '+',
+ opt.geom_flags & XNegative ? abs(opt.geom_x) : opt.geom_x);
+ if (opt.geom_flags & YValue) {
+ fprintf(fp, "%c%d",
+ opt.geom_flags & YNegative ? '-' : '+',
+ opt.geom_flags & YNegative ? abs(opt.geom_y) : opt.geom_y);
+ }
+ }
+ if (opt.force_aliasing) {
+ fputs(" --force-aliasing", fp);
+ }
+ fputc(' ', fp);
+ if (use_filelist) {
+#ifdef HAVE_LIBXINERAMA
+ for (int i = 0; (i < opt.xinerama ? num_xinerama_screens : 1) && filelist_pos; i++)
+#else
+ for (int i = 0; (i < 1 ) && filelist_pos; i++)
+#endif
+ {
+ absolute_path = feh_absolute_path(FEH_FILE(filelist_pos->data)->filename);
+ fputs(shell_escape(absolute_path), fp);
+ filelist_pos = filelist_pos->next;
+ free(absolute_path);
+ fputc(' ', fp);
+ }
+ } else if (fil) {
+ absolute_path = feh_absolute_path(fil);
+ fputs(shell_escape(absolute_path), fp);
+ free(absolute_path);
+ }
+ fputc('\n', fp);
+ fd = fileno(fp);
+ if (fstat(fd, &s) != 0 || fchmod(fd, s.st_mode | S_IXUSR | S_IXGRP) != 0) {
+ weprintf("Can't set %s as executable", path);
+ }
+ fclose(fp);
+ }
+ free(path);
+
+ if(exec_method != cmdargv[0])
+ free(exec_method);
+}
+
void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
int filled, int desktop, int use_filelist)
{
@@ -225,7 +352,7 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
XGCValues gcval;
GC gc;
char bgname[20];
- int num = (int) rand();
+ int num = (int) random();
char bgfil[4096];
char sendbuf[4096];
@@ -255,7 +382,10 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
feh_wm_load_next(&im);
fil = FEH_FILE(filelist->data)->filename;
}
- snprintf(sendbuf, sizeof(sendbuf), "background %s bg.file %s", bgname, fil);
+ 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) {
@@ -295,8 +425,6 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
unsigned char *data_root = NULL, *data_esetroot = NULL;
Pixmap pmap_d1, pmap_d2;
- char *home;
-
/* local display to set closedownmode on */
Display *disp2;
Window root2;
@@ -305,15 +433,19 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
D(("Falling back to XSetRootWindowPixmap\n"));
+ XColor color;
+ Colormap cmap = DefaultColormap(disp, DefaultScreen(disp));
+ if (opt.image_bg)
+ XAllocNamedColor(disp, cmap, (char*) opt.image_bg, &color, &color);
+ else
+ XAllocNamedColor(disp, cmap, "black", &color, &color);
+
if (scaled) {
pmap_d1 = XCreatePixmap(disp, root, scr->width, scr->height, depth);
#ifdef HAVE_LIBXINERAMA
if (opt.xinerama_index >= 0) {
- if (opt.image_bg == IMAGE_BG_WHITE)
- gcval.foreground = WhitePixel(disp, DefaultScreen(disp));
- else
- gcval.foreground = BlackPixel(disp, DefaultScreen(disp));
+ gcval.foreground = color.pixel;
gc = XCreateGC(disp, root, GCForeground, &gcval);
XFillRectangle(disp, pmap_d1, gc, 0, 0, scr->width, scr->height);
XFreeGC(disp, gc);
@@ -337,10 +469,7 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
D(("centering\n"));
pmap_d1 = XCreatePixmap(disp, root, scr->width, scr->height, depth);
- if (opt.image_bg == IMAGE_BG_WHITE)
- gcval.foreground = WhitePixel(disp, DefaultScreen(disp));
- else
- gcval.foreground = BlackPixel(disp, DefaultScreen(disp));
+ gcval.foreground = color.pixel;
gc = XCreateGC(disp, root, GCForeground, &gcval);
XFillRectangle(disp, pmap_d1, gc, 0, 0, scr->width, scr->height);
@@ -367,10 +496,7 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
#ifdef HAVE_LIBXINERAMA
if (opt.xinerama_index >= 0) {
- if (opt.image_bg == IMAGE_BG_WHITE)
- gcval.foreground = WhitePixel(disp, DefaultScreen(disp));
- else
- gcval.foreground = BlackPixel(disp, DefaultScreen(disp));
+ gcval.foreground = color.pixel;
gc = XCreateGC(disp, root, GCForeground, &gcval);
XFillRectangle(disp, pmap_d1, gc, 0, 0, scr->width, scr->height);
XFreeGC(disp, gc);
@@ -393,10 +519,7 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
} else if (filled == 2) {
pmap_d1 = XCreatePixmap(disp, root, scr->width, scr->height, depth);
- if (opt.image_bg == IMAGE_BG_WHITE)
- gcval.foreground = WhitePixel(disp, DefaultScreen(disp));
- else
- gcval.foreground = BlackPixel(disp, DefaultScreen(disp));
+ gcval.foreground = color.pixel;
gc = XCreateGC(disp, root, GCForeground, &gcval);
XFillRectangle(disp, pmap_d1, gc, 0, 0, scr->width, scr->height);
@@ -423,56 +546,12 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
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);
+ gib_imlib_render_image_on_drawable(pmap_d1, im, 0, 0, 1, 1, 0);
}
- if (!opt.no_fehbg) {
- home = getenv("HOME");
- if (home) {
- FILE *fp;
- char *path;
- struct stat s;
- path = estrjoin("/", home, ".fehbg", NULL);
- if ((fp = fopen(path, "w")) == NULL) {
- weprintf("Can't write to %s", path);
- } else {
- fputs("#!/bin/sh\n", fp);
- if (use_filelist) {
- for (int i = 0; i < cmdargc; i++) {
- fputs(shell_escape(cmdargv[i]), fp);
- fputc(' ', fp);
- }
- } else if (fil) {
- fputs("feh --bg-", fp);
- if (centered)
- fputs("center", fp);
- else if (scaled)
- fputs("scale", fp);
- else if (filled)
- fputs("fill", fp);
- else
- fputs("tile", fp);
-
- if (opt.force_aliasing)
- fputs(" --force-aliasing", fp);
-#ifdef HAVE_LIBXINERAMA
- if (!opt.xinerama)
- fputs(" --no-xinerama", fp);
-#endif
- fputc(' ', fp);
- fputs(shell_escape(fil), fp);
- }
- fputc('\n', fp);
- fclose(fp);
- stat(path, &s);
- if (chmod(path, s.st_mode | S_IXUSR | S_IXGRP) != 0) {
- weprintf("Can't set %s as executable", path);
- }
- }
- free(path);
- }
- }
-
+ if (!opt.no_fehbg)
+ feh_wm_gen_bg_script(fil, centered, scaled, filled, use_filelist);
+
/* create new display, copy pixmap to new display */
disp2 = XOpenDisplay(NULL);
if (!disp2)
@@ -512,7 +591,7 @@ 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);
diff --git a/src/wallpaper.h b/src/wallpaper.h
index 0921129..c836c0f 100644
--- a/src/wallpaper.h
+++ b/src/wallpaper.h
@@ -1,7 +1,7 @@
/* wallpaper.h
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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
@@ -42,7 +42,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
extern Window ipc_win;
extern Atom ipc_atom;
-_XFUNCPROTOBEGIN extern Window enl_ipc_get_win(void);
+extern Window enl_ipc_get_win(void);
extern void enl_ipc_send(char *);
extern char *enl_wait_for_reply(void);
extern char *enl_ipc_get(const char *);
@@ -53,5 +53,4 @@ extern int feh_wm_get_num_desks(void);
extern signed char feh_wm_get_wm_is_e(void);
void feh_wm_set_bg_filelist(unsigned char bgmode);
-_XFUNCPROTOEND
#endif
diff --git a/src/winwidget.c b/src/winwidget.c
index 6f64844..3b90158 100644
--- a/src/winwidget.c
+++ b/src/winwidget.c
@@ -1,7 +1,7 @@
/* winwidget.c
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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
@@ -29,6 +29,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "winwidget.h"
#include "options.h"
#include "events.h"
+#include "timers.h"
+
+#ifdef HAVE_INOTIFY
+#include <sys/inotify.h>
+#endif
static void winwidget_unregister(winwidget win);
static void winwidget_register(winwidget win);
@@ -78,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;
@@ -95,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, 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);
@@ -119,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);
}
@@ -133,8 +138,13 @@ 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, 0);
}
@@ -144,6 +154,7 @@ 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;
@@ -212,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;
@@ -251,11 +261,31 @@ void winwidget_create_window(winwidget ret, int w, int h)
}
}
- ret->win =
- XCreateWindow(disp, DefaultRootWindow(disp), x, y, w, h, 0,
- depth, InputOutput, vis,
- CWOverrideRedirect | CWSaveUnder | CWBackingStore
- | CWColormap | CWBackPixel | CWBorderPixel | CWEventMask, &attr);
+ if (opt.x11_windowid == 0) {
+ ret->win =
+ XCreateWindow(disp, DefaultRootWindow(disp), x, y, w, h, 0,
+ depth, InputOutput, vis,
+ CWOverrideRedirect | CWSaveUnder | CWBackingStore
+ | CWColormap | CWBackPixel | CWBorderPixel | CWEventMask,
+ &attr);
+ } else {
+ /* x11_windowid is a pointer to the window */
+ ret->win = (Window) opt.x11_windowid;
+
+ /* set our attributes on the window */
+ XChangeWindowAttributes(disp, ret->win,
+ CWOverrideRedirect | CWSaveUnder | CWBackingStore
+ | CWColormap | CWBackPixel | CWBorderPixel
+ | CWEventMask, &attr);
+
+ /* determine the size and visibility of the window */
+ XGetWindowAttributes(disp, ret->win, &window_attr);
+ ret->visible = (window_attr.map_state == IsViewable);
+ ret->x = window_attr.x;
+ ret->y = window_attr.y;
+ ret->w = window_attr.width;
+ ret->h = window_attr.height;
+ }
if (mwmhints.flags) {
XChangeProperty(disp, ret->win, prop, prop, 32,
@@ -307,7 +337,7 @@ void winwidget_create_window(winwidget ret, int w, int h)
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);
@@ -332,11 +362,12 @@ void winwidget_create_window(winwidget ret, int w, int h)
winwidget_register(ret);
/* do not scale down a thumbnail list window, only those created from it */
- if (opt.scale_down && (ret->type != WIN_TYPE_THUMBNAIL)) {
+ if (opt.geom_enabled && (ret->type != WIN_TYPE_THUMBNAIL)) {
opt.geom_w = w;
opt.geom_h = h;
opt.geom_flags |= WidthValue | HeightValue;
}
+
return;
}
@@ -391,17 +422,18 @@ void winwidget_setup_pixmaps(winwidget winwid)
if (winwid->gc == None) {
XGCValues gcval;
- if (opt.image_bg == IMAGE_BG_WHITE) {
- gcval.foreground = WhitePixel(disp, DefaultScreen(disp));
+ 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 (opt.image_bg == IMAGE_BG_CHECKS) {
+ } 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 {
- gcval.foreground = BlackPixel(disp, DefaultScreen(disp));
+ } 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);
}
}
@@ -430,140 +462,70 @@ 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;
- int need_center = winwid->had_resize;
if (!winwid->full_screen && resize) {
- winwidget_resize(winwid, winwid->im_w, winwid->im_h, 0);
+ if (opt.default_zoom) {
+ winwid->zoom = 0.01 * opt.default_zoom;
+ winwidget_resize(winwid, winwid->im_w * winwid->zoom, winwid->im_h * winwid->zoom, 0);
+ } else {
+ winwidget_resize(winwid, winwid->im_w, winwid->im_h, 0);
+ }
winwidget_reset_image(winwid);
}
- /* bounds checks for panning */
- if (winwid->im_x > winwid->w)
- winwid->im_x = winwid->w;
- if (winwid->im_y > winwid->h)
- winwid->im_y = winwid->h;
-
D(("winwidget_render_image resize %d force_alias %d im %dx%d\n",
resize, force_alias, winwid->im_w, winwid->im_h));
- winwidget_setup_pixmaps(winwid);
-
- if (!winwid->full_screen && ((gib_imlib_image_has_alpha(winwid->im))
- || (opt.geom_flags & (WidthValue | HeightValue))
- || (winwid->im_x || winwid->im_y) || (winwid->zoom != 1.0)
- || (winwid->w > winwid->im_w || winwid->h > winwid->im_h)
- || (winwid->has_rotated)))
- feh_draw_checks(winwid);
+ /* winwidget_setup_pixmaps(winwid) resets the winwid->had_resize flag */
+ int had_resize = winwid->had_resize || resize;
- if (!winwid->full_screen && opt.zoom_mode
- && (winwid->zoom == 1.0) && ! (opt.geom_flags & (WidthValue | HeightValue))
- && (winwid->w > winwid->im_w) && (winwid->h > winwid->im_h))
- feh_calc_needed_zoom(&(winwid->zoom), winwid->im_w, winwid->im_h, winwid->w, winwid->h);
+ winwidget_setup_pixmaps(winwid);
- /*
- * In case of a resize, the geomflags (and im_w, im_h) get updated by
- * the ConfigureNotify handler.
- */
- if (need_center && !winwid->full_screen
- && (opt.geom_flags & (WidthValue | HeightValue))
- && ((winwid->w < winwid->im_w) || (winwid->h < winwid->im_h)))
- feh_calc_needed_zoom(&(winwid->zoom), winwid->im_w, winwid->im_h, 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);
+ winwid->zoom = opt.default_zoom ? (0.01 * opt.default_zoom) : 1.0;
- if (resize && (winwid->full_screen
- || (opt.geom_flags & (WidthValue | HeightValue)))) {
- int smaller; /* Is the image smaller than screen? */
- int max_w = 0, max_h = 0;
+ if ((opt.scale_down || (winwid->full_screen && !opt.default_zoom))
+ && winwid->zoom > required_zoom)
+ winwid->zoom = required_zoom;
+ else if ((opt.zoom_mode && required_zoom > 1)
+ && (!opt.default_zoom || required_zoom < winwid->zoom))
+ winwid->zoom = required_zoom;
- if (winwid->full_screen) {
- max_w = scr->width;
- max_h = scr->height;
-#ifdef HAVE_LIBXINERAMA
- if (opt.xinerama && xinerama_screens) {
- max_w = xinerama_screens[xinerama_screen].width;
- max_h = xinerama_screens[xinerama_screen].height;
+ if (opt.offset_flags & XValue) {
+ if (opt.offset_flags & XNegative) {
+ winwid->im_x = winwid->w - (winwid->im_w * winwid->zoom) - opt.offset_x;
+ } else {
+ winwid->im_x = - opt.offset_x * winwid->zoom;
}
-#endif /* HAVE_LIBXINERAMA */
} else {
- if (opt.geom_flags & WidthValue) {
- max_w = opt.geom_w;
- }
- if (opt.geom_flags & HeightValue) {
- max_h = opt.geom_h;
- }
+ winwid->im_x = (int) (winwid->w - (winwid->im_w * winwid->zoom)) >> 1;
}
-
- D(("Calculating for fullscreen/fixed geom render\n"));
- smaller = ((winwid->im_w < max_w)
- && (winwid->im_h < max_h));
-
- if (!smaller || opt.zoom_mode) {
- 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 (opt.default_zoom != 100) {
- if ((winwid->im_h * winwid->zoom) > max_h)
- winwid->zoom = old_zoom;
- else 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;
}
}
- else if (need_center && !winwid->full_screen
- && (winwid->type != WIN_TYPE_THUMBNAIL) && !opt.keep_zoom_vp) {
- winwid->im_x = (int) (winwid->w - (winwid->im_w * 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;
@@ -587,10 +549,18 @@ void winwidget_render_image(winwidget winwid, int resize, int force_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)
@@ -602,7 +572,7 @@ void winwidget_render_image(winwidget winwid, int resize, int force_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) && !force_alias && !winwid->force_aliasing)
+ 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));
@@ -634,16 +604,12 @@ void winwidget_render_image(winwidget winwid, int resize, int force_alias)
feh_draw_info(winwid);
if (winwid->errstr)
feh_draw_errstr(winwid);
- if (opt.title && (winwid->type != WIN_TYPE_THUMBNAIL_VIEWER) &&
- (winwid->file != NULL)) {
- char *s = slideshow_create_name(FEH_FILE(winwid->file->data), winwid);
- winwidget_rename(winwid, s);
- free(s);
- } else if (opt.thumb_title && (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER) &&
- (winwid->file != NULL)) {
- char *s = thumbnail_create_name(FEH_FILE(winwid->file->data), winwid);
- winwidget_rename(winwid, s);
- free(s);
+ 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);
@@ -702,14 +668,15 @@ Pixmap feh_create_checks(void)
if (!checks)
eprintf("Unable to create a teeny weeny imlib image. I detect problems");
- if (opt.image_bg == IMAGE_BG_WHITE)
- gib_imlib_image_fill_rectangle(checks, 0, 0, 16, 16, 255, 255, 255, 255);
- else if (opt.image_bg == IMAGE_BG_BLACK)
- gib_imlib_image_fill_rectangle(checks, 0, 0, 16, 16, 0, 0, 0, 255);
- else {
+ 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);
@@ -719,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;
@@ -755,19 +715,93 @@ 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->gc)
XFreeGC(disp, winwid->gc);
- if ((winwid->type == WIN_TYPE_THUMBNAIL_VIEWER) && (winwid->file != NULL))
- gib_list_free(winwid->file);
if (winwid->im)
gib_imlib_free_image_and_decache(winwid->im);
free(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;
@@ -801,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)
@@ -816,6 +859,8 @@ 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
@@ -834,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);
@@ -887,30 +930,38 @@ void winwidget_resize(winwidget winwid, int w, int h, int force_resize)
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 ((opt.geom_flags & (WidthValue | HeightValue)) && !force_resize) {
+ winwid->had_resize = 1;
+ return;
+ }
if (winwid && ((winwid->w != w) || (winwid->h != h))) {
- /* 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;
}
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);
+ 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);
+ winwidget_get_geometry(winwid, NULL);
+
if (force_resize && (opt.geom_flags & (WidthValue | HeightValue))
&& (winwid->type != WIN_TYPE_THUMBNAIL)) {
opt.geom_w = winwid->w;
@@ -1007,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;
@@ -1032,10 +1084,12 @@ void feh_debug_print_winwid(winwidget w)
void winwidget_reset_image(winwidget winwid)
{
- winwid->zoom = 1.0;
- winwid->old_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;
@@ -1148,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);
diff --git a/src/winwidget.h b/src/winwidget.h
index 6a794e7..0894b5a 100644
--- a/src/winwidget.h
+++ b/src/winwidget.h
@@ -1,7 +1,7 @@
/* winwidget.h
Copyright (C) 1999-2003 Tom Gilbert.
-Copyright (C) 2010-2011 Daniel Friesel.
+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
@@ -116,8 +116,17 @@ struct __winwidget {
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);
@@ -141,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);