diff options
49 files changed, 1864 insertions, 1375 deletions
diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f59e68c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,29 @@ +language: c +addons: + apt: + packages: + - libcurl4-openssl-dev + - libx11-dev + - libxt-dev + - libimlib2-dev + - libxinerama-dev + - libjpeg-progs + - libtest-command-perl + - libtest-simple-perl + - libexif-dev + - libexif12 +script: + - make + - make test +compiler: + - clang + - gcc +env: + - default=1 + - app=1 + - curl=0 + - exif=1 + - help=1 + - stat64=1 + - verscmp=0 + - xinerama=0 @@ -1,5 +1,5 @@ Copyright (C) 1999,2000 Tom Gilbert. -Copyright (C) 2010-2016 Daniel Friesel. +Copyright (C) 2010-2018 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 @@ -1,3 +1,158 @@ +Tue, 17 Jul 2018 17:33:10 +0200 Daniel 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 Daniel 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 Daniel 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 Daniel 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 Daniel 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 Daniel 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 Daniel 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 Daniel 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 Daniel 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 Daniel 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 Daniel 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 Daniel 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 Daniel 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 Daniel 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 Daniel 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 Daniel 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 Daniel 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 Daniel 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 Daniel Friesel <derf+feh@finalrewind.org> * Release v2.19.3 @@ -129,7 +284,7 @@ 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 Daniel Friesel <derf+feh@finalrewind.org> * Release v2.15.2 * Fix --keep-zoom-vp not keeping the viewport x/y offsets (broken by 2.15) @@ -701,7 +856,7 @@ 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> @@ -58,18 +58,21 @@ install-doc: install-bin: @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}/* @@ -1,3 +1,5 @@ +[![build status](https://travis-ci.org/derf/feh.svg?branch=master)](https://travis-ci.org/derf/feh) + # feh Imlib2 based image viewer --- @@ -84,6 +86,7 @@ Available flags are: | 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 | +| verscmp | 1 | Support naturing sorting (`--version-sort`). Requires a GNU-compatible libc exposing `strverscmp` | | xinerama | 1 | Support Xinerama/XRandR multiscreen setups | So, by default **libcurl** and **Xinerama** are enabled, the rest is disabled. @@ -5,14 +5,18 @@ app ?= 0 cam ?= 0 curl ?= 1 debug ?= 0 +exif ?= 0 help ?= 0 +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 +38,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 + ifeq (${curl},1) CFLAGS += -DHAVE_LIBCURL LDLIBS += -lcurl @@ -57,6 +64,13 @@ ifeq (${stat64},1) CFLAGS += -D_FILE_OFFSET_BITS=64 endif +ifeq (${verscmp},1) + CFLAGS += -DHAVE_VERSCMP + MAN_VERSCMP = enabled +else + MAN_VERSCMP = disabled +endif + ifeq (${xinerama},1) CFLAGS += -DHAVE_LIBXINERAMA LDLIBS += -lXinerama diff --git a/man/Makefile b/man/Makefile index 3be07e3..acf8629 100644 --- a/man/Makefile +++ b/man/Makefile @@ -13,6 +13,7 @@ all: ${TARGETS} -e 's/\$$MAN_DEBUG\$$/${MAN_DEBUG}/' \ -e 's/\$$MAN_EXIF\$$/${MAN_EXIF}/' \ -e 's/\$$MAN_INOTIFY\$$/${MAN_INOTIFY}/' \ + -e 's/\$$MAN_VERSCMP\$$/${MAN_VERSCMP}/' \ -e 's/\$$MAN_XINERAMA\$$/${MAN_XINERAMA}/' \ < ${@:.1=.pre} > $@ diff --git a/man/feh.pre b/man/feh.pre index b258b2b..e1ade10 100644 --- a/man/feh.pre +++ b/man/feh.pre @@ -13,7 +13,7 @@ . .Nm .Op Ar options -.Op Ar files No | Ar directories No | Ar URLs ... +.Op Ar files | Ar directories | Ar URLs ... . . .Sh VERSION @@ -24,7 +24,8 @@ $VERSION$ . .Pp . -Compile-time switches: libcurl support $MAN_CURL$, Xinerama support +Compile-time switches: libcurl support $MAN_CURL$, natural sorting support +$MAN_VERSCMP$, Xinerama support $MAN_XINERAMA$, builtin EXIF support $MAN_EXIF$, inotify support $MAN_INOTIFY$$MAN_DEBUG$ . @@ -32,9 +33,9 @@ $MAN_INOTIFY$$MAN_DEBUG$ .Sh DESCRIPTION . .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 +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 @@ -46,7 +47,8 @@ displays all files in the current directory. . .Nm supports filelists, various image sorting modes, image captions, HTTP and more. -Configurable keyboard and mouse shortcuts are used to control it. +It can be controlled by configurable keyboard and mouse shortcuts, terminal +input and signals. . .Pp . @@ -74,8 +76,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 @@ -86,14 +89,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, +filesize and pixel size, 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 @@ -104,35 +110,45 @@ 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. +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. . 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 +svg, xcf and otf. +If dcraw is available, +.Nm +also supports RAW files provided by cameras and will display the embedded +thumbnails. +Use +.Cm --conversion-timeout Ar num with a non-negative value to enable it. . . @@ -142,11 +158,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 . @@ -163,7 +181,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 @@ -171,10 +189,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. @@ -182,25 +202,22 @@ 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: +for details. +Example usage: .Qq feh -A Qo mv ~/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 only if compiled with inotify=1 -automatically reload shown image if file was changed -. .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 +Automatically rotate images based on EXIF data. +Does not alter the image files. . .It Cm -Z , --auto-zoom . @@ -210,19 +227,34 @@ 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 , @@ -233,22 +265,17 @@ the caption will be looked for in . 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 . @@ -265,7 +292,8 @@ on a semi-transparent background to improve their readability . .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 @@ -276,7 +304,8 @@ directory, one filename per line. 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. . @@ -288,7 +317,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 @@ -302,8 +332,9 @@ 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 +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 . . .It Cm -C , --fontpath Ar path @@ -331,7 +362,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. . @@ -342,7 +374,8 @@ Note that in this mode, large images will always be scaled down to fit the screen, .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. @@ -351,20 +384,22 @@ their dimensions. . 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 +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 +.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. +WM or manually is still possible. +After each resize, the resulting window size is used as the new size limit. . .It Cm -Y , --hide-pointer . @@ -373,16 +408,24 @@ 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. +Use +.Ar style +as background for transparent image parts and the like. +Accepted values: default, checks, or an XColor +.Pq eg. Qo black Qc or Qo #428bdd Qc . . -The default for windowed mode is checks, while fullscreen defaults to black. +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, 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 . @@ -391,8 +434,9 @@ and . 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 +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 @@ -415,8 +459,9 @@ 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. +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 . @@ -425,25 +470,27 @@ 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 +.It Cm --conversion-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 +seconds. +Negative values disable conversion altogether, zero causes .Nm -to try indefinitely. By default, magick support is disabled. +to try indefinitely. +By default, conversion is disabled. . .It Cm --max-dimension Ar width No x Ar height . @@ -471,15 +518,18 @@ 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-jump-on-resort . @@ -491,15 +541,50 @@ 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. +. +.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 . @@ -510,23 +595,27 @@ 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 . @@ -542,29 +631,32 @@ Useful to override theme options. . Reload filelist and current image after .Ar int -seconds. Useful for viewing HTTP webcams or frequently changing directories. +seconds. +Useful for viewing HTTP webcams or frequently changing directories. .Pq Note that the filelist reloading is still experimental. . .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. . .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. . @@ -587,7 +679,8 @@ Default: 20 . 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 @@ -596,15 +689,16 @@ 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 -necessary, causing a delay proportional to the number of images in the list. +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 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. +The mtime sort mode sorts images by most recently modified. +To sort by oldest first, reverse the filelist with --reverse. . .It Cm -| , --start-at Ar filename . @@ -615,12 +709,14 @@ Note that at the moment, .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, +path in the filelist. +So, if the file to be matched is passed via an absolute path in the filelist, .Ar filename -must be an absolute path. If the file is passed via a relative path, +must be an absolute path. +If the file is passed via a relative path, .Ar filename -must be an identical relative path. This is a known issue. +must be an identical relative path. +This is a known issue. See also .Sx USAGE EXAMPLES . . @@ -630,7 +726,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 commandline options always override theme options. The theme can also be set via the program name .Pq e.g. with symlinks , so by default @@ -646,8 +743,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 @@ -657,19 +754,21 @@ 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. . .It Cm -V , --verbose @@ -680,12 +779,23 @@ output useful information, progress bars, etc. . output version information and exit. . +.It Cm --version-sort +. +.Pq optional feature, $MAN_VERSCMP$ in this build +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 . +. .It Cm --xinerama-index Ar screen . +.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 screen. +May be useful in certain circumstances where the window manager places the feh +window on Xinerama screen A while .Nm assumes that it will be placed on screen B. . @@ -697,18 +807,20 @@ 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 +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 .Qq --xinerama-index 0 when setting the wallpaper. . -.It Cm --zoom Ar percent No | Cm max No | Cm fill +.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 . @@ -741,14 +853,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 , @@ -757,8 +870,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. . @@ -792,8 +905,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. . @@ -805,38 +919,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. . @@ -852,7 +953,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. . @@ -860,6 +962,26 @@ 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. +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 . . @@ -867,41 +989,37 @@ 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. . .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 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. . .Pp . @@ -919,8 +1037,8 @@ on screen 0, the second on screen 1, and so on. . 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 screen when setting wallpapers. +You may also use .Cm --xinerama-index to use .Nm @@ -930,26 +1048,30 @@ as a background setter for a specific screen. . .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 . @@ -979,6 +1101,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 @@ -989,7 +1115,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 . @@ -1013,12 +1140,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 . @@ -1052,7 +1181,11 @@ Process ID . .It %z . -current image zoom +Current image zoom, rounded to two decimal places +. +.It %Z +. +Current image zoom, higher precision . .It %% . @@ -1101,29 +1234,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 @@ -1190,9 +1336,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 @@ -1216,8 +1362,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 . @@ -1232,12 +1390,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 @@ -1247,7 +1406,7 @@ 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 @@ -1255,10 +1414,14 @@ Toggle EXIF tag display Save the current filelist as .Qq feh_PID_ID_filelist . +.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 @@ -1268,19 +1431,22 @@ 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 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, .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 . @@ -1288,7 +1454,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 . @@ -1297,7 +1464,8 @@ Quit . .It r Bq reload_image . -Reload current image. Useful for webcams +Reload current image. +Useful for webcams . .It s Bq save_image . @@ -1312,8 +1480,7 @@ Toggle fullscreen . 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 . @@ -1323,27 +1490,35 @@ 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 +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. . @@ -1504,8 +1679,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 @@ -1513,7 +1688,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. @@ -1615,11 +1790,12 @@ 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 @@ -1643,67 +1819,71 @@ Switch to previous image . .Sh USAGE EXAMPLES . -Here are some examples of useful option combinations. See also: +Here are some examples of useful option combinations. +See also: .Aq http://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 \&. . -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 the current directory, starting with foo.jpg. +All other images are still in the slideshow and can be viewed normally . .It feh --start-at foo.jpg * . @@ -1736,25 +1916,26 @@ for lossless rotation. . To view images from URLs such as http://, you need .Nm -compiled with libcurl support (enabled by default). See the +compiled with libcurl support (enabled by default). +See the .Sx VERSION section. . . .Sh BUGS . -.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. @@ -1770,7 +1951,6 @@ 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 @@ -1803,7 +1983,7 @@ Make zoom options more intuitive . 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) 2010-2018 by Daniel Friesel (and even more contributors). . .Pp . @@ -1825,10 +2005,11 @@ 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 . diff --git a/src/Makefile b/src/Makefile index 0e2c543..8a9f97e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,40 @@ include ../config.mk -TARGETS = ${shell echo *.c} +TARGETS = \ + collage.c \ + events.c \ + feh_png.c \ + filelist.c \ + getopt.c \ + getopt1.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 + OBJECTS = ${TARGETS:.c=.o} I_SRCS = ${shell echo *.raw} @@ -17,9 +51,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 index b975136..2a4d9f9 100644 --- a/src/collage.c +++ b/src/collage.c @@ -1,7 +1,7 @@ /* collage.c Copyright (C) 1999-2003 Tom Gilbert. -Copyright (C) 2010-2011 Daniel Friesel. +Copyright (C) 2010-2018 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 @@ -41,7 +41,6 @@ void init_collage_mode(void) feh_file *file = NULL; unsigned char trans_bg = 0; gib_list *l, *last = NULL; - char *s; mode = "collage"; @@ -105,15 +104,9 @@ void init_collage_mode(void) 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); + winwid = winwidget_create_from_image(im_main, WIN_TYPE_SINGLE); + winwidget_rename(winwid, PACKAGE " [collage mode]"); winwidget_show(winwid); } @@ -210,7 +203,6 @@ void init_collage_mode(void) 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..38cf83f 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-2018 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 diff --git a/src/events.c b/src/events.c index b20fd4f..89eaab8 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-2018 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 @@ -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) @@ -101,13 +102,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 +137,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 +156,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 +201,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 +226,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 +241,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; @@ -285,7 +269,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; @@ -313,16 +297,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 +347,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; @@ -401,13 +385,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 +401,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 +509,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 +523,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); } @@ -1,7 +1,7 @@ /* feh.h Copyright (C) 1999-2003 Tom Gilbert. -Copyright (C) 2010-2011 Daniel Friesel. +Copyright (C) 2010-2018 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 @@ -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_VERSCMP +#define _GNU_SOURCE +#endif + #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xatom.h> @@ -107,8 +115,6 @@ 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 }; - #define INPLACE_EDIT_FLIP -1 #define INPLACE_EDIT_MIRROR -2 @@ -133,14 +139,15 @@ void init_list_mode(void); void init_loadables_mode(void); void init_unloadables_mode(void); void feh_clean_exit(void); +int feh_should_ignore_image(Imlib_Image * im); int feh_load_image(Imlib_Image * im, feh_file * file); void show_mini_usage(void); void slideshow_change_image(winwidget winwid, int change, int render); void slideshow_pause_toggle(winwidget w); -char *slideshow_create_name(feh_file * file, winwidget winwid); -char *thumbnail_create_name(feh_file * file, winwidget winwid); void init_keyevents(void); void init_buttonbindings(void); +void setup_stdin(void); +void restore_stdin(void); void feh_event_handle_keypress(XEvent * ev); void feh_event_handle_stdin(); void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysym, unsigned int button); @@ -204,4 +211,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..d0c1c8a 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) { 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 162fd57..870e463 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-2018 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 @@ -397,14 +397,26 @@ void feh_file_dirname(char *dst, feh_file * f, int maxlen) dst[n] = '\0'; } +#ifdef HAVE_VERSCMP +static inline int strcmp_or_strverscmp(const char *s1, const char *s2) +{ + if (!opt.version_sort) + return(strcmp(s1, s2)); + else + return(strverscmp(s1, s2)); +} +#else +#define strcmp_or_strverscmp strcmp +#endif + 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 +425,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)); } @@ -464,9 +476,17 @@ 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_MTIME) + || (opt.filter_by_dimensions && (opt.index || opt.collage || opt.thumbs || opt.bgmode))) { /* For these sort options, we have to preload images */ filelist = feh_file_info_preload(filelist); if (!gib_list_length(filelist)) @@ -554,7 +574,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 +582,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 +594,7 @@ gib_list *feh_read_filelist(char *filename) return NULL; } } - opt.magick_timeout = tmp_magick_timeout; + opt.conversion_timeout = tmp_conversion_timeout; errno = 0; diff --git a/src/gib_hash.c b/src/gib_hash.c index a378b9c..0d6a226 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" diff --git a/src/gib_list.c b/src/gib_list.c index 281f528..5384d98 100644 --- a/src/gib_list.c +++ b/src/gib_list.c @@ -360,7 +360,6 @@ 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 ); diff --git a/src/help.raw b/src/help.raw index 997a259..2532325 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 @@ -38,7 +38,8 @@ OPTIONS --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 + --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,6 +53,7 @@ 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 @@ -84,7 +86,7 @@ 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) -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 @@ -94,6 +96,7 @@ OPTIONS --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 MONTAGE MODE OPTIONS diff --git a/src/imlib.c b/src/imlib.c index ecc44b5..f41cdcd 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-2018 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 @@ -61,7 +61,9 @@ int num_xinerama_screens; 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 +133,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; @@ -202,15 +215,10 @@ void feh_imlib_print_load_error(char *file, winwidget w, Imlib_Load_Error err) 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 { 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) @@ -222,18 +230,25 @@ int feh_load_image(Imlib_Image * im, feh_file * file) if ((tmpname = feh_http_load_image(file->filename)) == NULL) err = IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST; } + else if (opt.conversion_timeout >= 0 && feh_file_is_raw(file->filename)) { + image_source = SRC_DCRAW; + tmpname = feh_dcraw_load_image(file->filename); + if (!tmpname) + 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)) { + if (opt.conversion_timeout >= 0 && ( + (err == IMLIB_LOAD_ERROR_UNKNOWN) || + (err == IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT))) { image_source = SRC_MAGICK; tmpname = feh_magick_load_image(file->filename); } - if ((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_file_info_load(file, *im); @@ -242,7 +257,7 @@ int feh_load_image(Imlib_Image * im, feh_file * file) file->ed = exif_get_data(tmpname); #endif } - if ((image_source == SRC_MAGICK) || !opt.keep_http) + if ((image_source != SRC_HTTP) || !opt.keep_http) unlink(tmpname); free(tmpname); @@ -258,6 +273,16 @@ int feh_load_image(Imlib_Image * im, feh_file * file) 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); @@ -281,17 +306,105 @@ int feh_load_image(Imlib_Image * im, feh_file * file) return(1); } +static int feh_file_is_raw(char *filename) +{ + childpid = fork(); + if (childpid == -1) { + perror("fork"); + return 0; + } + + if (childpid == 0) { + if (opt.quiet) { + 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; + + 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) { + + close(1); + dup(fd); + 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); + } + + 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; - - if (opt.magick_timeout < 0) - return NULL; + char created_tempdir = 0; basename = strrchr(filename, '/'); @@ -310,10 +423,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,39 +472,58 @@ 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); return sfn; } @@ -425,6 +580,10 @@ static char *feh_http_load_image(char *url) 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); @@ -459,7 +618,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 +834,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; + gib_imlib_get_text_size(fn, buffer, NULL, &width, &height, IMLIB_TEXT_TO_RIGHT); + info_buf[no_lines] = estrdup(buffer); + no_lines++; } else { @@ -1262,7 +1422,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 fbc25b8..af3adea 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-2018 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 @@ -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) { @@ -148,9 +147,16 @@ void init_index_mode(void) index_image_height = h + title_area_h; im_main = imlib_create_image(index_image_width, index_image_height); - if (!im_main) - eprintf("Failed to create %dx%d pixels (%d MB) index image. Do you have enough RAM?", - index_image_width, index_image_height, index_image_width * index_image_height * 4 / (1024*1024)); + if (!im_main) { + if (index_image_height >= 32768 || index_image_width >= 32768) { + eprintf("Failed to create %dx%d pixels (%d MB) index image.\n" + "This is probably due to Imlib2 issues when dealing with images larger than 32k x 32k pixels.", + index_image_width, index_image_height, index_image_width * index_image_height * 4 / (1024*1024)); + } else { + eprintf("Failed to create %dx%d pixels (%d MB) index image. Do you have enough RAM?", + index_image_width, index_image_height, index_image_width * index_image_height * 4 / (1024*1024)); + } + } if (bg_im) gib_imlib_blend_image_onto_image(im_main, bg_im, @@ -164,15 +170,9 @@ void init_index_mode(void) gib_imlib_image_fill_rectangle(im_main, 0, 0, w, h + title_area_h, 0, 0, 0, 255); } - /* Create the window title at this point */ - - if (!opt.title) - s = estrdup(PACKAGE " [index mode]"); - else - s = estrdup(feh_printf(opt.title, NULL, NULL)); - if (opt.display) { - winwid = winwidget_create_from_image(im_main, s, WIN_TYPE_SINGLE); + winwid = winwidget_create_from_image(im_main, WIN_TYPE_SINGLE); + winwidget_rename(winwid, PACKAGE " [index mode]"); winwidget_show(winwid); } @@ -348,7 +348,6 @@ void init_index_mode(void) if (!opt.display) gib_imlib_free_image_and_decache(im_main); - free(s); return; } diff --git a/src/index.h b/src/index.h index 08ab337..b022f1a 100644 --- a/src/index.h +++ b/src/index.h @@ -1,6 +1,6 @@ /* index.h -Copyright (C) 2011 Daniel Friesel. +Copyright (C) 2018 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 diff --git a/src/keyevents.c b/src/keyevents.c index b5c1949..689aebd 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-2018 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 @@ -29,17 +29,51 @@ 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() { + 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() { + 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) { @@ -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_v, 0, 0, 0, 0); + feh_set_kb("reload_image" , 0, XK_r, 0, 0, 0, 0); + feh_set_kb("save_image" , 0, XK_s, 0, 0, 0, 0); + feh_set_kb("save_filelist" , 0, XK_f, 0, 0, 0, 0); + feh_set_kb("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); @@ -269,22 +317,52 @@ void feh_event_invoke_action(winwidget winwid, unsigned char action) void feh_event_handle_stdin() { char stdin_buf[2]; + static char is_esc = 0; KeySym keysym = NoSymbol; if (read(STDIN_FILENO, &stdin_buf, 1) == -1) { - weprintf("reading a command from stdin failed"); + 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) - feh_event_handle_generic(windows[0], 0, keysym, 0); + feh_event_handle_generic(windows[0], is_esc * Mod1Mask, keysym, 0); + + is_esc = 0; } void feh_event_handle_keypress(XEvent * ev) @@ -314,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; } @@ -332,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) { @@ -383,272 +477,131 @@ 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; @@ -662,7 +615,7 @@ 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; @@ -676,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); @@ -694,46 +647,50 @@ 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)); else slideshow_change_image(winwid, SLIDE_RAND, 1); } - else if (feh_is_kp(&keys.toggle_caption, state, keysym, button)) { + else if (feh_is_kp(EVENT_toggle_caption, state, keysym, button)) { if (opt.caption_path) { /* * editing captions in slideshow mode does not make any sense @@ -745,44 +702,44 @@ 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); } - 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]; @@ -818,20 +775,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; } @@ -1,7 +1,7 @@ /* list.c Copyright (C) 1999-2003 Tom Gilbert. -Copyright (C) 2010-2011 Daniel Friesel. +Copyright (C) 2010-2018 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 @@ -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 { @@ -1,7 +1,7 @@ /* main.c Copyright (C) 1999-2003 Tom Gilbert. -Copyright (C) 2010-2011 Daniel Friesel. +Copyright (C) 2010-2018 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 @@ -42,13 +42,12 @@ char **cmdargv = NULL; int cmdargc = 0; char *mode = NULL; -struct termios old_term_settings; -int control_via_stdin = 0; - int main(int argc, char **argv) { atexit(feh_clean_exit); + srand(getpid() * time(NULL) % ((unsigned int) -1)); + setup_signal_handlers(); init_parse_options(argc, argv); @@ -89,16 +88,19 @@ 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); } /* Return 0 to stop iterating, 1 if ok to continue. */ @@ -115,7 +117,7 @@ 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 (first) { @@ -124,20 +126,18 @@ int feh_main_iteration(int block) fdsize = xfd + 1; pt = feh_get_time(); first = 0; - if (isatty(STDIN_FILENO)) { - control_via_stdin = 1; - struct termios ctrl; - 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"); + /* + * 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(); } } @@ -150,7 +150,7 @@ 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); } XFlush(disp); @@ -211,7 +211,7 @@ int feh_main_iteration(int block) in that */ feh_handle_timer(); } - else if (count && (FD_ISSET(0, &fdset))) + else if ((count > 0) && (FD_ISSET(0, &fdset))) feh_event_handle_stdin(); #ifdef HAVE_INOTIFY else if (count && (FD_ISSET(opt.inotify_fd, &fdset))) @@ -228,7 +228,7 @@ int feh_main_iteration(int block) && ((errno == ENOMEM) || (errno == EINVAL) || (errno == EBADF))) eprintf("Connection to X display lost"); - else if (count && (FD_ISSET(0, &fdset))) + else if ((count > 0) && (FD_ISSET(0, &fdset))) feh_event_handle_stdin(); #ifdef HAVE_INOTIFY else if (count && (FD_ISSET(opt.inotify_fd, &fdset))) @@ -236,7 +236,7 @@ int feh_main_iteration(int block) #endif } } - if (window_num == 0) + if (window_num == 0 || sig_exit != 0) return(0); return(1); @@ -258,9 +258,16 @@ void feh_clean_exit(void) if(disp) XCloseDisplay(disp); - if (control_via_stdin) - if (tcsetattr(STDIN_FILENO, TCSANOW, &old_term_settings) == -1) - eprintf("tcsetattr failed"); + /* + * 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); @@ -1,7 +1,7 @@ /* menu.c Copyright (C) 1999-2003 Tom Gilbert. -Copyright (C) 2010-2011 Daniel Friesel. +Copyright (C) 2010-2018 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 @@ -1,7 +1,7 @@ /* menu.h Copyright (C) 1999-2003 Tom Gilbert. -Copyright (C) 2010-2011 Daniel Friesel. +Copyright (C) 2010-2018 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 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 20e6272..364fb42 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-2018 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 @@ -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,7 +54,7 @@ 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; @@ -67,6 +68,7 @@ void init_parse_options(int argc, char **argv) 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; @@ -211,7 +213,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 +228,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 +239,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; } @@ -409,8 +413,13 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun) {"xinerama-index", 1, 0, 239}, {"insecure" , 0, 0, 240}, {"no-recursive" , 0, 0, 241}, + {"cache-size" , 1, 0, 243}, + {"on-last-slide" , 1, 0, 244}, + {"conversion-timeout" , 1, 0, 245}, + {"version-sort" , 0, 0, 246}, + {"offset" , 1, 0, 247}, #ifdef HAVE_INOTIFY - {"auto-reload" , 0, 0, 243}, + {"auto-reload" , 0, 0, 248}, #endif {0, 0, 0, 0} }; @@ -430,6 +439,7 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun) opt.debug = 1; break; case '<': + 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; @@ -437,6 +447,7 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun) opt.max_height = UINT_MAX; break; case '>': + opt.filter_by_dimensions = 1; XParseGeometry(optarg, &discard, &discard, &opt.min_width, &opt.min_height); break; case '.': @@ -449,14 +460,7 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun) 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); + opt.image_bg = estrdup(optarg); break; case 'C': D(("adding fontpath %s\n", optarg)); @@ -592,6 +596,7 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun) opt.filelistfile = estrdup(optarg); break; case 'g': + opt.geom_enabled = 1; opt.geom_flags = XParseGeometry(optarg, &opt.geom_x, &opt.geom_y, &opt.geom_w, &opt.geom_h); break; @@ -692,7 +697,8 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun) opt.index_info = estrdup(optarg); break; case 208: - opt.magick_timeout = atoi(optarg); + weprintf("--magick-timeout is deprecated, please use --conversion-timeout instead"); + opt.conversion_timeout = atoi(optarg); break; case 209: opt.actions[1] = estrdup(optarg); @@ -738,13 +744,9 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun) opt.auto_rotate = 1; break; #endif -#ifdef HAVE_INOTIFY - case 243: - opt.auto_reload = 1; - break; -#endif case 224: - opt.cycle_once = 1; + weprintf("--cycle-once is deprecated, please use --on-last-slide=quit instead"); + opt.on_last_slide = ON_LAST_SLIDE_QUIT; break; case 225: opt.xinerama = 0; @@ -776,8 +778,44 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun) break; case 240: opt.insecure_ssl = 1; + break; case 241: opt.recursive = 0; + break; + case 243: + 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 244: + 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 245: + opt.conversion_timeout = atoi(optarg); + break; + case 246: + opt.version_sort = 1; + break; + case 247: + opt.offset_flags = XParseGeometry(optarg, &opt.offset_x, + &opt.offset_y, (unsigned int *)&discard, (unsigned int *)&discard); + break; +#ifdef HAVE_INOTIFY + case 248: + opt.auto_reload = 1; + break; +#endif default: break; } diff --git a/src/options.h b/src/options.h index 2cfef2f..cae04ec 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-2018 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 @@ -27,6 +27,12 @@ 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; @@ -72,17 +78,18 @@ 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; char *output_file; char *output_dir; char *bg_file; + char *image_bg; char *font; char *title_font; char *title; @@ -107,12 +114,17 @@ 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; unsigned char adjust_reload; @@ -121,6 +133,9 @@ struct __fehoptions { /* 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; @@ -128,7 +143,7 @@ struct __fehoptions { double slideshow_delay; - signed short magick_timeout; + signed int conversion_timeout; Imlib_Font menu_fn; }; @@ -138,80 +153,83 @@ struct __fehkey { 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..aeaf889 100644 --- a/src/signals.c +++ b/src/signals.c @@ -1,6 +1,6 @@ /* signals.c -Copyright (C) 2010 by Daniel Friesel +Copyright (C) 2010-2018 by 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 @@ -28,6 +28,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "options.h" void feh_handle_signal(int); +int sig_exit = 0; void setup_signal_handlers() { @@ -40,7 +41,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 +58,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; @@ -75,12 +78,17 @@ void feh_handle_signal(int signo) 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); + sig_exit = 128 + signo; + return; } winwid = winwidget_get_first_window_of_type(WIN_TYPE_SLIDESHOW); diff --git a/src/signals.h b/src/signals.h index 526285d..090ab0b 100644 --- a/src/signals.h +++ b/src/signals.h @@ -27,5 +27,5 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define SIGNALS_H void setup_signal_handlers(); - +extern int sig_exit; #endif diff --git a/src/slideshow.c b/src/slideshow.c index 4a71dc3..3770677 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-2018 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 @@ -35,9 +35,7 @@ void init_slideshow_mode(void) { winwidget w = NULL; int success = 0; - char *s = NULL; gib_list *l = filelist, *last = NULL; - feh_file *file = NULL; for (; l && opt.start_list_at; l = l->next) { if (!strcmp(opt.start_list_at, FEH_FILE(l->data)->filename)) { @@ -50,17 +48,17 @@ void init_slideshow_mode(void) 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 +67,6 @@ void init_slideshow_mode(void) feh_add_unique_timer(cb_reload_timer, w, opt.reload); break; } else { - free(s); last = l; } } @@ -110,6 +107,10 @@ void cb_reload_timer(void *data) add_file_to_filelist_recursively(l->data, FILELIST_FIRST); else if (!opt.filelistfile && !opt.bgmode) add_file_to_filelist_recursively(".", FILELIST_FIRST); + + if (opt.filelistfile) { + filelist = gib_list_cat(filelist, feh_read_filelist(opt.filelistfile)); + } if (!(filelist_len = gib_list_length(filelist))) { eprintf("No files found to reload."); @@ -130,13 +131,6 @@ void cb_reload_timer(void *data) 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; @@ -144,21 +138,11 @@ void cb_reload_timer(void *data) void feh_reload_image(winwidget w, int resize, int force_new) { - char *title, *new_title; + char *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); @@ -173,8 +157,8 @@ void feh_reload_image(winwidget w, int resize, int force_new) len = strlen(w->name) + sizeof("Reloading: ") + 1; new_title = emalloc(len); snprintf(new_title, len, "Reloading: %s", w->name); - title = estrdup(w->name); winwidget_rename(w, new_title); + free(new_title); old_w = gib_imlib_image_get_width(w->im); old_h = gib_imlib_image_get_height(w->im); @@ -195,9 +179,6 @@ void feh_reload_image(winwidget w, int resize, int force_new) im_weprintf(w, "Couldn't reload image. Is it still there?"); winwidget_render_image(w, 0, 0); } - winwidget_rename(w, title); - free(title); - free(new_title); return; } @@ -226,20 +207,7 @@ void feh_reload_image(winwidget w, int resize, int force_new) 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); - } - - winwidget_rename(w, title); - free(title); - free(new_title); + winwidget_render_image(w, resize, 0); return; } @@ -247,22 +215,20 @@ void feh_reload_image(winwidget w, int resize, int force_new) 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,9 +238,11 @@ 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 */ @@ -370,43 +338,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 +371,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 +385,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) { @@ -464,29 +399,6 @@ void feh_action_run(feh_file * file, char *action, winwidget winwid) 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) { static char ret[5]; @@ -509,6 +421,7 @@ 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')) { @@ -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); @@ -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': @@ -619,6 +537,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 +573,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 +599,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)) { @@ -736,7 +656,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..ce30eb9 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-2018 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 diff --git a/src/thumbnail.c b/src/thumbnail.c index 761162f..5197618 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-2018 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 @@ -71,7 +71,6 @@ void init_thumbnail_mode(void) gib_list *l, *last = NULL; int lineno; int index_image_width, index_image_height; - char *s; unsigned int thumb_counter = 0; gib_list *line, *lines; @@ -92,6 +91,8 @@ void init_thumbnail_mode(void) td.vertical = 0; td.max_column_w = 0; + if (!opt.thumb_title) + opt.thumb_title = "%n"; mode = "thumbnail"; if (opt.font) @@ -147,9 +148,16 @@ void init_thumbnail_mode(void) D(("imlib_create_image(%d, %d)\n", index_image_width, index_image_height)); td.im_main = imlib_create_image(index_image_width, index_image_height); - if (!td.im_main) - eprintf("Failed to create %dx%d pixels (%d MB) index image. Do you have enough RAM?", - index_image_width, index_image_height, index_image_width * index_image_height * 4 / (1024*1024)); + if (!td.im_main) { + if (index_image_height >= 32768 || index_image_width >= 32768) { + eprintf("Failed to create %dx%d pixels (%d MB) index image.\n" + "This is probably due to Imlib2 issues when dealing with images larger than 32k x 32k pixels.", + index_image_width, index_image_height, index_image_width * index_image_height * 4 / (1024*1024)); + } else { + eprintf("Failed to create %dx%d pixels (%d MB) index image. Do you have enough RAM?", + index_image_width, index_image_height, index_image_width * index_image_height * 4 / (1024*1024)); + } + } gib_imlib_image_set_has_alpha(td.im_main, 1); @@ -168,15 +176,10 @@ 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); } @@ -416,7 +419,6 @@ void init_thumbnail_mode(void) } - free(s); return; } @@ -772,24 +774,25 @@ 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); + thumbwin->file = l; #ifdef HAVE_INOTIFY winwidget_inotify_remove(thumbwin); #endif @@ -931,16 +934,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..09cd771 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-2018 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 diff --git a/src/timers.c b/src/timers.c index 1cac94b..95fc9f8 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-2018 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 diff --git a/src/utils.c b/src/utils.c index 4c06a48..ec30d4a 100644 --- a/src/utils.c +++ b/src/utils.c @@ -157,7 +157,7 @@ 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 +201,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 2f7810f..db14a8c 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-2018 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 @@ -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); @@ -294,97 +321,30 @@ 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 *argptr = fehbg_args; 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(argptr, sizeof(fehbg_args), - "--xinerama-index %d", opt.xinerama_index); - } - } - else - snprintf(argptr, sizeof(fehbg_args), "--no-xinerama"); - argptr += strlen(argptr); -#endif /* HAVE_LIBXINERAMA */ - if ((opt.geom_flags & XValue) && (sizeof(fehbg_args) - strlen(fehbg_args) > 60)) { - snprintf(argptr, sizeof(fehbg_args) - strlen(fehbg_args), " --geometry %c%d", - opt.geom_flags & XNegative ? '-' : '+', - opt.geom_flags & XNegative ? abs(opt.geom_x) : opt.geom_x); - argptr += strlen(argptr); - if (opt.geom_flags & YValue) { - snprintf(argptr, sizeof(fehbg_args) - strlen(fehbg_args), "%c%d", - opt.geom_flags & YNegative ? '-' : '+', - opt.geom_flags & YNegative ? abs(opt.geom_y) : opt.geom_y); - argptr += strlen(argptr); - } - } /* 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); @@ -403,16 +363,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); @@ -433,18 +389,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); @@ -464,15 +415,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); @@ -493,29 +439,85 @@ 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) { + if (!opt.no_fehbg) { home = getenv("HOME"); if (home) { FILE *fp; char *path; + char *absolute_path; struct stat s; + gib_list *filelist_pos = filelist; path = estrjoin("/", home, ".fehbg", NULL); if ((fp = fopen(path, "w")) == NULL) { weprintf("Can't write to %s", path); } else { - fprintf(fp, "#!/bin/sh\n%s\n", fehbg); + fputs("#!/bin/sh\n", fp); + fputs(cmdargv[0], fp); + fputs(" --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) { + for (int i = 0; i < cmdargc; i++) { + if (filelist_pos && !strcmp(FEH_FILE(filelist_pos->data)->filename, cmdargv[i])) { + /* argument is a file */ + absolute_path = feh_absolute_path(cmdargv[i]); + 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); fclose(fp); stat(path, &s); if (chmod(path, s.st_mode | S_IXUSR | S_IXGRP) != 0) { @@ -525,8 +527,6 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled, free(path); } } - - free(fehbg); /* create new display, copy pixmap to new display */ disp2 = XOpenDisplay(NULL); @@ -779,10 +779,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) @@ -842,7 +843,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 @@ -861,7 +863,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); @@ -873,6 +887,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..02a6997 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-2018 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 diff --git a/src/winwidget.c b/src/winwidget.c index c012fc0..4993fb6 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-2018 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 @@ -89,7 +89,7 @@ static winwidget winwidget_allocate(void) 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; @@ -103,11 +103,6 @@ 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)) ret->full_screen = True; winwidget_create_window(ret, ret->w, ret->h); @@ -116,7 +111,7 @@ winwidget winwidget_create_from_image(Imlib_Image im, char *name, char type) 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); @@ -127,12 +122,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); } @@ -340,11 +331,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; } @@ -399,17 +391,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); } } @@ -438,140 +431,65 @@ 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); winwidget_reset_image(winwid); } - /* bounds checks for panning */ - if (winwid->im_x > winwid->w) - winwid->im_x = winwid->w; - if (winwid->im_y > winwid->h) - winwid->im_y = winwid->h; - D(("winwidget_render_image resize %d force_alias %d im %dx%d\n", resize, force_alias, winwid->im_w, winwid->im_h)); - winwidget_setup_pixmaps(winwid); + /* winwidget_setup_pixmaps(winwid) resets the winwid->had_resize flag */ + int had_resize = winwid->had_resize || resize; - if (!winwid->full_screen && ((gib_imlib_image_has_alpha(winwid->im)) - || (opt.geom_flags & (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); - - 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; @@ -610,7 +528,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)); @@ -642,16 +560,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); @@ -710,14 +624,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); @@ -727,13 +642,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; @@ -771,8 +679,6 @@ void winwidget_destroy(winwidget winwid) 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); @@ -968,24 +874,30 @@ 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); @@ -1089,7 +1001,7 @@ void winwidget_rename(winwidget winwid, char *newname) void winwidget_free_image(winwidget w) { if (w->im) - gib_imlib_free_image_and_decache(w->im); + gib_imlib_free_image(w->im); w->im = NULL; w->im_w = 0; w->im_h = 0; @@ -1113,10 +1025,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; diff --git a/src/winwidget.h b/src/winwidget.h index b8d777e..4d8fc4b 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-2018 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 @@ -150,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/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/mandoc.t b/test/mandoc.t index 3740809..638c5e9 100755 --- a/test/mandoc.t +++ b/test/mandoc.t @@ -6,15 +6,22 @@ use 5.010; use Test::More tests => 3; 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 ); } for my $file ( 'feh', 'feh-cam', 'gen-cam-menu' ) { - qx{mandoc -Tlint man/${file}.1}; + 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 |