summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/c.yml38
-rw-r--r--.mailmap4
-rw-r--r--AUTHORS2
-rw-r--r--COPYING2
-rw-r--r--ChangeLog627
-rw-r--r--Makefile32
-rw-r--r--README.md178
-rw-r--r--cam/ChangeLog14
-rw-r--r--cam/README45
-rw-r--r--cam/cam_bookmarks2
-rwxr-xr-xcam/feh-cam195
-rwxr-xr-xcam/gen-cam-menu44
-rw-r--r--config.mk43
-rw-r--r--examples/buttons2
-rwxr-xr-xexamples/find-lowres17
-rw-r--r--examples/keys2
-rw-r--r--examples/themes5
-rw-r--r--man/Makefile2
-rw-r--r--man/feh-cam.pre57
-rw-r--r--man/feh.pre1239
-rw-r--r--man/gen-cam-menu.pre33
-rwxr-xr-xscripts/checkkeys.pl2
-rwxr-xr-xscripts/checkopts.pl2
-rw-r--r--share/applications/feh.pre6
-rw-r--r--share/images/feh.svg234
-rw-r--r--share/images/menubg_aluminium.pngbin1862 -> 0 bytes
-rw-r--r--share/images/menubg_aqua.pngbin5325 -> 0 bytes
-rw-r--r--share/images/menubg_black.pngbin1716 -> 0 bytes
-rw-r--r--share/images/menubg_brushed.pngbin8649 -> 0 bytes
-rw-r--r--share/images/menubg_sky.pngbin1928 -> 0 bytes
-rw-r--r--src/Makefile41
-rw-r--r--src/collage.c216
-rw-r--r--src/debug.h2
-rw-r--r--src/events.c182
-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.h43
-rw-r--r--src/feh_png.c23
-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.c707
-rw-r--r--src/index.c30
-rw-r--r--src/index.h2
-rw-r--r--src/keyevents.c565
-rw-r--r--src/list.c6
-rw-r--r--src/main.c139
-rw-r--r--src/menu.c41
-rw-r--r--src/menu.h2
-rw-r--r--src/multiwindow.c20
-rw-r--r--src/options.c578
-rw-r--r--src/options.h286
-rw-r--r--src/signals.c37
-rw-r--r--src/signals.h7
-rw-r--r--src/slideshow.c424
-rw-r--r--src/structs.h2
-rw-r--r--src/strverscmp.c57
-rw-r--r--src/thumbnail.c108
-rw-r--r--src/thumbnail.h6
-rw-r--r--src/timers.c35
-rw-r--r--src/timers.h2
-rw-r--r--src/utils.c28
-rw-r--r--src/utils.h1
-rw-r--r--src/wallpaper.c298
-rw-r--r--src/wallpaper.h5
-rw-r--r--src/winwidget.c445
-rw-r--r--src/winwidget.h16
-rwxr-xr-xtest/feh-i.t12
-rw-r--r--test/feh.t36
-rw-r--r--test/list/custom8
-rw-r--r--test/list_imlib2_1.6/custom4
-rw-r--r--test/list_imlib2_1.6/default5
l---------test/list_imlib2_1.6/filename1
-rw-r--r--test/list_imlib2_1.6/filename_recursive7
l---------test/list_imlib2_1.6/format1
-rw-r--r--test/list_imlib2_1.6/format_reverse5
l---------test/list_imlib2_1.6/height1
l---------test/list_imlib2_1.6/name1
l---------test/list_imlib2_1.6/pixels1
-rw-r--r--test/list_imlib2_1.6/size5
l---------test/list_imlib2_1.6/width1
-rwxr-xr-xtest/mandoc.t19
-rw-r--r--test/nx_action/loadable_action8
-rw-r--r--test/nx_action/loadable_naction8
-rw-r--r--test/nx_action/unloadable_action8
-rw-r--r--test/nx_action/unloadable_naction8
-rw-r--r--test/status4
-rw-r--r--test/tiny.pbm4
102 files changed, 5435 insertions, 4954 deletions
diff --git a/.github/workflows/c.yml b/.github/workflows/c.yml
new file mode 100644
index 0000000..5477164
--- /dev/null
+++ b/.github/workflows/c.yml
@@ -0,0 +1,38 @@
+name: linux
+
+on:
+ push:
+ branches:
+ - '*'
+ pull_request:
+ branches:
+ - '*'
+
+jobs:
+ c:
+
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ curl: [0, 1]
+ exif: [0, 1]
+ xinerama: [0, 1]
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: APT
+ run: sudo apt-get -y update
+ - name: Install Dependencies
+ run: sudo apt-get -y install build-essential libx11-dev libxt-dev libimlib2-dev libtest-command-perl libtest-simple-perl
+ - name: Install libcurl
+ if: matrix.curl
+ run: sudo apt-get -y install libcurl4-openssl-dev
+ - name: Install libexif
+ if: matrix.exif
+ run: sudo apt-get -y install libexif-dev
+ - name: Install Xinerama
+ if: matrix.xinerama
+ run: sudo apt-get -y install libxinerama-dev
+ - name: Build and Test
+ run: for inotify in 0 1; do for verscmp in 0 1; do make curl=${{ matrix.curl }} exif=${{ matrix.exif }} inotify=$inotify verscmp=$verscmp xinerama=${{ matrix.xinerama }} && make test && make clean; done; done
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000..888b47c
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,4 @@
+Birte Kristina Friesel <derf@finalrewind.org>
+Birte Kristina Friesel <derf@chaosdorf.de>
+Birte Kristina Friesel <ghub@derf.homelinux.org>
+Birte Kristina Friesel <derf@derf.homelinux.org>
diff --git a/AUTHORS b/AUTHORS
index ede7d47..ad85f15 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -50,7 +50,7 @@ Yu-Jie Lin
Dennis Real
- patch for builtin exif support (exif=1)
-Daniel Friesel - derf (at) finalrewind (dot) org
+Birte Kristina Friesel - derf (at) finalrewind (dot) org
- Maintenance since 2010
Thanks to:
diff --git a/COPYING b/COPYING
index a71c9bd..9fc8e67 100644
--- a/COPYING
+++ b/COPYING
@@ -1,5 +1,5 @@
Copyright (C) 1999,2000 Tom Gilbert.
-Copyright (C) 2010-2016 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
diff --git a/ChangeLog b/ChangeLog
index 47d6f4e..d984cdf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,11 +1,473 @@
-Tue, 25 Jul 2017 18:40:33 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Fri, 29 Aug 2025 19:14:26 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.11.1
+ * Correctly center images in fullscreen mode. This fixes a regression
+ introduced in v3.11.
+
+Wed, 27 Aug 2025 21:39:43 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.11
+ * New option: --tap-zones enables support for simple prev/next tap zones
+ (patch by Finn Teegen, thanks!)
+ * Fix --scale-down not scaling down the first image in floating i3 windows
+ and similar setups (patch by Awal Garg, thanks!)
+
+Sun, 30 Jun 2024 11:47:16 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.10.3
+ * Fix feh not respecting aspect ratio of thumbnails that are smaller than
+ --thumb-width and --thumb-height
+ * Fix --no-recursive behaving like --recursive (Patch by GitHub user
+ wwsmiff, thanks!)
+ * Fix rotation by 180° corrupting images (Patch by GitHub user wwsmiff,
+ thanks!)
+ * Speed up --sort=size and --sort=mtime by caching stat(2) calls
+ (Patch by Naïm Favier, thanks!)
+
+Mon, 04 Dec 2023 21:25:49 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.10.2
+ * Fix crash in right-click / menu rendering code on some distributions
+ (patch by Ametov Imil)
+
+Mon, 02 Oct 2023 04:27:56 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.10.1
+ * Do not call signal-unsafe functions within signal handlers
+
+Thu, 06 Apr 2023 16:19:16 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.10
+ * Add --sort=none option to un-set sort modes specified earlier
+ * Improve error messages for Imlib2 ≥ 1.8
+ * Fix build with clang 16. (Patch by orbea)
+ * Fix tests when building with magic=1
+
+Mon, 22 Aug 2022 17:49:11 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.9.1
+ * Set libcurl user agent to "feh/3.9.1". Previously, feh did not send a
+ user agent.
+
+Sun, 12 Jun 2022 13:12:00 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.9
+ * Fix compilation on macOS by defining _DARWIN_C_SOURCE for
+ mkdtemp and mkstemps. (Patch by Ryan Schmidt)
+ * Remove magic byte check before passing an image to Imlib2.
+ This check was introduced in feh 3.6 to work around an issue with
+ Imlib2 being slow to determine image loadability in some cases
+ <https://phab.enlightenment.org/T8739>. By now, an Imlib2 version without
+ this issue should be available in all recent distributions, so the check
+ in feh is no longer required. For a slight speed-up, or to use feh with
+ an affected Imlib2 version (1.6.x or 1.7.0), compile feh with 'magic=1'.
+ In this case, feh will use libmagic to determine whether a file is an
+ image, and only pass images to Imlib2. (Patch by Christian Hesse)
+ * Fix crash (segmentation fault due to null pointer dereference) when
+ toggling fullscreen mode while moving the cursor.
+ * Note that feh no longer supplies its own getopt_long function.
+ getopt_long is provided by a wide range of libc implementations, so
+ there is no need for feh to duplicate it. (Patch by Guilherme Janczak)
+
+Mon, 03 Jan 2022 11:29:03 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.8
+ * Add button ID "0" to cursor bindings. This pseudo-button is triggered
+ whenever feh observes a cursor movement. It does not have a default
+ binding.
+ * Support hexadecimal IDs in --window-id
+ * Disable --auto-rotate in feh builds compiled with Imlib2 1.7.5 or later.
+ Imlib2 1.7.5 introduces transparent EXIF-based image orientation
+ adjustment, so --auto-rotate is no longer needed (and would cause
+ mis-orientation of images, as Imlib2 has already adjusted the
+ orientation).
+
+Sat, 25 Sep 2021 09:21:25 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.7.2
+ * Fix crash when running feh without stdin file descriptor
+
+Sat, 24 Jul 2021 22:30:12 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.7.1
+ * Support JPEG XL files when using imlib2-jxl (patch by Alistair)
+ * Fix support for images smaller than 16 bytes (patch by David Buchanan)
+ * Fix some out of bounds reads (patches by Tobias Stoeckmann)
+
+Sun, 09 May 2021 11:31:55 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.7
+ * Use compact representation instead of key-value pairs for common EXIF data
+ (only applies when compiling feh with exif=1)
+ * Add --window-id <windowid> option (draw to an existing window)
+ * Add --zoom-step <percent> option (specify zoom step size)
+ * Pass gopher:// and gophers:// URLs to libcurl
+ * Fix --reload / --auto-reload reloading the wrong directory when
+ using --start-at and no file arguments or filelists have been
+ specified
+ * Fix Ctrl+key causing unintended behaviour when controlling feh via stdin
+ * Fix high CPU usage when closing stdin after starting feh from a terminal
+
+Mon, 25 Jan 2021 17:46:57 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.6.3
+ * Fix --start-at not handling URL-encoded file:/// paths properly. Notably,
+ this also fixes feh not displaying images with spaces or unicode
+ elements in their path when opened from a file manager.
+
+Sat, 09 Jan 2021 12:28:06 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.6.2
+ * Fix save_filelist not respecting --output-dir
+ * Fix file descriptor leak when attempting to load truncated image files.
+ The issue was introduced in v3.6.
+
+Sun, 06 Dec 2020 08:34:15 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.6.1
+ * Fix excessive memory consumption when showing long-running slideshows
+ with thousands to tens of thousands of images and feh has been compiled
+ with exif=1 (see https://github.com/derf/feh/issues/553)
+ * Fix memory leak when showing long-running slideshows with relatively few
+ images and feh has been compiled with exif=1 (ibid.)
+ * Fix memory leak when reloading an image and feh has been compiled with
+ exif=1
+ * Fix memory leak in --draw-exif
+ * Fix memory leak when reloading HTTP files with --no-conversion-cache
+
+Mon, 30 Nov 2020 19:44:47 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.6
+ * Add flip and rotate options to the menu
+ * Improve unloadable image detection time (e.g. for large video files) by
+ checking a file's header before passing it to Imlib2. For rarely used
+ image formats, there is a very small chance that an image which could be
+ loaded by feh 3.5 is reported as unloadable by feh 3.6 due to this
+ change. Set FEH_SKIP_MAGIC=1 to bypass the header check in this case. See
+ <https://phab.enlightenment.org/T8739> and
+ <https://github.com/derf/feh/issues/505> for details.
+
+
+Sat, 29 Aug 2020 08:49:08 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.5
+ * Enable --version-sort on systems without strverscmp support. This
+ works by bundling the strverscmp of musl libc with feh and using it
+ when feh is compiled without the verscmp flag (i.e., when the system
+ libc does not provide the verscmp function). Patch by Tim van der Molen
+ * Add %a format specifier (slideshow state: "playing" / "paused")
+ * Fix crashes when combining --reload and --multiwindow
+
+Fri, 29 May 2020 23:52:35 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.4.1
+ * Fix lossless rotate not being lossless when using feh with Imlib2 v1.6
+ or later
+
+Sat, 11 Apr 2020 09:51:01 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.4
+ * Images loaded via HTTPS/curl, ImageMagick, and dcraw are now cached
+ by default to decrease image load time on subsequent slideshow passes.
+ Caching is disabled when using `--reload` and can also be disabled with
+ the new `--no-conversion-cache` option. Suggestion and initial patch by
+ Awal Garg.
+ * Handle SIGINT/SIGTERM/SIGQUIT signals while loading images using libcurl
+ < v7.32. Patch by <https://github.com/c99pedant>.
+ * "feh --start-at URL" now loads a single-image slideshow displaying URL.
+ This allows feh.desktop to handle URLs as well as ordinary files.
+ file:/// URLs are treated as local files, so "feh --start-at file:///..."
+ without filelist arguments behaves just like "feh --start-at ..."
+ (i.e., feh will load the entire directory and start the slideshow at ...)
+ * Fix a memory leak when repeatedly cycling through slideshows containing
+ images loaded via libcurl, ImageMagick or dcraw.
+
+Tue, 03 Dec 2019 17:27:46 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.3
+ * New option --class allows setting the X11 class hint per feh instance
+ (patch by Olof-Joachim Frahm)
+ * Improve handling of NULL returns from Imlib2 calls (patch by Ben Boeckel)
+ * Fix compilation with libcurl < v7.32
+
+Mon, 22 Jul 2019 20:17:03 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.2.1
+ * Use --no-fehbg option in ~/.fehbg. This fixes cases where an X11
+ setup change may inadvertently alter the commandline stored in .fehbg.
+ * Fix insufficient error handling when updating ~/.fehbg. Previously,
+ a stat() error may have caused .fehbg to be update with excessive file
+ permissions (patch by Tobias Stoeckmann)
+ * Fix TOCTTOU when setting the file mode of ~/.fehbg
+ (patch by Tobias Stoeckmann)
+
+Wed, 10 Jul 2019 17:40:29 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.2
+ * Build feh with inotify=1 to automatically reload changed files in
+ slideshow mode. Introduces the inotify build flag and the --auto-reload
+ option.
+ * Reload current image(s) when receiving SIGUSR1 or SIGUSR2 in single-image
+ slideshows or in multiwindow mode
+
+Sun, 17 Feb 2019 08:41:53 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Releasev v3.1.3
+ * Fix missing filename in ~/.fehbg when using --no-xinerama on a feh
+ binary compiled with xinerama=1. This issue was introduced in v3.1.2.
+
+Mon, 11 Feb 2019 17:24:13 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.1.2
+ * Fix missing filenames in ~/.fehbg when using --bg-* on directories and/or
+ with --randomize
+ * Fix repeated --slideshow-delay/-D option not properly overriding the
+ 'start paused' flag
+ * Fix repeated --info option not properly overriding the 'draw info' flag
+
+Fri, 07 Dec 2018 22:51:15 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.1.1
+ * Decrease libcurl timeout from indefinite to 30 minutes. This should
+ be sufficient even for use cases with unusually high timeouts (just in
+ case anyone wants to do interplanetary slideshows), while at the same
+ time avoiding stalls in unattended slideshows when encountering
+ temporary network issues.
+ * Handle SIGINT/SIGTERM/SIGQUIT signals while loading images using libcurl.
+ Previously, signals were ignored during a (possibly slow) libcurl
+ network transfer, which was not intended behaviour.
+
+Wed, 21 Nov 2018 19:37:34 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.1
+ * Running "feh --start-at .../file.jpg" without specifying images,
+ directories or filelists to load is now equivalent to running
+ "feh --start-at .../file.jpg $(dirname .../file.jpg)". This way, it is
+ possible to view a specific file and browse all other files in the
+ corresponding directory. This is especially useful when starting feh
+ from file managers.
+ * Introduce fuzzy matching in --start-at: If the specified path is not
+ found in the file list, feh now resorts to comparing basenames (i.e.,
+ file names without the directory components). This allows calls
+ like "feh --start-at cat.jpg ~/Pictures", which led to a file not found
+ error in previous versions.
+ * Respect -j / --output-dir when using save_image or save_filelist actions.
+
+Fri, 09 Nov 2018 17:17:15 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v3.0
+ * Remove deprecated webcam scripts (feh-cam and gen-cam-menu)
+ * Remove deprecated collage mode (-c/--collage)
+ * Remove deprecated option --cycle-once (use --on-last-slide=quit instead)
+ * Remove deprecated option --menu-bg
+ * Change default save_filelist key from "f" to "L" (mnemonic: fileList)
+ * Change default toggle_fullscreen key from "v" to "f" as this is also
+ used by mplayer, mpv and similar
+ * flip and rotation (keys "<", ">", "|", and "_") no longer change the
+ underlying file. This leaves delete ("Ctrl+Delete") as the only
+ destructive action which is enabled by default
+ * Add option --edit, which makes flip and rotation change the underlying
+ file as well as the displayed image. This was the default behaviour in
+ feh 1.x and 2.x
+
+Sat, 27 Oct 2018 19:46:48 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.28.1
+ * Do not ignore quit signals (SIGTERM, SIGINT, SIGQUIT) during preload
+ * Add missing EXIF orientations 2, 4, 5, and 7 (when built with exif=1,
+ patch by Olof-Joachim Frahm)
+ * Improve randomness on non-glibc systems
+
+Mon, 17 Sep 2018 21:17:04 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.28
+ * Decrease loading time for RAW images by utilizing dcraw to display the
+ embedded JPEG preview (patch by <https://github.com/ulteq>)
+ * Rename --magick-timeout to --conversion-timeout. --magick-timeout is
+ now deprecated and will be removed in a future release
+ * Fix unintened aliasing in rotated images whose rotation is not a
+ multiple of 90 degrees (patch by <https://github.com/ulteq>)
+ * New option: --on-last-slide=hold|quit|resume.
+ hold will cause feh to stop advancing beyond the last slide (patch by
+ <https://github.com/ulteq>), quit replaces --cycle-once (which is now
+ deprecated), and resume is the default (continue at the first image).
+
+Tue, 17 Jul 2018 17:33:10 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.27.1
+ * Fix feh occasionally becoming unresponsive when asked to terminate
+ via SIGINT/SIGQUIT/SIGTERM (based on a patch by
+ <https://github.com/giladogit>)
+ * Fix --keep-zoom-vp issues introduced in 2.27
+ (patch by <https://github.com/ulteq>)
+
+Thu, 28 Jun 2018 17:26:54 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.27
+ * Fix size_to_image ("w") command when both --scale-down and --keep-zoom-vp
+ are enabled
+ * Fix --auto-zoom not being triggered on window resize events when
+ --scale-down is enabled
+ * Fix --auto-zoom conflicting with manual zoom
+ * Fix feh_draw_checks not taking the zoom level into account properly
+ * Prevent --zoom <percent> from blocking --scale-down in fullscreen / fixed
+ geometry mode
+ * Prevent --keep-zoom-vp from blocking the dynamic window resizing
+ mechanism
+ * Prevent automatic recalculation of the zoom ratio when --keep_zoom_vp
+ is enabled
+ * All patches provided by <https://github.com/ulteq>. Thanks a lot!
+
+Tue, 26 Jun 2018 10:33:04 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.26.4
+ * Correctly save --bg-max in ~/.fehbg (patch by Sebastian Bickerle)
+
+Fri, 18 May 2018 22:58:02 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.26.3
+ * Properly escape --image-bg argument in ~/.fehbg (broken in 2.26.1)
+
+Sat, 12 May 2018 16:33:56 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.26.2
+ * Show correct filelist position in windows opened from thumbnail mode.
+ Note that navigation is still not supported in those windows
+ * Improve support for key input from stdin
+ * Do not push menus off the screen when hitting screen limits
+
+Fri, 11 May 2018 15:11:17 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.26.1
+ * Restore pre-2.21 ~/.fehbg behaviour. This fixes nondeterministic
+ wallpaper setting when using --bg-* --randomize, issues when specifying
+ --theme both in ~/.fehbg and on the commandline, and possibly other
+ edge cases
+ * Fix /tmp being cluttered with temporary ImageMagick files when using
+ --magick-timeout and a conversion takes longer than allowed
+
+Thu, 19 Apr 2018 21:43:12 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.26
+ * Save absolute file paths in ~/.fehbg, similar to the behaviour prior to
+ feh 2.21
+ * Add %g (window dimensions) and %Z (precise zoom level) format specifiers
+ * Improve -z/--randomize randomness
+
+Wed, 07 Mar 2018 17:49:52 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.25.1
+ * Fix compilation issues when using CFLAGS=-m64 on some gcc versions
+ * Re-render current image when toggle_fixed_geometry is input
+
+Sun, 04 Mar 2018 08:53:50 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.25
+ * Add --version-sort option to enable natural sorting of file and directory
+ names. This requires a libc with strverscmp support, which is a
+ non-POSIX GNU extension. Use the new build flag `verscmp=0` to disable
+ this feature on systems which do not ship strverscmp
+ (patch by ulteq)
+ * Allow arbitrary X11 colors as -B/--image-bg argument (patch by ulteq)
+ * Improve --image-bg support and transparency handling in --bg-* mode
+ * Respect --geometry settings in --bg-fill mode
+ * Add keybinding toggle_auto_zoom (default "Z") to toggle --auto-zoom
+ * Fix filelists specified by -f/--filelist not being reloaded when using
+ --reload
+
+Mon, 26 Feb 2018 21:41:38 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.24
+ * Improve performance when using --{max,min}-dimension in slideshow mode
+ (patch by ulteq)
+ * Fix crash when using %m format specifier in slideshow mode
+ (introduced in feh 2.23.1)
+
+
+Mon, 12 Feb 2018 22:11:55 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.23.2
+ * Fix support for nested quotes in .confeg/feh/themes
+
+Wed, 31 Jan 2018 17:38:25 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.23.1
+ * The Makefile no longer honors CPPFLAGS and instead consistently uses
+ CFLAGS for user-provided include paths
+ * Fix %u format specifier in multiwindow and list modes (patch by ulteq)
+ * Minor performance improvements (patches by ulteq)
+ * Stability improvements when using --magick-timeout (patch by ulteq)
+
+Thu, 28 Dec 2017 19:26:29 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.23
+ * Fix broken thumbnail/index windows when using --scale-down
+ * Use Imlib2 in-memory image cache (default cache size: 4MiB). This allows
+ for significant performance improvements especially in small slideshows
+ * Add --cache-size option to set Imlib2 image cache size
+
+Tue, 07 Nov 2017 17:36:26 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.22.2
+ * Fix HTTPS certificate errors on some systems (broken in 2.22)
+
+Tue, 07 Nov 2017 07:51:48 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.22.1
+ * Allow ~/.fehbg to be sourced (instead of executed) from other shell
+ scripts again (broken in 2.22)
+
+Sat, 04 Nov 2017 14:55:38 +0100 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.22
+ * Add support for CURL_CA_BUNDLE environment variable when loading images
+ via HTTPS
+ * Fix ~/.fehbg not being updated when setting a wallpaper via menu
+ (broken in 2.21)
+
+Sat, 07 Oct 2017 12:14:17 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.21
+ * Add toggle_fixed_geometry ('g') key binding to toggle window auto-resize
+ * Improve control via terminal input
+ * Fix crash (segmentation fault) when using feh -O in non-index mode
+ * Fix --force-aliasing (and possibly other options) missing from ~/.fehbg
+ when using them for background setting
+
+Thu, 07 Sep 2017 20:20:11 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.20
+ * Fix clang/gcc warnings (Patches by orbea)
+ * Add support for control via terminal input. feh will read key presses
+ from the controlling terminal and handle them like X11 key presses
+ inside the feh window. Note that at the moment, only lower / upper case
+ ASCII letters and a very small set of additional keys are supported.
+ * Fix broken ImageMagick support (see --magick-timeout) when using some
+ ImageMagick versions <https://github.com/derf/feh/issues/323>
+ * Remove images from the filelist if they were removed by executing a
+ user-defined action <https://github.com/derf/feh/issues/322>
+
+Mon, 21 Aug 2017 19:04:00 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.19.3
+ * Save geometry data in .fehbg when setting a wallpaper with --geometry
+ * Fix Imlib2 developer warning and improve out-of-memory error message
+ when using --thumbnails / --index on large directories
+
+Sat, 12 Aug 2017 05:05:24 +0200 Birte Friesel <derf+feh@finalrewind.org>
+
+* Release v2.19.2
+ * Show ImageMagick loader errors unless --quiet is specified
+ * Fix crash when handling certain media keys (introduced in 2.19.1)
+
+Tue, 25 Jul 2017 18:40:33 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.19.1
* Fix Shift modifier not being recognized for tab, space and similar keys.
This lead to keybindings like Shift+Tab or Shift+Space being parsed as if
the Shift modifier had not been specified
-Tue, 06 Jun 2017 20:40:00 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Tue, 06 Jun 2017 20:40:00 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.19
* Follow the freedesktop.org Thumbnail Managing Standard by saving
@@ -15,7 +477,7 @@ Tue, 06 Jun 2017 20:40:00 +0200 Daniel Friesel <derf+feh@finalrewind.org>
* Install app icons with the correct permissions of 644
* Documentation improvements
-Tue, 04 Apr 2017 21:22:16 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Tue, 04 Apr 2017 21:22:16 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.18.3
* Fix double-free/OOB-write in E17 IPC. This only affects the
@@ -32,19 +494,19 @@ Tue, 04 Apr 2017 21:22:16 +0200 Daniel Friesel <derf+feh@finalrewind.org>
* Fix memory leak when saving a filelist or image whose target filename
already exists. (patch by Tobias Stoeckmann)
-Thu, 16 Feb 2017 23:05:39 +0100 Daniel Friesel <derf+feh@finalrewind.org>
+Thu, 16 Feb 2017 23:05:39 +0100 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.18.2
* Fix crash when using both --thumbnails and --title. This bug was
introduced in v2.18.1.
-Sun, 22 Jan 2017 19:11:32 +0100 Daniel Friesel <derf+feh@finalrewind.org>
+Sun, 22 Jan 2017 19:11:32 +0100 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.18.1
* Fix image-specific format specifiers not being updated correctly
(e.g. %z not displaying the correct zoom value after zooming in / out)
-Tue, 01 Nov 2016 10:55:04 +0100 Daniel Friesel <derf+feh@finalrewind.org>
+Tue, 01 Nov 2016 10:55:04 +0100 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.18
* Move README to README.md
@@ -53,13 +515,13 @@ Tue, 01 Nov 2016 10:55:04 +0100 Daniel Friesel <derf+feh@finalrewind.org>
* Only for builds with exif=1: Disable EXIF-based auto rotation by
default, add --auto-rotate option to enable it (Patch by Elliot Wolk)
-Wed, 31 Aug 2016 20:27:20 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Wed, 31 Aug 2016 20:27:20 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.17.1
* Fix compilation on systems where HOST_NAME_MAX is not defined, such as
FreeBSD (patch by Niclas Zeising)
-Sun, 28 Aug 2016 21:26:54 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Sun, 28 Aug 2016 21:26:54 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.17
* Install feh icon (both 48x48 and scalable SVG) to /usr/share/icons
@@ -75,20 +537,20 @@ Sun, 28 Aug 2016 21:26:54 +0200 Daniel Friesel <derf+feh@finalrewind.org>
* feh now also sets the X11 _NET_WM_PID and WM_CLIENT_MACHINE window
properties
-Sun, 31 Jul 2016 16:59:07 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Sun, 31 Jul 2016 16:59:07 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.16.2
* Also support in-place editing for images loaded via libcurl or
imagemagick. Results will not be written back to disk in this case.
-Fri, 24 Jun 2016 00:31:56 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Fri, 24 Jun 2016 00:31:56 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.16.1
* Fix crash when trying to rotate a JPEG image without having
jpegtran / jpegexiforient installed
* Handle failing fork() calls gracefully
-Thu, 09 Jun 2016 08:59:35 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Thu, 09 Jun 2016 08:59:35 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.16
* Fix invalid key/button definitions mis-assigning keys/buttons to other
@@ -101,13 +563,13 @@ Thu, 09 Jun 2016 08:59:35 +0200 Daniel Friesel <derf+feh@finalrewind.org>
* Add navigation keys next_dir (]) and prev_dir ([) to jump to the first
image of the nex/previous directory (Patch by Sung Pae)
-Fri, 27 May 2016 13:15:49 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Fri, 27 May 2016 13:15:49 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.15.4
* Fix toggle_filenames key displaying wrong file numbers in multiwindow
mode
-Thu, 28 Apr 2016 11:41:04 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Thu, 28 Apr 2016 11:41:04 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.15.3
* Rescale image when resizing a window and --scale-down or --geometry is
@@ -116,18 +578,18 @@ Thu, 28 Apr 2016 11:41:04 +0200 Daniel Friesel <derf+feh@finalrewind.org>
size which will not be updated when changing images (as was the case in
feh < 2.15). This may or may not be fixed in the future.
-Sat, 16 Apr 2016 18:32:38 +0200 Daniel Frisel <derf+feh@finalrewind.org>
+Sat, 16 Apr 2016 18:32:38 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.15.2
* Fix --keep-zoom-vp not keeping the viewport x/y offsets (broken by 2.15)
-Fri, 15 Apr 2016 10:18:37 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Fri, 15 Apr 2016 10:18:37 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.15.1
* Fix w (size_to_image) key not updating window size when --scale-down
or --geometry is active
-Sat, 09 Apr 2016 20:42:23 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Sat, 09 Apr 2016 20:42:23 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.15
* Patch by William Woodruff: Add --insecure option to disable HTTPS
@@ -146,7 +608,7 @@ Sat, 09 Apr 2016 20:42:23 +0200 Daniel Friesel <derf+feh@finalrewind.org>
start with a space. Titles starting with a space are treated as part of
of the command so that actions like '[ -L %F ] && foo' still work
-Thu, 18 Feb 2016 20:40:19 +0100 Daniel Friesel <derf+feh@finalrewind.org>
+Thu, 18 Feb 2016 20:40:19 +0100 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.14.2
* make test: Ignore results on arm and mips since they expose a bug in
@@ -159,7 +621,7 @@ Thu, 18 Feb 2016 20:40:19 +0100 Daniel Friesel <derf+feh@finalrewind.org>
* -f / --filelist: Fix bug in "-" / "/dev/stdin" handling affecting feh
running in ksh and possibly other environments
-Thu, 04 Feb 2016 20:31:38 +0100 Daniel Friesel <derf+feh@finalrewind.org>
+Thu, 04 Feb 2016 20:31:38 +0100 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.14.1
* Skip a small set of build tests on Debian and derivatives, since they
@@ -168,7 +630,7 @@ Thu, 04 Feb 2016 20:31:38 +0100 Daniel Friesel <derf+feh@finalrewind.org>
<https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=813729> for more
information
-Sun, 04 Oct 2015 10:01:20 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Sun, 04 Oct 2015 10:01:20 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.14
* Add --xinerama-index option for background setting
@@ -185,12 +647,12 @@ Sun, 04 Oct 2015 10:01:20 +0200 Daniel Friesel <derf+feh@finalrewind.org>
active xinerama screen by setting the XINERAMA_SCREEN environment
variable
-Sun, 24 May 2015 11:45:18 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Sun, 24 May 2015 11:45:18 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.13.1
* Fix --scale-down breaking image centering in fullscreen mode
-Sun, 17 May 2015 20:40:36 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Sun, 17 May 2015 20:40:36 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.13
* print --verbose output to stderr
@@ -203,14 +665,14 @@ Sun, 17 May 2015 20:40:36 +0200 Daniel Friesel <derf+feh@finalrewind.org>
* Fix potential out of bounds array access in EXIF code
(when built with exif=1)
-Wed, 08 Apr 2015 11:18:41 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Wed, 08 Apr 2015 11:18:41 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.12.1
* Handle missing HOME in environment
* Fix memory leak when a slideshow contains many unloadable images
* Fix memory leak when --prelaod removes files from the filelist
-Thu, 15 May 2014 23:41:07 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Thu, 15 May 2014 23:41:07 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.12
* feh-cam and gen-cam-menu are no longer installed by default. Use
@@ -228,7 +690,7 @@ Thu, 15 May 2014 23:41:07 +0200 Daniel Friesel <derf+feh@finalrewind.org>
--thumb-title and --title)
* Update help (when built with help=1)
-Sun, 27 Apr 2014 20:28:02 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Sun, 27 Apr 2014 20:28:02 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.11
* Patch by Michael Vorburger: Fix erroneous free() in case of failed
@@ -244,7 +706,7 @@ Sun, 27 Apr 2014 20:28:02 +0200 Daniel Friesel <derf+feh@finalrewind.org>
* Respect --image-bg=checks in fullscreen mode (default remains black)
(closes #156)
-Fri, 28 Feb 2014 18:20:25 +0100 Daniel Friesel <derf+feh@finalrewind.org>
+Fri, 28 Feb 2014 18:20:25 +0100 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.10
* Allow non-centered wallpapers using the --geometry option
@@ -260,7 +722,7 @@ Fri, 28 Feb 2014 18:20:25 +0100 Daniel Friesel <derf+feh@finalrewind.org>
(Patch by Brian Mattern)
* Always use absolute paths in .fehbg
-Tue, 11 Jun 2013 08:27:24 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Tue, 11 Jun 2013 08:27:24 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.9.3
* Patch by David Gowers: Add %L format code (temporary copy of filelist)
@@ -268,20 +730,20 @@ Tue, 11 Jun 2013 08:27:24 +0200 Daniel Friesel <derf+feh@finalrewind.org>
--customlist, --index-info, --info, --thumb-title, --title)
* Fix tests failing when compiled with help=1
-Fri, 03 May 2013 21:16:59 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Fri, 03 May 2013 21:16:59 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.9.2
* Fix -F --zoom 100 not working in Gnome+Unity when compiling feh with
gcc and enabled optimizations (not -O0)
* EXIF updates by Dennis Real
-Thu, 14 Feb 2013 12:52:02 +0100 Daniel Friesel <derf+feh@finalrewind.org>
+Thu, 14 Feb 2013 12:52:02 +0100 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.9.1
* Set correct window dimensions on any Xinerama screen, not just the
first one (active screen is determined by current pointer location)
-Wed, 13 Feb 2013 01:46:56 +0100 Daniel Friesel <derf+feh@finalrewind.org>
+Wed, 13 Feb 2013 01:46:56 +0100 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.9
* Add --keep-zoom-vp option to keep zoom and offsets when switching
@@ -293,7 +755,7 @@ Wed, 13 Feb 2013 01:46:56 +0100 Daniel Friesel <derf+feh@finalrewind.org>
error
* Add button bindings to zoom in / out (patch by sdaau)
-Mon, 24 Dec 2012 15:45:54 +0100 Daniel Friesel <derf+feh@finalrewind.org>
+Mon, 24 Dec 2012 15:45:54 +0100 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.8
* Do not apply --scale-down to the thumbnail window. It will be applied
@@ -314,19 +776,19 @@ Mon, 24 Dec 2012 15:45:54 +0100 Daniel Friesel <derf+feh@finalrewind.org>
* Make 'z' (jump_random) work in thumbnail mode as well, fix thumbnail
selection roll-over <https://github.com/derf/feh/issues/115>
-Tue, 16 Oct 2012 06:29:58 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Tue, 16 Oct 2012 06:29:58 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.7
* Add --min-dim and --max-dim options to only process images with certain
dimensions
-Thu, 27 Sep 2012 16:48:48 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Thu, 27 Sep 2012 16:48:48 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.6.3
* Fix segfault when doing lossless mirror/rotate and jpegexiforient is
not present <https://github.com/derf/feh/issues/100>
-Wed, 26 Sep 2012 17:06:50 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Wed, 26 Sep 2012 17:06:50 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.6.2
* Set EXIF orientation tag to 1 ("0,0 is top left" aka normal) after
@@ -336,7 +798,7 @@ Wed, 26 Sep 2012 17:06:50 +0200 Daniel Friesel <derf+feh@finalrewind.org>
* Fix spelling in feh(1)
* Compile debug builds with -O0
-Thu, 13 Sep 2012 12:00:06 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Thu, 13 Sep 2012 12:00:06 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.6.1
* Fix freedesktop.org Thumbnail Managing Standard implementation:
@@ -348,7 +810,7 @@ Thu, 13 Sep 2012 12:00:06 +0200 Daniel Friesel <derf+feh@finalrewind.org>
* Show error message if load failed and magick is disabled (was broken
by feh 2.4)
-Tue, 28 Aug 2012 11:46:19 +0200 Daniel Friesel <derf+feh@finalrewind.org>
+Tue, 28 Aug 2012 11:46:19 +0200 Birte Friesel <derf+feh@finalrewind.org>
* Release v2.6
* Dennis Real: EXIF mode fixes, support for more camera models
@@ -369,7 +831,7 @@ Tue, 28 Aug 2012 11:46:19 +0200 Daniel Friesel <derf+feh@finalrewind.org>
less confusing. Set --magick-timeout to a non-negative value to enable
it (--magick-timeout 5 for feh-2.5 behaviour)
-Sun, 25 Mar 2012 13:13:26 +0200 Daniel Friesel <derf@finalrewind.org>
+Sun, 25 Mar 2012 13:13:26 +0200 Birte Friesel <derf@finalrewind.org>
* Release v2.5
* Add R, * and / bindings for <keypad begin>, <keypad *> and <keypad />
@@ -380,7 +842,7 @@ Sun, 25 Mar 2012 13:13:26 +0200 Daniel Friesel <derf@finalrewind.org>
* Do not scroll past image borders when using key bindings
* --loadable / --unloadable: indicate result in exit status
-Tue, 06 Mar 2012 13:13:35 +0100 Daniel Friesel <derf@finalrewind.org>
+Tue, 06 Mar 2012 13:13:35 +0100 Birte Friesel <derf@finalrewind.org>
* Release v2.4
* exif-support fixes by Dennis Real
@@ -392,7 +854,7 @@ Tue, 06 Mar 2012 13:13:35 +0100 Daniel Friesel <derf@finalrewind.org>
ignore out of memory errors
* Use ImageMagick (convert) as loader for unsupported file formats
-Thu, 02 Feb 2012 21:04:06 +0100 Daniel Friesel <derf@finalrewind.org>
+Thu, 02 Feb 2012 21:04:06 +0100 Birte Friesel <derf@finalrewind.org>
* Release v2.3
* Add %F and %N format specifiers, containing an escaped version of %f/%n.
@@ -409,7 +871,7 @@ Thu, 02 Feb 2012 21:04:06 +0100 Daniel Friesel <derf@finalrewind.org>
* Accept offset-only arguments for --geometry
<https://github.com/derf/feh/issues/73>
-Mon, 02 Jan 2012 11:54:01 +0100 Daniel Friesel <derf@finalrewind.org>
+Mon, 02 Jan 2012 11:54:01 +0100 Birte Friesel <derf@finalrewind.org>
* Release v2.2
* Add --no-fehbg option to disable ~/.fehbg creation (patch by Felix Crux)
@@ -421,7 +883,7 @@ Mon, 02 Jan 2012 11:54:01 +0100 Daniel Friesel <derf@finalrewind.org>
* Follow HTTP redirects
<http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=653689>
-Wed, 02 Nov 2011 10:56:10 +0100 Daniel Friesel <derf@finalrewind.org>
+Wed, 02 Nov 2011 10:56:10 +0100 Birte Friesel <derf@finalrewind.org>
* Release v2.1
* Experimental --scale-down and --auto-zoom tiling support
@@ -430,7 +892,7 @@ Wed, 02 Nov 2011 10:56:10 +0100 Daniel Friesel <derf@finalrewind.org>
* Fix http image load for long image names
<http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=646421>
-Mon, 10 Oct 2011 12:25:00 +0200 Daniel Friesel <derf@finalrewind.org>
+Mon, 10 Oct 2011 12:25:00 +0200 Birte Friesel <derf@finalrewind.org>
* Release v2.0
* The --bg-options now accept multiple filenames, one per Xinerama screen
@@ -446,7 +908,7 @@ Mon, 10 Oct 2011 12:25:00 +0200 Daniel Friesel <derf@finalrewind.org>
hosts if the local system is 32bit (for 32<->32 and 64<->64, this works
anyways)
-Mon, 26 Sep 2011 09:35:41 +0200 Daniel Friesel <derf@finalrewind.org>
+Mon, 26 Sep 2011 09:35:41 +0200 Birte Friesel <derf@finalrewind.org>
* Release v1.16.2
* Fix useless memory use when using feh --reload on HTTP URLs
@@ -456,13 +918,13 @@ Mon, 26 Sep 2011 09:35:41 +0200 Daniel Friesel <derf@finalrewind.org>
* Fix --title-font fallback behaviour
* Fix delayed title display when using --title-font
-Sun, 11 Sep 2011 12:46:50 +0200 Daniel Friesel <derf@finalrewind.org>
+Sun, 11 Sep 2011 12:46:50 +0200 Birte Friesel <derf@finalrewind.org>
* Release v1.16.1
* Fix reload after image rotation and similar (broken by 1.16)
<https://github.com/derf/feh/issues/63>
-Mon, 05 Sep 2011 10:56:58 +0200 Daniel Friesel <derf@finalrewind.org>
+Mon, 05 Sep 2011 10:56:58 +0200 Birte Friesel <derf@finalrewind.org>
* Release v1.16
* Reload image after executing an action with the hold-action flag set
@@ -485,14 +947,14 @@ Mon, 05 Sep 2011 10:56:58 +0200 Daniel Friesel <derf@finalrewind.org>
* When using --no-menus and clicking the menu button, feh will now ignore
it instead of quitting (which was undocumented behaviour anyways)
-Tue, 16 Aug 2011 22:48:06 +0200 Daniel Friesel <derf@finalrewind.org>
+Tue, 16 Aug 2011 22:48:06 +0200 Birte Friesel <derf@finalrewind.org>
* Release v1.15.1
* Fix segfault when selecting menu items (broken by 1.15)
<https://github.com/derf/feh/issues/58>,
<https://bugs.archlinux.org/task/25612>
-Mon, 15 Aug 2011 11:12:34 +0200 Daniel Friesel <derf@finalrewind.org>
+Mon, 15 Aug 2011 11:12:34 +0200 Birte Friesel <derf@finalrewind.org>
* Release v1.15
* Respect --image-bg option in full-screen mode
@@ -517,7 +979,7 @@ Mon, 15 Aug 2011 11:12:34 +0200 Daniel Friesel <derf@finalrewind.org>
* Add experimental reload functionality for directories
<https://github.com/derf/feh/issues/14>
-Mon, 04 Jul 2011 14:46:36 +0200 Daniel Friesel <derf@finalrewind.org>
+Mon, 04 Jul 2011 14:46:36 +0200 Birte Friesel <derf@finalrewind.org>
* Release v1.14.2
* Fix --draw-filename "x of y" being cut off by short filenames
@@ -529,13 +991,13 @@ Mon, 04 Jul 2011 14:46:36 +0200 Daniel Friesel <derf@finalrewind.org>
* Fix minor documentation bugs
* Fix minor memleak in the recursive file loader for directories
-Thu, 19 May 2011 22:32:42 +0200 Daniel Friesel <derf@finalrewind.org>
+Thu, 19 May 2011 22:32:42 +0200 Birte Friesel <derf@finalrewind.org>
* Release v1.14.1
* Fix compilation with curl=0
* Make zoom_default key work properly with --geometry
-Wed, 11 May 2011 11:37:32 +0200 Daniel Friesel <derf@finalrewind.org>
+Wed, 11 May 2011 11:37:32 +0200 Birte Friesel <derf@finalrewind.org>
* Release v1.14
* Only create caption directory when actually writing out a caption.
@@ -573,7 +1035,7 @@ Wed, 11 May 2011 11:37:32 +0200 Daniel Friesel <derf@finalrewind.org>
theme definition handling. Having a theme line with just one
option/value pair used to produce undefined behaviour
-Sat, 23 Apr 2011 22:00:27 +0200 Daniel Friesel <derf@finalrewind.org>
+Sat, 23 Apr 2011 22:00:27 +0200 Birte Friesel <derf@finalrewind.org>
* Release v1.13
* Fix segfault upon unloadable images when image-related format specifiers
@@ -587,7 +1049,7 @@ Sat, 23 Apr 2011 22:00:27 +0200 Daniel Friesel <derf@finalrewind.org>
the respective mode (scale/fill/max/center) on each Xinerama screen. Use
--no-xinerama to disable this.
-Sat, 12 Mar 2011 22:49:53 +0100 Daniel Friesel <derf@finalrewind.org>
+Sat, 12 Mar 2011 22:49:53 +0100 Birte Friesel <derf@finalrewind.org>
* Release v1.12
* Add --zoom fill as equivalent for --auto-zoom
@@ -601,7 +1063,7 @@ Sat, 12 Mar 2011 22:49:53 +0100 Daniel Friesel <derf@finalrewind.org>
* Remove builtin http client (--builtin)
* Fix compilation issues with libpng 1.5.1
-Wed, 09 Feb 2011 20:11:26 +0100 Daniel Friesel <derf@finalrewind.org>
+Wed, 09 Feb 2011 20:11:26 +0100 Birte Friesel <derf@finalrewind.org>
* Release v1.11.2
* Use wget --no-clobber to prevent TOCTTOU-based hole allowing a
@@ -610,7 +1072,7 @@ Wed, 09 Feb 2011 20:11:26 +0100 Daniel Friesel <derf@finalrewind.org>
It is still possible for an attacker to _create_ arbitrary files via the
same hole.
-Wed, 26 Jan 2011 21:07:19 +0100 Daniel Friesel <derf@finalrewind.org>
+Wed, 26 Jan 2011 21:07:19 +0100 Birte Friesel <derf@finalrewind.org>
* Release v1.11.1
* Show correct image dimensions in for cached thumbnails
@@ -618,7 +1080,7 @@ Wed, 26 Jan 2011 21:07:19 +0100 Daniel Friesel <derf@finalrewind.org>
* Remove support for FEH_OPTIONS (was deprecated >5 years ago)
* Restrict available modifiers to Control/Mod1/Mod4
-Sat, 22 Jan 2011 11:48:33 +0100 Daniel Friesel <derf@finalrewind.org>
+Sat, 22 Jan 2011 11:48:33 +0100 Birte Friesel <derf@finalrewind.org>
* Release v1.11
* Patch by Pascal Bleser: Use getaddrinfo for builtin http client,
@@ -630,7 +1092,7 @@ Sat, 22 Jan 2011 11:48:33 +0100 Daniel Friesel <derf@finalrewind.org>
* Increase movement steps for Ctrl+Left etc.
* Make in/out zoom use equal zoom ratio
-Fri, 03 Dec 2010 19:41:45 +0100 Daniel Friesel <derf@finalrewind.org>
+Fri, 03 Dec 2010 19:41:45 +0100 Birte Friesel <derf@finalrewind.org>
* Release v1.10.1
* Partially fix --scale-down behaviour (zooming is still broken)
@@ -638,7 +1100,7 @@ Fri, 03 Dec 2010 19:41:45 +0100 Daniel Friesel <derf@finalrewind.org>
* Fix segfaults after trying to load several unloadable images
* Fix fehrc created by feh (contined escape errors)
-Thu, 07 Oct 2010 20:15:12 +0200 Daniel Friesel <derf@finalrewind.org>
+Thu, 07 Oct 2010 20:15:12 +0200 Birte Friesel <derf@finalrewind.org>
* Release v1.10 (aka "2.0 will come real soon now")
* Patch by Stefan Mark: Add --bg-max (scaled with borders)
@@ -654,7 +1116,7 @@ Thu, 07 Oct 2010 20:15:12 +0200 Daniel Friesel <derf@finalrewind.org>
* Zoom button: Zoom to 100% on button release, not button click
* --draw-filename: Always show position in filelist
-Tue, 24 Aug 2010 19:23:36 +0200 Daniel Friesel <derf@chaosdorf.de>
+Tue, 24 Aug 2010 19:23:36 +0200 Birte Friesel <derf@chaosdorf.de>
* Release v1.9
* Add --fullscreen option, --full-screen is now deprecated
@@ -669,7 +1131,7 @@ Tue, 24 Aug 2010 19:23:36 +0200 Daniel Friesel <derf@chaosdorf.de>
* When zooming via keyboard: Always zoom around the center of the window
* The image can now be panned with Ctrl + arrow keys
-Fri, 25 Jun 2010 16:07:20 +0200 Daniel Friesel <derf@chaosdorf.de>
+Fri, 25 Jun 2010 16:07:20 +0200 Birte Friesel <derf@chaosdorf.de>
* Release v1.8
* support LDLIBS in Makefile/config.mk
@@ -688,9 +1150,9 @@ Fri, 25 Jun 2010 16:07:20 +0200 Daniel Friesel <derf@chaosdorf.de>
malicious URLs containing shell metacharacters (but only if those URLs
led to a valid file)
* Don't add ?randomnumber to URLs when downloading them, it confuses some
- servers and is not really neccessary in general
+ servers and is not really necessary in general
-Thu Jun 10 12:12:04 CEST 2010 Daniel Friesel <derf@chaosdorf.de>
+Thu Jun 10 12:12:04 CEST 2010 Birte Friesel <derf@chaosdorf.de>
* Release v1.7
* Fix segfault in Thumbnail mode when trying to open a no longer
@@ -708,14 +1170,14 @@ Thu Jun 10 12:12:04 CEST 2010 Daniel Friesel <derf@chaosdorf.de>
* caption mode: Automatically create caption directory if it doesn't exist
* Slideshow mode: SIGUSR1 = next image, SIGUSR2 = previous image
-Sat Jun 5 21:35:25 CEST 2010 Daniel Friesel <derf@chaosdorf.de
+Sat Jun 5 21:35:25 CEST 2010 Birte Friesel <derf@chaosdorf.de
* Release v1.6.1
* Fix omitted image borders at high zoom levels
* Re-add getopt_long files (possibly relevant for non-glibc systems)
* Do not require a running X server for -L, -u, -U options
-Tue Jun 1 10:21:19 CEST 2010 Daniel Friesel <derf@chaosdorf.de>
+Tue Jun 1 10:21:19 CEST 2010 Birte Friesel <derf@chaosdorf.de>
* Release v1.6
* Patch by aaptel: Support numpad keys for actions
@@ -731,7 +1193,7 @@ Tue Jun 1 10:21:19 CEST 2010 Daniel Friesel <derf@chaosdorf.de>
* Fix "make uninstall". You do NOT want to call this for feh versions
1.4.2 to 1.5
-Thu May 6 08:34:39 CEST 2010 Daniel Friesel <derf@chaosdorf.de>
+Thu May 6 08:34:39 CEST 2010 Birte Friesel <derf@chaosdorf.de>
* Release v1.5
* Rewrite parts of the menu code & fix a memory leak while there
@@ -739,7 +1201,7 @@ Thu May 6 08:34:39 CEST 2010 Daniel Friesel <derf@chaosdorf.de>
* Add keybinding to toggle pointer visibility (see --hide-pointer)
* Sort manual a bit
-Thu Apr 22 22:28:09 CEST 2010 Daniel Friesel <derf@chaosdorf.de>
+Thu Apr 22 22:28:09 CEST 2010 Birte Friesel <derf@chaosdorf.de>
* Release v1.4.3
* Warp the pointer when reaching a window border in pan mode
@@ -749,7 +1211,7 @@ Thu Apr 22 22:28:09 CEST 2010 Daniel Friesel <derf@chaosdorf.de>
is. The zoom happen around at that pixel.
* Manpage review
-Fri Apr 2 16:20:55 CEST 2010 Daniel Friesel <derf@chaosdorf.de>
+Fri Apr 2 16:20:55 CEST 2010 Birte Friesel <derf@chaosdorf.de>
* Release v1.4.2
* Replace autoconf by config.mk
@@ -758,7 +1220,7 @@ Fri Apr 2 16:20:55 CEST 2010 Daniel Friesel <derf@chaosdorf.de>
* patch by dylan: Remove temporary files if url opening fails
* Fix problems with unexpectedly empty filelists
-Tue Mar 16 07:56:36 CET 2010 Daniel Friesel <derf@chaosdorf.de>
+Tue Mar 16 07:56:36 CET 2010 Birte Friesel <derf@chaosdorf.de>
* Release v1.4.1
* Fix chrome theme in the default .fehrc
@@ -766,7 +1228,7 @@ Tue Mar 16 07:56:36 CET 2010 Daniel Friesel <derf@chaosdorf.de>
* Add manual for feh-cam and gen-cam-menu (from Debian)
* Fix lossless rotate for filenames with spaces etc.
-Thu Mar 4 14:55:02 CET 2010 Daniel Friesel <derf@chaosdorf.de>
+Thu Mar 4 14:55:02 CET 2010 Birte Friesel <derf@chaosdorf.de>
* Release v1.4
* Lots of documentation fixes/improvements
@@ -780,7 +1242,7 @@ Thu Mar 4 14:55:02 CET 2010 Daniel Friesel <derf@chaosdorf.de>
* Use jpegtran binary instead of libjpeg for lossless rotation
* Add --bg-fill option (patch by Anonymous)
-Mon Feb 8 21:47:56 CET 2010 Daniel Friesel <derf@chaosdorf.de>
+Mon Feb 8 21:47:56 CET 2010 Birte Friesel <derf@chaosdorf.de>
* Release v1.3.5
* Import various Debian patches
@@ -860,8 +1322,8 @@ Mon Mar 07 23:56:03 GMT 2005 Tom Gilbert <tom@linuxbrit.co.uk>
> In short, it enables the user to use feh as an image viewer used by a
> file manager like ROX-Filer or Nautilus when invoked with the --fmmode
> option. The file manager passes the file that the user wants to view to
- > feh. My function then reads the directory in which the file resides and
- > first passes the current image, then alphabetically all the following
+ > feh. My function then reads the directory in which the file resides and
+ > first passes the current image, then alphabetically all the following
> images and at last the images that are alphabetically before the current
> file to the 'filelist'.
> Afaik that's the default behaviour of gqview and gthumb.
@@ -907,7 +1369,7 @@ Fri Sep 03 13:40:48 BST 2004 Tom Gilbert <tom@linuxbrit.co.uk>
Sat Jul 24 14:52:19 BST 2004 Tom Gilbert <tom@linuxbrit.co.uk>
- * Various warning fixes from Claes Nasten <pekdon@pekdon.net>
+ * Various warning fixes from Claes Nasten <pekdon@pekdon.net>
Thu Jun 10 23:14:36 BST 2004 Tom Gilbert <tom@linuxbrit.co.uk>
@@ -1038,13 +1500,13 @@ Sun Oct 20 20:12:23 2002 EDT, Paul Duncan <pabs@pablotron.org>
* AUTHORS: added Jon Bernard
Sun Oct 20 14:49:46 2002 EDT, Paul Duncan <pabs@pablotron.org>
-
+
* New beveled, off-white background image for menus. I made it the
default after consulting with the boss (eg Sue Gilbert). Tom thinks
it's okay too.
Sun Oct 20 05:48:40 2002 EDT Paul Duncan <pabs@pablotron.org>
-
+
* Added Xinerama support. Currently defaults to fullscreen on the
first head (this can be fixed with a little work).
* Added winwidget_{move,get_geometry}();
@@ -1605,7 +2067,7 @@ Fri Jun 23 11:37:56 2000 Tom Gilbert <gilbertt@linuxbrit.co.uk>
Thu Jun 22 23:57:33 2000 Tom Gilbert <gilbertt@linuxbrit.co.uk>
* Optimisations to thumb hilites. Only show hilite when you can
- click to open an image, don't show when over emtpy space, and
+ click to open an image, don't show when over emtpy space, and
don't re-render if the selection hasn't changed from the last
mouseover. Speeds it up a lot.
@@ -1708,7 +2170,7 @@ Sun Jun 18 03:47:58 2000 Tom Gilbert <gilbertt@linuxbrit.co.uk>
Sun Jun 18 01:29:16 2000 Tom Gilbert <gilbertt@linuxbrit.co.uk>
- * The first part of some background setting code from richlowe
+ * The first part of some background setting code from richlowe
<richlowe@btinternet.com> - more to come, it'll be sweet.
* Doesn't work yet btw, so don't get excited ;)
@@ -1730,7 +2192,7 @@ Sat Jun 17 23:46:32 2000 Tom Gilbert <gilbertt@linuxbrit.co.uk>
bg. Same for --theme chrome.
Sat Jun 17 12:57:49 PDT 2000 Paul Duncan <pabs@pablotron.org>
-
+
* wget is now quiet by default (wget -q). it's verbose if the
-V or --verbose flag is passed to feh. Your terminal will
thank you.
@@ -1805,7 +2267,7 @@ Sun Jun 4 15:44:05 2000 Tom Gilbert <gilbertt@linuxbrit.co.uk>
the singleton context (accidentally leaving context_antialias on,
not blending when I should be etc), and to fix them I was adding
lines and lines of context_set this, context_set that. I've
- wrapped the imlib calls in an imlib1
+ wrapped the imlib calls in an imlib1
lots-of-params-per-function-call stylee, and now it's much more
readable and harder to break.
* In the process, made big speedups by not antialiasing when I
@@ -2144,7 +2606,7 @@ Fri Mar 24 19:22:51 2000 Tom Gilbert <gilbertt@linuxbrit.co.uk>
* If ~/.fehrc exists, or if not, but /etc/fehrc exists, feh will
look in it for name/options pairs. An example entry would be:
imagemap -rVq --thumb-width 40 --thumb-height 30
- * You can use the theme in two ways. Either
+ * You can use the theme in two ways. Either
feh -C themename [images]
or you can create a symbolic link to feh with the name of the
options you want it to use. So from the example above:
@@ -2157,7 +2619,7 @@ Fri Mar 24 19:22:51 2000 Tom Gilbert <gilbertt@linuxbrit.co.uk>
create an index.jpg in the current directory. I just run:
$ mkindex.
* An example.fehrc is provided with a couple of cool examples.
-
+
Fri Mar 24 19:17:27 2000 Tom Gilbert <gilbertt@linuxbrit.co.uk>
* Slightly increased the default index mode font size.
@@ -2393,11 +2855,11 @@ Fri Mar 3 22:44:25 PST 2000 Michael Jennings <mej@eterm.org>
tired right now to try to make sense out of gilbertt's callback
logic. :-) I'll try to fix it this weekend if he doesn't beat me
to it.
-
+
Wed Mar 1 15:13:04 PST 2000 Michael Jennings <mej@eterm.org>
* Math lib
-
+
Sun Feb 20 15:22:00 2000 Tom Gilbert <gilbertt@linuxbrit.co.uk>
* Remove some crufty duplication.
@@ -2700,7 +3162,7 @@ Sun Dec 19 22:06:43 1999 Tom Gilbert <gilbertt@linuxbrit.co.uk>
Sun Dec 19 20:29:33 1999 Tom Gilbert <gilbertt@linuxbrit.co.uk>
* Added a .spec file for feh. Contributed by Alistair Sutton
- <metallica@freenet.co.uk>, (who is obviously a metallica fan ;),
+ <metallica@freenet.co.uk>, (who is obviously a metallica fan ;),
and has written specs for me before ::) Thanks dude :)
* Urm. I haven't actually tested this yet :)
@@ -2810,4 +3272,3 @@ Thu Dec 16 22:58:21 1999 Tom Gilbert <gilbertt@linuxbrit.co.uk>
Thu Dec 16 22:10:50 1999 Tom Gilbert <gilbertt@linuxbrit.co.uk>
* Initial import. Feh is currently at 0.5.0 release level.
-
diff --git a/Makefile b/Makefile
index 5d369bc..1e7e4e5 100644
--- a/Makefile
+++ b/Makefile
@@ -12,7 +12,7 @@ build-applications:
@${MAKE} -C share/applications
test: all
- @if ! uname -m | fgrep -q -e arm -e mips; then \
+ @if ! uname -m | grep -q -e arm -e mips; then \
PACKAGE=${PACKAGE} prove test/feh.t test/mandoc.t; \
else \
PACKAGE=${PACKAGE} prove test/feh.t test/mandoc.t || cat test/imlib2-bug-notice; \
@@ -25,51 +25,37 @@ test-x11: all
install: install-man install-doc install-bin install-font install-img
install: install-icon install-examples install-applications
-ifeq (${cam},1)
-install: install-cam
-uninstall: uninstall-cam
-endif
-
-install-cam:
- @echo installing fe-cam and gen-cam-menu
- @cp man/feh-cam.1 man/gen-cam-menu.1 ${man_dir}/man1
- @chmod 644 ${man_dir}/man1/feh-cam.1 ${man_dir}/man1/gen-cam-menu.1
- @cp cam/feh-cam cam/gen-cam-menu ${bin_dir}
- @chmod 755 ${bin_dir}/feh-cam ${bin_dir}/gen-cam-menu
-
-uninstall-cam:
- rm -f ${man_dir}/man1/feh-cam.1
- rm -f ${man_dir}/man1/gen-cam-menu.1
- rm -f ${bin_dir}/feh-cam ${bin_dir}/gen-cam-menu
-
-install-man:
+install-man: man/feh.1
@echo installing manuals to ${man_dir}
@mkdir -p ${man_dir}/man1
@cp man/feh.1 ${man_dir}/man1
@chmod 644 ${man_dir}/man1/feh.1
-install-doc:
+install-doc: AUTHORS ChangeLog README.md TODO
@echo installing docs to ${doc_dir}
@mkdir -p ${doc_dir}
@cp AUTHORS ChangeLog README.md TODO ${doc_dir}
@chmod 644 ${doc_dir}/AUTHORS ${doc_dir}/ChangeLog ${doc_dir}/README.md \
${doc_dir}/TODO
-install-bin:
+install-bin: src/feh
@echo installing executables to ${bin_dir}
@mkdir -p ${bin_dir}
- @cp src/feh ${bin_dir}
+ @cp src/feh ${bin_dir}/feh.tmp
+ @mv ${bin_dir}/feh.tmp ${bin_dir}/feh
@chmod 755 ${bin_dir}/feh
install-font:
@echo installing fonts to ${font_dir}
@mkdir -p ${font_dir}
+ @chmod 755 ${font_dir}
@cp share/fonts/* ${font_dir}
@chmod 644 ${font_dir}/*
install-img:
@echo installing images to ${image_dir}
@mkdir -p ${image_dir}
+ @chmod 755 ${image_dir}
@cp share/images/* ${image_dir}
@chmod 644 ${image_dir}/*
@@ -92,7 +78,7 @@ install-examples:
@cp examples/* ${example_dir}
@chmod 644 ${example_dir}/*
-install-applications:
+install-applications: share/applications/feh.desktop
@echo installing desktop file to ${desktop_dir}
@mkdir -p ${desktop_dir}
@cp share/applications/feh.desktop ${desktop_dir}
diff --git a/README.md b/README.md
index 328897a..0b30b88 100644
--- a/README.md
+++ b/README.md
@@ -1,136 +1,158 @@
-# feh
-Imlib2 based image viewer
----
+# feh - Image Viewer and Cataloguer
- * http://feh.finalrewind.org/
- * http://linuxbrit.co.uk/feh/
- * #feh on irc.oftc.net
+**feh** is a light-weight, configurable and versatile image viewer.
+It is aimed at command line users, but can also be started from graphical file
+managers. Apart from viewing images, it can compile text and thumbnail
+listings, show (un)loadable files, set X11 backgrounds, and more.
-Dependencies
----
+Features include filelists, various image sorting modes, custom action scripts,
+and image captions. feh can be controlled by configurable keyboard and mouse
+shortcuts, terminal input and signals. When no file arguments or filelists are
+specified, feh displays all files in the current directory.
+
+This README focuses on installation and contribution instructions. See the
+[feh homepage](https://feh.finalrewind.org/) and the
+[feh(1) manual](https://man.finalrewind.org/1/feh/) for usage instructions.
+
+## Dependencies
* Imlib2
- * libcurl (disable with make curl=0)
+ * libcurl (disable with `curl=0`)
* libpng
* libX11
- * libXinerama (disable with make xinerama=0)
+ * libXt
+ * libXinerama (disable with `xinerama=0`)
-If built with exif=1:
+Only when building with `exif=1`:
* libexif-dev
* libexif12
-Recommended
----
-
- * jpegtran (supplied by the jpeg library, for lossless image rotation)
- * convert (supplied by ImageMagick, can be used to load unsupported formats)
+Only when building with `magic=1`:
-Installation
----
-
-**For end users:**
-```bash
-$ make
-$ sudo make install app=1
-```
-
-**For package maintainers and users who do not want feh to install its
-icons into /usr/share:**
-```bash
-$ make
-$ sudo make install
-```
-
-**Explanation:** feh ships some icons and an X11 desktop entry, which allow it to
-be used from file managers, desktop menus and similar. However, installing
-icons to /usr/local/share/... does not seem to work reliable in all cases.
-Because of this, when using "make install app=1", feh will install its icons
-to /usr/share/..., even though they technically belong into /usr/local.
+ * libmagic
+## Build Process
-ZSH Completion for feh is available [here](http://git.finalrewind.org/zsh/plain/etc/completions/_feh)
+feh has been packaged for a variety of distributions, including
+[Arch Linux](https://archlinux.org/packages/extra/x86_64/feh/),
+[Debian](https://packages.debian.org/search?keywords=feh&exact=1),
+[FreeBSD](https://www.freshports.org/graphics/feh), and
+[Ubuntu](https://packages.ubuntu.com/search?keywords=feh&exact=1).
+You can configure, compile and install a custom version as follows.
-Make flags
-----------
+### Configuration
-Flags can be used to control the build and installation process.
+feh's build process uses make flags to enable/disable optional features and
+fine-tune the build and installation process. It uses (hopefully) reasonable
+defaults, so you can skip this section if you like.
-e.g.
+Make flags can be passed as **make** arguments or set as environment variables,
+like so:
```bash
-make flag=bool
-```
-```bash
-make install flag=bool
+$ make flag=bool
+$ make install flag=bool
```
or
```bash
-export flag=bool
-make && make install
+$ export flag=bool
+$ make && make install
```
-For example, `make xinerama=0 debug=1` will disable Xinerama support and produce a debug build.
-
-Available flags are:
+The following flags are respected by the makefile. A default value of **1**
+indicates that the corresponding feature is enabled by default.
| Flag | Default value | Description |
| :--- | :---: | :--- |
-| app | 0 | install icons to /usr/share, regardless of `DESTDIR` and `PREFIX, and call gtk-update-icon-cache afterwards |
-| cam | 0 | install deprecated feh-cam und gen-cam-menu scripts |
-| curl | 1 | use libcurl to view http:// and similar images |
+| app | 0 | install icons to /usr/share, regardless of `DESTDIR` and `PREFIX`, and call gtk-update-icon-cache afterwards |
+| curl | 1 | use libcurl to view https:// and similar images |
| debug | 0 | debug build, enables `--debug` |
| exif | 0 | Builtin EXIF tag display support |
| help | 0 | include help text (refers to the manpage otherwise) |
+| inotify | 0 | enable inotify, needed for `--auto-reload` |
| stat64 | 0 | Support CIFS shares from 64bit hosts on 32bit machines |
+| magic | 0 | Use libmagic to filter unsupported file formats |
+| mkstemps | 1 | Whether your libc provides `mkstemps()`. If set to 0, feh will be unable to load gif images via libcurl |
+| verscmp | 1 | Whether your libc provides `strvercmp()`. If set to 0, feh will use an internal implementation. |
| xinerama | 1 | Support Xinerama/XRandR multiscreen setups |
-So, by default **libcurl** and **Xinerama** are enabled, the rest is disabled.
+For example, `make xinerama=0 debug=1` will disable Xinerama support and
+produce a debug build; libcurl and natural sorting support will remain enabled.
-Additionally, the standard variables `PREFIX` and `DESTDIR` are supported.
+Additionally, it supports the standard variables `CFLAGS`, `LDLIBS`, `PREFIX`,
+and `DESTDIR`.
-**PREFIX _(default: /usr)_** controls where the application and its data files
+**PREFIX _(default: /usr/local)_** controls where the application and its data files
will be installed. It must be set both during `make` and `make install`.
**DESTDIR _(default: empty)_** sets the installation root during "make install". It
is mostly useful for package maintainers.
-**Note:** config.mk is designed so that in most cases, you can set environment
-variables instead of editing it. E.g.:
+**Note:** Defaults are specified in `config.mk`. It is designed so that in most
+cases, you can set environment variables instead of editing it. E.g.:
```bash
-CFLAGS='-g -Os' make
+$ CFLAGS='-g -Os' make
```
```bash
-export DESTDIR=/tmp/feh PREFIX=/usr
-make && make install
+$ export DESTDIR=/tmp/feh PREFIX=/usr
+$ make && make install
```
Builtin EXIF support is maintained by Dennis Real, [here](https://github.com/reald/feh).
+### Installation
-Testing (non-X)
----------------
+Add your own make flags to the following examples as needed.
+
+**For end users:**
```bash
-$ make test
+$ make
+$ sudo make install app=1
```
-Requires **perl >= 5.10** with `Test::Command`. The tests are non-interactive and
-work without X, so they can safely be run even on a headless buildserver.
+**For package maintainers and users who do not want feh to install its
+icons into /usr/share:**
+```bash
+$ make
+$ sudo make install
+```
+
+**Explanation:** feh ships some icons and an X11 desktop entry, which allow it to
+be used from file managers, desktop menus and similar. However, installing
+icons to /usr/local/share/... does not seem to work reliably.
+Because of this, when using "make install app=1", feh will install its icons
+to /usr/share/..., even though they technically belong into /usr/local.
+[ZSH completion for
+feh](https://git.finalrewind.org/zsh/plain/etc/completions/_feh) is also
+available.
-Testing (X)
------------
+## Testing (non-X11)
-Requires
- * import (usually supplied by imagemagick)
- * perl >= 5.10 with GD, Test::More and X11::GUITest
- * twm
- * Xephyr
+The non-X11 parts of feh can be automatically tested by running
```bash
-$ make test-x11
+$ make test
```
-
-**_Be aware that this is quite experimental, so far the X-tests have only been
-run on one machine. So they may or may not work for you._**
+This requires **perl >= 5.10** and the perl module `Test::Command`. Tests are
+non-interactive and do not require a running X11, so they can safely be run on
+a headless buildserver.
+
+## Contributing
+
+Bugfixes are always welcome, just open a pull request :)
+
+Before proposing a new feature, please consider the scope of feh: It is an
+image viewer and cataloguer, not an image editor or similar. Also, its option
+list is already pretty long. Please discuss your ideas in a feature request
+before opening a pull request in this case. Also, keep in mind that feh is
+developed as a hobby project and that there is absolutely no obligation for
+anyone to implement requested features or review merge requests.
+
+Please keep in mind that feh's options, key bindings and format specifiers are
+documented in two different places: The manual (man/feh.pre) and the help text
+(src/help.raw). Although the help is not compiled in by default, it should be
+kept up-to-date. On space-constrained embedded systems, it may be more useful
+than the (significantly larger) man page.
diff --git a/cam/ChangeLog b/cam/ChangeLog
deleted file mode 100644
index 4a562bd..0000000
--- a/cam/ChangeLog
+++ /dev/null
@@ -1,14 +0,0 @@
-0.3
----
-- added this changelog..
-
-0.4
----
-- added richlowe's patch. since he pretty much rewrote everything, he's
-an author, not a contributor. :)
-- uses "use strict" now
-- uses POD for documentation
-- added title support in cam bookmarks file
-- cmd line flag for different bookmars file
-- getoppt_long use for command-line flags
-- rmeoved richlow's damn nonstandard Pod::Usage stuff
diff --git a/cam/README b/cam/README
deleted file mode 100644
index e67d60e..0000000
--- a/cam/README
+++ /dev/null
@@ -1,45 +0,0 @@
-Cam 0.4 README
-==============
-This document was last updated on 20010223-1620-PST.
-Please see the file COPYING for licensing information.
-
-Description
-===========
-feh-cam is a Perl wrapper for feh that simlifies viewing webcams. It uses
-keyed bookmarks. Type "feh-cam --help" at the command line for usage
-information.
-
-Webcam Information
-==================
-All webcam images are the property of their respective owners. If you
-enjoy the cam, you should check out the page too! The initial list of
-cam bookmarks (in the cam_bookmarks file) is a combination of webcams
-scoured from the #E People Page (http://bma.debian.net/~bma/e-irc/),
-my favorite webcams from the Stile Project's cam pages
-(http://www.stileproject.com/), and additional #e people cams that
-have come online since cam's original release (0.1).
-
-Installation
-============
-- Copy the "feh-cam" script to a location in your PATH (ex /usr/bin,
- /usr/local/bin, or $HOME/bin), and make sure hte execute bit on the
- script is set (type "chmod a+x $HOME/.cam_bookmarks").
-- Copy the "cam_bookmarks" file to $HOME/.cam_bookmarks and make sure
- cam can see it by typing "feh-cam --list".
-- Optionally, modify the options inside the feh-cam script. You can pass
- feh any options before the image by adjusting the $PRE variable,
- and any options after the image by adjustin the $POST variable.
- For example, I like to keep all cam images by default; you can enable
- this behavior with the -k command line option, or enable it permanently
- by adding it to the $PRE flags in feh-cam.
-
-About the Authors
-=================
-Paul Duncan <pabs@pablotron.org>, pabs on #e
-http://www.pablotron.org/
-
-Richard Lowe <richlowe@btinternet.com>, richlowe on #e
-http://www.richlowe.btinternet.co.uk/
-
-...but the real credit goes to Tom Gilbert for making feh, an awesome
-image viewer. :)
diff --git a/cam/cam_bookmarks b/cam/cam_bookmarks
deleted file mode 100644
index 8d45413..0000000
--- a/cam/cam_bookmarks
+++ /dev/null
@@ -1,2 +0,0 @@
-nasa_shuttle=60,http://science.ksc.nasa.gov/shuttle/countdown/video/chan2large.jpg
-
diff --git a/cam/feh-cam b/cam/feh-cam
deleted file mode 100755
index 29b254f..0000000
--- a/cam/feh-cam
+++ /dev/null
@@ -1,195 +0,0 @@
-#!/usr/bin/perl -w
-
-use strict;
-use Getopt::Long;
-
-#############################################
-############# CAM RUN-TIME OPTIONS ##########
-#############################################
-my $feh = "feh";
-
-# additional feh cmdline options
-my $PRE = " -q -G -Twebcam -1 0 -0 1 ";
-my $POST = "";
-#############################################
-#############################################
-
-# Options
-my $help = '';
-my $fullscreen = '';
-my $geometry = '';
-my $list = '';
-my $verbose = '';
-my $add = '';
-my $keep = '';
-my $deftitle = '%cCAM - %u';
-my $title = '';
-my $bp = $ENV{HOME}."/.cam_bookmarks";
-my $DEBUG = 0;
-
-# check args
-&print_usage_and_exit unless (@ARGV);
-
-print STDERR "Note: feh-cam will be removed from the feh distribution in 2013.\n";
-print STDERR "Please mail derf\@finalrewind.org if you still use it.\n";
-
-
-# Url, Refresh, and bookmarks
-my $url = "";
-my $ref = "";
-my %bms = ();
-
-GetOptions('help|?|h' => \$help,
- 'full-screen|f|giblets-mom' => \$fullscreen,
- 'list|l' => \$list,
- 'geometry|g=s' => \$geometry,
- 'verbose|v' => \$verbose,
- 'add|a' => \$add,
- 'keep-images|k|save-pr0n' => \$keep,
- 'title|t=s' => \$title,
- 'debug|d' => \$DEBUG,
- 'bookmarks|b=s' => \$bp
- );
-
-my $key = shift @ARGV;
-
-&print_usage_and_exit if ($help);
-
-if ($verbose) {
- $PRE .= " -V ";
-}
-
-if ($fullscreen) {
- $PRE =~ s/-w//;
- $PRE .= " --full-screen --auto-zoom ";
-}
-
-if ($geometry) {
- $PRE .= " --geometry $geometry ";
-}
-
-# if requested, add a key/url pair to bookmarks file
-if ($add) {
- my $mytitle = '';
- ($url,$ref,$mytitle) = @ARGV;
- die "Bad key syntax\n" unless ($key && $url && $ref);
-
- $mytitle="" unless ($mytitle);
-
- open(BMF, ">>$bp") or die "Couldn't open bookmarks file \"$bp\": $!\n";
- print BMF "$key=$ref,$url \"$mytitle\"\n";
- close BMF;
- print "Added URL key \"$key\" = $url, $ref.\n"; # its useful to have this even if you arent debugging --richlowe
- exit 0;
-}
-
-if ($keep) {
- $PRE .= " -k ";
-}
-
-
-
-# load bookmarks
-open(BMF, "$bp") or die "Couldn't open bookmarks file \"$bp\": $!\n";
-foreach (<BMF>) {
- next unless /^(.*?)=(.*)$/;
- $bms{$1} = $2;
- print "key=$1, url=$2\n" if ($DEBUG);
-}
-close BMF;
-
-# if requested, dump a list of key/url pair values
-if ($list) {
- foreach (sort keys %bms) {
- my $t = $bms{$_};
-
- $t =~ s/^(.+?),(.+?)(^ "(.*)"|)?$/$2/;
- chomp $t;
- print "$_ = $t, $1, $3\n";
- }
- exit 0;
-}
-
-# main loop
-MAIN: {
- do {
- $title="";
- $url = $bms{$key};
- die "Couldn't find URL key \"$key.\"\n" unless($url);
-
- $url =~ s/^(.+?),(.+?)( "(.*)")?$/$2/;
- $ref = $1;
-
- if ($4) {
- $title = $4;
- } else {
- $title = $deftitle;
- }
-
- if ($title) {
- $title =~ s/\%c/$key/g;
- $title =~ s/\%u/$url/g;
- $title =~ s/\%r/$ref/g;
- $title =~ s/\%\%/\%/g;
- $title = " --title \"$title\" ";
- }
-
- my $cmd = "$feh $PRE $title -T".$key."cam -R $ref $url $POST";
- print "$cmd\n" if ($DEBUG);
- FORK: {
- my $pid;
- if ($pid = fork) {
- # We're a daddy! :)
- } elsif (defined $pid) {
- # child
- exec "$cmd" or die "Couldnt exec() $feh: $!\n";
- } elsif ( $! =~ /No more process/) {
- sleep 5;
- redo FORK;
- } else {
- # wtf?
- die "Unrecoverable fork() error: $!\n";
- }
- }
- } while ($key = shift @ARGV);
-}
-
-
-sub print_usage_and_exit() {
- print <<END_USAGE;
-$0 0.4
-by Paul Duncan <pabs\@pablotron.org>, and
- Richard Lowe <richlowe\@btinternet.com>
-
-Description:
- A convenient webcam wrapper for feh.
-
-Usage:
- $0 <keys>
- Load the urls specified by the given keys.
- key : a url key stored in the bookmarks file (\"$bp\").
- $0 <-a|--add> key url refresh
- Add a key to the bookmarks file.
- key : short key (ex \"jenni\"),
- url : url (ex \"http://www.jennicam.org/webcam/cam.jpg\"),
- refresh : refresh, in seconds (ex 120)
- $0 <-l|--list>
- List each url key in the bookmarks file (\"$bp\").
- $0 [-f|--full-screen] keys
- Start feh in full-screen mode (aka --giblets-mom \"viewing\" mode).
- $0 [-k|--keep-images] keys
- Save each image in the current directory (aka --save-pr0n).
- $0 [-g|--geometry] xxx
- Use window geometry xxx (e.g. 640x480).
- $0 [-v|--verbose] options
- Start feh in verbose mode (feh -V).
- $0 <-h|-?|--help>
- Display usage information (this screen).
-
-Notes:
-Thanks to giblet for feh, an awesome image and webcam viewing program,
-and raster for Imlib2.
-END_USAGE
- exit(-1);
-}
-
diff --git a/cam/gen-cam-menu b/cam/gen-cam-menu
deleted file mode 100755
index 710070b..0000000
--- a/cam/gen-cam-menu
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/sh
-
-######################################################################
-# gen_cam_menu.sh 0.1 #
-# by Paul Duncan <pabs@pablotron.org> #
-# #
-# This script will generate a menu of cam bookmarks for #
-# Enlightenment DR0.16.x. TYou can safely run this script more than #
-# once; it won't add an another entry to the left-click menu if it's #
-# already been run once. It doesn't delete any existing menu #
-# entries, and it backs up your existing menu files as well. (just #
-# in case I screwed up.. hehe). THe two variables below allow you #
-# rename the left-click menuitem, and the menu title. #
-# #
-# #
-MENU_ITEM="Webcams"; #
-MENU_TITLE="Webcam List"; #
-BMARKS=$HOME"/.cam_bookmarks"; #
-# #
-######################################################################
-
-
-C_MENUFILE="webcam.menu";
-F_MENUFILE="file.menu";
-C_MENU=$HOME"/.enlightenment/"$C_MENUFILE;
-F_MENU=$HOME"/.enlightenment/"$F_MENUFILE;
-
-# make backups, just in case
-cp -f $C_MENU $C_MENU"-cam_menu.backup"
-cp -f $F_MENU $F_MENU"-cam_menu.backup"
-
-echo "Note: gen-cam-menu will be removed from the feh distribution by 2013." > /dev/stderr
-echo "Please mail derf@finalrewind.org if you still use it." > /dev/stderr
-
-# generate cam menu
-echo "Generating \""$C_MENU"\".";
-echo "\"$TITLE\"" > "$C_MENU";
-cat $BMARKS | perl -e "while (<>) { /(.*?)=/; \$keys{\$1}=\"1\"; } foreach(sort keys %keys) { /(.)(.*$)/; print \"\\\"\".uc(\$1).\"\$2\\\" NULL exec \\\"feh-cam \$1\$2\\\"\\n\"; }">> $C_MENU;
-
-# add entry to file menu if there isn't one
-echo "Generating \""$F_MENU"\".";
-perl -i -e "\$already_there=0; while (<>) { \$already_there++ if (/$MENU_ITEM/); print \"\\\"$MENU_ITEM\\\" NULL menu \\\"$C_MENUFILE\\\"\\n\" if (!\$already_there&&/Restart/); print; }" $F_MENU;
-
-echo "Done.";
diff --git a/config.mk b/config.mk
index 93bb1c3..5cad703 100644
--- a/config.mk
+++ b/config.mk
@@ -2,17 +2,22 @@ PACKAGE ?= feh
VERSION ?= ${shell git describe --dirty}
app ?= 0
-cam ?= 0
curl ?= 1
debug ?= 0
+exif ?= 0
help ?= 0
+magic ?= 0
+mkstemps ?= 1
+verscmp ?= 1
xinerama ?= 1
-exif ?= 0
# Prefix for all installed files
PREFIX ?= /usr/local
ICON_PREFIX ?= ${DESTDIR}${PREFIX}/share/icons
+# icons in /usr/share/local/icons (and other prefixes != /usr) are not
+# generally supported. So ignore PREFIX and always install icons into
+# /usr/share/icons if the user wants to install feh on their local machine.
ifeq (${app},1)
ICON_PREFIX = /usr/share/icons
endif
@@ -34,6 +39,9 @@ scalable_icon_dir = ${icon_dir}/scalable/apps
CFLAGS ?= -g -O2
CFLAGS += -Wall -Wextra -pedantic
+# Settings for glibc >= 2.19 - may need to be adjusted for other systems
+CFLAGS += -std=c11 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -D_DARWIN_C_SOURCE
+
ifeq (${curl},1)
CFLAGS += -DHAVE_LIBCURL
LDLIBS += -lcurl
@@ -44,9 +52,9 @@ endif
ifeq (${debug},1)
CFLAGS += -DDEBUG -O0
- MAN_DEBUG = . This is a debug build.
+ MAN_DEBUG = This is a debug build.
else
- MAN_DEBUG =
+ MAN_DEBUG = .
endif
ifeq (${help},1)
@@ -57,6 +65,22 @@ ifeq (${stat64},1)
CFLAGS += -D_FILE_OFFSET_BITS=64
endif
+ifeq (${mkstemps},1)
+ CFLAGS += -DHAVE_MKSTEMPS
+endif
+
+ifeq (${magic},1)
+ CFLAGS += -DHAVE_LIBMAGIC
+ LDLIBS += -lmagic
+ MAN_MAGIC = enabled
+else
+ MAN_MAGIC = disabled
+endif
+
+ifeq (${verscmp},1)
+ CFLAGS += -DHAVE_STRVERSCMP
+endif
+
ifeq (${xinerama},1)
CFLAGS += -DHAVE_LIBXINERAMA
LDLIBS += -lXinerama
@@ -68,9 +92,16 @@ endif
ifeq (${exif},1)
CFLAGS += -DHAVE_LIBEXIF
LDLIBS += -lexif
- MAN_EXIF = enabled
+ MAN_EXIF = available
+else
+ MAN_EXIF = not available
+endif
+
+ifeq (${inotify},1)
+ CFLAGS += -DHAVE_INOTIFY
+ MAN_INOTIFY = enabled
else
- MAN_EXIF = disabled
+ MAN_INOTIFY = disabled
endif
MAN_DATE ?= ${shell date '+%B %d, %Y'}
diff --git a/examples/buttons b/examples/buttons
index 3c79413..be6ce39 100644
--- a/examples/buttons
+++ b/examples/buttons
@@ -14,4 +14,4 @@ zoom 3
# make scroll wheel (mousewheel up and down) zoom, instead of flipping images
zoom_in 4
-zoom_out 5 \ No newline at end of file
+zoom_out 5
diff --git a/examples/find-lowres b/examples/find-lowres
index 4a7d9a9..ac77e7b 100755
--- a/examples/find-lowres
+++ b/examples/find-lowres
@@ -1,4 +1,4 @@
-#!/usr/bin/env zsh
+#!/bin/sh
# Recursively find images below a certain resolution
#
# Usage: find-lowres [-r] [directory [dimension]]
@@ -10,20 +10,23 @@
remove=0
-while [[ $1 == -* ]]; do
+while true
+do
case $1 in
-r) remove=1 ;;
+ -*) echo "option \"$1\" ignored" ;;
-|--) shift; break ;;
+ *) break ;;
esac
shift
done
-base=${1-.}
-dimension=${2-1000x800}
+dir=${1:-.}
+dimension=${2:-1000x800}
-if (( remove ))
+if [ "$remove" = "1" ]
then
- feh --action 'rm %F' -rlV --max-dim ${dimension} ${base}
+ feh --action 'rm %F' -rlV --max-dim "${dimension}" "${dir}"
else
- feh -rlV --max-dim ${dimension} ${base}
+ feh -rlV --max-dim "${dimension}" "${dir}"
fi
diff --git a/examples/keys b/examples/keys
index d221d29..c1bb091 100644
--- a/examples/keys
+++ b/examples/keys
@@ -37,7 +37,7 @@ zoom_out C-Down a
zoom_default d
zoom_fit s
-# I only hit these accidentaly
+# I only hit these accidentally
save_image
save_filelist
diff --git a/examples/themes b/examples/themes
index d315942..2d77770 100644
--- a/examples/themes
+++ b/examples/themes
@@ -41,11 +41,6 @@ booth --full-screen --hide-pointer --slideshow-delay 20
# Screw xscreensaver, use feh =)
screensave --recursive --full-screen --randomize --slideshow-delay 10 --hide-pointer
-# Different menus
-aqua --menu-bg /usr/share/feh/images/menubg_aqua.png
-sky --menu-bg /usr/share/feh/images/menubg_sky.png
-black --menu-bg /usr/share/feh/images/menubg_black.png
-
# Some more examples, used by the feh developer
rfs --full-screen --hide-pointer --auto-zoom --randomize
diff --git a/man/Makefile b/man/Makefile
index 65f2bc2..8fe2cbd 100644
--- a/man/Makefile
+++ b/man/Makefile
@@ -12,6 +12,8 @@ all: ${TARGETS}
-e 's/\$$MAN_CURL\$$/${MAN_CURL}/' \
-e 's/\$$MAN_DEBUG\$$/${MAN_DEBUG}/' \
-e 's/\$$MAN_EXIF\$$/${MAN_EXIF}/' \
+ -e 's/\$$MAN_INOTIFY\$$/${MAN_INOTIFY}/' \
+ -e 's/\$$MAN_MAGIC\$$/${MAN_MAGIC}/' \
-e 's/\$$MAN_XINERAMA\$$/${MAN_XINERAMA}/' \
< ${@:.1=.pre} > $@
diff --git a/man/feh-cam.pre b/man/feh-cam.pre
deleted file mode 100644
index 4a02753..0000000
--- a/man/feh-cam.pre
+++ /dev/null
@@ -1,57 +0,0 @@
-.Dd $DATE$
-.Dt FEH-CAM 1
-.Os
-.
-.Sh NAME
-.Nm feh-cam
-.Nd utility for viewing live webcam images
-.Sh SYNOPSIS
-.Nm
-.Op Ar options
-.Ar keys
-.
-.Sh VERSION
-This manual documents feh-cam, shipped with feh version $VERSION$
-.
-.Sh DEPRECATION WARNING
-.
-This tool will be removed from the feh distribution by 2013.
-.
-.Pp
-.
-If you still use it, please contact me at
-.Aq derf@finalrewind.org
-.
-.Sh DESCRIPTION
-.Nm
-is a perl wrapper for feh which simplifies viewing webcams using keyed
-bookmarks. It helps manage viewing your favourite webcam sites with feh.
-.
-.Sh OPTIONS
-.Bl -tag -width indent
-.It Cm -a , --add Ar key url refresh
-Add
-.Ar url
-as
-.Ar key
-to the bookmarks file; when viewing, reload it every
-.Ar refresh
-seconds
-.It Cm -l , --list
-List each url / key pair in the bookmarks file
-.It Cm -f , --full-screen
-Start feh in full-screen mode
-.It Cm -k , --keep-images
-Save each webcam image in the current directory
-.It Cm -g , --geometry Ar width No x Ar height
-Use window geometry
-.Ar width No x Ar height
-.It Cm -v , --verbose
-Start feh in verbose mode
-.El
-.
-.Sh FILES
-The bookmarks are stored in
-.Pa ~/.cam_bookmarks
-.Sh SEE ALSO
-.Xr feh 1
diff --git a/man/feh.pre b/man/feh.pre
index 4a8b0f8..ca64d37 100644
--- a/man/feh.pre
+++ b/man/feh.pre
@@ -13,7 +13,8 @@
.
.Nm
.Op Ar options
-.Op Ar files No | Ar directories No | Ar URLs ...
+.Op Cm --
+.Op Ar files | Ar directories | Ar URLs ...
.
.
.Sh VERSION
@@ -24,34 +25,51 @@ $VERSION$
.
.Pp
.
-Compile-time switches: libcurl support $MAN_CURL$, Xinerama support
-$MAN_XINERAMA$, builtin EXIF support $MAN_EXIF$$MAN_DEBUG$
+Compile-time switches in this build:
.
+.Bl -bullet -compact
.
-.Sh DESCRIPTION
+.It
+libcurl remote file support $MAN_CURL$
.
-.Nm
-is a mode-based image viewer. It is especially aimed at command line users who
-need a fast image viewer without huge GUI dependencies, though it can also be
-started by
-.Pq graphical
-file managers to view an image.
-By default
-.Pq unless arguments or a filelist are specified ,
-.Nm
-displays all files in the current directory.
+.It
+Xinerama multi-monitor support $MAN_XINERAMA$
.
-.Pp
+.It
+libexif builtin EXIF reader $MAN_EXIF$
+.
+.It
+inotify-based auto-reload of changed files $MAN_INOTIFY$
+.
+.It
+libmagic $MAN_MAGIC$
+.
+.El
+.
+$MAN_DEBUG$
+.
+.
+.Sh DESCRIPTION
.
.Nm
-supports filelists, various image sorting modes, image captions, HTTP and more.
-Configurable keyboard and mouse shortcuts are used to control it.
+is a light-weight, configurable and versatile image viewer.
+It is aimed at command line users, but can also be started from graphical file
+managers.
+Apart from viewing images, it can compile text and thumbnail
+listings, show (un)loadable files, set X11 backgrounds, and more.
.
.Pp
.
-In many desktop environments,
+Features include filelists, various image sorting modes, custom action scripts,
+and image captions.
.Nm
-can also be used as wallpaper setter.
+can be controlled by configurable keyboard and mouse shortcuts, terminal
+input and signals.
+When no file arguments or filelists are specified and
+.Cm --start-at
+is not used,
+.Nm
+displays all files in the current directory.
.
.Pp
.
@@ -73,8 +91,9 @@ options.
.
.Pp
.
-Slideshow mode is the default. It opens a window and displays the first
-image in it; the slideshow position can be advanced
+Slideshow mode is the default.
+It opens a window and displays the first image in it;
+the slideshow position can be advanced
.Pq or otherwise changed
using keyboard and mouse shortcuts.
In slideshow mode, images can be deleted either from the filelist or from the
@@ -85,14 +104,17 @@ An image can also be read from stdin via
.
.Pp
.
-Montage mode forms a montage from the filelist. The resulting image can be
-viewed or saved, and its size can be limited by height, width or both.
+Montage mode forms a montage from the filelist.
+The resulting image can be viewed or saved,
+and its size can be limited by height, width or both.
.
.Pp
.
-Index mode forms an index print from the filelist. Image thumbnails are shown
-along with the filename, filesize and pixel size, printed using a truetype
-font of your choice. The resulting image can be viewed or saved, and its size
+Index mode forms an index print from the filelist.
+Image thumbnails are shown along with the filename,
+size and dimensions, printed using a truetype
+font of your choice.
+The resulting image can be viewed or saved, and its size
can be limited by height, width or both.
.
.Pp
@@ -103,37 +125,71 @@ the selected image in a new window.
.Pp
.
Multiwindow mode shows images in multiple windows, instead of as a slideshow
-in one window. Don't use with a large filelist ;)
+in one window.
+Don't use with a large filelist ;)
.
.Pp
.
-List mode doesn't display images. Instead, it outputs an
+List mode doesn't display images.
+Instead, it outputs an
.Cm ls Ns No - Ns style
listing of the files in the filelist, including image info such as size, number
-of pixels, type, etc. There is also a Customlist mode which prints image info
+of pixels, type, etc.
+There is also a Customlist mode which prints image info
in a custom format specified by a printf-like format string.
.
.Pp
.
.Nm
can also list either all the loadable files in a filelist or all the
-unloadable files. This is useful for preening a directory.
+unloadable files.
+This is useful for preening a directory.
.
.
.Sh SUPPORTED FORMATS
.
.Nm
-can open any format supported by Imlib2, most notably jpeg, png,
-pnm, tiff, and bmp. The gif format is also supported, but only for static
-images. In case of animations, only the first frame will be shown.
+can open any format supported by imlib2, most notably jpeg, png,
+pnm, tiff, and bmp.
+The gif format is also supported, but only for static images.
+In case of animations, only the first frame will be shown.
+.
+.Pp
+.
+When invoked with
+.Cm --conversion-timeout Ar timeout
+.Po
+and a non-negative
+.Ar timeout
+value
+.Pc ,
+.Nm
+also has limited support for various other file types by means of external
+conversion programs.
+If the dcraw binary is available,
+.Nm
+will use it to display the thumbnails embedded into RAW files provided by
+digital cameras and similar.
+If the ImageMagick convert binary is available,
+.Nm
+will use it to load file types such as svg, xcf, and otf.
.
-If the convert binary
-.Pq supplied by ImageMagick
-is available, it also has limited support for many other filetypes, such as
-svg, xcf and otf. Use
-.Cm --magick-timeout Ar num
-with a non-negative value to enable it.
+.Pp
.
+.Pq optional feature, $MAN_MAGIC$ in this build
+.Nm
+can use libmagic to only pass image files to Imlib2.
+When using
+.Nm
+with lots of non-image files
+.Pq especially with Imlib2 version 1.6.x or 1.7.0 ,
+this can speed up the detection of non-image files significantly.
+If you think that Imlib2 can load a file which
+.Nm
+has determined to be likely not an image, set the environment variable
+.Qq FEH_SKIP_MAGIC
+to pass all files directly to Imlib2, bypassing this check.
+The environment variable's value does not matter, it just needs to be set.
.
.Sh OPTIONS
.
@@ -141,11 +197,13 @@ with a non-negative value to enable it.
.
.It Cm -A , --action Oo Ar flag Oc Ns Oo [ Ar title ] Oc Ns Ar action
.
-Specify a shell command as an action to perform on the image. In slideshow or
-multiwindow mode, the action will be run when the action_0 key is pressed, in
-list mode, it will be run for each file. In loadable/unloadable mode, it will
-be run for each loadable/unloadable file, respectively. In thumbnail mode,
-clicking on an image will cause the action to run instead of opening the image.
+Specify a shell command as an action to perform on the image.
+In slideshow or multiwindow mode, the action will be run when
+the action_0 key is pressed, in list mode, it will be run for each file.
+In loadable/unloadable mode, it will be run for each loadable/unloadable
+file, respectively.
+In thumbnail mode, clicking on an image will cause the action to run instead
+of opening the image.
.
.Pp
.
@@ -162,7 +220,7 @@ after
executing the action.
.
If
-.No [ Ar title ]
+.Ar [ title ]
is specified
.Pq note the literal Qo \&[ Qc and Qo ] Qc ,
.Cm --draw-actions
@@ -170,10 +228,12 @@ will display
.Ar title
instead of
.Ar action
-in the action list. Note that
+in the action list.
+Note that
.Ar title
-must not start with a space. If it does, the action is handled as if it did
-not have a title. This special case exists for backwards compatibility reasons
+must not start with a space.
+If it does, the action is handled as if it did not have a title.
+This special case exists for backwards compatibility reasons
and makes sure that actions like
.Qq \&[ -L %F \&] && foo
still work.
@@ -181,20 +241,45 @@ still work.
.
.Pp
.
-The action will be executed by /bin/sh. Use format specifiers to refer to
-image info, see
+The action will be executed by /bin/sh.
+Use format specifiers to refer to image info, see
.Sx FORMAT SPECIFIERS
-for details. Example usage:
-.Qq feh -A Qo mv ~/images/%N Qc * .
+for details.
+Example usage:
+.Qq feh -A Qo mv %F ~/images/%N Qc * .
.
.It Cm --action1 No .. Cm --action9 Oo Ar flag Oc Ns Oo [ Ar title ] Oc Ns Ar action
.
Extra actions which can be set and triggered using the appropriate number key.
.
+.It Cm --auto-reload
+.
+.Pq optional feature, $MAN_INOTIFY$ in this build
+automatically reload image when the underlying file changes.
+Note that auto-reload
+.Pq if enabled in the build
+is on by default.
+This option is only useful to re-enable auto-reload after it has been
+disabled by a preceding
+.Cm --reload=0
+option.
+.
+.Pp
+.
+Automatic reload is not supported in montage, index, or thumbnail mode.
+.
.It Cm --auto-rotate
.
-.Pq only if compiled with exif=1
-Automatically rotate images based on EXIF data. Does not alter the image files.
+.Pq optional feature, $MAN_EXIF$ in this build
+.Pq deprecated in favor of Imlib2's auto-orientation support
+Automatically rotate images based on EXIF data.
+Does not alter the image files.
+.
+.Pp
+.
+Note that Imlib2 version 1.7.5+ performs auto-rotation by itself, so this option is obsolete on systems with Imlib2 version 1.7.5 or later.
+.Nm
+currently cannot detect this at runtime.
.
.It Cm -Z , --auto-zoom
.
@@ -204,73 +289,115 @@ Zoom pictures to screen size in fullscreen / fixed geometry mode.
.
Create borderless windows.
.
+.It Cm --cache-size Ar size
+.
+Set imlib2 in-memory cache to
+.Ar size
+MiB.
+A higher cache size can significantly improve performance especially for small
+slide shows, however at the cost of increased memory consumption.
+.Ar size
+must be between 0 and 2048 MiB and defaults to 4.
+.
.It Cm -P , --cache-thumbnails
.
-Enable thumbnail caching in
-.Pa ~/.thumbnails .
-Only works with thumbnails <= 256x256 pixels.
+Enable thumbnail caching.
+Thumbnails are saved in
+.Pa $XDG_CACHE_HOME/thumbnails ,
+which defaults to
+.Pa ~/.cache/thumbnails .
+Note that thumbnails are only cached if the configured thumbnail size does
+not exceed 256x256 pixels.
.
.It Cm -K , --caption-path Ar path
.
-Path to directory containing image captions. This turns on caption viewing,
-and if captions are found in
+Path to directory containing image captions.
+This turns on caption viewing, and if captions are found in
.Ar path ,
which is relative to the directory of each image, they are overlayed on the
-displayed image. E.g. with caption path
+displayed image.
+E.g. with caption path
.Qq captions/ ,
and viewing image
.Qq images/foo.jpg ,
the caption will be looked for in
.Qq images/captions/foo.jpg.txt .
.
+.It Cm --conversion-timeout Ar timeout
+.
+.Nm
+can use ImageMagick to try converting unloadable files into a supported
+file format.
+As this can take a long time, it is disabled by default.
+Set
+.Ar timeout
+to a non-negative value to enable it.
+A positive value
+specifies after how many seconds conversion attempts should be aborted,
+zero causes
+.Nm
+to try indefinitely.
+Negative values restore the default by disabling conversion altogether.
+.
+.It Cm --class Ar class
+.
+Set the X11 class hint to
+.Ar class .
+.
+Default: feh
+.
.It Cm -L , --customlist Ar format
.
Don't display images, print image info according to
.Ar format
-instead. See
+instead.
+See
.Sx FORMAT SPECIFIERS .
.
-.It Cm --cycle-once
-.
-Exit
-.Nm
-after one loop through the slideshow.
-.
.It Cm -G , --draw-actions
.
Draw the defined actions and what they do at the top-left of the image.
.
.It Cm --draw-exif
.
-.Pq only if compiled with exif=1
+.Pq optional feature, $MAN_EXIF$ in this build
display some EXIF information in the bottom left corner, similar to using
.Cm --info
-with exiv2 / exifgrep .
+with exiv2 / exifgrep.
.
.It Cm -d , --draw-filename
.
-Draw the filename at the top-left of the image.
+Draw the file name at the top-left of the image.
.
.It Cm --draw-tinted
.
Show overlay texts
.Pq as created by Cm --draw-filename No et al
-on a semi-transparent background to improve their readability
+on a semi-transparent background to improve their readability.
+.
+.It Cm --edit
+.
+Enable basic editing of files.
+This makes rotation and mirroring
+.Pq bound to Qo < Qc , Qo > Qc , Qo | Qc , and Qo _ Qc by default
+change the underlying file and not just its displayed content.
.
.It Cm -f , --filelist Ar file
.
-This option is similar to the playlists used by music software. If
+This option is similar to the playlists used by music software.
+If
.Ar file
exists, it will be read for a list of files to load, in the order they appear.
-The format is a list of image filenames, absolute or relative to the current
-directory, one filename per line.
+The format is a list of image file names, absolute or relative to the current
+directory, one file name per line.
.
.Pp
.
If
.Ar file
doesn't exist, it will be created from the internal filelist at the end of a
-viewing session. This is best used to store the results of complex sorts
+viewing session.
+This is best used to store the results of complex sorts
.Pq Cm -Spixels No for example
for later viewing.
.
@@ -282,7 +409,8 @@ will be saved to
.Ar file
when
.Nm
-exits. You can add files to filelists by specifying them on the command line
+exits.
+You can add files to filelists by specifying them on the command line
when also specifying the list.
.
.Pp
@@ -296,9 +424,11 @@ will read the filelist from its standard input.
.
.It Cm -e , --font Ar font
.
-Set global font. Should be a truetype font, resident in the current directory
-or the font directory, and should be defined in the form fontname/points, like
-.Qq myfont/12 .
+Set global font.
+Should be a truetype font, resident in the current directory or the font
+directory, and should be defined in the form fontname/size, like
+.Qq yudit/12
+.Pq which is the default .
.
.It Cm -C , --fontpath Ar path
.
@@ -307,9 +437,13 @@ Specify
as extra directory in which to search for fonts; can be used multiple times to
add multiple paths.
.
+.It Cm --tap-zones
+.
+Enable tap zones for previous/next file in slide show mode
+.
.It Cm --force-aliasing
.
-Disable antialiasing for zooming, background setting etc.
+Disable anti-aliasing for zooming, background setting etc.
.
.It Cm -I , --fullindex
.
@@ -325,7 +459,8 @@ Note: This option needs to load all images to calculate the dimensions of the
.Nm
window, so when using it with many files it will take a while before a
.Nm
-window is visible. Use
+window is visible.
+Use
.Cm --preload
to get a progress bar.
.
@@ -333,32 +468,34 @@ to get a progress bar.
.
Make the window fullscreen.
Note that in this mode, large images will always be scaled down to fit the
-screen,
+screen, and
.Cm --zoom Ar zoom
only affects smaller images and never scales larger than necessary to fit the
-screen size. The only exception is a
+screen size.
+The only exception is a
.Ar zoom
-of 100, in which case images will always be shown at 100% zoom, no matter
-their dimensions.
+of 100, in which case images will always be shown at 100% zoom.
.
.Pp
.
When combined with
.Cm --thumbnails ,
-this option only affects images opened from the thumbnail overview. The
-thumbnail list itself will still be windowed.
+this option only affects images opened from the thumbnail overview.
+The thumbnail list itself will still be windowed.
.
-.It Cm -g , --geometry Oo Ar width No x Ar height Oc Op + Ar x No + Ar y
+.It Cm -g , --geometry Ar width Cm x Ar height | Cm + Ar x Cm + Ar y | Ar width Cm x Ar height Cm + Ar x Cm + Ar y
.
-Limit (and don't change) the window size. Takes an X-style geometry
-.Ar string
-like 640x480 with optional +x+y window offset.
-Note that larger images will be zoomed out to fit, but you can see them at 1:1
-by clicking the zoom button.
+Use a fixed window size as specified in the X-style geometry
+.Ar string ,
+e.g. 640x480.
+An optional +x+y window offset can be specified.
+Combine with
+.Cm --scale-down
+to scale down larger images like in fullscreen mode.
.
-Also note that this option does not enforce the geometry, changing it by a tiling
-WM or manually is still possible. After each resize, the resulting window size
-is used as the new size limit.
+Note that this option does not enforce the window size; changing it by a tiling
+WM or manually is still possible.
+However, auto-resize remains disabled.
.
.It Cm -Y , --hide-pointer
.
@@ -367,26 +504,38 @@ Hide the pointer
.
.It Cm -B , --image-bg Ar style
.
-Use style as background for transparent image parts and the like.
-Accepted values: checks, white, black.
-.
-The default for windowed mode is checks, while fullscreen defaults to black.
+Use
+.Ar style
+as background for transparent image parts and the like.
+Accepted values: default, checks, or an XColor
+.Pq e.g. Qo black Qc or Qo #428bdd Qc .
+Note that some shells treat the hash symbol as a special character, so you
+may need to quote or escape it for the XColor code to work.
+.
+In windowed mode, the default is checks
+.Pq a checkered background so transparent image parts are easy to see .
+In fullscreen and background setting mode,
+.Cm checks
+is not accepted and the default is black.
.
.It Cm -i , --index
.
-Enable Index mode. Index mode is similar to montage mode, and accepts the
-same options. It creates an index print of thumbnails, printing the image
-name beneath each thumbnail. Index mode enables certain other options, see
+Enable Index mode.
+Index mode is similar to montage mode, and accepts the same options.
+It creates an index print of thumbnails, printing the image name beneath
+each thumbnail.
+Index mode enables certain other options, see
.Sx INDEX AND THUMBNAIL MODE OPTIONS
and
.Sx MONTAGE MODE OPTIONS .
.
-.It Cm --info Oo Ar flag Oc Ns Ar commandline
+.It Cm --info Oo Ar flag Oc Ns Ar command_line
.
Execute
-.Ar commandline
-and display its output in the bottom left corner of the image. Can be used to
-display e.g. image dimensions or EXIF information. Supports
+.Ar command_line
+and display its output in the bottom left corner of the image.
+Can be used to display e.g. image dimensions or EXIF information.
+Supports
.Sx FORMAT SPECIFIERS .
.
If
@@ -396,6 +545,12 @@ is set to
the output will not be displayed by default, but has to be enabled by the
toggle_info key.
.
+.It Cm --insecure
+.
+When viewing files with HTTPS, this option disables all certificate checks.
+It allows images on sites with self-signed or expired certificates to be
+opened, but is no more secure than plain HTTP.
+.
.It Cm -k , --keep-http
.
When viewing files using HTTP,
@@ -406,12 +561,6 @@ specified by
.Cm --output-dir ,
or in the current working directory.
.
-.It Cm --insecure
-.
-When viewing files with HTTPS, this option disables strict hostname and peer
-checking. This allows images on sites with self-signed certificates to be
-opened, but is no more secure than plain HTTP.
-.
.It Cm --keep-zoom-vp
.
When switching images, keep zoom and viewport settings
@@ -419,25 +568,20 @@ When switching images, keep zoom and viewport settings
.
.It Cm -l , --list
.
-Don't display images. Analyze them and display an
+Don't display images.
+Analyze them and display an
.Xr ls 1 - No style
-listing. Useful in scripts to hunt out images of a certain
-size/resolution/type etc.
+listing.
+Useful in scripts to hunt out images of a certain size/resolution/type etc.
.
.It Cm -U , --loadable
.
-Don't display images. Just print out their names if imlib2 can successfully
-load them.
+Don't display images.
+Just print out their names if imlib2 can successfully load them.
Returns false if at least one image failed to load.
-.
-.It Cm --magick-timeout Ar timeout
-.
-Stop trying to convert unloadable files after
-.Ar timeout
-seconds. A negative value disables covert / magick support altogether, a value
-of zero causes
-.Nm
-to try indefinitely. By default, magick support is disabled.
+When combined with
+.Cm --action ,
+the specified action will be run for each loadable image.
.
.It Cm --max-dimension Ar width No x Ar height
.
@@ -465,15 +609,32 @@ If you only care about one parameter, set the other to 0.
.
.It Cm -m , --montage
.
-Enable montage mode. Montage mode creates a new image consisting of a grid of
-thumbnails of the images in the filelist. When montage mode is selected,
-certain other options become available. See
+Enable montage mode.
+Montage mode creates a new image consisting of a grid of thumbnails of the
+images in the filelist.
+When montage mode is selected, certain other options become available.
+See
.Sx MONTAGE MODE OPTIONS .
.
.It Cm -w , --multiwindow
.
-Disable slideshow mode. With this setting, instead of opening multiple files
-in slideshow mode, multiple windows will be opened; one per file.
+Disable slideshow mode.
+With this setting, instead of opening multiple files in slideshow mode,
+multiple windows will be opened; one per file.
+.
+.It Cm --no-conversion-cache
+.
+When loading images via HTTP, ImageMagick or dcraw,
+.Nm
+will only load/convert them once and re-use the cached file on subsequent
+slideshow passes.
+This option disables the cache.
+It is also disabled when
+.Cm --reload
+is used.
+Use it if you rely on frequently changing files loaded via one of these
+sources.
+Note that it will impair performance.
.
.It Cm --no-jump-on-resort
.
@@ -485,80 +646,134 @@ Don't load or show any menus.
.
.It Cm --no-screen-clip
.
-By default, window sizes are limited to the screen size. With this option,
-windows will have the size of the image inside them. Note that they may
-become very large this way, making them unmanageable in certain window
-managers.
+By default, window sizes are limited to the screen size.
+With this option, windows will have the size of the image inside them.
+Note that they may become very large this way, making them unmanageable
+in certain window managers.
.
.It Cm --no-xinerama
.
-Disable Xinerama support. Only makes sense when you have Xinerama support
-compiled in.
+.Pq optional feature, $MAN_XINERAMA$ in this build
+Disable Xinerama support.
+.
+.It Cm --on-last-slide Cm hold | Cm quit | Cm resume
+.
+Select behaviour when trying to select the next image on the last slide
+.Pq or the previous image on the first slide
+in a slide show.
+.
+.Pp
+.
+With
+.Cm hold ,
+.Nm
+will stop advancing images in this case and continue displaying the first/last
+image, respectively.
+This is intended for linear slide shows.
+Behaviour is unspecified when using other navigation commands than previous
+and next image.
+.
+.Pp
+.
+.Cm quit
+will cause
+.Nm
+to quit when trying to advance past the last image in the slide show. This is
+the behavior of the obsolete
+.Cm --cycle-once
+option.
+.
+.Pp
+.
+.Cm resume
+is the default behaviour: On the last
+.Pq first
+image,
+.Nm
+will wrap around to the first
+.Pq last
+image.
.
.It Cm -j , --output-dir Ar directory
.
Save files to
.Ar directory
-.Pq only useful with -k .
+when using
+.Cm --keep-http
+or the save_image or save_filelist command.
By default, files are saved in the current working directory.
.
.It Cm -p , --preload
.
-Preload images. This doesn't mean hold them in RAM, it means run through
-them and eliminate unloadable images first. Otherwise they will be removed
-as you flick through. This also analyses the images to get data for use in
-sorting, such as pixel size, type etc. A preload run will be automatically
-performed if you specify one of these sort modes.
+Preload images.
+This doesn't mean hold them in RAM, it means run through them and eliminate
+unloadable images first.
+Otherwise they will be removed as you flick through.
+This also analyses the images to get data for use in sorting, such as pixel
+size, type etc.
+A preload run will be automatically performed if you specify one of these
+sort modes.
.
.It Cm -q , --quiet
.
-Don't report non-fatal errors for failed loads. Verbose and quiet modes are
-not mutually exclusive, the first controls informational messages, the second
-only errors.
+Don't report non-fatal errors for failed loads.
+Verbose and quiet modes are not mutually exclusive, the first controls
+informational messages, the second only errors.
.
.It Cm -z , --randomize
.
When viewing multiple files in a slideshow, randomize the file list before
-displaying. The list is re-randomized whenever the slideshow cycles (that is,
-transitions from last to first image).
+displaying.
+The list is re-randomized whenever the slideshow cycles (that is, transitions
+from last to first image).
.
.It Cm -r , --recursive
.
-Recursively expand any directories in the commandline arguments
+Recursively expand any directories in the command line arguments
to the content of those directories, all the way down to the bottom level.
.
.It Cm --no-recursive
.
-Don't recursively expand any directories (enabled by default).
-Useful to override theme options.
+Don't recursively expand any directories.
+This is the default, but this option is useful to override themes containing
+.Cm --recursive .
.
.It Cm -R , --reload Ar int
.
Reload filelist and current image after
.Ar int
-seconds. Useful for viewing HTTP webcams or frequently changing directories.
-.Pq Note that the filelist reloading is still experimental.
+seconds.
+Useful for viewing HTTP webcams or frequently changing directories.
+.Pq Note that filelist reloading is still experimental.
+Set to zero to disable any kind of automatic reloading.
.
.Pp
.
If an image is removed,
.Nm
-will either show the next one or quit. However, if an image still exists, but
-can no longer be loaded,
+will either show the next one or quit.
+However, if an image still exists, but can no longer be loaded,
.Nm
will continue to try loading it.
.
+.Pp
+.
+Setting this option causes inotify-based auto-reload to be disabled.
+Reload is not supported in montage, index, or thumbnail mode.
+.
.It Cm -n , --reverse
.
-Reverse the sort order. Use this to invert the order of the filelist.
+Reverse the sort order.
+Use this to invert the order of the filelist.
E.g. to sort in reverse width order, use
.Cm -nSwidth .
.
.It Cm -. , --scale-down
.
Scale images to fit window geometry (defaults to screen size when no geometry
-was specified). Note that the window geometry is not updated when changing
-images at the moment. This option is recommended for tiling window managers.
+was specified).
+Note that the window geometry is not updated when changing images at the moment.
+This option is recommended for tiling window managers.
.
This option is ignored when in fullscreen and thumbnail list mode.
.
@@ -571,17 +786,19 @@ In tiling environments, this also causes the image to be centered in the window.
Scroll
.Ar count
pixels whenever scroll_up, scroll_down, scroll_left or scroll_right is pressed.
-Note that this option accepts negative numbers in case you need to inverse the
-scroll direction; see
+Note that this option accepts negative numbers in case you need to reverse the
+scroll direction.
+See
.Sx KEYS CONFIG SYNTAX
-to change it permanently.
+for how to reverse it permanently.
Default: 20
.
.It Cm -D , --slideshow-delay Ar float
.
For slideshow mode, wait
.Ar float
-seconds between automatically changing slides. Useful for presentations.
+seconds between automatically changing slides.
+Useful for presentations.
Specify a negative number to set the delay
.Pq which will then be Ar float No * (-1) ,
but start
@@ -590,33 +807,78 @@ in paused mode.
.
.It Cm -S , --sort Ar sort_type
.
-The file list may be sorted according to image parameters. Allowed sort types
-are: name, filename, dirname, mtime, width, height, pixels, size, format. For
-sort modes other than name, filename, dirname, or mtime, a preload run will be
+Sort file list according to image parameters.
+Allowed sort types are:
+.Cm name , none , filename , dirname , mtime , width , height , pixels , size , format .
+For sort modes other than
+.Cm name , none , filename , dirname ,
+or
+.Cm mtime ,
+a preload run is
necessary, causing a delay proportional to the number of images in the list.
.
.Pp
.
-The mtime sort mode sorts images by most recently modified. To sort by oldest
-first, reverse the filelist with --reverse.
+.Cm mtime
+starts with the most recently modified image.
+.Cm width , height , pixels
+and
+.Cm size
+start with the smallest.
+Use
+.Cm --reverse
+to sort by oldest or largest first.
+.
+.Pp
+.
+For
+.Cm name , filename ,
+and
+.Cm dirname
+you can use
+.Cm --version-sort
+to sort numbers naturally, so that e.g. 10.jpg comes after 2.jpg.
.
+.Pp
+.
+.Cm none
+is the default; you can specify it explicitly to discard a sort mode that has
+been specified at an earlier point in the command line arguments.
.It Cm -| , --start-at Ar filename
.
Start the filelist at
.Ar filename .
-.
-Note that at the moment,
+If no other files or filelists were specified on the command line,
+.Nm
+will first load all files from the directory in which
.Ar filename
-must match an
-.Pq expanded
-path in the filelist. So, if the file to be matched is passed via an absolute
-path in the filelist,
+resides.
+This way, it's possible to look at a specific image and use the next / prev
+keys to browse through the directory.
+See
+.Sx USAGE EXAMPLES
+for examples.
+If
.Ar filename
-must be an absolute path. If the file is passed via a relative path,
+is a remote URL and no files or filelists were specified,
+.Nm
+will show
.Ar filename
-must be an identical relative path. This is a known issue.
-See also
-.Sx USAGE EXAMPLES .
+and not attempt to load additional files or directories.
+.
+.Pp
+.
+Note: If you use relative paths in your filelist,
+.Ar filename
+should also be a relative path.
+If you use absolute paths, it should also be an absolute path.
+.
+If
+.Nm
+cannot find an exact match, it will compare basenames
+.Pq filenames without the directory suffix .
+This may lead to mismatches if several files in your filelist
+have the same basename.
.
.It Cm -T , --theme Ar theme
.
@@ -624,7 +886,8 @@ Load options from config file with name
.Ar theme
- see
.Sx THEMES CONFIG SYNTAX
-for more info. Note that commandline options always override theme options.
+for more info.
+Note that command line options always override theme options.
The theme can also be set via the program name
.Pq e.g. with symlinks ,
so by default
@@ -640,8 +903,8 @@ Note that
.Cm --fullscreen
and
.Cm --scale-down
-do not affect the thumbnail window. They do, however, work for image windows
-launched from thumbnail mode.
+do not affect the thumbnail window.
+They do, however, work for image windows launched from thumbnail mode.
Also supports
.Sx INDEX AND THUMBNAIL MODE OPTIONS
as well as
@@ -651,20 +914,25 @@ as well as
.
Set
.Ar title
-for windows opened from thumbnail mode. See also
+for windows opened from thumbnail mode.
+See also
.Sx FORMAT SPECIFIERS .
.
.It Cm -^ , --title Ar title
.
-Set window title. Applies to all windows except those opened from thumbnail
-mode. See
+Set window title.
+Applies to all windows except those opened from thumbnail mode.
+See
.Sx FORMAT SPECIFIERS .
.
.It Cm -u , --unloadable
.
-Don't display images. Just print out their names if imlib2 can NOT
-successfully load them.
+Don't display images.
+Just print out their names if imlib2 can NOT successfully load them.
Returns false if at least one image was loadable.
+When combined with
+.Cm --action ,
+the specified action will be run for each unloadable file.
.
.It Cm -V , --verbose
.
@@ -674,35 +942,72 @@ output useful information, progress bars, etc.
.
output version information and exit.
.
-.It Cm --xinerama-index Ar screen
+.It Cm --version-sort
+.
+When combined with
+.Cm --sort name , --sort filename ,
+or
+.Cm --sort dirname :
+use natural sorting for file and directory names.
+In this mode, filenames are sorted as an ordinary human would expect, e.g.
+.Qq 2.jpg
+comes before
+.Qq 10.jpg .
+Note that this option only has an effect when a sort mode is set using
+.Cm --sort .
+.
+.It Cm --window-id Ar windowid
+.
+Draw to an existing X11 window by its ID
+.Ar windowid .
+This option is intended for use with software such as xcreensaver or
+xsecurelock, which provide a window for other applications to draw into.
+Unexpected things will happen if you specify a window belonging to software
+which does not expect
+.Nm
+to draw into it or attempt to use options or keybindings which affect window
+attributes, such as full-screen mode.
.
+.It Cm --xinerama-index Ar monitor
+.
+.Pq optional feature, $MAN_XINERAMA$ in this build
Override
.Nm Ns No 's
-idea of the active Xinerama screen. May be useful in certain circumstances
-where the window manager places the feh window on Xinerama screen A while
+idea of the active Xinerama monitor.
+May be useful in certain circumstances where the window manager places the feh
+window on Xinerama monitor A while
.Nm
-assumes that it will be placed on screen B.
+assumes that it will be placed on monitor B.
.
.Pp
.
In background setting mode: When used with any option other than
.Cm --bg-tile :
Only set wallpaper on
-.Ar screen .
-All other screens will be filled black/white.
-.
-This is most useful in a Xinerama configuration with
-overlapping screens. For instance, assume you have two overlapping displays
-(index 0 and 1), where index 0 is smaller. To center a background on the
-display with index 0 and fill the extra space on index 1 black/white, use
+.Ar monitor .
+All other monitors will be filled black/white.
+.
+This is most useful in a Xinerama configuration with overlapping monitors.
+For instance, assume you have two overlapping displays (index 0 and 1),
+where index 0 is smaller.
+To center a background on the display with index 0 and fill the extra space
+on index 1 black/white, use
.Qq --xinerama-index 0
when setting the wallpaper.
.
-.It Cm --zoom Ar percent No | Cm max No | Cm fill
+.Pp
+.
+Use
+.Cm xrandr --listmonitors
+to determine how Xinerama monitor IDs map to screens/monitors in your setup.
+.
+.
+.It Cm --zoom Ar percent | Cm max | Cm fill
.
Zoom images by
.Ar percent
-when in full screen mode or when window geometry is fixed. When combined with
+when in full screen mode or when window geometry is fixed.
+When combined with
.Cm --auto-zoom ,
zooming will be limited to the specified
.Ar percent .
@@ -718,8 +1023,14 @@ zoom the image like the
.Cm --bg-fill
mode.
.
-.El
+.It Cm --zoom-step Ar percent
.
+Zoom images in and out by
+.Ar percent
+.Pq default: 25
+when using the zoom keys and buttons.
+.
+.El
.
.Sh MONTAGE MODE OPTIONS
.
@@ -735,14 +1046,15 @@ When drawing thumbnails onto the background, set their transparency level to
.Ar int
.Pq 0 - 255 .
.
-.It Cm -b , --bg Ar file No | Cm trans
+.It Cm -b , --bg Ar file | Cm trans
.
Use
.Ar file
-as background for your montage. With this option specified, the montage size
-will default to the size of
+as background for your montage.
+With this option specified, the montage size will default to the size of
.Ar file
-if no size restrictions were specified. Alternatively, if
+if no size restrictions were specified.
+Alternatively, if
.Ar file
is
.Qq trans ,
@@ -751,8 +1063,8 @@ the background will be made transparent.
.It Cm -X , --ignore-aspect
.
By default, the montage thumbnails will retain their aspect ratios, while
-fitting into thumb-width/-height. This options forces them to be the size set
-by
+fitting into thumb-width/-height.
+This options forces them to be the size set by
.Cm --thumb-width No and Cm --thumb-height .
This will prevent any empty space in the final montage.
.
@@ -786,8 +1098,9 @@ without displaying it.
.It Cm -s , --stretch
.
Normally, if an image is smaller than the specified thumbnail size, it will
-not be enlarged. If this option is set, the image will be scaled up to fit
-the thumbnail size. Aspect ratio will be maintained unless
+not be enlarged.
+If this option is set, the image will be scaled up to fit the thumbnail size.
+Aspect ratio will be maintained unless
.Cm --ignore-aspect
is specified.
.
@@ -799,38 +1112,25 @@ Set thumbnail height.
.
Set thumbnail width.
.
-.It Cm -J , --thumb-redraw Ar n
-.
-Only relevant for
-.Cm --thumbnails :
-Redraw thumbnail window every
-.Ar n
-images. In
-.Nm
-<= 1.5, the thumbnail image used to be redrawn after every computed thumbnail
-.Pq so, it updated immediately .
-However, since the redrawing takes quite long
-.Pq especially for thumbnail mode on a large filelist ,
-this turned out to be a major performance penalty.
-As a workaround, the thumbnail image is redrawn every 10th image now by
-default. Set
-.Ar n No = 1
-to get the old behaviour,
-.Ar n No = 0
-will only redraw once all thumbnails are loaded.
-.
.El
.
.
.Sh INDEX AND THUMBNAIL MODE OPTIONS
.
+In addition to
+.Sx MONTAGE MODE OPTIONS
+.Cm --alpha , --bg , --limit-height , --limit-width , --output , --output-only ,
+.Cm --thumb-height , --thumb-width ,
+the following options can be used.
+.
.Bl -tag -width indent
.
.It Cm --index-info Ar format
.
Show image information based on
.Ar format
-below thumbnails in index / thumbnail mode. See
+below thumbnails in index / thumbnail mode.
+See
.Sx FORMAT SPECIFIERS .
May contain newlines.
.
@@ -846,7 +1146,8 @@ Note: If you specify image-related formats
needs to load all images to calculate the dimensions of its own window.
So when using them with many files, it will take a while before a
.Nm
-window becomes visible. Use
+window becomes visible.
+Use
.Cm --preload
to get a progress bar.
.
@@ -854,6 +1155,20 @@ to get a progress bar.
.
Set font to print a title on the index, if no font is specified, no title will
be printed.
+.
+.It Cm -J , --thumb-redraw Ar n
+.
+Redraw thumbnail window every
+.Ar n
+images while generating thumbnails.
+Redrawing takes quite long, so the default is 10.
+Set
+.Ar n No = 1
+to update the thumbnail window immediately.
+With
+.Ar n No = 0 ,
+there will only be one redraw once all thumbnails are loaded.
+.
.El
.
.
@@ -861,89 +1176,98 @@ be printed.
.
In many desktop environments,
.Nm
-can also be used as a background setter. Unless you pass the
+can also be used as a background setter.
+Unless you pass the
.Cm --no-fehbg
option, it will write a script to set the current background to
.Pa ~/.fehbg .
So to have your background restored every time you start X, you can add
-.Qq sh ~/.fehbg &
+.Qq ~/.fehbg &
to your X startup script
.Pq such as Pa ~/.xinitrc .
-As of
-.Nm
-2.13, this script is executable, so
-.Qq ~/.fehbg &
-will work as well.
+Note that the commandline written to
+.Pa ~/.fehbg
+always includes the
+.Cm --no-fehbg
+option to ensure that it is not inadvertently changed by differences in
+X11 screen layout or similar.
.
.Pp
.
Note that
.Nm
-does not support setting the wallpaper of GNOME shell desktops. In this
-environment, you can use
+does not support setting the wallpaper of GNOME shell desktops.
+In this environment, you can use
.Qq gsettings set org.gnome.desktop.background picture-uri file:/// Ns Ar path
instead.
.
.Pp
.
-For the
-.Cm --bg-center
+For
+.Cm --bg-center , --bg-fill ,
and
-.Cm --bg-max
-options, you can use the
+.Cm --bg-max ,
+you can use
.Cm --geometry
-option to specify an offset from one side of the screen instead of
-centering the image. Positive values will offset from the left/top
-side, negative values from the bottom/right. +0 and -0 are both
-valid and distinct values.
+to specify an offset from one side of the monitor instead of centering the image.
+Positive values will offset from the left/top side, negative values from the
+bottom/right.
++0 and -0 are both valid and distinct values.
.
.Pp
.
Note that all options except
.Cm --bg-tile
support Xinerama.
-For instance, if you have multiple screens connected and use e.g.
+For instance, if you have multiple monitors connected and use e.g.
.Cm --bg-center ,
.Nm
-will center or appropriately offset the image on each screen.
+will center or appropriately offset the image on each monitor.
You may even specify more than one file, in that case, the first file is set
-on screen 0, the second on screen 1, and so on.
+on monitor 0, the second on monitor 1, and so on.
+Use
+.Cm xrandr --listmonitors
+to determine how Xinerama monitor IDs map to screens / monitors in your setup.
.
.Pp
.
Use
.Cm --no-xinerama
-to treat the whole X display as one screen when setting wallpapers. You
-may also use
+to treat the whole X display as one monitor when setting wallpapers.
+You may also use
.Cm --xinerama-index
to use
.Nm
-as a background setter for a specific screen.
+as a background setter for a specific monitor.
.
.Bl -tag -width indent
.
.It Cm --bg-center
.
-Center the file on the background. If it is too small, it will be surrounded
-by a black border
+Center the file on the background.
+If it is too small, it will be surrounded by a border as specified by
+.Cm --image-bg .
.
.It Cm --bg-fill
.
Like
.Cm --bg-scale ,
-but preserves aspect ratio by zooming the image until it fits. Either a
-horizontal or a vertical part of the image will be cut off
+but preserves aspect ratio by zooming the image until it fits.
+Either a horizontal or a vertical part of the image will be cut off
.
.It Cm --bg-max
.
Like
.Cm --bg-fill ,
-but scale the image to the maximum size that fits the screen with black borders on one side.
+but scale the image to the maximum size that fits the screen with borders on one side.
+The border color can be set using
+.Cm --image-bg .
.
.It Cm --bg-scale
.
Fit the file into the background without repeating it, cutting off stuff or
-using borders. But the aspect ratio is not preserved either
+using borders.
+But the aspect ratio is not preserved either
.
.It Cm --bg-tile
.
@@ -964,6 +1288,10 @@ file
.
.Bl -tag -width indent
.
+.It %a
+.
+Information about slideshow state (playing/paused)
+.
.It %f
.
Image path/filename
@@ -973,6 +1301,10 @@ Image path/filename
Escaped image path/filename
.Pq for use in shell commands
.
+.It %g
+.
+w,h window dimensions in pixels (mnemonic: geometry)
+.
.It %h
.
Image height
@@ -983,7 +1315,8 @@ Total number of files in filelist
.
.It %L
.
-Temporary copy of filelist. Multiple uses of %L within the same format string will return the same copy.
+Temporary copy of filelist.
+Multiple uses of %L within the same format string will return the same copy.
.
.It %m
.
@@ -1007,12 +1340,14 @@ Number of image pixels
.
.It \&%P
.
-Number of image pixels
+Number of image pixels in human-readable format with k/M
.Pq kilopixels / megapixels
+suffix
.
.It %r
.
-Image rotation. A half right turn equals pi.
+Image rotation.
+A half right turn equals pi.
.
.It %s
.
@@ -1035,6 +1370,14 @@ Number of current file
.
Image width
.
+.\" .It %W
+.\" .
+.\" Window dimensions and offset as WxH+x+y
+.\" .Pq X11 geometry format .
+.\" Note that this is currently only properly updated when changing images;
+.\" offsets for the first image after starting feh and after changing window
+.\" geometry may be bogus.
+.
.It %v
.
.Nm
@@ -1046,7 +1389,11 @@ Process ID
.
.It %z
.
-current image zoom
+Current image zoom, rounded to two decimal places
+.
+.It %Z
+.
+Current image zoom, higher precision
.
.It %%
.
@@ -1077,7 +1424,7 @@ If the files are not found in that directory, it will also try
All config files treat lines starting with a
.Qq #
character as comments.
-Note that mid-line comments are not supported.
+Comments at the end of a line are not supported.
.
.
.Sh THEMES CONFIG SYNTAX
@@ -1095,29 +1442,42 @@ is the name of the entry and
.Ar options
are the options which will be applied when the theme is used.
.
-Note that the options are not parsed by any shell. Therefore, filename expansion
-.Po
-.Qq *.jpg
-and similar
-.Pc
-is not supported. Quoting with both single and double quotes works, though.
+.Pp
+.
+Note that the option parser does not behave like a normal shell: filename
+expansion and backslash escape sequences are not supported and passed to
+feh's option parser as-is.
+However, quoting of arguments is respected and can be used for arguments
+with whitespace.
+.
+So, the sequence
+.Qq --info Qq foo bar
+works as intended
+.Pq that is, it display the string Qq foo bar ,
+whereas the option string
+.Qq --info foo\e bar
+will only display
+.Qq foo\e
+and complain about the file bar not existing.
+Please keep this in mind when writing theme files.
.
.Pp
.
-An example entry would be
+An example entry is
.Qq imagemap -rVq --thumb-width 40 --thumb-height 30 --index-info \&'%n\en\&%wx\&%h\&' .
.
.Pp
.
-You can use this theme in two ways. Either call
+You can use this theme in two ways.
+Either call
.Qo
.Nm
-Timagemap *.jpg
.Qc ,
or create a symbolic link to
.Nm
-with the name of the theme you want it to use. For the example above,
-this would be
+with the name of the theme you want it to use.
+For the example above, this would be
.Qo
ln -s `which
.Nm
@@ -1131,6 +1491,9 @@ to use these options.
.
Note that you can split a theme over several lines by placing a backslash at
the end of an unfinished line.
+A single option-argument-pair must not span multiple lines.
+A single line must not be longer than 1023 characters, but there's no upper
+limit for the length of a theme.
.
.Pp
.
@@ -1184,9 +1547,9 @@ without any keys unbinds it (i.e. the default bindings are removed).
.
.Pp
.
-.Em Note :
-Do not use the same keybinding for multiple actions. When binding an action
-to a new key
+.Em Note:\&
+Do not use the same keybinding for multiple actions.
+When binding an action to a new key
.Pq or mouse button ,
make sure to unbind it from its previous action, if present.
.Nm
@@ -1210,8 +1573,20 @@ do not.
.
.Sh KEYS
.
-In an image window, the following keys may be used
-.Pq The strings in Bo square brackets Bc are the config action names :
+The following actions and default key bindings can be used in an image window.
+.Pq The strings in Bo square brackets Bc are the config action names .
+.
+If
+.Nm
+is running inside a terminal and its standard input is not used for images or
+filelists, key input from the terminal is also accepted.
+However, terminal input support is currently limited to most alphanumeric
+characters
+.Pq 0-9 a-z A-Z and some more ,
+arrow keys, return and backspace.
+The Alt
+.Pq Mod1
+modifier is also supported.
.
.Bl -tag -width indent
.
@@ -1226,12 +1601,13 @@ Enable/Disable anti-aliasing
.
.It c Bq toggle_caption
.
-Caption entry mode. If
+Caption entry mode.
+If
.Cm --caption-path
-has been specified, then this enables caption editing. The caption at the
-bottom of the screen will turn yellow and can be edited. Hit return to confirm
-and save the caption, or escape to cancel editing. Note that you can insert
-an actual newline into the caption using
+has been specified, then this enables caption editing.
+The caption at the bottom of the screen will turn yellow and can be edited.
+Hit return to confirm and save the caption, or escape to cancel editing.
+Note that you can insert an actual newline into the caption using
.Aq Ctrl+return .
.
.It d Bq toggle_filenames
@@ -1241,18 +1617,21 @@ Toggle filename display
.
.It e Bq toggle_exif
.
-.Pq only if compiled with exif=1
+.Pq optional feature, $MAN_EXIF$ in this build
Toggle EXIF tag display
.
-.It f Bq save_filelist
+.It f Bq toggle_fullscreen
.
-Save the current filelist as
-.Qq feh_PID_ID_filelist
+Toggle fullscreen
+.
+.It g Bq toggle_fixed_geometry
+.
+Enable/Disable automatic window resize when changing images.
.
.It h Bq toggle_pause
.
-Pause/Continue the slideshow. When it is paused, it will not automatically
-change slides based on
+Pause/Continue the slideshow.
+When it is paused, it will not automatically change slides based on
.Cm --slideshow-delay .
.
.It i Bq toggle_info
@@ -1262,19 +1641,30 @@ Toggle info display
.
.It k Bq toggle_keep_vp
.
-Toggle zoom and viewport keeping. When enabled,
+Toggle zoom and viewport keeping.
+When enabled,
.Nm
will keep zoom and X, Y offset when switching images.
.
+.It L Bq save_filelist
+.
+Save the current filelist as
+.Qq feh_PID_ID_filelist .
+It is saved in the directory specified by
+.Cm --output-dir ,
+if set, and in the current working directory otherwise.
+.
.It m Bq toggle_menu
.
-Show menu. Use the arrow keys and return to select items,
+Show menu.
+Use the arrow keys and return to select items, and
.Aq escape
to close the menu.
.
.It n , Ao space Ac , Ao Right Ac Bq next_img
.
-Show next image. Selects the next image in thumbnail mode.
+Show next image.
+Selects the next image in thumbnail mode.
.
.It o Bq toggle_pointer
.
@@ -1282,7 +1672,8 @@ Toggle pointer visibility
.
.It p , Ao BackSpace Ac , Ao Left Ac Bq prev_img
.
-Show previous image. Selects the previous image in thumbnail mode.
+Show previous image.
+Selects the previous image in thumbnail mode.
.
.It q , Ao Escape Ac Bq quit
.
@@ -1291,23 +1682,22 @@ Quit
.
.It r Bq reload_image
.
-Reload current image. Useful for webcams
+Reload current image.
+Useful for webcams
.
.It s Bq save_image
.
Save the current image as
-.Qq feh_PID_ID_FILENAME
-.
-.It v Bq toggle_fullscreen
-.
-Toggle fullscreen
+.Qq feh_PID_ID_FILENAME .
+It is saved in the directory specified by
+.Cm --output-dir ,
+if set, and in the current working directory otherwise.
.
.It w Bq size_to_image
.
Change window size to fit current image size
.Pq plus/minus zoom, if set .
-In scale-down and fixed-geometry mode, this also updates the window size
-limits.
+In scale-down and fixed-geometry mode, this also updates the window size limits.
.
.It x Bq close
.
@@ -1317,37 +1707,59 @@ Close current window
.
Jump to a random position in the current filelist
.
+.It Z Bq toggle_auto_zoom
+.
+Toggle auto-zoom.
+.
.It \&[, \&] Bq prev_dir, next_dir
.
Jump to the first image of the previous or next sequence of images sharing
-a directory name in the current filelist. Use --sort dirname if you would
-like to ensure that all images in a directory are grouped together.
+a directory name in the current filelist.
+Use --sort dirname if you would like to ensure that all images in a directory
+are grouped together.
.
.It < , > Bq orient_3 , orient_1
.
-In place editing - rotate the image 90 degrees (counter)clockwise.
-The rotation is lossless, but may create artifacts in some image corners when
-used with JPEG images. Rotating in the reverse direction will make them go
-away. See
+rotate the image 90 degrees (counter)clockwise.
+.
+.Pp
+.
+When
+.Cm --edit
+is used, this also rotates the image in the underlying file.
+Rotation is lossless, but may create artifacts in some image corners when
+used with JPEG images.
+Rotating in the reverse direction will make them go away.
+See
.Xr jpegtran 1
for more about lossless JPEG rotation.
.
-.Em Note:
+.Em Note:\&
.Nm
-assumes that this feature is used to normalize image orientation. For JPEG
-images, it will unconditionally set the EXIF orientation tag to 1
+assumes that this feature is used to normalize image orientation.
+For JPEG images, it will unconditionally set the EXIF orientation
+tag to 1
.Pq Qq 0,0 is top left
-after every rotation. See
+after every rotation.
+See
.Xr jpegexiforient 1
for details on how to change this flag.
.
.It _ Bq flip
.
-In place editing - vertical flip
+Vertically flip image.
+When
+.Cm --edit
+is used, this also flips the image in the underlying file
+.Pq see above .
.
.It | Bq mirror
.
-In place editing - horizontal flip
+Horizontally flip image.
+When
+.Cm --edit
+is used, this also flips the image in the underlying file
+.Pq see above .
.
.It 0 .. 9 Bq action_0 .. action_9
.
@@ -1406,8 +1818,8 @@ Scroll up
.It Ao keypad Down Ac , Ao Ctrl+Down Ac Bq scroll_down
.
Scroll down.
-Note that the scroll keys work without anti-aliasing for performance reasons,
-hit the render key after scrolling to antialias the image.
+Note that the scroll keys work without anti-aliasing for performance reasons;
+hit the render key after scrolling to anti-alias the image.
.
.It Aq Alt+Left Bq scroll_left_page
.
@@ -1427,7 +1839,7 @@ Scroll down by one page
.
.It R, Ao keypad begin Ac Bq render
.
-Antialias the image.
+Anti-alias the image.
Opens the currently selected image in thumbnail mode.
.
.It Ao keypad + Ac , Ao Up Ac Bq zoom_in
@@ -1498,8 +1910,8 @@ This works like the keys config file: the entries are of the form
.
Each
.Ar binding
-is a button number. It may optionally start with modifiers for things like
-Control, in which case
+is a button number.
+It may optionally start with modifiers for things like Control, in which case
.Ar binding
looks like
.Ar mod Ns No - Ns Ar button
@@ -1507,7 +1919,7 @@ looks like
.
.Pp
.
-.Em Note :
+.Em Note:\&
Do not use the same button for multiple actions.
.Nm
does not check for conflicting bindings, so their behaviour is undefined.
@@ -1536,6 +1948,13 @@ section can also be bound to a button.
.
Reload current image
.
+.It 0 Ao cursor movement while not panning, zooming, or similar Ac
+.
+Does not have a default binding.
+By binding it to
+.Cm quit ,
+you can turn feh into a simple screensaver.
+.
.It 1 Ao left mouse button Ac Bq pan
.
pan the current image
@@ -1609,16 +2028,17 @@ will warp your cursor to the opposite border so you can continue panning.
.Pp
.
When clicking the zoom button and immediately releasing it, the image will be
-back at 100% zoom. When clicking it and moving the mouse while holding the
-button down, the zoom will be continued at the previous zoom level. The zoom
-will always happen so that the pixel on which you entered the zoom mode
-remains stationary. So, to enlarge a specific part of an image, click the
-zoom button on that part.
+back at 100% zoom.
+When clicking it and moving the mouse while holding the button down, the zoom
+will be continued at the previous zoom level.
+The zoom will always happen so that the pixel on which you entered the zoom mode
+remains stationary.
+So, to enlarge a specific part of an image, click the zoom button on that part.
.
.
.Sh SIGNALS
.
-In slideshow mode,
+In slideshow and multiwindow mode,
.Nm
handles the following signals:
.
@@ -1626,135 +2046,173 @@ handles the following signals:
.
.It Dv SIGUSR1
.
-Switch to next image
+Slideshow mode: switch to next image;
+reload current image if the slideshow consists of a single file.
+Multiwindow mode: reload all images.
.
.It Dv SIGUSR2
.
-Switch to previous image
+Slideshow mode: switch to previous image;
+reload current image if the slideshow consists of a single file.
+Multiwindow mode: reload all images.
.
.El
.
.
.Sh USAGE EXAMPLES
.
-Here are some examples of useful option combinations. See also:
-.Aq http://feh.finalrewind.org/examples/
+Here are some examples of useful option combinations.
+See also:
+.Aq https://feh.finalrewind.org/examples/
.
.Bl -tag -width indent
.
-.It feh /opt/images
+.It feh ~/Pictures
.
-Show all images in /opt/images
+Show all images in ~/Pictures
.
-.It feh -r /opt/images
+.It feh -r ~/Pictures
.
-Recursively show all images found in /opt/images and subdirectories
+Recursively show all images found in ~/Pictures and subdirectories
.
-.It feh -rSfilename /opt/images
+.It feh -rSfilename --version-sort ~/Pictures
.
-Same as above, but sort by filename. By default, feh will show files in the
-order it finds them on the hard disk, which is usually somewhat random.
+Same as above, but sort naturally.
+By default, feh will show files in the string order of their names, meaning e.g.
+.Qq foo 10.jpg
+will come before
+.Qq foo 2.jpg .
+In this case, they are instead ordered as a human would expect.
.
-.It feh -t -Sfilename -E 128 -y 128 -W 1024 /opt/images
+.It feh -t -Sfilename -E 128 -y 128 -W 1024 ~/Pictures
.
Show 128x128 pixel thumbnails, limit window width to 1024 pixels.
.
-.It feh -t -Sfilename -E 128 -y 128 -W 1024 -P -C /usr/share/fonts/truetype/ttf-dejavu/ -e DejaVuSans/8 /opt/images
+.It feh -t -Sfilename -E 128 -y 128 -W 1024 -P -C /usr/share/fonts/truetype/ttf-dejavu/ -e DejaVuSans/8 ~/Pictures
.
-Same as above, but enable thumbnail caching in ~/.thumbnails and use a smaller
-font.
+Same as above, but enable thumbnail caching and use a smaller font.
.
-.It feh -irFarial/14 -O index.jpg /opt/images
+.It feh -irFarial/14 -O index.jpg ~/Pictures
.
-Make an index print of /opt/images and all directories below it, using 14 point
-Arial to write the image info under each thumbnail. Save the image as
-index.jpg and don't display it, just exit. Note that this even works without
-a running X server
+Make an index print of ~/Pictures and all directories below it, using 14 point
+Arial to write the image info under each thumbnail.
+Save the image as index.jpg and don't display it, just exit.
+Note that this even works without a running X server
.
-.It feh --unloadable -r /opt/images
+.It feh --unloadable -r ~/Pictures
.
-Print all unloadable images in /opt/images, recursively
+Print all unloadable images in ~/Pictures, recursively
.
.It feh -f by_width -S width --reverse --list \&.
.
Write a list of all images in the directory to by_width, sorted by width
.Pq widest images first
.
-.It feh -w /opt/images/holidays
+.It feh -w ~/Pictures/holidays
.
-Open each image in /opt/images/holidays in its own window
+Open each image in ~/Pictures/holidays in its own window
.
-.It feh -FD5 -Sname /opt/images/presentation
+.It feh -FD5 -Sname ~/Pictures/presentation
.
Show the images in .../presentation, sorted by name, in fullscreen,
automatically change to the next image after 5 seconds
.
-.It feh -rSwidth -A Qo mv %F ~/images/\&%N Qc /opt/images
+.It feh -rSwidth -A Qo mv %F ~/images/\&%N Qc ~/Pictures
.
-View all images in /opt/images and below, sorted by width, move an image to
+View all images in ~/Pictures and below, sorted by width, move an image to
~/image/image_name when enter is pressed
.
-.It feh --start-at ./foo.jpg \&.
+.It feh --start-at ~/Pictures/foo.jpg
.
-View all images in the current directory, starting with foo.jpg. All other
-images are still in the slideshow and can be viewed normally
+View all images in ~/Pictures, starting with foo.jpg.
+All other images are still in the slideshow and can be viewed normally
.
-.It feh --start-at foo.jpg *
+.It feh --start-at ~/Pictures/foo.jpg ~/Pictures
.
-Same as above
+Same as above.
.
.It feh --info \&"exifgrep '\&(Model\&|DateTimeOriginal\&|FNumber\&|ISO\&|Flash\&)' %F \&| cut -d \&. -f 4-\&" \&.
.
Show some EXIF information, extracted by exifprobe/exifgrep
.
-.It feh --action 'rm %F' -rl --max-dim 1000x800
+.It feh --action 'rm %F' -rl --max-dimension 1000x800
.
Recursively remove all images with dimensions below or equal to 1000x800 pixels
from the current directory.
.
+.It feh -L '%w %h %f' \&| awk '{ if \&($1 > $2\&) { print $0 } }' \&| cut -d ' ' -f 3- \&| feh -f -
+.
+Show landscape pictures
+.Pq image width greater than height
+in the current directory.
+.
.El
.
.
.Sh DEPENDENCIES
.
+When
+.Cm --edit
+is used,
.Nm
-requires the
+needs the
.Cm jpegtran
and
.Cm jpegexiforient
binaries
.Pq usually distributed in Qo libjpeg-progs Qc or similar
-for lossless rotation.
+for lossless JPEG rotation.
.
.Pp
.
-To view images from URLs such as http://, you need
+To view images from URLs such as "http://",
.Nm
-compiled with libcurl support (enabled by default). See the
-.Sx VERSION
-section.
+must be compiled with libcurl support.
+It is $MAN_CURL$ in this build.
.
.
-.Sh BUGS
+.Sh KNOWN BUGS
.
+Imlib2 releases prior to 1.7.4 are unable to load gif, tiff, and webp images
+if the filename does not end with gif, tiff, or webp, respectively.
+Notably, this means that feh is unable to show gif, tiff, and webp images from
+stdin or network URLs on systems using an Imlib2 version older than 1.7.4.
+.
+.Pp
+.
+Imlib2 releases 1.7.5+ automatically rotate images based on their EXIF tags.
+When combined with the
+.Cm --auto-rotate
+option, this causes images to be rotated twice and end up in an incorrect orientation.
+As a workaround, do not use
+.Cm --auto-rotate
+on systems with Imlib2 version 1.7.5 or later.
.Pp
.
On systems with giflib 5.1.2,
.Nm
-may be unable to load gif images. For affected mips, mipsel and arm devices,
-gif support is completely broken, while on x86 / x86_64 gifs can usually
-only be loaded if they are the first image in the filelist.
+may be unable to load gif images.
+For affected mips, mipsel and arm devices, gif support is completely
+broken, while on x86 / x86_64 gifs can usually only be loaded if they are
+the first image in the filelist.
This appears to be a bug in giflib,
see
.Aq https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=813729
-for details. Workaround: Use
-.Cm --magick-timeout 5
+for details.
+Workaround: Use
+.Cm --conversion-timeout 5
.Pq or some other positive value
to load gifs with imagemagick instead, or downgrade to giflib 5.1.1, or
upgrade to giflib 5.1.4.
.
.Pp
.
+While loading images using libcurl,
+.Nm
+will not react to key or mouse actions.
+.
+.Pp
+.
Thumbnail mode is somewhat inefficient, and because of that not nearly as fast
as it could be.
.
@@ -1764,40 +2222,28 @@ as it could be.
does not take window decorations into account and may therefore make the
window slightly too large.
.
-.
.Ss REPORTING BUGS
.
If you find a bug, please report it to
.Aq derf+feh@finalrewind.org
or via
-.Aq http://github.com/derf/feh/issues .
-You are also welcome to direct any feh-related comments/questions/... to #feh
-on irc.oftc.net.
+.Aq https://github.com/derf/feh/issues .
.
.Pp
.
Please include the feh version
.Aq the output of Qq feh --version ,
steps to reproduce the bug and, if necessary, images to reproduce it.
-.
-.
-.Sh FUTURE PLANS
-.
-Plans for the following releases:
-.
-.Bl -bullet -compact
-.
-.It
-Make zoom options more intuitive
-.
-.El
+Note that
+.Nm
+is a hobby project, so bug reports may be addressed with significant delays.
.
.
.Sh LICENSE
.
Copyright (C) 1999, 2000 by Paul Duncan.
-Copyright (C) 1999, 2000 by Tom Gilbert (and various contributors).
-Copyright (C) 2010-2016 by Daniel Friesel (and even more contributors).
+Copyright (C) 1999, 2000 by Tom Gilbert and contributors.
+Copyright (C) 2010-2025 by Birte Kristina Friesel and contributors.
.
.Pp
.
@@ -1819,14 +2265,15 @@ 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.
+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.
.
.Pp
.
-Current developer: Daniel Friesel
+Current developer: Birte Friesel
.Aq derf@finalrewind.org
.
.Pp
@@ -1838,5 +2285,5 @@ Tom Gilbert
.
.Pp
.
-See also:
-http://feh.finalrewind.org
+Website:
+https://feh.finalrewind.org
diff --git a/man/gen-cam-menu.pre b/man/gen-cam-menu.pre
deleted file mode 100644
index 18064f1..0000000
--- a/man/gen-cam-menu.pre
+++ /dev/null
@@ -1,33 +0,0 @@
-.Dd $DATE$
-.Dt GEN-CAM-MENU 1
-.Os
-.
-.Sh NAME
-.Nm gen-cam-menu
-.Nd utility for updating Enlightenment user menus for feh-cam
-.Sh SYNOPSIS
-.Nm
-.
-.Sh VERSION
-This manual documents gen-cam-menu, shipped with feh version $VERSION$
-.
-.Sh DEPRECATION WARNING
-.
-This tool will be removed from the feh distribution by 2013.
-.
-.Pp
-.
-If you still use it, please contact me at
-.Aq derf@finalrewind.org
-.
-.Sh DESCRIPTION
-.Nm
-is a shell script which creates Enlightenment user menu entries for the
-bookmarks stored by
-.Xr feh-cam 1
-in
-.Pa ~/.enlightenment/ .
-.
-.Sh SEE ALSO
-.Xr feh 1 ,
-.Xr feh-cam 1
diff --git a/scripts/checkkeys.pl b/scripts/checkkeys.pl
index 54978c5..ecf01e0 100755
--- a/scripts/checkkeys.pl
+++ b/scripts/checkkeys.pl
@@ -1,5 +1,5 @@
#!/usr/bin/env perl
-## Copyright © 2011 by Daniel Friesel <derf@finalrewind.org>
+## Copyright © 2011 by Birte Kristina Friesel <derf@finalrewind.org>
## License: WTFPL:
## 0. You just DO WHAT THE FUCK YOU WANT TO.
use strict;
diff --git a/scripts/checkopts.pl b/scripts/checkopts.pl
index 3a4b884..e5779ca 100755
--- a/scripts/checkopts.pl
+++ b/scripts/checkopts.pl
@@ -1,5 +1,5 @@
#!/usr/bin/env perl
-## Copyright © 2010 by Daniel Friesel <derf@finalrewind.org>
+## Copyright © 2010 by Birte Kristina Friesel <derf@finalrewind.org>
## License: WTFPL <http://sam.zoy.org/wtfpl>
use strict;
use warnings;
diff --git a/share/applications/feh.pre b/share/applications/feh.pre
index 8eb592a..8007630 100644
--- a/share/applications/feh.pre
+++ b/share/applications/feh.pre
@@ -3,11 +3,11 @@ Name=Feh
Name[en_US]=feh
GenericName=Image viewer
GenericName[en_US]=Image viewer
-Comment=Fast Imlib2-based Image Viewer
-Exec=feh %U
+Comment=Image viewer and cataloguer
+Exec=feh --start-at %u
Terminal=false
Type=Application
Icon=feh
Categories=Graphics;2DGraphics;Viewer;
-MimeType=image/bmp;image/gif;image/jpeg;image/jpg;image/pjpeg;image/png;image/tiff;image/x-bmp;image/x-pcx;image/x-png;image/x-portable-anymap;image/x-portable-bitmap;image/x-portable-graymap;image/x-portable-pixmap;image/x-tga;image/x-xbitmap;
+MimeType=image/bmp;image/gif;image/jpeg;image/jpg;image/pjpeg;image/png;image/tiff;image/webp;image/x-bmp;image/x-pcx;image/x-png;image/x-portable-anymap;image/x-portable-bitmap;image/x-portable-graymap;image/x-portable-pixmap;image/x-tga;image/x-xbitmap;image/heic;
NoDisplay=true
diff --git a/share/images/feh.svg b/share/images/feh.svg
index 7984c46..66f3f1c 100644
--- a/share/images/feh.svg
+++ b/share/images/feh.svg
@@ -2,198 +2,106 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="100"
height="100"
- id="svg2"
version="1.1"
- inkscape:version="0.91 r13725"
- sodipodi:docname="feh.svg">
- <defs
- id="defs4">
+>
+ <defs>
<linearGradient
- id="linearGradient3659"
- inkscape:collect="always">
- <stop
- id="stop3661"
- offset="0"
- style="stop-color:#cd022f;stop-opacity:1;" />
- <stop
- id="stop3663"
- offset="1"
- style="stop-color:#cd022f;stop-opacity:0;" />
- </linearGradient>
- <linearGradient
- id="linearGradient3619">
+ id="linear_gradient_for_letters"
+ x1="60.296875"
+ y1="1026.6521"
+ x2="141.11719"
+ y2="1026.6521"
+ gradientUnits="userSpaceOnUse"
+ >
<stop
- style="stop-color:#b60000;stop-opacity:0.80392158;"
offset="0"
- id="stop3621" />
+ stop-color="#cd022f"
+ stop-opacity="1"
+ />
<stop
- style="stop-color:#b60000;stop-opacity:0;"
offset="1"
- id="stop3623" />
+ stop-color="#cd022f"
+ stop-opacity="0"
+ />
</linearGradient>
- <inkscape:perspective
- sodipodi:type="inkscape:persp3d"
- inkscape:vp_x="0 : 526.18109 : 1"
- inkscape:vp_y="0 : 1000 : 0"
- inkscape:vp_z="744.09448 : 526.18109 : 1"
- inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
- id="perspective10" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient3659"
- id="linearGradient3667"
- x1="60.296875"
- y1="1026.6521"
- x2="141.11719"
- y2="1026.6521"
- gradientUnits="userSpaceOnUse" />
<filter
- id="filter3859"
- inkscape:label="Drop shadow"
+ id="filter_for_shadow"
width="1.5"
height="1.5"
x="-0.25"
- y="-0.25">
+ y="-0.25"
+ >
<feGaussianBlur
- id="feGaussianBlur3861"
in="SourceAlpha"
- stdDeviation="1.000000"
- result="blur" />
+ stdDeviation="1"
+ result="blur"
+ />
<feColorMatrix
- id="feColorMatrix3863"
result="bluralpha"
type="matrix"
- values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.900000 0 " />
+ values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.9 0"
+ />
<feOffset
- id="feOffset3865"
in="bluralpha"
- dx="2.000000"
- dy="2.000000"
- result="offsetBlur" />
- <feMerge
- id="feMerge3867">
+ dx="2"
+ dy="2"
+ result="offsetBlur"
+ />
+ <feMerge>
<feMergeNode
- id="feMergeNode3869"
- in="offsetBlur" />
+ in="offsetBlur"
+ />
<feMergeNode
- id="feMergeNode3871"
- in="SourceGraphic" />
+ in="SourceGraphic"
+ />
</feMerge>
</filter>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient3659"
- id="linearGradient3356"
- gradientUnits="userSpaceOnUse"
- x1="60.296875"
- y1="1026.6521"
- x2="141.11719"
- y2="1026.6521" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient3659"
- id="linearGradient3358"
- gradientUnits="userSpaceOnUse"
- x1="60.296875"
- y1="1026.6521"
- x2="141.11719"
- y2="1026.6521" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient3659"
- id="linearGradient3360"
- gradientUnits="userSpaceOnUse"
- x1="60.296875"
- y1="1026.6521"
- x2="141.11719"
- y2="1026.6521" />
</defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="2"
- inkscape:cx="74.605459"
- inkscape:cy="135"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="false"
- inkscape:window-width="1088"
- inkscape:window-height="883"
- inkscape:window-x="0"
- inkscape:window-y="0"
- inkscape:window-maximized="0" />
- <metadata
- id="metadata7">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
<g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(0,-952.3622)">
+ transform="translate(-50.707033,-976.6521)"
+ opacity="1"
+ fill-opacity="1"
+ fill-rule="nonzero"
+ stroke="none"
+ >
+ <!-- shadows -->
+ <g
+ fill="black"
+ filter="url(#filter_for_shadow)"
+ >
+ <!-- shadow for letter "F" -->
+ <path
+ d="m 60.296875,1012.072 20.292969,0 0,5.6836 -12.773438,0 0,5.4297 12.011719,0 0,5.6836 -12.011719,0 0,12.3633 -7.519531,0 0,-29.1602"
+ />
+ <!-- shadow for letter "E" -->
+ <path
+ d="m 87.640625,1012.072 20.292965,0 0,5.6836 -12.77343,0 0,5.4297 12.01172,0 0,5.6836 -12.01172,0 0,6.6797 13.20312,0 0,5.6836 -20.722655,0 0,-29.1602"
+ />
+ <!-- shadow for letter "H" -->
+ <path
+ d="m 114.98438,1012.072 7.51953,0 0,11.1133 11.09375,0 0,-11.1133 7.51953,0 0,29.1602 -7.51953,0 0,-12.3633 -11.09375,0 0,12.3633 -7.51953,0 0,-29.1602"
+ />
+ </g>
+ <!-- letters -->
<g
- id="g3362"
- transform="translate(-50.707033,-24.2899)">
- <g
- id="g3669"
- style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-opacity:1;filter:url(#filter3859)">
- <path
- inkscape:connector-curvature="0"
- d="m 60.296875,1012.072 20.292969,0 0,5.6836 -12.773438,0 0,5.4297 12.011719,0 0,5.6836 -12.011719,0 0,12.3633 -7.519531,0 0,-29.1602"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'DejaVu Sans';-inkscape-font-specification:'Catharsis Bedouin Bold';fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-opacity:1"
- id="path3671" />
- <path
- inkscape:connector-curvature="0"
- d="m 87.640625,1012.072 20.292965,0 0,5.6836 -12.77343,0 0,5.4297 12.01172,0 0,5.6836 -12.01172,0 0,6.6797 13.20312,0 0,5.6836 -20.722655,0 0,-29.1602"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'DejaVu Sans';-inkscape-font-specification:'Catharsis Bedouin Bold';fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-opacity:1"
- id="path3673" />
- <path
- inkscape:connector-curvature="0"
- d="m 114.98438,1012.072 7.51953,0 0,11.1133 11.09375,0 0,-11.1133 7.51953,0 0,29.1602 -7.51953,0 0,-12.3633 -11.09375,0 0,12.3633 -7.51953,0 0,-29.1602"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'DejaVu Sans';-inkscape-font-specification:'Catharsis Bedouin Bold';fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-opacity:1"
- id="path3675" />
- </g>
- <g
- style="opacity:1;fill:url(#linearGradient3667);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-opacity:1"
- id="g2834">
- <path
- inkscape:connector-curvature="0"
- id="path2825"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'DejaVu Sans';-inkscape-font-specification:'Catharsis Bedouin Bold';fill:url(#linearGradient3356);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-opacity:1"
- d="m 60.296875,1012.072 20.292969,0 0,5.6836 -12.773438,0 0,5.4297 12.011719,0 0,5.6836 -12.011719,0 0,12.3633 -7.519531,0 0,-29.1602" />
- <path
- inkscape:connector-curvature="0"
- id="path2827"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'DejaVu Sans';-inkscape-font-specification:'Catharsis Bedouin Bold';fill:url(#linearGradient3358);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-opacity:1"
- d="m 87.640625,1012.072 20.292965,0 0,5.6836 -12.77343,0 0,5.4297 12.01172,0 0,5.6836 -12.01172,0 0,6.6797 13.20312,0 0,5.6836 -20.722655,0 0,-29.1602" />
- <path
- inkscape:connector-curvature="0"
- id="path2829"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'DejaVu Sans';-inkscape-font-specification:'Catharsis Bedouin Bold';fill:url(#linearGradient3360);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-opacity:1"
- d="m 114.98438,1012.072 7.51953,0 0,11.1133 11.09375,0 0,-11.1133 7.51953,0 0,29.1602 -7.51953,0 0,-12.3633 -11.09375,0 0,12.3633 -7.51953,0 0,-29.1602" />
- </g>
+ fill="url(#linear_gradient_for_letters)"
+ >
+ <!-- letter "F" -->
+ <path
+ d="m 60.296875,1012.072 20.292969,0 0,5.6836 -12.773438,0 0,5.4297 12.011719,0 0,5.6836 -12.011719,0 0,12.3633 -7.519531,0 0,-29.1602"
+ />
+ <!-- letter "E" -->
+ <path
+ d="m 87.640625,1012.072 20.292965,0 0,5.6836 -12.77343,0 0,5.4297 12.01172,0 0,5.6836 -12.01172,0 0,6.6797 13.20312,0 0,5.6836 -20.722655,0 0,-29.1602"
+ />
+ <!-- letter "H" -->
+ <path
+ d="m 114.98438,1012.072 7.51953,0 0,11.1133 11.09375,0 0,-11.1133 7.51953,0 0,29.1602 -7.51953,0 0,-12.3633 -11.09375,0 0,12.3633 -7.51953,0 0,-29.1602"
+ />
</g>
</g>
</svg>
diff --git a/share/images/menubg_aluminium.png b/share/images/menubg_aluminium.png
deleted file mode 100644
index eed00f1..0000000
--- a/share/images/menubg_aluminium.png
+++ /dev/null
Binary files differ
diff --git a/share/images/menubg_aqua.png b/share/images/menubg_aqua.png
deleted file mode 100644
index 3a72590..0000000
--- a/share/images/menubg_aqua.png
+++ /dev/null
Binary files differ
diff --git a/share/images/menubg_black.png b/share/images/menubg_black.png
deleted file mode 100644
index 08b4c2b..0000000
--- a/share/images/menubg_black.png
+++ /dev/null
Binary files differ
diff --git a/share/images/menubg_brushed.png b/share/images/menubg_brushed.png
deleted file mode 100644
index 32fad47..0000000
--- a/share/images/menubg_brushed.png
+++ /dev/null
Binary files differ
diff --git a/share/images/menubg_sky.png b/share/images/menubg_sky.png
deleted file mode 100644
index e0be8ca..0000000
--- a/share/images/menubg_sky.png
+++ /dev/null
Binary files differ
diff --git a/src/Makefile b/src/Makefile
index 0e2c543..2968671 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,6 +1,41 @@
include ../config.mk
-TARGETS = ${shell echo *.c}
+TARGETS = \
+ events.c \
+ feh_png.c \
+ filelist.c \
+ gib_hash.c \
+ gib_imlib.c \
+ gib_list.c \
+ gib_style.c \
+ imlib.c \
+ index.c \
+ keyevents.c \
+ list.c \
+ main.c \
+ md5.c \
+ menu.c \
+ multiwindow.c \
+ options.c \
+ signals.c \
+ slideshow.c \
+ thumbnail.c \
+ timers.c \
+ utils.c \
+ wallpaper.c \
+ winwidget.c
+
+ifeq (${exif},1)
+ TARGETS += \
+ exif.c \
+ exif_canon.c \
+ exif_nikon.c
+endif
+
+ifneq (${verscmp},1)
+ TARGETS += strverscmp.c
+endif
+
OBJECTS = ${TARGETS:.c=.o}
I_SRCS = ${shell echo *.raw}
@@ -17,9 +52,9 @@ include deps.mk
fehrc.inc: fehrc.raw
help.inc: help.raw
-
+# CFLAGS might contain include paths needed to resolve includes in headers
deps.mk: ${TARGETS} ${I_DSTS}
- ${CC} ${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 b20fd4f..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
@@ -35,7 +35,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define FEH_JITTER_OFFSET 2
#define FEH_JITTER_TIME 1
-extern fehkb keys;
+extern struct __fehkey keys[EVENT_LIST_END];
+fehkey *feh_str_to_kb(char *action);
feh_event_handler *ev_handler[LASTEvent];
@@ -45,10 +46,10 @@ static void feh_event_handle_LeaveNotify(XEvent * ev);
static void feh_event_handle_MotionNotify(XEvent * ev);
static void feh_event_handle_ClientMessage(XEvent * ev);
-static void feh_set_bb(fehkey *bb, int modifier, char button)
+static void feh_set_bb(unsigned int bb_index, int modifier, char button)
{
- bb->state = modifier;
- bb->button = button;
+ keys[bb_index].state = modifier;
+ keys[bb_index].button = button;
}
static void feh_set_parse_bb_partial(fehkey *button, char *binding)
@@ -84,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;
+ }
}
/*
@@ -101,13 +111,13 @@ void init_buttonbindings(void)
FILE *conf = NULL;
int read = 0;
- feh_set_bb(&keys.pan, 0, 1);
- feh_set_bb(&keys.zoom, 0, 2);
- feh_set_bb(&keys.toggle_menu, 0, 3);
- feh_set_bb(&keys.prev_img, 0, 4);
- feh_set_bb(&keys.next_img, 0, 5);
- feh_set_bb(&keys.blur, 4, 1);
- feh_set_bb(&keys.rotate, 4, 2);
+ feh_set_bb(EVENT_pan, 0, 1);
+ feh_set_bb(EVENT_zoom, 0, 2);
+ feh_set_bb(EVENT_toggle_menu, 0, 3);
+ feh_set_bb(EVENT_prev_img, 0, 4);
+ feh_set_bb(EVENT_next_img, 0, 5);
+ feh_set_bb(EVENT_blur, 4, 1);
+ feh_set_bb(EVENT_rotate, 4, 2);
home = getenv("HOME");
confhome = getenv("XDG_CONFIG_HOME");
@@ -136,34 +146,17 @@ void init_buttonbindings(void)
if ((read == EOF) || (read == 0) || (line[0] == '#'))
continue;
- /*
- * Note: This isn't really good code. But it works, and since it only
- * runs once for each button config line the runtime penalty compared to
- * e.g. a hash table is negligible in this case.
- */
- if (!strcmp(action, "reload"))
- cur_bb = &keys.reload;
- else if (!strcmp(action, "pan"))
- cur_bb = &keys.pan;
- else if (!strcmp(action, "zoom"))
- cur_bb = &keys.zoom;
- else if (!strcmp(action, "menu") || !strcmp(action, "toggle_menu"))
- cur_bb = &keys.toggle_menu;
- else if (!strcmp(action, "prev") || !strcmp(action, "prev_img"))
- cur_bb = &keys.prev_img;
- else if (!strcmp(action, "next") || !strcmp(action, "next_img"))
- cur_bb = &keys.next_img;
- else if (!strcmp(action, "blur"))
- cur_bb = &keys.blur;
- else if (!strcmp(action, "rotate"))
- cur_bb = &keys.rotate;
- else if (!strcmp(action, "zoom_in"))
- cur_bb = &keys.zoom_in;
- else if (!strcmp(action, "zoom_out"))
- cur_bb = &keys.zoom_out;
- else
- cur_bb = feh_str_to_kb(action);
-
+ cur_bb = feh_str_to_kb(action);
+ if (cur_bb == NULL) {
+ if (!strcmp(action, "reload"))
+ cur_bb = &keys[EVENT_reload_image];
+ else if (!strcmp(action, "menu"))
+ cur_bb = &keys[EVENT_toggle_menu];
+ else if (!strcmp(action, "prev"))
+ cur_bb = &keys[EVENT_prev_img];
+ else if (!strcmp(action, "next"))
+ cur_bb = &keys[EVENT_next_img];
+ }
if (cur_bb)
feh_set_parse_bb_partial(cur_bb, button);
else
@@ -172,9 +165,9 @@ void init_buttonbindings(void)
fclose(conf);
}
-static short feh_is_bb(fehkey *bb, unsigned int button, unsigned int mod)
+static short feh_is_bb(unsigned int key_index, unsigned int button, unsigned int mod)
{
- if ((bb->state == mod) && (bb->button == button))
+ if ((keys[key_index].state == mod) && (keys[key_index].button == button))
return 1;
return 0;
}
@@ -217,23 +210,23 @@ static void feh_event_handle_ButtonPress(XEvent * ev)
state = ev->xbutton.state & (ControlMask | ShiftMask | Mod1Mask | Mod4Mask);
button = ev->xbutton.button;
- if (!opt.no_menus && feh_is_bb(&keys.toggle_menu, button, state)) {
+ if (!opt.no_menus && feh_is_bb(EVENT_toggle_menu, button, state)) {
D(("Menu Button Press event\n"));
winwidget_show_menu(winwid);
- } else if (feh_is_bb(&keys.rotate, button, state)
+ } else if (feh_is_bb(EVENT_rotate, button, state)
&& (winwid->type != WIN_TYPE_THUMBNAIL)) {
opt.mode = MODE_ROTATE;
winwid->mode = MODE_ROTATE;
D(("rotate starting at %d, %d\n", ev->xbutton.x, ev->xbutton.y));
- } else if (feh_is_bb(&keys.blur, button, state)
+ } else if (feh_is_bb(EVENT_blur, button, state)
&& (winwid->type != WIN_TYPE_THUMBNAIL)) {
opt.mode = MODE_BLUR;
winwid->mode = MODE_BLUR;
D(("blur starting at %d, %d\n", ev->xbutton.x, ev->xbutton.y));
- } else if (feh_is_bb(&keys.pan, button, state)) {
+ } else if (feh_is_bb(EVENT_pan, button, state)) {
D(("Next button, but could be pan mode\n"));
opt.mode = MODE_NEXT;
winwid->mode = MODE_NEXT;
@@ -242,7 +235,7 @@ static void feh_event_handle_ButtonPress(XEvent * ev)
winwid->click_offset_y = ev->xbutton.y - winwid->im_y;
winwid->click_start_time = time(NULL);
- } else if (feh_is_bb(&keys.zoom, button, state)) {
+ } else if (feh_is_bb(EVENT_zoom, button, state)) {
D(("Zoom Button Press event\n"));
opt.mode = MODE_ZOOM;
winwid->mode = MODE_ZOOM;
@@ -257,7 +250,7 @@ static void feh_event_handle_ButtonPress(XEvent * ev)
winwid->im_click_offset_y = (winwid->click_offset_y
- winwid->im_y) / winwid->old_zoom;
- } else if (feh_is_bb(&keys.zoom_in, button, state)) {
+ } else if (feh_is_bb(EVENT_zoom_in, button, state)) {
D(("Zoom_In Button Press event\n"));
D(("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y));
winwid->click_offset_x = ev->xbutton.x;
@@ -271,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;
@@ -285,7 +278,7 @@ static void feh_event_handle_ButtonPress(XEvent * ev)
winwidget_sanitise_offsets(winwid);
winwidget_render_image(winwid, 0, 0);
- } else if (feh_is_bb(&keys.zoom_out, button, state)) {
+ } else if (feh_is_bb(EVENT_zoom_out, button, state)) {
D(("Zoom_Out Button Press event\n"));
D(("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y));
winwid->click_offset_x = ev->xbutton.x;
@@ -299,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;
@@ -313,16 +306,16 @@ static void feh_event_handle_ButtonPress(XEvent * ev)
winwidget_sanitise_offsets(winwid);
winwidget_render_image(winwid, 0, 0);
- } else if (feh_is_bb(&keys.reload, button, state)) {
+ } else if (feh_is_bb(EVENT_reload_image, button, state)) {
D(("Reload Button Press event\n"));
feh_reload_image(winwid, 0, 1);
- } else if (feh_is_bb(&keys.prev_img, button, state)) {
+ } else if (feh_is_bb(EVENT_prev_img, button, state)) {
D(("Prev Button Press event\n"));
if (winwid->type == WIN_TYPE_SLIDESHOW)
slideshow_change_image(winwid, SLIDE_PREV, 1);
- } else if (feh_is_bb(&keys.next_img, button, state)) {
+ } else if (feh_is_bb(EVENT_next_img, button, state)) {
D(("Next Button Press event\n"));
if (winwid->type == WIN_TYPE_SLIDESHOW)
slideshow_change_image(winwid, SLIDE_NEXT, 1);
@@ -363,7 +356,7 @@ static void feh_event_handle_ButtonRelease(XEvent * ev)
return;
}
- if (feh_is_bb(&keys.pan, button, state)) {
+ if (feh_is_bb(EVENT_pan, button, state)) {
if (opt.mode == MODE_PAN) {
D(("Disabling pan mode\n"));
opt.mode = MODE_NORMAL;
@@ -373,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;
@@ -401,13 +397,13 @@ static void feh_event_handle_ButtonRelease(XEvent * ev)
winwid->mode = MODE_NORMAL;
}
- } else if (feh_is_bb(&keys.rotate, button, state)
- || feh_is_bb(&keys.zoom, button, state)) {
+ } else if (feh_is_bb(EVENT_rotate, button, state)
+ || feh_is_bb(EVENT_zoom, button, state)) {
D(("Disabling mode\n"));
opt.mode = MODE_NORMAL;
winwid->mode = MODE_NORMAL;
- if ((feh_is_bb(&keys.zoom, button, state))
+ if ((feh_is_bb(EVENT_zoom, button, state))
&& (ev->xbutton.x == winwid->click_offset_x)
&& (ev->xbutton.y == winwid->click_offset_y)) {
winwid->zoom = 1.0;
@@ -417,7 +413,7 @@ static void feh_event_handle_ButtonRelease(XEvent * ev)
winwidget_render_image(winwid, 0, 0);
- } else if (feh_is_bb(&keys.blur, button, state)) {
+ } else if (feh_is_bb(EVENT_blur, button, state)) {
D(("Disabling Blur mode\n"));
opt.mode = MODE_NORMAL;
winwid->mode = MODE_NORMAL;
@@ -525,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);
}
@@ -537,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);
}
@@ -644,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));
@@ -665,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 53d3894..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,26 +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);
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);
@@ -165,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;
@@ -200,4 +225,6 @@ extern char *mode; /* label for the current mode */
/* to terminate long-running children with SIGALRM */
extern int childpid;
+extern unsigned char control_via_stdin;
+
#endif
diff --git a/src/feh_png.c b/src/feh_png.c
index ff73f56..8f5b94d 100644
--- a/src/feh_png.c
+++ b/src/feh_png.c
@@ -23,20 +23,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-#include "feh_png.h"
-
#include <png.h>
#include <stdio.h>
#include <stdarg.h>
+#include "feh_png.h"
+
#define FEH_PNG_COMPRESSION 3
#define FEH_PNG_NUM_COMMENTS 4
gib_hash *feh_png_read_comments(char *file)
{
- gib_hash *hash = NULL;
-
FILE *fp;
int i, sig_bytes, comments = 0;
@@ -45,31 +43,31 @@ gib_hash *feh_png_read_comments(char *file)
png_textp text_ptr;
if (!(fp = fopen(file, "rb")))
- return hash;
+ return NULL;
if (!(sig_bytes = feh_png_file_is_png(fp))) {
fclose(fp);
- return hash;
+ return NULL;
}
/* initialize data structures */
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) {
fclose(fp);
- return hash;
+ return NULL;
}
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
fclose(fp);
- return hash;
+ return NULL;
}
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(fp);
- return hash;
+ return NULL;
}
/* initialize reading */
@@ -78,6 +76,8 @@ gib_hash *feh_png_read_comments(char *file)
png_read_info(png_ptr, info_ptr);
+ gib_hash *hash = NULL;
+
#ifdef PNG_TEXT_SUPPORTED
png_get_text(png_ptr, info_ptr, &text_ptr, &comments);
if (comments > 0) {
@@ -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 ecc44b5..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,18 +249,91 @@ 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;
-#ifdef HAVE_LIBEXIF
- ExifEntry *entry;
-#endif
-
D(("filename is %s, image is %p\n", file->filename, im));
if (!file || !file->filename)
@@ -219,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)) {
@@ -253,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
@@ -281,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_fd[12];
+ 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, '/');
@@ -310,10 +694,33 @@ static char *feh_magick_load_image(char *filename)
fd = mkstemp(sfn);
- if (fd == -1)
+ if (fd == -1) {
+ free(sfn);
return NULL;
+ }
- snprintf(argv_fd, sizeof(argv_fd), "png:fd:%d", fd);
+ /*
+ * We could use png:fd:(whatever mkstemp returned) as target filename
+ * for convert, but this seems to be broken in some ImageMagick versions.
+ * So we resort to png:(sfn) instead.
+ */
+ argv_fn = estrjoin(":", "png", sfn, NULL);
+
+ /*
+ * By default, ImageMagick saves (occasionally lots of) temporary files
+ * in /tmp. It doesn't remove them if it runs into a timeout and is killed
+ * by us, no matter whether we use SIGINT, SIGTERM or SIGKILL. So, unless
+ * MAGICK_TMPDIR has already been set by the user, we create our own
+ * temporary directory for ImageMagick and remove its contents at the end of
+ * this function.
+ */
+ if (getenv("MAGICK_TMPDIR") == NULL) {
+ if (mkdtemp(tempdir) == NULL) {
+ weprintf("%s: ImageMagick may leave temporary files in /tmp. mkdtemp failed:", filename);
+ } else {
+ created_tempdir = 1;
+ }
+ }
if ((childpid = fork()) < 0) {
weprintf("%s: Can't load with imagemagick. Fork failed:", filename);
@@ -336,44 +743,89 @@ static char *feh_magick_load_image(char *filename)
*/
setpgid(0, 0);
- execlp("convert", "convert", filename, argv_fd, NULL);
+ 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;
@@ -386,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;
@@ -401,36 +860,73 @@ 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);
+ } else if (getenv("CURL_CA_BUNDLE") != NULL) {
+ // Allow the user to specify custom CA certificates.
+ curl_easy_setopt(curl, CURLOPT_CAINFO,
+ getenv("CURL_CA_BUNDLE"));
}
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (res != CURLE_OK) {
- weprintf("open url: %s", ebuff);
+ if (res != CURLE_ABORTED_BY_CALLBACK) {
+ weprintf("open url: %s", ebuff);
+ }
unlink(sfn);
close(fd);
free(sfn);
@@ -439,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:");
@@ -447,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);
@@ -459,7 +961,7 @@ static char *feh_http_load_image(char *url)
char *feh_http_load_image(char *url)
{
weprintf(
- "Cannot load image %s\n Please recompile with libcurl support",
+ "Cannot load image %s\nPlease recompile feh with libcurl support",
url
);
return NULL;
@@ -675,11 +1177,12 @@ void feh_draw_exif(winwidget w)
fn = feh_load_font(w);
- if (buffer == NULL)
+ if (buffer[0] == '\0')
{
- snprintf(buffer, EXIF_MAX_DATA, "%s", estrdup("Failed to run exif command"));
- gib_imlib_get_text_size(fn, &buffer[0], NULL, &width, &height, IMLIB_TEXT_TO_RIGHT);
- no_lines = 1;
+ snprintf(buffer, EXIF_MAX_DATA, "%s", "Failed to run exif command");
+ gib_imlib_get_text_size(fn, buffer, NULL, &width, &height, IMLIB_TEXT_TO_RIGHT);
+ info_buf[no_lines] = estrdup(buffer);
+ no_lines++;
}
else
{
@@ -688,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);
@@ -745,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]);
}
@@ -1032,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;
@@ -1053,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 {
/*
@@ -1069,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);
@@ -1262,7 +1793,7 @@ void feh_draw_actions(winwidget w)
int i = 0;
int num_actions = 0;
int cur_action = 0;
- char index[2];
+ char index[3];
char *line;
/* Count number of defined actions. This method sucks a bit since it needs
diff --git a/src/index.c b/src/index.c
index 7a2f5fc..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,8 +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("Imlib error creating index image, are you low on RAM?");
+ if (!im_main) {
+ if (index_image_height >= 32768 || index_image_width >= 32768) {
+ eprintf("Failed to create %dx%d pixels (%d MB) index image.\n"
+ "This is probably due to Imlib2 issues when dealing with images larger than 32k x 32k pixels.",
+ index_image_width, index_image_height, index_image_width * index_image_height * 4 / (1024*1024));
+ } else {
+ eprintf("Failed to create %dx%d pixels (%d MB) index image. Do you have enough RAM?",
+ index_image_width, index_image_height, index_image_width * index_image_height * 4 / (1024*1024));
+ }
+ }
if (bg_im)
gib_imlib_blend_image_onto_image(im_main, bg_im,
@@ -163,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);
}
@@ -331,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;
@@ -347,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 1905ea5..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
@@ -29,22 +29,56 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "filelist.h"
#include "winwidget.h"
#include "options.h"
+#include <termios.h>
-fehkb keys;
+struct __fehkey keys[EVENT_LIST_END];
+struct termios old_term_settings;
+unsigned char control_via_stdin = 0;
-static void feh_set_kb(fehkey *key, unsigned int s0, unsigned int y0, unsigned
- int s1, unsigned int y1, unsigned int s2, unsigned int y2) {
+void setup_stdin(void) {
+ struct termios ctrl;
+
+ control_via_stdin = 1;
+
+ if (tcgetattr(STDIN_FILENO, &old_term_settings) == -1)
+ eprintf("tcgetattr failed");
+ if (tcgetattr(STDIN_FILENO, &ctrl) == -1)
+ eprintf("tcgetattr failed");
+
+ ctrl.c_iflag &= ~(PARMRK | ISTRIP
+ | INLCR | IGNCR | IXON);
+ ctrl.c_lflag &= ~(ECHO | ICANON | IEXTEN);
+ ctrl.c_cflag &= ~(CSIZE | PARENB);
+ ctrl.c_cflag |= CS8;
+
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &ctrl) == -1)
+ eprintf("tcsetattr failed");
+}
+
+void restore_stdin(void) {
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &old_term_settings) == -1)
+ eprintf("tcsetattr failed");
+}
+
+static void feh_set_kb(char *name, unsigned int s0, unsigned int y0,
+ unsigned int s1, unsigned int y1, unsigned int s2, unsigned int y2) {
+ static int key_index = 0;
+ fehkey *key = &keys[key_index];
key->keystates[0] = s0;
key->keystates[1] = s1;
key->keystates[2] = s2;
key->keysyms[0] = y0;
key->keysyms[1] = y1;
key->keysyms[2] = y2;
+ key->state = 0;
+ key->button = 0;
+ key->name = name;
+ key_index++;
}
static inline int ignore_space(int keysym) {
/*
- * Passing values which do not find inside a signed 8bit char to isprint,
+ * Passing values which do not fit inside a signed 8bit char to isprint,
* isspace and the likes is undefined behaviour... which glibc (for some
* values) implements as a segmentation fault. So let's not do that.
*/
@@ -100,74 +134,83 @@ void init_keyevents(void) {
FILE *conf = NULL;
int read = 0;
- memset(&keys, 0, sizeof(keys));
-
- feh_set_kb(&keys.menu_close, 0, XK_Escape , 0, 0 , 0, 0);
- feh_set_kb(&keys.menu_parent,0, XK_Left , 0, 0 , 0, 0);
- feh_set_kb(&keys.menu_down , 0, XK_Down , 0, 0 , 0, 0);
- feh_set_kb(&keys.menu_up , 0, XK_Up , 0, 0 , 0, 0);
- feh_set_kb(&keys.menu_child, 0, XK_Right , 0, 0 , 0, 0);
- feh_set_kb(&keys.menu_select,0, XK_Return , 0, XK_space , 0, 0);
- feh_set_kb(&keys.scroll_left,0, XK_KP_Left , 4, XK_Left , 0, 0);
- feh_set_kb(&keys.scroll_right,0,XK_KP_Right , 4, XK_Right , 0, 0);
- feh_set_kb(&keys.scroll_down,0, XK_KP_Down , 4, XK_Down , 0, 0);
- feh_set_kb(&keys.scroll_up , 0, XK_KP_Up , 4, XK_Up , 0, 0);
- feh_set_kb(&keys.scroll_left_page , 8, XK_Left , 0, 0 , 0, 0);
- feh_set_kb(&keys.scroll_right_page, 8, XK_Right, 0, 0 , 0, 0);
- feh_set_kb(&keys.scroll_down_page , 8, XK_Down , 0, 0 , 0, 0);
- feh_set_kb(&keys.scroll_up_page , 8, XK_Up , 0, 0 , 0, 0);
- feh_set_kb(&keys.prev_img , 0, XK_Left , 0, XK_p , 0, XK_BackSpace);
- feh_set_kb(&keys.next_img , 0, XK_Right , 0, XK_n , 0, XK_space);
- feh_set_kb(&keys.jump_back , 0, XK_Page_Up , 0, XK_KP_Page_Up, 0, 0);
- feh_set_kb(&keys.jump_fwd , 0, XK_Page_Down , 0, XK_KP_Page_Down,0,0);
- feh_set_kb(&keys.prev_dir , 0, XK_bracketleft, 0, 0 , 0, 0);
- feh_set_kb(&keys.next_dir , 0, XK_bracketright, 0, 0 , 0, 0);
- feh_set_kb(&keys.jump_random,0, XK_z , 0, 0 , 0, 0);
- feh_set_kb(&keys.quit , 0, XK_Escape , 0, XK_q , 0, 0);
- feh_set_kb(&keys.close , 0, XK_x , 0, 0 , 0, 0);
- feh_set_kb(&keys.remove , 0, XK_Delete , 0, 0 , 0, 0);
- feh_set_kb(&keys.delete , 4, XK_Delete , 0, 0 , 0, 0);
- feh_set_kb(&keys.jump_first, 0, XK_Home , 0, XK_KP_Home , 0, 0);
- feh_set_kb(&keys.jump_last , 0, XK_End , 0, XK_KP_End , 0, 0);
- feh_set_kb(&keys.action_0 , 0, XK_Return , 0, XK_0 , 0, XK_KP_0);
- feh_set_kb(&keys.action_1 , 0, XK_1 , 0, XK_KP_1 , 0, 0);
- feh_set_kb(&keys.action_2 , 0, XK_2 , 0, XK_KP_2 , 0, 0);
- feh_set_kb(&keys.action_3 , 0, XK_3 , 0, XK_KP_3 , 0, 0);
- feh_set_kb(&keys.action_4 , 0, XK_4 , 0, XK_KP_4 , 0, 0);
- feh_set_kb(&keys.action_5 , 0, XK_5 , 0, XK_KP_5 , 0, 0);
- feh_set_kb(&keys.action_6 , 0, XK_6 , 0, XK_KP_6 , 0, 0);
- feh_set_kb(&keys.action_7 , 0, XK_7 , 0, XK_KP_7 , 0, 0);
- feh_set_kb(&keys.action_8 , 0, XK_8 , 0, XK_KP_8 , 0, 0);
- feh_set_kb(&keys.action_9 , 0, XK_9 , 0, XK_KP_9 , 0, 0);
- feh_set_kb(&keys.zoom_in , 0, XK_Up , 0, XK_KP_Add , 0, 0);
- feh_set_kb(&keys.zoom_out , 0, XK_Down , 0, XK_KP_Subtract,0, 0);
- feh_set_kb(&keys.zoom_default, 0, XK_KP_Multiply, 0, XK_asterisk,0, 0);
- feh_set_kb(&keys.zoom_fit , 0, XK_KP_Divide , 0, XK_slash , 0, 0);
- feh_set_kb(&keys.zoom_fill , 0, XK_exclam , 0, 0 , 0, 0);
- feh_set_kb(&keys.size_to_image, 0, XK_w , 0, 0 , 0, 0);
- feh_set_kb(&keys.render , 0, XK_KP_Begin , 0, XK_R , 0, 0);
- feh_set_kb(&keys.toggle_actions, 0, XK_a, 0, 0, 0, 0);
- feh_set_kb(&keys.toggle_aliasing, 0, XK_A, 0, 0, 0, 0);
- feh_set_kb(&keys.toggle_filenames, 0, XK_d, 0, 0, 0, 0);
+ /*
+ * The feh_set_kb statements must have the same order as the key_action
+ * enum.
+ */
+
+ feh_set_kb("menu_close" , 0, XK_Escape , 0, 0 , 0, 0);
+ feh_set_kb("menu_parent", 0, XK_Left , 0, 0 , 0, 0);
+ feh_set_kb("menu_down", 0, XK_Down , 0, 0 , 0, 0);
+ feh_set_kb("menu_up", 0, XK_Up , 0, 0 , 0, 0);
+ feh_set_kb("menu_child", 0, XK_Right , 0, 0 , 0, 0);
+ feh_set_kb("menu_select", 0, XK_Return , 0, XK_space , 0, 0);
+ feh_set_kb("scroll_left",0, XK_KP_Left , 4, XK_Left , 0, 0);
+ feh_set_kb("scroll_right", 0,XK_KP_Right , 4, XK_Right , 0, 0);
+ feh_set_kb("scroll_down",0, XK_KP_Down , 4, XK_Down , 0, 0);
+ feh_set_kb("scroll_up", 0, XK_KP_Up , 4, XK_Up , 0, 0);
+ feh_set_kb("scroll_left_page" , 8, XK_Left , 0, 0 , 0, 0);
+ feh_set_kb("scroll_right_page", 8, XK_Right, 0, 0 , 0, 0);
+ feh_set_kb("scroll_down_page" , 8, XK_Down , 0, 0 , 0, 0);
+ feh_set_kb("scroll_up_page" , 8, XK_Up , 0, 0 , 0, 0);
+ feh_set_kb("prev_img" , 0, XK_Left , 0, XK_p , 0, XK_BackSpace);
+ feh_set_kb("next_img" , 0, XK_Right , 0, XK_n , 0, XK_space);
+ feh_set_kb("jump_back" , 0, XK_Page_Up , 0, XK_KP_Page_Up, 0, 0);
+ feh_set_kb("jump_fwd" , 0, XK_Page_Down , 0, XK_KP_Page_Down,0,0);
+ feh_set_kb("prev_dir" , 0, XK_bracketleft, 0, 0 , 0, 0);
+ feh_set_kb("next_dir" , 0, XK_bracketright, 0, 0 , 0, 0);
+ feh_set_kb("jump_random" ,0, XK_z , 0, 0 , 0, 0);
+ feh_set_kb("quit" , 0, XK_Escape , 0, XK_q , 0, 0);
+ feh_set_kb("close" , 0, XK_x , 0, 0 , 0, 0);
+ feh_set_kb("remove" , 0, XK_Delete , 0, 0 , 0, 0);
+ feh_set_kb("delete" , 4, XK_Delete , 0, 0 , 0, 0);
+ feh_set_kb("jump_first" , 0, XK_Home , 0, XK_KP_Home , 0, 0);
+ feh_set_kb("jump_last" , 0, XK_End , 0, XK_KP_End , 0, 0);
+ feh_set_kb("action_0" , 0, XK_Return , 0, XK_0 , 0, XK_KP_0);
+ feh_set_kb("action_1" , 0, XK_1 , 0, XK_KP_1 , 0, 0);
+ feh_set_kb("action_2" , 0, XK_2 , 0, XK_KP_2 , 0, 0);
+ feh_set_kb("action_3" , 0, XK_3 , 0, XK_KP_3 , 0, 0);
+ feh_set_kb("action_4" , 0, XK_4 , 0, XK_KP_4 , 0, 0);
+ feh_set_kb("action_5" , 0, XK_5 , 0, XK_KP_5 , 0, 0);
+ feh_set_kb("action_6" , 0, XK_6 , 0, XK_KP_6 , 0, 0);
+ feh_set_kb("action_7" , 0, XK_7 , 0, XK_KP_7 , 0, 0);
+ feh_set_kb("action_8" , 0, XK_8 , 0, XK_KP_8 , 0, 0);
+ feh_set_kb("action_9" , 0, XK_9 , 0, XK_KP_9 , 0, 0);
+ feh_set_kb("zoom_in" , 0, XK_Up , 0, XK_KP_Add , 0, 0);
+ feh_set_kb("zoom_out" , 0, XK_Down , 0, XK_KP_Subtract,0, 0);
+ feh_set_kb("zoom_default" , 0, XK_KP_Multiply, 0, XK_asterisk,0, 0);
+ feh_set_kb("zoom_fit" , 0, XK_KP_Divide , 0, XK_slash , 0, 0);
+ feh_set_kb("zoom_fill" , 0, XK_exclam , 0, 0 , 0, 0);
+ feh_set_kb("size_to_image" , 0, XK_w , 0, 0 , 0, 0);
+ feh_set_kb("render" , 0, XK_KP_Begin , 0, XK_R , 0, 0);
+ feh_set_kb("toggle_actions" , 0, XK_a, 0, 0, 0, 0);
+ feh_set_kb("toggle_aliasing" , 0, XK_A, 0, 0, 0, 0);
+ feh_set_kb("toggle_auto_zoom" , 0, XK_Z, 0, 0, 0, 0);
#ifdef HAVE_LIBEXIF
- feh_set_kb(&keys.toggle_exif, 0, XK_e, 0, 0, 0, 0);
+ feh_set_kb("toggle_exif" , 0, XK_e, 0, 0, 0, 0);
#endif
- feh_set_kb(&keys.toggle_info, 0, XK_i, 0, 0, 0, 0);
- feh_set_kb(&keys.toggle_pointer, 0, XK_o, 0, 0, 0, 0);
- feh_set_kb(&keys.toggle_caption, 0, XK_c, 0, 0, 0, 0);
- feh_set_kb(&keys.toggle_pause, 0, XK_h, 0, 0, 0, 0);
- feh_set_kb(&keys.toggle_menu, 0, XK_m, 0, 0, 0, 0);
- feh_set_kb(&keys.toggle_fullscreen, 0, XK_v, 0, 0, 0, 0);
- feh_set_kb(&keys.reload_image, 0, XK_r, 0, 0, 0, 0);
- feh_set_kb(&keys.save_image, 0, XK_s, 0, 0, 0, 0);
- feh_set_kb(&keys.save_filelist, 0, XK_f, 0, 0, 0, 0);
- feh_set_kb(&keys.orient_1, 0, XK_greater, 0, 0, 0, 0);
- feh_set_kb(&keys.orient_3, 0, XK_less, 0, 0, 0, 0);
- feh_set_kb(&keys.flip, 0, XK_underscore, 0, 0, 0, 0);
- feh_set_kb(&keys.mirror, 0, XK_bar, 0, 0, 0, 0);
- feh_set_kb(&keys.reload_minus, 0, XK_minus, 0, 0, 0, 0);
- feh_set_kb(&keys.reload_plus, 0, XK_plus, 0, 0, 0, 0);
- feh_set_kb(&keys.toggle_keep_vp, 0, XK_k, 0, 0, 0, 0);
+ feh_set_kb("toggle_filenames" , 0, XK_d, 0, 0, 0, 0);
+ feh_set_kb("toggle_info" , 0, XK_i, 0, 0, 0, 0);
+ feh_set_kb("toggle_pointer" , 0, XK_o, 0, 0, 0, 0);
+ feh_set_kb("toggle_caption" , 0, XK_c, 0, 0, 0, 0);
+ feh_set_kb("toggle_pause" , 0, XK_h, 0, 0, 0, 0);
+ feh_set_kb("toggle_menu" , 0, XK_m, 0, 0, 0, 0);
+ feh_set_kb("toggle_fullscreen" , 0, XK_f, 0, 0, 0, 0);
+ feh_set_kb("reload_image" , 0, XK_r, 0, 0, 0, 0);
+ feh_set_kb("save_image" , 0, XK_s, 0, 0, 0, 0);
+ feh_set_kb("save_filelist" , 0, XK_L, 0, 0, 0, 0);
+ feh_set_kb("orient_1" , 0, XK_greater, 0, 0, 0, 0);
+ feh_set_kb("orient_3" , 0, XK_less, 0, 0, 0, 0);
+ feh_set_kb("flip" , 0, XK_underscore, 0, 0, 0, 0);
+ feh_set_kb("mirror" , 0, XK_bar, 0, 0, 0, 0);
+ feh_set_kb("reload_minus" , 0, XK_minus, 0, 0, 0, 0);
+ feh_set_kb("reload_plus" , 0, XK_plus, 0, 0, 0, 0);
+ feh_set_kb("toggle_keep_vp" , 0, XK_k, 0, 0, 0, 0);
+ feh_set_kb("toggle_fixed_geometry" , 0, XK_g, 0, 0, 0, 0);
+ feh_set_kb("pan" , 0, 0, 0, 0, 0, 0);
+ feh_set_kb("zoom" , 0, 0, 0, 0, 0, 0);
+ feh_set_kb("blur" , 0, 0, 0, 0, 0, 0);
+ feh_set_kb("rotate" , 0, 0, 0, 0, 0, 0);
home = getenv("HOME");
confhome = getenv("XDG_CONFIG_HOME");
@@ -212,21 +255,23 @@ void init_keyevents(void) {
fclose(conf);
}
-static short feh_is_kp(fehkey *key, unsigned int state, unsigned int sym, unsigned int button) {
+static short feh_is_kp(unsigned int key_index, unsigned int state,
+ unsigned int sym, unsigned int button) {
int i;
if (sym != NoSymbol) {
for (i = 0; i < 3; i++) {
if (
- (key->keysyms[i] == sym) &&
- (key->keystates[i] == state))
+ (keys[key_index].keysyms[i] == sym) &&
+ (keys[key_index].keystates[i] == state))
return 1;
- else if (key->keysyms[i] == 0)
+ else if (keys[key_index].keysyms[i] == 0)
return 0;
}
return 0;
}
- if ((key->state == state) && (key->button == button)) {
+ if ((keys[key_index].state == state)
+ && (keys[key_index].button == button)) {
return 1;
}
return 0;
@@ -234,12 +279,15 @@ static short feh_is_kp(fehkey *key, unsigned int state, unsigned int sym, unsign
void feh_event_invoke_action(winwidget winwid, unsigned char action)
{
+ struct stat st;
if (opt.actions[action]) {
if (opt.slideshow) {
feh_action_run(FEH_FILE(winwid->file->data), opt.actions[action], winwid);
if (opt.hold_actions[action])
feh_reload_image(winwid, 1, 1);
+ else if (stat(FEH_FILE(winwid->file->data)->filename, &st) == -1)
+ feh_filelist_image_remove(winwid, 0);
else
slideshow_change_image(winwid, SLIDE_NEXT, 1);
@@ -266,6 +314,57 @@ void feh_event_invoke_action(winwidget winwid, unsigned char action)
return;
}
+void feh_event_handle_stdin(void)
+{
+ char stdin_buf[2];
+ static char is_esc = 0;
+ KeySym keysym = NoSymbol;
+ if (read(STDIN_FILENO, &stdin_buf, 1) <= 0) {
+ control_via_stdin = 0;
+ if (isatty(STDIN_FILENO) && getpgrp() == (tcgetpgrp(STDIN_FILENO))) {
+ weprintf("reading a command from stdin failed - disabling control via stdin");
+ restore_stdin();
+ }
+ return;
+ }
+ stdin_buf[1] = '\0';
+
+ // escape?
+ if (stdin_buf[0] == 0x1b) {
+ is_esc = 1;
+ return;
+ }
+ if ((is_esc == 1) && (stdin_buf[0] == '[')) {
+ is_esc = 2;
+ return;
+ }
+
+ if (stdin_buf[0] == ' ')
+ keysym = XK_space;
+ else if (stdin_buf[0] == '\n')
+ keysym = XK_Return;
+ else if ((stdin_buf[0] == '\b') || (stdin_buf[0] == 127))
+ keysym = XK_BackSpace;
+ else if (is_esc == 2) {
+ if (stdin_buf[0] == 'A')
+ keysym = XK_Up;
+ else if (stdin_buf[0] == 'B')
+ keysym = XK_Down;
+ else if (stdin_buf[0] == 'C')
+ keysym = XK_Right;
+ else if (stdin_buf[0] == 'D')
+ keysym = XK_Left;
+ is_esc = 0;
+ }
+ else
+ keysym = XStringToKeysym(stdin_buf);
+
+ if (window_num && keysym)
+ feh_event_handle_generic(windows[0], is_esc * Mod1Mask, keysym, 0);
+
+ is_esc = 0;
+}
+
void feh_event_handle_keypress(XEvent * ev)
{
int state;
@@ -293,17 +392,17 @@ void feh_event_handle_keypress(XEvent * ev)
/* menus are showing, so this is a menu control keypress */
if (ev->xbutton.window == menu_cover) {
selected_item = feh_menu_find_selected_r(menu_root, &selected_menu);
- if (feh_is_kp(&keys.menu_close, state, keysym, 0))
+ if (feh_is_kp(EVENT_menu_close, state, keysym, 0))
feh_menu_hide(menu_root, True);
- else if (feh_is_kp(&keys.menu_parent, state, keysym, 0))
+ else if (feh_is_kp(EVENT_menu_parent, state, keysym, 0))
feh_menu_select_parent(selected_menu);
- else if (feh_is_kp(&keys.menu_down, state, keysym, 0))
+ else if (feh_is_kp(EVENT_menu_down, state, keysym, 0))
feh_menu_select_next(selected_menu, selected_item);
- else if (feh_is_kp(&keys.menu_up, state, keysym, 0))
+ else if (feh_is_kp(EVENT_menu_up, state, keysym, 0))
feh_menu_select_prev(selected_menu, selected_item);
- else if (feh_is_kp(&keys.menu_child, state, keysym, 0))
+ else if (feh_is_kp(EVENT_menu_child, state, keysym, 0))
feh_menu_select_submenu(selected_menu);
- else if (feh_is_kp(&keys.menu_select, state, keysym, 0))
+ else if (feh_is_kp(EVENT_menu_select, state, keysym, 0))
feh_menu_item_activate(selected_menu, selected_item);
return;
}
@@ -311,7 +410,23 @@ void feh_event_handle_keypress(XEvent * ev)
if (winwid == NULL)
return;
- if (winwid->caption_entry) {
+ feh_event_handle_generic(winwid, state, keysym, 0);
+}
+
+fehkey *feh_str_to_kb(char *action)
+{
+ for (unsigned int i = 0; i < EVENT_LIST_END; i++) {
+ if (!strcmp(action, keys[i].name)) {
+ return &keys[i];
+ }
+ }
+ return NULL;
+}
+
+void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysym, unsigned int button) {
+ int curr_screen = 0;
+
+ if (winwid->caption_entry && (keysym != NoSymbol)) {
switch (keysym) {
case XK_Return:
if (state & ControlMask) {
@@ -362,274 +477,133 @@ void feh_event_handle_keypress(XEvent * ev)
}
return;
}
- feh_event_handle_generic(winwid, state, keysym, 0);
-}
-
-fehkey *feh_str_to_kb(char *action)
-{
- if (!strcmp(action, "menu_close"))
- return &keys.menu_close;
- else if (!strcmp(action, "menu_parent"))
- return &keys.menu_parent;
- else if (!strcmp(action, "menu_down"))
- return &keys.menu_down;
- else if (!strcmp(action, "menu_up"))
- return &keys.menu_up;
- else if (!strcmp(action, "menu_child"))
- return &keys.menu_child;
- else if (!strcmp(action, "menu_select"))
- return &keys.menu_select;
- else if (!strcmp(action, "scroll_right"))
- return &keys.scroll_right;
- else if (!strcmp(action, "scroll_left"))
- return &keys.scroll_left;
- else if (!strcmp(action, "scroll_up"))
- return &keys.scroll_up;
- else if (!strcmp(action, "scroll_down"))
- return &keys.scroll_down;
- else if (!strcmp(action, "scroll_right_page"))
- return &keys.scroll_right_page;
- else if (!strcmp(action, "scroll_left_page"))
- return &keys.scroll_left_page;
- else if (!strcmp(action, "scroll_up_page"))
- return &keys.scroll_up_page;
- else if (!strcmp(action, "scroll_down_page"))
- return &keys.scroll_down_page;
- else if (!strcmp(action, "prev_img"))
- return &keys.prev_img;
- else if (!strcmp(action, "next_img"))
- return &keys.next_img;
- else if (!strcmp(action, "jump_back"))
- return &keys.jump_back;
- else if (!strcmp(action, "jump_fwd"))
- return &keys.jump_fwd;
- else if (!strcmp(action, "prev_dir"))
- return &keys.prev_dir;
- else if (!strcmp(action, "next_dir"))
- return &keys.next_dir;
- else if (!strcmp(action, "jump_random"))
- return &keys.jump_random;
- else if (!strcmp(action, "quit"))
- return &keys.quit;
- else if (!strcmp(action, "close"))
- return &keys.close;
- else if (!strcmp(action, "remove"))
- return &keys.remove;
- else if (!strcmp(action, "delete"))
- return &keys.delete;
- else if (!strcmp(action, "jump_first"))
- return &keys.jump_first;
- else if (!strcmp(action, "jump_last"))
- return &keys.jump_last;
- else if (!strcmp(action, "action_0"))
- return &keys.action_0;
- else if (!strcmp(action, "action_1"))
- return &keys.action_1;
- else if (!strcmp(action, "action_2"))
- return &keys.action_2;
- else if (!strcmp(action, "action_3"))
- return &keys.action_3;
- else if (!strcmp(action, "action_4"))
- return &keys.action_4;
- else if (!strcmp(action, "action_5"))
- return &keys.action_5;
- else if (!strcmp(action, "action_6"))
- return &keys.action_6;
- else if (!strcmp(action, "action_7"))
- return &keys.action_7;
- else if (!strcmp(action, "action_8"))
- return &keys.action_8;
- else if (!strcmp(action, "action_9"))
- return &keys.action_9;
- else if (!strcmp(action, "zoom_in"))
- return &keys.zoom_in;
- else if (!strcmp(action, "zoom_out"))
- return &keys.zoom_out;
- else if (!strcmp(action, "zoom_default"))
- return &keys.zoom_default;
- else if (!strcmp(action, "zoom_fit"))
- return &keys.zoom_fit;
- else if (!strcmp(action, "zoom_fill"))
- return &keys.zoom_fill;
- else if (!strcmp(action, "size_to_image"))
- return &keys.size_to_image;
- else if (!strcmp(action, "render"))
- return &keys.render;
- else if (!strcmp(action, "toggle_actions"))
- return &keys.toggle_actions;
- else if (!strcmp(action, "toggle_aliasing"))
- return &keys.toggle_aliasing;
- else if (!strcmp(action, "toggle_filenames"))
- return &keys.toggle_filenames;
-#ifdef HAVE_LIBEXIF
- else if (!strcmp(action, "toggle_exif"))
- return &keys.toggle_exif;
-#endif
- else if (!strcmp(action, "toggle_info"))
- return &keys.toggle_info;
- else if (!strcmp(action, "toggle_pointer"))
- return &keys.toggle_pointer;
- else if (!strcmp(action, "toggle_caption"))
- return &keys.toggle_caption;
- else if (!strcmp(action, "toggle_pause"))
- return &keys.toggle_pause;
- else if (!strcmp(action, "toggle_menu"))
- return &keys.toggle_menu;
- else if (!strcmp(action, "toggle_fullscreen"))
- return &keys.toggle_fullscreen;
- else if (!strcmp(action, "reload_image"))
- return &keys.reload_image;
- else if (!strcmp(action, "save_image"))
- return &keys.save_image;
- else if (!strcmp(action, "save_filelist"))
- return &keys.save_filelist;
- else if (!strcmp(action, "orient_1"))
- return &keys.orient_1;
- else if (!strcmp(action, "orient_3"))
- return &keys.orient_3;
- else if (!strcmp(action, "flip"))
- return &keys.flip;
- else if (!strcmp(action, "mirror"))
- return &keys.mirror;
- else if (!strcmp(action, "reload_minus"))
- return &keys.reload_minus;
- else if (!strcmp(action, "reload_plus"))
- return &keys.reload_plus;
- else if (!strcmp(action, "toggle_keep_vp"))
- return &keys.toggle_keep_vp;
-
- return NULL;
-}
-
-void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysym, unsigned int button) {
- int curr_screen = 0;
- if (feh_is_kp(&keys.next_img, state, keysym, button)) {
+ if (feh_is_kp(EVENT_next_img, state, keysym, button)) {
if (opt.slideshow)
slideshow_change_image(winwid, SLIDE_NEXT, 1);
else if (winwid->type == WIN_TYPE_THUMBNAIL)
feh_thumbnail_select_next(winwid, 1);
}
- else if (feh_is_kp(&keys.prev_img, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_prev_img, state, keysym, button)) {
if (opt.slideshow)
slideshow_change_image(winwid, SLIDE_PREV, 1);
else if (winwid->type == WIN_TYPE_THUMBNAIL)
feh_thumbnail_select_prev(winwid, 1);
}
- else if (feh_is_kp(&keys.scroll_right, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_scroll_right, state, keysym, button)) {
winwid->im_x -= opt.scroll_step;;
winwidget_sanitise_offsets(winwid);
winwidget_render_image(winwid, 0, 1);
}
- else if (feh_is_kp(&keys.scroll_left, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_scroll_left, state, keysym, button)) {
winwid->im_x += opt.scroll_step;
winwidget_sanitise_offsets(winwid);
winwidget_render_image(winwid, 0, 1);
}
- else if (feh_is_kp(&keys.scroll_down, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_scroll_down, state, keysym, button)) {
winwid->im_y -= opt.scroll_step;
winwidget_sanitise_offsets(winwid);
winwidget_render_image(winwid, 0, 1);
}
- else if (feh_is_kp(&keys.scroll_up, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_scroll_up, state, keysym, button)) {
winwid->im_y += opt.scroll_step;
winwidget_sanitise_offsets(winwid);
winwidget_render_image(winwid, 0, 1);
}
- else if (feh_is_kp(&keys.scroll_right_page, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_scroll_right_page, state, keysym, button)) {
winwid->im_x -= winwid->w;
winwidget_sanitise_offsets(winwid);
winwidget_render_image(winwid, 0, 0);
}
- else if (feh_is_kp(&keys.scroll_left_page, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_scroll_left_page, state, keysym, button)) {
winwid->im_x += winwid->w;
winwidget_sanitise_offsets(winwid);
winwidget_render_image(winwid, 0, 0);
}
- else if (feh_is_kp(&keys.scroll_down_page, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_scroll_down_page, state, keysym, button)) {
winwid->im_y -= winwid->h;
winwidget_sanitise_offsets(winwid);
winwidget_render_image(winwid, 0, 0);
}
- else if (feh_is_kp(&keys.scroll_up_page, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_scroll_up_page, state, keysym, button)) {
winwid->im_y += winwid->h;
winwidget_sanitise_offsets(winwid);
winwidget_render_image(winwid, 0, 0);
}
- else if (feh_is_kp(&keys.jump_back, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_jump_back, state, keysym, button)) {
if (opt.slideshow)
slideshow_change_image(winwid, SLIDE_JUMP_BACK, 1);
else if (winwid->type == WIN_TYPE_THUMBNAIL)
feh_thumbnail_select_prev(winwid, 10);
}
- else if (feh_is_kp(&keys.jump_fwd, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_jump_fwd, state, keysym, button)) {
if (opt.slideshow)
slideshow_change_image(winwid, SLIDE_JUMP_FWD, 1);
else if (winwid->type == WIN_TYPE_THUMBNAIL)
feh_thumbnail_select_next(winwid, 10);
}
- else if (feh_is_kp(&keys.next_dir, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_next_dir, state, keysym, button)) {
if (opt.slideshow)
slideshow_change_image(winwid, SLIDE_JUMP_NEXT_DIR, 1);
}
- else if (feh_is_kp(&keys.prev_dir, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_prev_dir, state, keysym, button)) {
if (opt.slideshow)
slideshow_change_image(winwid, SLIDE_JUMP_PREV_DIR, 1);
}
- else if (feh_is_kp(&keys.quit, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_quit, state, keysym, button)) {
winwidget_destroy_all();
}
- else if (feh_is_kp(&keys.delete, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_delete, state, keysym, button)) {
if (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER)
feh_thumbnail_mark_removed(FEH_FILE(winwid->file->data), 1);
feh_filelist_image_remove(winwid, 1);
}
- else if (feh_is_kp(&keys.remove, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_remove, state, keysym, button)) {
if (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER)
feh_thumbnail_mark_removed(FEH_FILE(winwid->file->data), 0);
feh_filelist_image_remove(winwid, 0);
}
- else if (feh_is_kp(&keys.jump_first, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_jump_first, state, keysym, button)) {
if (opt.slideshow)
slideshow_change_image(winwid, SLIDE_FIRST, 1);
}
- else if (feh_is_kp(&keys.jump_last, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_jump_last, state, keysym, button)) {
if (opt.slideshow)
slideshow_change_image(winwid, SLIDE_LAST, 1);
}
- else if (feh_is_kp(&keys.action_0, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_action_0, state, keysym, button)) {
feh_event_invoke_action(winwid, 0);
}
- else if (feh_is_kp(&keys.action_1, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_action_1, state, keysym, button)) {
feh_event_invoke_action(winwid, 1);
}
- else if (feh_is_kp(&keys.action_2, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_action_2, state, keysym, button)) {
feh_event_invoke_action(winwid, 2);
}
- else if (feh_is_kp(&keys.action_3, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_action_3, state, keysym, button)) {
feh_event_invoke_action(winwid, 3);
}
- else if (feh_is_kp(&keys.action_4, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_action_4, state, keysym, button)) {
feh_event_invoke_action(winwid, 4);
}
- else if (feh_is_kp(&keys.action_5, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_action_5, state, keysym, button)) {
feh_event_invoke_action(winwid, 5);
}
- else if (feh_is_kp(&keys.action_6, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_action_6, state, keysym, button)) {
feh_event_invoke_action(winwid, 6);
}
- else if (feh_is_kp(&keys.action_7, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_action_7, state, keysym, button)) {
feh_event_invoke_action(winwid, 7);
}
- else if (feh_is_kp(&keys.action_8, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_action_8, state, keysym, button)) {
feh_event_invoke_action(winwid, 8);
}
- else if (feh_is_kp(&keys.action_9, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_action_9, state, keysym, button)) {
feh_event_invoke_action(winwid, 9);
}
- else if (feh_is_kp(&keys.zoom_in, state, keysym, button)) {
+ 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;
@@ -641,9 +615,9 @@ void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysy
winwidget_sanitise_offsets(winwid);
winwidget_render_image(winwid, 0, 0);
}
- else if (feh_is_kp(&keys.zoom_out, state, keysym, button)) {
+ 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;
@@ -655,17 +629,17 @@ void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysy
winwidget_sanitise_offsets(winwid);
winwidget_render_image(winwid, 0, 0);
}
- else if (feh_is_kp(&keys.zoom_default, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_zoom_default, state, keysym, button)) {
winwid->zoom = 1.0;
winwidget_center_image(winwid);
winwidget_render_image(winwid, 0, 0);
}
- else if (feh_is_kp(&keys.zoom_fit, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_zoom_fit, state, keysym, button)) {
feh_calc_needed_zoom(&winwid->zoom, winwid->im_w, winwid->im_h, winwid->w, winwid->h);
winwidget_center_image(winwid);
winwidget_render_image(winwid, 0, 0);
}
- else if (feh_is_kp(&keys.zoom_fill, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_zoom_fill, state, keysym, button)) {
int save_zoom = opt.zoom_mode;
opt.zoom_mode = ZOOM_MODE_FILL;
feh_calc_needed_zoom(&winwid->zoom, winwid->im_w, winwid->im_h, winwid->w, winwid->h);
@@ -673,47 +647,54 @@ void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysy
winwidget_render_image(winwid, 0, 0);
opt.zoom_mode = save_zoom;
}
- else if (feh_is_kp(&keys.render, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_render, state, keysym, button)) {
if (winwid->type == WIN_TYPE_THUMBNAIL)
feh_thumbnail_show_selected();
else
winwidget_render_image(winwid, 0, 0);
}
- else if (feh_is_kp(&keys.toggle_actions, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_toggle_actions, state, keysym, button)) {
opt.draw_actions = !opt.draw_actions;
winwidget_rerender_all(0);
}
- else if (feh_is_kp(&keys.toggle_aliasing, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_toggle_aliasing, state, keysym, button)) {
opt.force_aliasing = !opt.force_aliasing;
winwid->force_aliasing = !winwid->force_aliasing;
winwidget_render_image(winwid, 0, 0);
}
- else if (feh_is_kp(&keys.toggle_filenames, state, keysym, button)) {
+ 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);
}
#ifdef HAVE_LIBEXIF
- else if (feh_is_kp(&keys.toggle_exif, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_toggle_exif, state, keysym, button)) {
opt.draw_exif = !opt.draw_exif;
winwidget_rerender_all(0);
}
#endif
- else if (feh_is_kp(&keys.toggle_info, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_toggle_info, state, keysym, button)) {
opt.draw_info = !opt.draw_info;
winwidget_rerender_all(0);
}
- else if (feh_is_kp(&keys.toggle_pointer, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_toggle_pointer, state, keysym, button)) {
winwidget_set_pointer(winwid, opt.hide_pointer);
opt.hide_pointer = !opt.hide_pointer;
}
- else if (feh_is_kp(&keys.jump_random, state, keysym, button)) {
+ 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(&keys.toggle_caption, state, keysym, button)) {
- if (opt.caption_path) {
+ else if (feh_is_kp(EVENT_toggle_caption, state, keysym, button)) {
+ if (opt.caption_path && path_is_url(FEH_FILE(winwid->file->data)->filename)) {
+ im_weprintf(winwid, "Caption entry is not supported on URLs");
+ }
+ else if (opt.caption_path) {
/*
* editing captions in slideshow mode does not make any sense
* at all; this is just in case someone accidentally does it...
@@ -724,44 +705,46 @@ void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysy
}
winwidget_render_image(winwid, 0, 0);
}
- else if (feh_is_kp(&keys.reload_image, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_reload_image, state, keysym, button)) {
feh_reload_image(winwid, 0, 0);
}
- else if (feh_is_kp(&keys.toggle_pause, state, keysym, button)) {
+ 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(&keys.save_image, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_save_image, state, keysym, button)) {
slideshow_save_image(winwid);
}
- else if (feh_is_kp(&keys.save_filelist, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_save_filelist, state, keysym, button)) {
if ((winwid->type == WIN_TYPE_THUMBNAIL)
|| (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER))
weprintf("Filelist saving is not supported in thumbnail mode");
else
feh_save_filelist();
}
- else if (feh_is_kp(&keys.size_to_image, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_size_to_image, state, keysym, button)) {
winwidget_size_to_image(winwid);
}
- else if (feh_is_kp(&keys.toggle_menu, state, keysym, button)) {
+ else if (!opt.no_menus && feh_is_kp(EVENT_toggle_menu, state, keysym, button)) {
winwidget_show_menu(winwid);
}
- else if (feh_is_kp(&keys.close, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_close, state, keysym, button)) {
winwidget_destroy(winwid);
}
- else if (feh_is_kp(&keys.orient_1, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_orient_1, state, keysym, button)) {
feh_edit_inplace(winwid, 1);
}
- else if (feh_is_kp(&keys.orient_3, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_orient_3, state, keysym, button)) {
feh_edit_inplace(winwid, 3);
}
- else if (feh_is_kp(&keys.flip, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_flip, state, keysym, button)) {
feh_edit_inplace(winwid, INPLACE_EDIT_FLIP);
}
- else if (feh_is_kp(&keys.mirror, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_mirror, state, keysym, button)) {
feh_edit_inplace(winwid, INPLACE_EDIT_MIRROR);
}
- else if (feh_is_kp(&keys.toggle_fullscreen, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_toggle_fullscreen, state, keysym, button)) {
#ifdef HAVE_LIBXINERAMA
if (opt.xinerama && xinerama_screens) {
int i, rect[4];
@@ -797,20 +780,30 @@ void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysy
}
#endif /* HAVE_LIBXINERAMA */
}
- else if (feh_is_kp(&keys.reload_plus, state, keysym, button)){
+ else if (feh_is_kp(EVENT_reload_plus, state, keysym, button)){
if (opt.reload < SLIDESHOW_RELOAD_MAX)
opt.reload++;
else if (opt.verbose)
weprintf("Cannot set RELOAD higher than %f seconds.", opt.reload);
}
- else if (feh_is_kp(&keys.reload_minus, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_reload_minus, state, keysym, button)) {
if (opt.reload > 1)
opt.reload--;
else if (opt.verbose)
weprintf("Cannot set RELOAD lower than 1 second.");
}
- else if (feh_is_kp(&keys.toggle_keep_vp, state, keysym, button)) {
+ else if (feh_is_kp(EVENT_toggle_keep_vp, state, keysym, button)) {
opt.keep_zoom_vp = !opt.keep_zoom_vp;
}
+ else if (feh_is_kp(EVENT_toggle_fixed_geometry, state, keysym, button)) {
+ if (opt.geom_flags & ((WidthValue | HeightValue))) {
+ opt.geom_flags &= ~(WidthValue | HeightValue);
+ } else {
+ opt.geom_flags |= (WidthValue | HeightValue);
+ opt.geom_w = winwid->w;
+ opt.geom_h = winwid->h;
+ }
+ winwidget_render_image(winwid, 1, 0);
+ }
return;
}
diff --git a/src/list.c b/src/list.c
index 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 46ab73d..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
@@ -32,6 +32,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "events.h"
#include "signals.h"
#include "wallpaper.h"
+#include <termios.h>
+
+#ifdef HAVE_INOTIFY
+#include <sys/inotify.h>
+#endif
char **cmdargv = NULL;
int cmdargc = 0;
@@ -41,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();
@@ -50,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)
@@ -72,16 +92,41 @@ int main(int argc, char **argv)
feh_wm_set_bg_filelist(opt.bgmode);
exit(0);
}
- else {
+ else if (opt.display){
/* Slideshow mode is the default. Because it's spiffy */
opt.slideshow = 1;
init_slideshow_mode();
}
+ else {
+ eprintf("Invalid option combination");
+ }
/* main event loop */
while (feh_main_iteration(1));
- return(0);
+ return(sig_exit);
+}
+
+static void feh_process_signal(void)
+{
+ winwidget winwid = winwidget_get_first_window_of_type(WIN_TYPE_SLIDESHOW);
+ int i;
+ int signo = sig_received;
+ sig_received = 0;
+
+ if (winwid) {
+ if (filelist_len > 1) {
+ if (signo == SIGUSR1)
+ slideshow_change_image(winwid, SLIDE_NEXT, 1);
+ else if (signo == SIGUSR2)
+ slideshow_change_image(winwid, SLIDE_PREV, 1);
+ } else {
+ feh_reload_image(winwid, 0, 0);
+ }
+ } else if (opt.multiwindow) {
+ for (i = window_num - 1; i >= 0; i--)
+ feh_reload_image(windows[i], 0, 0);
+ }
}
/* Return 0 to stop iterating, 1 if ok to continue. */
@@ -98,15 +143,32 @@ int feh_main_iteration(int block)
double t1 = 0.0, t2 = 0.0;
fehtimer ft;
- if (window_num == 0)
+ if (window_num == 0 || sig_exit != 0)
return(0);
+ if (sig_received) {
+ feh_process_signal();
+ }
+
if (first) {
/* Only need to set these up the first time */
xfd = ConnectionNumber(disp);
fdsize = xfd + 1;
pt = feh_get_time();
first = 0;
+ /*
+ * Only accept commands from stdin if
+ * - stdin is a terminal (otherwise it's probably used as an image / filelist)
+ * - we aren't running in multiwindow mode (cause it's not clear which
+ * window commands should be applied to in that case)
+ * - we're in the same process group as stdin, AKA we're not running
+ * in the background. Background processes are stopped with SIGTTOU
+ * if they try to write to stdout or change terminal attributes. They
+ * also don't get input from stdin anyway.
+ */
+ if (isatty(STDIN_FILENO) && !opt.multiwindow && getpgrp() == (tcgetpgrp(STDIN_FILENO))) {
+ setup_stdin();
+ }
}
/* Timers */
@@ -118,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);
@@ -127,6 +193,16 @@ int feh_main_iteration(int block)
FD_ZERO(&fdset);
FD_SET(xfd, &fdset);
+ if (control_via_stdin) {
+ FD_SET(STDIN_FILENO, &fdset);
+ }
+#ifdef HAVE_INOTIFY
+ if (opt.auto_reload) {
+ FD_SET(opt.inotify_fd, &fdset);
+ if (opt.inotify_fd >= fdsize)
+ fdsize = opt.inotify_fd + 1;
+ }
+#endif
/* Timers */
ft = first_timer;
@@ -164,12 +240,23 @@ int feh_main_iteration(int block)
&& ((errno == ENOMEM) || (errno == EINVAL)
|| (errno == EBADF)))
eprintf("Connection to X display lost");
- if ((ft) && (count == 0)) {
+ if (count == 0) {
/* This means the timer is due to be executed. If count was > 0,
that would mean an X event had woken us, we're not interested
in that */
feh_handle_timer();
}
+ /*
+ * Beware: If stdin is not connected, we may end up with xfd == 0.
+ * However, STDIN_FILENO == 0 holds as well in most cases. So we must
+ * check control_via_stdin to avoid mistaking an X11 event for stdin.
+ */
+ else if ((count > 0) && control_via_stdin && (FD_ISSET(STDIN_FILENO, &fdset)))
+ feh_event_handle_stdin();
+#ifdef HAVE_INOTIFY
+ else if ((count > 0) && (FD_ISSET(opt.inotify_fd, &fdset)))
+ feh_event_handle_inotify();
+#endif
}
} else {
/* Don't block if there are events in the queue. That's a bit rude ;-) */
@@ -181,11 +268,21 @@ int feh_main_iteration(int block)
&& ((errno == ENOMEM) || (errno == EINVAL)
|| (errno == EBADF)))
eprintf("Connection to X display lost");
+ else if ((count > 0) && control_via_stdin && (FD_ISSET(STDIN_FILENO, &fdset)))
+ feh_event_handle_stdin();
+#ifdef HAVE_INOTIFY
+ else if ((count > 0) && (FD_ISSET(opt.inotify_fd, &fdset)))
+ feh_event_handle_inotify();
+#endif
}
}
- if (window_num == 0)
+ if (window_num == 0 || sig_exit != 0)
return(0);
-
+
+ if (sig_received) {
+ feh_process_signal();
+ }
+
return(1);
}
@@ -193,12 +290,32 @@ 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
+ * - stdin still is a terminal (it might have been closed)
+ * - stdin still belongs to us (we might have been detached from the
+ * controlling terminal, in that case we probably shouldn't be messing
+ * around with it) <https://github.com/derf/feh/issues/324>
+ */
+ if (control_via_stdin && isatty(STDIN_FILENO) && getpgrp() == (tcgetpgrp(STDIN_FILENO)))
+ restore_stdin();
+
if (opt.filelistfile)
feh_write_filelist(filelist, opt.filelistfile);
diff --git a/src/menu.c b/src/menu.c
index 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 56323a8..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,94 +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;
- case 241:
+ break;
+ case OPTION_no_recursive:
opt.recursive = 0;
+ break;
+ case OPTION_cache_size:
+ opt.cache_size = atoi(optarg);
+ if (opt.cache_size < 0)
+ opt.cache_size = 0;
+ if (opt.cache_size > 2048)
+ opt.cache_size = 2048;
+ break;
+ case OPTION_on_last_slide:
+ if (!strcmp(optarg, "quit")) {
+ opt.on_last_slide = ON_LAST_SLIDE_QUIT;
+ } else if (!strcmp(optarg, "hold")) {
+ opt.on_last_slide = ON_LAST_SLIDE_HOLD;
+ } else if (!strcmp(optarg, "resume")) {
+ opt.on_last_slide = ON_LAST_SLIDE_RESUME;
+ } else {
+ weprintf("Unrecognized on-last-slide action \"%s\"."
+ "Supported actions: hold, resume, quit\n", optarg);
+ }
+ break;
+ case OPTION_conversion_timeout:
+ opt.conversion_timeout = atoi(optarg);
+ break;
+ case OPTION_version_sort:
+ opt.version_sort = 1;
+ break;
+ case OPTION_offset:
+ opt.offset_flags = XParseGeometry(optarg, &opt.offset_x,
+ &opt.offset_y, (unsigned int *)&discard, (unsigned int *)&discard);
+ break;
+#ifdef HAVE_INOTIFY
+ case OPTION_auto_reload:
+ opt.auto_reload = 1;
+ break;
+#endif
+ case OPTION_class:
+ opt.x11_class = estrdup(optarg);
+ break;
+ case OPTION_no_conversion_cache:
+ opt.use_conversion_cache = 0;
+ break;
+ case OPTION_window_id:
+ opt.x11_windowid = strtol(optarg, NULL, 0);
+ break;
+ case OPTION_zoom_step:
+ opt.zoom_rate = atof(optarg);
+ if ((opt.zoom_rate <= 0)) {
+ weprintf("Zooming disabled due to --zoom-step=%f", opt.zoom_rate);
+ opt.zoom_rate = 1.0;
+ } else {
+ opt.zoom_rate = 1 + ((float)opt.zoom_rate / 100);
+ }
+ break;
default:
break;
}
@@ -785,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;
@@ -814,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");
}
@@ -852,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 5a5ce84..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,90 +147,201 @@ 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];
unsigned int state;
unsigned int button;
+ char *name;
};
-struct __fehkb {
- struct __fehkey menu_close;
- struct __fehkey menu_parent;
- struct __fehkey menu_down;
- struct __fehkey menu_up;
- struct __fehkey menu_child;
- struct __fehkey menu_select;
- struct __fehkey scroll_right;
- struct __fehkey prev_img;
- struct __fehkey scroll_left;
- struct __fehkey next_img;
- struct __fehkey scroll_up;
- struct __fehkey scroll_down;
- struct __fehkey scroll_right_page;
- struct __fehkey scroll_left_page;
- struct __fehkey scroll_up_page;
- struct __fehkey scroll_down_page;
- struct __fehkey jump_back;
- struct __fehkey quit;
- struct __fehkey jump_fwd;
- struct __fehkey prev_dir;
- struct __fehkey next_dir;
- struct __fehkey remove;
- struct __fehkey delete;
- struct __fehkey jump_first;
- struct __fehkey jump_last;
- struct __fehkey action_0;
- struct __fehkey action_1;
- struct __fehkey action_2;
- struct __fehkey action_3;
- struct __fehkey action_4;
- struct __fehkey action_5;
- struct __fehkey action_6;
- struct __fehkey action_7;
- struct __fehkey action_8;
- struct __fehkey action_9;
- struct __fehkey zoom_in;
- struct __fehkey zoom_out;
- struct __fehkey zoom_default;
- struct __fehkey zoom_fit;
- struct __fehkey zoom_fill;
- struct __fehkey render;
- struct __fehkey toggle_actions;
- struct __fehkey toggle_filenames;
+enum key_action {
+ EVENT_menu_close = 0,
+ EVENT_menu_parent,
+ EVENT_menu_down,
+ EVENT_menu_up,
+ EVENT_menu_child,
+ EVENT_menu_select,
+ EVENT_scroll_left,
+ EVENT_scroll_right,
+ EVENT_scroll_down,
+ EVENT_scroll_up,
+ EVENT_scroll_left_page,
+ EVENT_scroll_right_page,
+ EVENT_scroll_down_page,
+ EVENT_scroll_up_page,
+ EVENT_prev_img,
+ EVENT_next_img,
+ EVENT_jump_back,
+ EVENT_jump_fwd,
+ EVENT_prev_dir,
+ EVENT_next_dir,
+ EVENT_jump_random,
+ EVENT_quit,
+ EVENT_close,
+ EVENT_remove,
+ EVENT_delete,
+ EVENT_jump_first,
+ EVENT_jump_last,
+ EVENT_action_0,
+ EVENT_action_1,
+ EVENT_action_2,
+ EVENT_action_3,
+ EVENT_action_4,
+ EVENT_action_5,
+ EVENT_action_6,
+ EVENT_action_7,
+ EVENT_action_8,
+ EVENT_action_9,
+ EVENT_zoom_in,
+ EVENT_zoom_out,
+ EVENT_zoom_default,
+ EVENT_zoom_fit,
+ EVENT_zoom_fill,
+ EVENT_size_to_image,
+ EVENT_render,
+ EVENT_toggle_actions,
+ EVENT_toggle_aliasing,
+ EVENT_toggle_auto_zoom,
#ifdef HAVE_LIBEXIF
- struct __fehkey toggle_exif;
+ EVENT_toggle_exif,
#endif
- struct __fehkey toggle_info;
- struct __fehkey toggle_pointer;
- struct __fehkey toggle_aliasing;
- struct __fehkey jump_random;
- struct __fehkey toggle_caption;
- struct __fehkey toggle_pause;
- struct __fehkey reload_image;
- struct __fehkey save_image;
- struct __fehkey save_filelist;
- struct __fehkey size_to_image;
- struct __fehkey toggle_menu;
- struct __fehkey close;
- struct __fehkey orient_1;
- struct __fehkey orient_3;
- struct __fehkey flip;
- struct __fehkey mirror;
- struct __fehkey toggle_fullscreen;
- struct __fehkey reload_minus;
- struct __fehkey reload_plus;
- struct __fehkey toggle_keep_vp;
- struct __fehkey pan;
- struct __fehkey zoom;
- struct __fehkey reload;
- struct __fehkey blur;
- struct __fehkey rotate;
+ EVENT_toggle_filenames,
+ EVENT_toggle_info,
+ EVENT_toggle_pointer,
+ EVENT_toggle_caption,
+ EVENT_toggle_pause,
+ EVENT_toggle_menu,
+ EVENT_toggle_fullscreen,
+ EVENT_reload_image,
+ EVENT_save_image,
+ EVENT_save_filelist,
+ EVENT_orient_1,
+ EVENT_orient_3,
+ EVENT_flip,
+ EVENT_mirror,
+ EVENT_reload_minus,
+ EVENT_reload_plus,
+ EVENT_toggle_keep_vp,
+ EVENT_toggle_fixed_geometry,
+ EVENT_pan,
+ EVENT_zoom,
+ EVENT_blur,
+ EVENT_rotate,
+ EVENT_LIST_END
};
void init_parse_options(int argc, char **argv);
diff --git a/src/signals.c b/src/signals.c
index 0b18aac..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;
@@ -40,7 +43,8 @@ void setup_signal_handlers()
(sigaddset(&feh_ss, SIGALRM) == -1) ||
(sigaddset(&feh_ss, SIGTERM) == -1) ||
(sigaddset(&feh_ss, SIGQUIT) == -1) ||
- (sigaddset(&feh_ss, SIGINT) == -1))
+ (sigaddset(&feh_ss, SIGINT) == -1) ||
+ (sigaddset(&feh_ss, SIGTTIN) == -1))
{
weprintf("Failed to set up signal masks");
return;
@@ -56,7 +60,8 @@ void setup_signal_handlers()
(sigaction(SIGALRM, &feh_sh, NULL) == -1) ||
(sigaction(SIGTERM, &feh_sh, NULL) == -1) ||
(sigaction(SIGQUIT, &feh_sh, NULL) == -1) ||
- (sigaction(SIGINT, &feh_sh, NULL) == -1))
+ (sigaction(SIGINT, &feh_sh, NULL) == -1) ||
+ (sigaction(SIGTTIN, &feh_sh, NULL) == -1))
{
weprintf("Failed to set up signal handler");
return;
@@ -67,33 +72,23 @@ void setup_signal_handlers()
void feh_handle_signal(int signo)
{
- winwidget winwid;
- int i;
-
switch (signo) {
case SIGALRM:
if (childpid)
killpg(childpid, SIGINT);
return;
+ case SIGTTIN:
+ // we were probably backgrounded while we were running
+ control_via_stdin = 0;
+ return;
case SIGINT:
case SIGTERM:
case SIGQUIT:
if (childpid)
killpg(childpid, SIGINT);
- 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 4a71dc3..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,35 +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 *shell_escape(char *input)
-{
- static char ret[1024];
- unsigned int out = 0, in = 0;
-
- ret[out++] = '\'';
- for (in = 0; input[in] && (out < (sizeof(ret) - 7)); in++) {
- if (input[in] == '\'') {
- ret[out++] = '\'';
- ret[out++] = '"';
- ret[out++] = '\'';
- ret[out++] = '"';
- ret[out++] = '\'';
- }
- else
- ret[out++] = input[in];
- }
- ret[out++] = '\'';
- ret[out++] = '\0';
-
- return ret;
-}
-
-char *format_size(int size)
+char *format_size(double size)
{
static char ret[5];
char units[] = {' ', 'k', 'M', 'G', 'T'};
@@ -496,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;
}
@@ -509,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);
@@ -522,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);
@@ -577,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':
@@ -593,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':
@@ -611,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);
@@ -619,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;
@@ -649,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);
}
@@ -676,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)) {
@@ -695,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)
@@ -713,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;
@@ -736,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 08da0b2..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)
@@ -144,12 +145,21 @@ void init_thumbnail_mode(void)
index_image_width = td.w;
index_image_height = td.h + title_area_h;
- D(("imlib_create_image(%d, %d)", index_image_width, index_image_height));
+ D(("imlib_create_image(%d, %d)\n", index_image_width, index_image_height));
td.im_main = imlib_create_image(index_image_width, index_image_height);
- gib_imlib_image_set_has_alpha(td.im_main, 1);
- if (!td.im_main)
- eprintf("Imlib error creating index image, are you low on RAM?");
+ if (!td.im_main) {
+ if (index_image_height >= 32768 || index_image_width >= 32768) {
+ eprintf("Failed to create %dx%d pixels (%d MB) index image.\n"
+ "This is probably due to Imlib2 issues when dealing with images larger than 32k x 32k pixels.",
+ index_image_width, index_image_height, index_image_width * index_image_height * 4 / (1024*1024));
+ } else {
+ eprintf("Failed to create %dx%d pixels (%d MB) index image. Do you have enough RAM?",
+ index_image_width, index_image_height, index_image_width * index_image_height * 4 / (1024*1024));
+ }
+ }
+
+ gib_imlib_image_set_has_alpha(td.im_main, 1);
if (td.im_bg)
gib_imlib_blend_image_onto_image(td.im_main, td.im_bg,
@@ -166,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) {
@@ -188,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");
@@ -387,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;
@@ -406,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;
@@ -414,7 +424,6 @@ void init_thumbnail_mode(void)
}
- free(s);
return;
}
@@ -588,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;
@@ -631,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)
@@ -694,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,
@@ -770,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
}
}
@@ -870,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;
@@ -923,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 4c06a48..eb128a2 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -146,18 +146,21 @@ 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)
{
char *tmpname;
char num[10];
- char cppid[10];
+ char cppid[12];
static long int i = 1;
struct stat st;
pid_t ppid;
@@ -201,3 +204,26 @@ char *ereadfile(char *path)
return estrdup(buffer);
}
+
+char *shell_escape(char *input)
+{
+ static char ret[1024];
+ unsigned int out = 0, in = 0;
+
+ ret[out++] = '\'';
+ for (in = 0; input[in] && (out < (sizeof(ret) - 7)); in++) {
+ if (input[in] == '\'') {
+ ret[out++] = '\'';
+ ret[out++] = '"';
+ ret[out++] = '\'';
+ ret[out++] = '"';
+ ret[out++] = '\'';
+ }
+ else
+ ret[out++] = input[in];
+ }
+ ret[out++] = '\'';
+ ret[out++] = '\0';
+
+ return ret;
+}
diff --git a/src/utils.h b/src/utils.h
index c0d243b..22cbbf8 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -46,6 +46,7 @@ char *estrjoin(const char *separator, ...);
char path_is_url(char *path);
char *feh_unique_filename(char *path, char *basename);
char *ereadfile(char *path);
+char *shell_escape(char *input);
#define ESTRAPPEND(a,b) \
{\
diff --git a/src/wallpaper.c b/src/wallpaper.c
index 93994b3..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) {
@@ -294,83 +424,28 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
unsigned long length, after;
unsigned char *data_root = NULL, *data_esetroot = NULL;
Pixmap pmap_d1, pmap_d2;
- gib_list *l;
-
- /* string for sticking in ~/.fehbg */
- char *fehbg = NULL;
- char fehbg_args[512];
- fehbg_args[0] = '\0';
- char *home;
- char filbuf[4096];
- char *bgfill = NULL;
- bgfill = opt.image_bg == IMAGE_BG_WHITE ? "--image-bg white" : "--image-bg black" ;
-
-#ifdef HAVE_LIBXINERAMA
- if (opt.xinerama) {
- if (opt.xinerama_index >= 0) {
- snprintf(fehbg_args, sizeof(fehbg_args),
- "--xinerama-index %d", opt.xinerama_index);
- }
- }
- else
- snprintf(fehbg_args, sizeof(fehbg_args), "--no-xinerama");
-#endif /* HAVE_LIBXINERAMA */
/* local display to set closedownmode on */
Display *disp2;
Window root2;
int depth2;
- int in, out, w, h;
+ int w, h;
D(("Falling back to XSetRootWindowPixmap\n"));
- /* Put the filename in filbuf between ' and escape ' in the filename */
- out = 0;
-
- if (fil && !use_filelist) {
- filbuf[out++] = '\'';
-
- fil = feh_absolute_path(fil);
-
- for (in = 0; fil[in] && out < 4092; in++) {
-
- if (fil[in] == '\'')
- filbuf[out++] = '\\';
- filbuf[out++] = fil[in];
- }
- filbuf[out++] = '\'';
- free(fil);
-
- } else {
- for (l = filelist; l && out < 4092; l = l->next) {
- filbuf[out++] = '\'';
-
- fil = feh_absolute_path(FEH_FILE(l->data)->filename);
-
- for (in = 0; fil[in] && out < 4092; in++) {
-
- if (fil[in] == '\'')
- filbuf[out++] = '\\';
- filbuf[out++] = fil[in];
- }
- filbuf[out++] = '\'';
- filbuf[out++] = ' ';
- free(fil);
- }
- }
-
-
- filbuf[out++] = 0;
+ XColor color;
+ Colormap cmap = DefaultColormap(disp, DefaultScreen(disp));
+ if (opt.image_bg)
+ XAllocNamedColor(disp, cmap, (char*) opt.image_bg, &color, &color);
+ else
+ XAllocNamedColor(disp, cmap, "black", &color, &color);
if (scaled) {
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);
@@ -389,16 +464,12 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
#endif /* HAVE_LIBXINERAMA */
feh_wm_set_bg_scaled(pmap_d1, im, use_filelist,
0, 0, scr->width, scr->height);
- fehbg = estrjoin(" ", "feh", fehbg_args, "--bg-scale", filbuf, NULL);
} else if (centered) {
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);
@@ -419,18 +490,13 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
XFreeGC(disp, gc);
- fehbg = estrjoin(" ", "feh", fehbg_args, bgfill, "--bg-center", filbuf, NULL);
-
} else if (filled == 1) {
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);
@@ -450,15 +516,10 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
feh_wm_set_bg_filled(pmap_d1, im, use_filelist
, 0, 0, scr->width, scr->height);
- fehbg = estrjoin(" ", "feh", fehbg_args, "--bg-fill", filbuf, NULL);
-
} 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);
@@ -479,40 +540,17 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,
XFreeGC(disp, gc);
- fehbg = estrjoin(" ", "feh", fehbg_args, bgfill, "--bg-max", filbuf, NULL);
-
} else {
if (use_filelist)
feh_wm_load_next(&im);
w = gib_imlib_image_get_width(im);
h = gib_imlib_image_get_height(im);
pmap_d1 = XCreatePixmap(disp, root, w, h, depth);
- gib_imlib_render_image_on_drawable(pmap_d1, im, 0, 0, 1, 0, 0);
- fehbg = estrjoin(" ", "feh --bg-tile", filbuf, NULL);
+ gib_imlib_render_image_on_drawable(pmap_d1, im, 0, 0, 1, 1, 0);
}
- if (fehbg && !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 {
- fprintf(fp, "#!/bin/sh\n%s\n", fehbg);
- 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);
- }
- }
-
- free(fehbg);
+ if (!opt.no_fehbg)
+ feh_wm_gen_bg_script(fil, centered, scaled, filled, use_filelist);
/* create new display, copy pixmap to new display */
disp2 = XOpenDisplay(NULL);
@@ -553,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);
@@ -765,10 +803,11 @@ void enl_ipc_send(char *str)
return;
}
-static sighandler_t *enl_ipc_timeout(int sig)
+void enl_ipc_timeout(int sig)
{
- timeout = 1;
- return((sighandler_t *) sig);
+ if (sig == SIGALRM)
+ timeout = 1;
+ return;
}
char *enl_wait_for_reply(void)
@@ -828,7 +867,8 @@ char *enl_ipc_get(const char *msg_data)
char *enl_send_and_wait(char *msg)
{
char *reply = IPC_TIMEOUT;
- sighandler_t old_alrm;
+ struct sigaction e17_sh, feh_sh;
+ sigset_t e17_ss;
/*
* Shortcut this func and return IPC_FAKE
@@ -847,7 +887,19 @@ char *enl_send_and_wait(char *msg)
sleep(1);
}
}
- old_alrm = (sighandler_t) signal(SIGALRM, (sighandler_t) enl_ipc_timeout);
+
+ if ((sigemptyset(&e17_ss) == -1) || sigaddset(&e17_ss, SIGALRM) == -1) {
+ weprintf("Failed to set up temporary E17 signal masks");
+ return reply;
+ }
+ e17_sh.sa_handler = enl_ipc_timeout;
+ e17_sh.sa_mask = e17_ss;
+ e17_sh.sa_flags = 0;
+ if (sigaction(SIGALRM, &e17_sh, &feh_sh) == -1) {
+ weprintf("Failed to set up temporary E17 signal handler");
+ return reply;
+ }
+
for (; reply == IPC_TIMEOUT;) {
timeout = 0;
enl_ipc_send(msg);
@@ -859,6 +911,8 @@ char *enl_send_and_wait(char *msg)
ipc_win = None;
}
}
- signal(SIGALRM, old_alrm);
+ if (sigaction(SIGALRM, &feh_sh, NULL) == -1) {
+ weprintf("Failed to restore signal handler");
+ }
return(reply);
}
diff --git a/src/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);
diff --git a/test/feh-i.t b/test/feh-i.t
index ff247a2..24775e3 100755
--- a/test/feh-i.t
+++ b/test/feh-i.t
@@ -138,28 +138,28 @@ SendKeys('{RIG}');
test_win_title( $win, 'feh slideshow 2/3 jpg' );
feh_stop();
-feh_start( '--cycle-once', 'test/ok/png test/ok/jpg' );
+feh_start( '--on-last-slide=quit', 'test/ok/png test/ok/jpg' );
for ( 1 .. 2 ) {
SendKeys('{RIG}');
}
-test_no_win("--cycle-once -> window closed");
+test_no_win("--on-last-slide=quit -> window closed");
feh_start(
- '--cycle-once --slideshow-delay 0.5',
+ '--on-last-slide=quit --slideshow-delay 0.5',
'test/ok/png test/ok/jpg test/ok/gif'
);
sleep(1.5);
-test_no_win('cycle-once + slideshow-delay -> window closed');
+test_no_win('on-last-slide=quit + slideshow-delay -> window closed');
$win = feh_start(
- '--cycle-once --slideshow-delay -0.01',
+ '--on-last-slide=quit --slideshow-delay -0.01',
'test/ok/png test/ok/jpg test/ok/gif'
);
test_win_title( $win, 'feh [1 of 3] - test/ok/png [Paused]' );
SendKeys('h');
-test_no_win('cycle-once + negative delay + [h]');
+test_no_win('on-last-slide=quit + negative delay + [h]');
$win = feh_start( q{}, 'test/ok/png test/ok/gif test/ok/gif test/ok/jpg' );
for ( 1 .. 2 ) {
diff --git a/test/feh.t b/test/feh.t
index 47dfbc3..b9025b4 100644
--- a/test/feh.t
+++ b/test/feh.t
@@ -2,7 +2,7 @@
use strict;
use warnings;
use 5.010;
-use Test::Command tests => 71;
+use Test::Command tests => 73;
$ENV{HOME} = 'test';
@@ -34,13 +34,23 @@ if ( length($feh_name) == 0 ) {
die($err_no_env);
}
+# Imlib2 1.6+ reports JPEG file format as 'jpg', older versions use 'jpeg'.
+# Determine the output format used in this version with a --customlist call.
+my $list_dir = 'list';
+if (qx{$feh --customlist %t test/ok/jpg} =~ m{jpg}) {
+ $list_dir = 'list_imlib2_1.6';
+}
+
my $version = qx{$feh --version};
if ( $version =~ m{ Compile-time \s switches : \s .* help }ox ) {
$has_help = 1;
}
+# Imlib2 1.8+ returns "Invalid image file" rather than "No Imlib2 loader".
+# feh compiled with magic=1 returns "Does not look like an image (magic bytes missing)"
+# Here, we accept all three.
my $re_warning
- = qr{${feh_name} WARNING: test/fail/... \- No Imlib2 loader for that file format\n};
+ = qr{${feh_name} WARNING: test/fail/... \- (Invalid image file|No Imlib2 loader for that file format|Does not look like an image \(magic bytes missing\))\n};
my $re_loadable = qr{test/ok/...};
my $re_unloadable = qr{test/fail/...};
my $re_list_action = qr{test/ok/... 16x16};
@@ -93,14 +103,14 @@ $cmd->stderr_is_eq('');
$cmd = Test::Command->new( cmd => "$feh --list $images" );
$cmd->exit_is_num(0);
-$cmd->stdout_is_file('test/list/default');
+$cmd->stdout_is_file("test/${list_dir}/default");
$cmd->stderr_like($re_warning);
for my $sort (qw/name filename width height pixels size format/) {
$cmd = Test::Command->new( cmd => "$feh --list $images --sort $sort" );
$cmd->exit_is_num(0);
- $cmd->stdout_is_file("test/list/$sort");
+ $cmd->stdout_is_file("test/${list_dir}/$sort");
$cmd->stderr_like($re_warning);
}
@@ -108,7 +118,7 @@ $cmd
= Test::Command->new( cmd => "$feh --list $images --sort format --reverse" );
$cmd->exit_is_num(0);
-$cmd->stdout_is_file('test/list/format_reverse');
+$cmd->stdout_is_file("test/${list_dir}/format_reverse");
$cmd->stderr_like($re_warning);
$cmd = Test::Command->new(
@@ -117,7 +127,7 @@ $cmd = Test::Command->new(
$cmd->exit_is_num(0);
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=813729
-#$cmd->stdout_is_file('test/list/filename_recursive');
+#$cmd->stdout_is_file("test/${list_dir}/filename_recursive");
#$cmd->stderr_is_eq('');
# dummy tests to match number of planned tests
$cmd->exit_is_num(0);
@@ -127,19 +137,19 @@ $cmd = Test::Command->new( cmd => "$feh --customlist '%f; %h; %l; %m; %n; %p; "
. "%s; %t; %u; %w' $images" );
$cmd->exit_is_num(0);
-$cmd->stdout_is_file('test/list/custom');
+$cmd->stdout_is_file("test/${list_dir}/custom");
$cmd->stderr_like($re_warning);
$cmd = Test::Command->new( cmd => "$feh --list --quiet $images" );
$cmd->exit_is_num(0);
-$cmd->stdout_is_file('test/list/default');
+$cmd->stdout_is_file("test/${list_dir}/default");
$cmd->stderr_is_eq('');
$cmd = Test::Command->new(
cmd => "$feh --quiet --list --action 'echo \"%f %wx%h\" >&2' $images" );
$cmd->exit_is_num(0);
-$cmd->stdout_is_file('test/list/default');
+$cmd->stdout_is_file("test/${list_dir}/default");
$cmd->stderr_like($re_list_action);
$cmd
@@ -170,12 +180,16 @@ $cmd
= Test::Command->new( cmd => "$feh --list --min-dimension 16x16 $images_ok" );
$cmd->exit_is_num(0);
-$cmd->stdout_is_file('test/list/default');
+$cmd->stdout_is_file("test/${list_dir}/default");
$cmd->stderr_is_eq('');
$cmd
= Test::Command->new( cmd => "$feh --list --max-dimension 16x16 $images_ok" );
$cmd->exit_is_num(0);
-$cmd->stdout_is_file('test/list/default');
+$cmd->stdout_is_file("test/${list_dir}/default");
+$cmd->stderr_is_eq('');
+
+$cmd = Test::Command->new( cmd => "$feh --list test/tiny.pbm" );
+$cmd->exit_is_num(0);
$cmd->stderr_is_eq('');
diff --git a/test/list/custom b/test/list/custom
index b5ddb32..dbe2074 100644
--- a/test/list/custom
+++ b/test/list/custom
@@ -1,4 +1,4 @@
-test/ok/gif; 16; 4; list; gif; 256; 953; gif; 0; 16
-test/ok/jpg; 16; 4; list; jpg; 256; 354; jpeg; 0; 16
-test/ok/png; 16; 4; list; png; 256; 403; png; 0; 16
-test/ok/pnm; 16; 4; list; pnm; 256; 269; pnm; 0; 16
+test/ok/gif; 16; 4; list; gif; 256; 953; gif; 1; 16
+test/ok/jpg; 16; 4; list; jpg; 256; 354; jpeg; 2; 16
+test/ok/png; 16; 4; list; png; 256; 403; png; 3; 16
+test/ok/pnm; 16; 4; list; pnm; 256; 269; pnm; 4; 16
diff --git a/test/list_imlib2_1.6/custom b/test/list_imlib2_1.6/custom
new file mode 100644
index 0000000..40ac557
--- /dev/null
+++ b/test/list_imlib2_1.6/custom
@@ -0,0 +1,4 @@
+test/ok/gif; 16; 4; list; gif; 256; 953; gif; 1; 16
+test/ok/jpg; 16; 4; list; jpg; 256; 354; jpg; 2; 16
+test/ok/png; 16; 4; list; png; 256; 403; png; 3; 16
+test/ok/pnm; 16; 4; list; pnm; 256; 269; pnm; 4; 16
diff --git a/test/list_imlib2_1.6/default b/test/list_imlib2_1.6/default
new file mode 100644
index 0000000..e480db3
--- /dev/null
+++ b/test/list_imlib2_1.6/default
@@ -0,0 +1,5 @@
+NUM FORMAT WIDTH HEIGHT PIXELS SIZE ALPHA FILENAME
+1 gif 16 16 256 953 - test/ok/gif
+2 jpg 16 16 256 354 - test/ok/jpg
+3 png 16 16 256 403 X test/ok/png
+4 pnm 16 16 256 269 - test/ok/pnm
diff --git a/test/list_imlib2_1.6/filename b/test/list_imlib2_1.6/filename
new file mode 120000
index 0000000..331d858
--- /dev/null
+++ b/test/list_imlib2_1.6/filename
@@ -0,0 +1 @@
+default \ No newline at end of file
diff --git a/test/list_imlib2_1.6/filename_recursive b/test/list_imlib2_1.6/filename_recursive
new file mode 100644
index 0000000..e42ce14
--- /dev/null
+++ b/test/list_imlib2_1.6/filename_recursive
@@ -0,0 +1,7 @@
+NUM FORMAT WIDTH HEIGHT PIXELS SIZE ALPHA FILENAME
+1 gif 16 16 256 953 - test/ok/gif
+2 jpg 16 16 256 354 - test/ok/jpg
+3 jpg 16 16 256 9k - test/ok/jpg_exif
+4 png 16 16 256 403 X test/ok/png
+5 pnm 16 16 256 269 - test/ok/pnm
+6 png 16 16 256 403 X test/ok/recursive/png
diff --git a/test/list_imlib2_1.6/format b/test/list_imlib2_1.6/format
new file mode 120000
index 0000000..331d858
--- /dev/null
+++ b/test/list_imlib2_1.6/format
@@ -0,0 +1 @@
+default \ No newline at end of file
diff --git a/test/list_imlib2_1.6/format_reverse b/test/list_imlib2_1.6/format_reverse
new file mode 100644
index 0000000..3301f78
--- /dev/null
+++ b/test/list_imlib2_1.6/format_reverse
@@ -0,0 +1,5 @@
+NUM FORMAT WIDTH HEIGHT PIXELS SIZE ALPHA FILENAME
+1 pnm 16 16 256 269 - test/ok/pnm
+2 png 16 16 256 403 X test/ok/png
+3 jpg 16 16 256 354 - test/ok/jpg
+4 gif 16 16 256 953 - test/ok/gif
diff --git a/test/list_imlib2_1.6/height b/test/list_imlib2_1.6/height
new file mode 120000
index 0000000..331d858
--- /dev/null
+++ b/test/list_imlib2_1.6/height
@@ -0,0 +1 @@
+default \ No newline at end of file
diff --git a/test/list_imlib2_1.6/name b/test/list_imlib2_1.6/name
new file mode 120000
index 0000000..331d858
--- /dev/null
+++ b/test/list_imlib2_1.6/name
@@ -0,0 +1 @@
+default \ No newline at end of file
diff --git a/test/list_imlib2_1.6/pixels b/test/list_imlib2_1.6/pixels
new file mode 120000
index 0000000..331d858
--- /dev/null
+++ b/test/list_imlib2_1.6/pixels
@@ -0,0 +1 @@
+default \ No newline at end of file
diff --git a/test/list_imlib2_1.6/size b/test/list_imlib2_1.6/size
new file mode 100644
index 0000000..7716239
--- /dev/null
+++ b/test/list_imlib2_1.6/size
@@ -0,0 +1,5 @@
+NUM FORMAT WIDTH HEIGHT PIXELS SIZE ALPHA FILENAME
+1 pnm 16 16 256 269 - test/ok/pnm
+2 jpg 16 16 256 354 - test/ok/jpg
+3 png 16 16 256 403 X test/ok/png
+4 gif 16 16 256 953 - test/ok/gif
diff --git a/test/list_imlib2_1.6/width b/test/list_imlib2_1.6/width
new file mode 120000
index 0000000..331d858
--- /dev/null
+++ b/test/list_imlib2_1.6/width
@@ -0,0 +1 @@
+default \ No newline at end of file
diff --git a/test/mandoc.t b/test/mandoc.t
index 3740809..9e7ffc3 100755
--- a/test/mandoc.t
+++ b/test/mandoc.t
@@ -3,18 +3,25 @@ use strict;
use warnings;
use 5.010;
-use Test::More tests => 3;
+use Test::More tests => 1;
SKIP: {
- qx{mandoc -V};
+ my $mandoc_present = 0;
- if ( $? != 0 ) {
+ for my $path (split(qr{:}, $ENV{PATH})) {
+ if (-x "${path}/mandoc") {
+ $mandoc_present = 1;
+ last;
+ }
+ }
+
+ if ( not $mandoc_present ) {
diag('mandoc not installed, test skipped. This is NOT fatal.');
- skip( 'mandoc not installed', 3 );
+ skip( 'mandoc not installed', 1 );
}
- for my $file ( 'feh', 'feh-cam', 'gen-cam-menu' ) {
- qx{mandoc -Tlint man/${file}.1};
+ for my $file ('feh') {
+ qx{mandoc -Tlint -Werror man/${file}.1};
is( $?, 0, "${file}.1: Valid mdoc syntax" );
}
}
diff --git a/test/nx_action/loadable_action b/test/nx_action/loadable_action
index d173261..fbf517b 100644
--- a/test/nx_action/loadable_action
+++ b/test/nx_action/loadable_action
@@ -1,8 +1,8 @@
-touch test/ok/gif
-touch test/ok/jpg
-touch test/ok/png
-touch test/ok/pnm
test/ok/gif
+touch test/ok/gif
test/ok/jpg
+touch test/ok/jpg
test/ok/png
+touch test/ok/png
test/ok/pnm
+touch test/ok/pnm
diff --git a/test/nx_action/loadable_naction b/test/nx_action/loadable_naction
index d173261..fbf517b 100644
--- a/test/nx_action/loadable_naction
+++ b/test/nx_action/loadable_naction
@@ -1,8 +1,8 @@
-touch test/ok/gif
-touch test/ok/jpg
-touch test/ok/png
-touch test/ok/pnm
test/ok/gif
+touch test/ok/gif
test/ok/jpg
+touch test/ok/jpg
test/ok/png
+touch test/ok/png
test/ok/pnm
+touch test/ok/pnm
diff --git a/test/nx_action/unloadable_action b/test/nx_action/unloadable_action
index c16572e..cdf3ed8 100644
--- a/test/nx_action/unloadable_action
+++ b/test/nx_action/unloadable_action
@@ -1,8 +1,8 @@
-rm test/fail/gif
-rm test/fail/jpg
-rm test/fail/png
-rm test/fail/pnm
test/fail/gif
+rm test/fail/gif
test/fail/jpg
+rm test/fail/jpg
test/fail/png
+rm test/fail/png
test/fail/pnm
+rm test/fail/pnm
diff --git a/test/nx_action/unloadable_naction b/test/nx_action/unloadable_naction
index c16572e..cdf3ed8 100644
--- a/test/nx_action/unloadable_naction
+++ b/test/nx_action/unloadable_naction
@@ -1,8 +1,8 @@
-rm test/fail/gif
-rm test/fail/jpg
-rm test/fail/png
-rm test/fail/pnm
test/fail/gif
+rm test/fail/gif
test/fail/jpg
+rm test/fail/jpg
test/fail/png
+rm test/fail/png
test/fail/pnm
+rm test/fail/pnm
diff --git a/test/status b/test/status
index 2cda6d8..6db362e 100644
--- a/test/status
+++ b/test/status
@@ -76,15 +76,13 @@ Overall test status, what's covered / missing
[x] correct caption display
---collage
-
--customlist
[x] correct output
[x] format specifiers
---cycle-once
+--on-last-slide=quit
[x] closes feh window at end of slideshow
[x] combination with --slideshow-delay
diff --git a/test/tiny.pbm b/test/tiny.pbm
new file mode 100644
index 0000000..3fb3e4e
--- /dev/null
+++ b/test/tiny.pbm
@@ -0,0 +1,4 @@
+P4
+1 1
+
+