diff options
| -rw-r--r-- | .github/workflows/c.yml | 10 | ||||
| -rw-r--r-- | ChangeLog | 55 | ||||
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | config.mk | 5 | ||||
| -rw-r--r-- | man/Makefile | 1 | ||||
| -rw-r--r-- | man/feh.pre | 82 | ||||
| -rw-r--r-- | src/Makefile | 4 | ||||
| -rw-r--r-- | src/feh.h | 15 | ||||
| -rw-r--r-- | src/filelist.c | 43 | ||||
| -rw-r--r-- | src/filelist.h | 1 | ||||
| -rw-r--r-- | src/help.raw | 6 | ||||
| -rw-r--r-- | src/imlib.c | 200 | ||||
| -rw-r--r-- | src/index.c | 2 | ||||
| -rw-r--r-- | src/keyevents.c | 4 | ||||
| -rw-r--r-- | src/menu.c | 17 | ||||
| -rw-r--r-- | src/options.c | 29 | ||||
| -rw-r--r-- | src/options.h | 1 | ||||
| -rw-r--r-- | src/slideshow.c | 103 | ||||
| -rw-r--r-- | src/strverscmp.c | 57 | ||||
| -rw-r--r-- | src/thumbnail.c | 3 | ||||
| -rw-r--r-- | src/timers.c | 33 | ||||
| -rw-r--r-- | src/timers.h | 2 | ||||
| -rw-r--r-- | src/wallpaper.c | 185 | ||||
| -rw-r--r-- | src/wallpaper.h | 3 | ||||
| -rw-r--r-- | src/winwidget.c | 38 | ||||
| -rw-r--r-- | test/feh.t | 2 | 
26 files changed, 691 insertions, 212 deletions
| diff --git a/.github/workflows/c.yml b/.github/workflows/c.yml index 941075e..e7a4207 100644 --- a/.github/workflows/c.yml +++ b/.github/workflows/c.yml @@ -21,16 +21,18 @@ jobs:      steps:        - uses: actions/checkout@v2 +      - name: APT +        run: sudo apt-get -y update        - name: Install Dependencies -        run: sudo apt install build-essential libx11-dev libxt-dev libimlib2-dev libtest-command-perl libtest-simple-perl +        run: sudo apt-get -y install build-essential libx11-dev libxt-dev libimlib2-dev libtest-command-perl libtest-simple-perl        - name: Install libcurl          if: matrix.curl -        run: sudo apt install libcurl4-openssl-dev +        run: sudo apt-get -y  install libcurl4-openssl-dev        - name: Install libexif          if: matrix.exif -        run: sudo apt install libexif-dev +        run: sudo apt-get -y  install libexif-dev        - name: Install Xinerama          if: matrix.xinerama -        run: sudo apt install libxinerama-dev +        run: sudo apt-get -y  install libxinerama-dev        - name: Build and Test          run: for inotify in 0 1; do for verscmp in 0 1; do make curl=${{ matrix.curl }} exif=${{ matrix.exif }} inotify=$inotify verscmp=$verscmp xinerama=${{ matrix.xinerama }} && make test && make clean; done; done @@ -1,3 +1,58 @@ +Mon, 25 Jan 2021 17:46:57 +0100  Daniel Friesel <derf+feh@finalrewind.org> + +* Release v3.6.3 +    * Fix --start-at not handling URL-encoded file:/// paths properly. Notably, +      this also fixes feh not displaying images with spaces or unicode +      elements in their path when opened from a file manager. + +Sat, 09 Jan 2021 12:28:06 +0100  Daniel Friesel <derf+feh@finalrewind.org> + +* Release v3.6.2 +    * Fix save_filelist not respecting --output-dir +    * Fix file descriptor leak when attempting to load truncated image files. +      The issue was introduced in v3.6. + +Sun, 06 Dec 2020 08:34:15 +0100  Daniel Friesel <derf+feh@finalrewind.org> + +* Release v3.6.1 +    * Fix excessive memory consumption when showing long-running slideshows +      with thousands to tens of thousands of images and feh has been compiled +      with exif=1 (see https://github.com/derf/feh/issues/553) +    * Fix memory leak when showing long-running slideshows with relatively few +      images and feh has been compiled with exif=1 (ibid.) +    * Fix memory leak when reloading an image and feh has been compiled with +      exif=1 +    * Fix memory leak in --draw-exif +    * Fix memory leak when reloading HTTP files with --no-conversion-cache + +Mon, 30 Nov 2020 19:44:47 +0100  Daniel Friesel <derf+feh@finalrewind.org> +* Release v3.6 +    * Add flip and rotate options to the menu +    * Improve unloadable image detection time (e.g. for large video files) by +      checking a file's header before passing it to Imlib2. For rarely used +      image formats, there is a very small chance that an image which could be +      loaded by feh 3.5 is reported as unloadable by feh 3.6 due to this +      change. Set FEH_SKIP_MAGIC=1 to bypass the header check in this case. See +      <https://phab.enlightenment.org/T8739> and +      <https://github.com/derf/feh/issues/505> for details. + + +Sat, 29 Aug 2020 08:49:08 +0200  Daniel Friesel <derf+feh@finalrewind.org> + +* Release v3.5 +    * Enable --version-sort on systems without strverscmp support. This +      works by bundling the strverscmp of musl libc with feh and using it +      when feh is compiled without the verscmp flag (i.e., when the system +      libc does not provide the verscmp function). Patch by Tim van der Molen +    * Add %a format specifier (slideshow state: "playing" / "paused") +    * Fix crashes when combining --reload and --multiwindow + +Fri, 29 May 2020 23:52:35 +0200  Daniel Friesel <derf+feh@finalrewind.org> + +* Release v3.4.1 +    * Fix lossless rotate not being lossless when using feh with Imlib2 v1.6 +      or later +  Sat, 11 Apr 2020 09:51:01 +0200  Daniel Friesel <derf+feh@finalrewind.org>  * Release v3.4 @@ -91,7 +91,7 @@ indicates that the corresponding feature is enabled by default.  | 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` | +| verscmp | 1 | Whether your libc provides `strvercmp()`. If set to 0, feh will use an internal implementation. |  | xinerama | 1 | Support Xinerama/XRandR multiscreen setups |  For example, `make xinerama=0 debug=1` will disable Xinerama support and @@ -64,10 +64,7 @@ ifeq (${stat64},1)  endif  ifeq (${verscmp},1) -	CFLAGS += -DHAVE_VERSCMP -	MAN_VERSCMP = available -else -	MAN_VERSCMP = not available +	CFLAGS += -DHAVE_STRVERSCMP  endif  ifeq (${xinerama},1) diff --git a/man/Makefile b/man/Makefile index acf8629..3be07e3 100644 --- a/man/Makefile +++ b/man/Makefile @@ -13,7 +13,6 @@ 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 c6dae5f..79aa355 100644 --- a/man/feh.pre +++ b/man/feh.pre @@ -13,6 +13,7 @@  .  .Nm  .Op Ar options +.Op Cm --  .Op Ar files | Ar directories | Ar URLs ...  .  . @@ -29,16 +30,13 @@ Compile-time switches in this build:  .Bl -bullet -compact  .  .It -remote file support: libcurl $MAN_CURL$ -. -.It -natural sorting option $MAN_VERSCMP$ +libcurl remote file support $MAN_CURL$  .  .It  Xinerama multi-monitor support $MAN_XINERAMA$  .  .It -builtin EXIF reader $MAN_EXIF$ +libexif builtin EXIF reader $MAN_EXIF$  .  .It  inotify-based auto-reload of changed files $MAN_INOTIFY$ @@ -165,6 +163,23 @@ Use  .Cm --conversion-timeout Ar timeout  with a non-negative value to enable support for these formats.  . +.Pp +. +As Imlib2 may take several seconds to determine whether it can load a file or +not +.Pq e.g. when attempting to open a large video , +.Nm +checks each file's header before loading it. +If it looks like an image, it is passed on to Imlib2, otherwise, it is +assumed to be unloadable. +This greatly improves performance when working in directories with mixed files +.Pq i.e., directories which do not exclusively contain image files . +If you think that Imlib2 can load a file which +.Nm +has determined to be likely not an image, set the environment variable +.Qq FEH_SKIP_MAGIC +to pass all files directly to Imlib2, bypassing the header check. +The environment variable's value does not matter, it just needs to be set.  .  .Sh OPTIONS  . @@ -221,7 +236,7 @@ Use format specifiers to refer to image info, see  .Sx FORMAT SPECIFIERS  for details.  Example usage: -.Qq feh -A Qo mv ~/images/%N Qc * . +.Qq feh -A Qo mv %F ~/images/%N Qc * .  .  .It Cm --action1 No .. Cm --action9 Oo Ar flag Oc Ns Oo [ Ar title ] Oc Ns Ar action  . @@ -239,6 +254,10 @@ disabled by a preceding  .Cm --reload=0  option.  . +.Pp +. +Automatic reload is not supported in montage, index, or thumbnail mode. +.  .It Cm --auto-rotate  .  .Pq optional feature, $MAN_EXIF$ in this build @@ -584,7 +603,8 @@ When loading images via HTTP, ImageMagick or dcraw,  .Nm  will only load/convert them once and re-use the cached file on subsequent  slideshow passes. -This option disables the cache. It is also disabled when +This option disables the cache. +It is also disabled when  .Cm --reload  is used.  Use it if you rely on frequently changing files loaded via one of these @@ -652,7 +672,7 @@ Save files to  .Ar directory  when using  .Cm --keep-http -or the save_image command. +or the save_image or save_filelist command.  By default, files are saved in the current working directory.  .  .It Cm -p , --preload @@ -711,6 +731,7 @@ will continue to try loading it.  .Pp  .  Setting this option causes inotify-based auto-reload to be disabled. +Reload is not supported in montage, index, or thumbnail mode.  .  .It Cm -n , --reverse  . @@ -887,7 +908,6 @@ output version information and exit.  .  .It Cm --version-sort  . -.Pq optional feature, $MAN_VERSCMP$ in this build  When combined with  .Cm --sort name , --sort filename ,  or @@ -900,6 +920,18 @@ comes before  Note that this option only has an effect when a sort mode is set using  .Cm --sort .  . +.It Cm --window-id Ar windowid +. +Draw to an existing X11 window by its ID +.Ar windowid . +This option is intended for use with software such as xcreensaver or +xsecurelock, which provide a window for other applications to draw into. +Unexpected things will happen if you specify a window belonging to software +which does not expect +.Nm +to draw into it or attempt to use options or keybindings which affect window +attributes, such as full-screen mode. +.  .It Cm --xinerama-index Ar screen  .  .Pq optional feature, $MAN_XINERAMA$ in this build @@ -1204,6 +1236,10 @@ file  .  .Bl -tag -width indent  . +.It %a +. +Information about slideshow state (playing/paused) +.  .It %f  .  Image path/filename @@ -1553,7 +1589,10 @@ will keep zoom and X, Y offset when switching images.  .It L Bq save_filelist  .  Save the current filelist as -.Qq feh_PID_ID_filelist +.Qq feh_PID_ID_filelist . +It is saved in the directory specified by +.Cm --output-dir , +if set, and in the current working directory otherwise.  .  .It m Bq toggle_menu  . @@ -1589,7 +1628,10 @@ Useful for webcams  .It s Bq save_image  .  Save the current image as -.Qq feh_PID_ID_FILENAME +.Qq feh_PID_ID_FILENAME . +It is saved in the directory specified by +.Cm --output-dir , +if set, and in the current working directory otherwise.  .  .It w Bq size_to_image  . @@ -2113,23 +2155,11 @@ Please include the feh version  steps to reproduce the bug and, if necessary, images to reproduce it.  .  . -.Sh FUTURE PLANS -. -Plans for the following releases: -. -.Bl -bullet -compact -. -.It -Make zoom options more intuitive -. -.El -. -.  .Sh LICENSE  .  Copyright (C) 1999, 2000 by Paul Duncan. -Copyright (C) 1999, 2000 by Tom Gilbert (and various contributors). -Copyright (C) 2010-2020 by Daniel Friesel (and even more contributors). +Copyright (C) 1999, 2000 by Tom Gilbert and contributors. +Copyright (C) 2010-2020 by Daniel Friesel and contributors.  .  .Pp  . @@ -2171,5 +2201,5 @@ Tom Gilbert  .  .Pp  . -See also: +Website:  https://feh.finalrewind.org diff --git a/src/Makefile b/src/Makefile index ac4d2fe..10e33eb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -34,6 +34,10 @@ ifeq (${exif},1)  		exif_nikon.c  endif +ifneq (${verscmp},1) +	TARGETS += strverscmp.c +endif +  OBJECTS = ${TARGETS:.c=.o}  I_SRCS = ${shell echo *.raw} @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.   * strverscmp(3) is a GNU extension. In most supporting C libraries it   * requires _GNU_SOURCE to be defined.   */ -#ifdef HAVE_VERSCMP +#ifdef HAVE_STRVERSCMP  #define _GNU_SOURCE  #endif @@ -115,6 +115,14 @@ enum slide_change { SLIDE_NEXT, SLIDE_PREV, SLIDE_RAND, SLIDE_FIRST, SLIDE_LAST,  	SLIDE_JUMP_PREV_DIR  }; +enum feh_load_error { +	LOAD_ERROR_IMLIB = 0, +	LOAD_ERROR_IMAGEMAGICK, +	LOAD_ERROR_CURL, +	LOAD_ERROR_DCRAW, +	LOAD_ERROR_MAGICBYTES +}; +  #define INPLACE_EDIT_FLIP   -1  #define INPLACE_EDIT_MIRROR -2 @@ -172,7 +180,7 @@ void feh_display_status(char stat);  void real_loadables_mode(int loadable);  void feh_reload_image(winwidget w, int resize, int force_new);  void feh_filelist_image_remove(winwidget winwid, char do_delete); -void feh_imlib_print_load_error(char *file, winwidget w, Imlib_Load_Error err); +void feh_print_load_error(char *file, winwidget w, Imlib_Load_Error err, enum feh_load_error feh_err);  void slideshow_save_image(winwidget win);  void feh_edit_inplace(winwidget w, int orientation);  void feh_edit_inplace_lossless(winwidget w, int orientation); @@ -182,6 +190,9 @@ gib_list *feh_list_jump(gib_list * root, gib_list * l, int direction, int num);  #ifdef HAVE_INOTIFY  void feh_event_handle_inotify(void);  #endif +#ifndef HAVE_STRVERSCMP +int strverscmp(const char *l0, const char *r0); +#endif  /* Imlib stuff */  extern Display *disp; diff --git a/src/filelist.c b/src/filelist.c index d5b0750..49355c4 100644 --- a/src/filelist.c +++ b/src/filelist.c @@ -28,6 +28,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  #include <libexif/exif-data.h>  #endif +#ifdef HAVE_LIBCURL +#include <curl/curl.h> +#endif +  #include "feh.h"  #include "filelist.h"  #include "signals.h" @@ -402,7 +406,6 @@ 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) @@ -410,9 +413,6 @@ static inline int strcmp_or_strverscmp(const char *s1, const char *s2)  	else  		return(strverscmp(s1, s2));  } -#else -#define strcmp_or_strverscmp strcmp -#endif  int feh_cmp_filename(void *file1, void *file2)  { @@ -664,8 +664,17 @@ char *feh_absolute_path(char *path)  void feh_save_filelist()  {  	char *tmpname; +	char *base_dir = ""; + +	if (opt.output_dir) { +		base_dir = estrjoin("", opt.output_dir, "/", NULL); +	} + +	tmpname = feh_unique_filename(base_dir, "filelist"); -	tmpname = feh_unique_filename("", "filelist"); +	if (opt.output_dir) { +		free(base_dir); +	}  	if (opt.verbose)  		fprintf(stderr, "saving filelist to filename '%s'\n", tmpname); @@ -674,3 +683,27 @@ void feh_save_filelist()  	free(tmpname);  	return;  } + +#ifdef HAVE_LIBCURL + +char *feh_http_unescape(char *url) +{ +	CURL *curl = curl_easy_init(); +	if (!curl) { +		return NULL; +	} +	char *tmp_url = curl_easy_unescape(curl, url, 0, NULL); +	char *new_url = estrdup(tmp_url); +	curl_free(tmp_url); +	curl_easy_cleanup(curl); +	return new_url; +} + +#else + +char *feh_http_unescape(char *url) +{ +	return NULL; +} + +#endif diff --git a/src/filelist.h b/src/filelist.h index e24a6a6..ff0645e 100644 --- a/src/filelist.h +++ b/src/filelist.h @@ -97,6 +97,7 @@ gib_list *feh_read_filelist(char *filename);  char *feh_absolute_path(char *path);  gib_list *feh_file_remove_from_list(gib_list * list, gib_list * l);  void feh_save_filelist(); +char *feh_http_unescape(char * url);  int feh_cmp_name(void *file1, void *file2);  int feh_cmp_dirname(void *file1, void *file2); diff --git a/src/help.raw b/src/help.raw index a9cf9da..1846908 100644 --- a/src/help.raw +++ b/src/help.raw @@ -100,6 +100,7 @@ OPTIONS       --scroll-step COUNT   scroll COUNT pixels when movement key is pressed       --cache-size NUM      imlib cache size in mebibytes (0 .. 2048)       --auto-reload         automatically reload shown image if file was changed +     --window-id           Draw to an existing X11 window by its ID  MONTAGE MODE OPTIONS   -X, --ignore-aspect       Set thumbnail to specified width/height without @@ -124,6 +125,7 @@ INDEX MODE OPTIONS                             font is specified, a title will not be printed  FORMAT SPECIFIERS + %a     information about slideshow state (playing/paused)   %f     image path/filename   %F     image path/filename (shell-escaped)   %g     window dimensions (\"width,height\") in pixels @@ -193,8 +195,8 @@ DEFAULT KEYS   <KEYPAD UP>             Move the image up   <KEYPAD DOWN>           Move the image down   <KEYPAD BEGIN>          Antialias the image - <KEYPAD +>              Zoom in - <KEYPAD ->              Zoom out + <KEYPAD +>, <UP>        Zoom in + <KEYPAD ->, <DOWN>      Zoom out   <KEYPAD *>              Zoom to 100%   <KEYPAD />              Zoom to fit the window diff --git a/src/imlib.c b/src/imlib.c index 62d88c6..fc81bdd 100644 --- a/src/imlib.c +++ b/src/imlib.c @@ -164,11 +164,31 @@ int feh_load_image_char(Imlib_Image * im, char *filename)  	return(i);  } -void feh_imlib_print_load_error(char *file, winwidget w, Imlib_Load_Error err) +void feh_print_load_error(char *file, winwidget w, Imlib_Load_Error err, enum feh_load_error feh_err)  {  	if (err == IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS)  		eprintf("%s - Out of file descriptors while loading", file);  	else if (!opt.quiet || w) { +		switch (feh_err) { +			case LOAD_ERROR_IMLIB: +				// handled in the next switch/case statement +				break; +			case LOAD_ERROR_IMAGEMAGICK: +				im_weprintf(w, "%s - No ImageMagick loader for that file format", file); +				break; +			case LOAD_ERROR_CURL: +				im_weprintf(w, "%s - libcurl was unable to retrieve the file", file); +				break; +			case LOAD_ERROR_DCRAW: +				im_weprintf(w, "%s - Unable to open preview via dcraw", file); +				break; +			case LOAD_ERROR_MAGICBYTES: +				im_weprintf(w, "%s - Does not look like an image (magic bytes missing)", file); +				break; +		} +		if (feh_err != LOAD_ERROR_IMLIB) { +			return; +		}  		switch (err) {  		case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST:  			im_weprintf(w, "%s - File does not exist", file); @@ -215,9 +235,104 @@ void feh_imlib_print_load_error(char *file, winwidget w, Imlib_Load_Error err)  	}  } +/* + * This is a workaround for an Imlib2 regression, causing unloadable image + * detection to be excessively slow (and, thus, causing feh to hang for a while + * when encountering an unloadable image). We use magic byte detection to + * avoid calling Imlib2 for files it probably cannot handle. See + * <https://phab.enlightenment.org/T8739> and + * <https://github.com/derf/feh/issues/505>. + * + * Note that this drops support for bz2-compressed files, unless + * FEH_SKIP_MAGIC is set + */ +int feh_is_image(feh_file * file) +{ +	unsigned char buf[16]; +	FILE *fh = fopen(file->filename, "r"); +	if (!fh) { +		return 0; +	} +	if (fread(buf, 1, 16, fh) != 16) { +		fclose(fh); +		return 0; +	} +	fclose(fh); + +	if (buf[0] == 0xff && buf[1] == 0xd8) { +		// JPEG +		return 1; +	} +	if (!memcmp(buf, "\x89PNG\x0d\x0a\x1a\x0a", 8)) { +		// PNG +		return 1; +	} +	if (buf[0] == 'A' && buf[1] == 'R' && buf[2] == 'G' && buf[3] == 'B') { +		// ARGB +		return 1; +	} +	if (buf[0] == 'B' && buf[1] == 'M') { +		// BMP +		return 1; +	} +	if (!memcmp(buf, "farbfeld", 8)) { +		// farbfeld +		return 1; +	} +	if (buf[0] == 'G' && buf[1] == 'I' && buf[2] == 'F') { +		// GIF +		return 1; +	} +	if (buf[0] == 0x00 && buf[1] == 0x00 && buf[2] <= 0x02 && buf[3] == 0x00) { +		// ICO +		return 1; +	} +	if (!memcmp(buf, "FORM", 4)) { +		// Amiga IFF ILBM +		return 1; +	} +	if (buf[0] == 'P' && buf[1] >= '1' && buf[1] <= '7') { +		// PNM et al. +		return 1; +	} +	if (strstr(file->filename, ".tga")) { +		// TGA +		return 1; +	} +	if (!memcmp(buf, "II\x2a\x00", 4) || !memcmp(buf, "MM\x00\x2a", 4)) { +		// TIFF +		return 1; +	} +	if (!memcmp(buf, "RIFF", 4)) { +		// might be webp +		return 1; +	} +	if (!memcmp(buf + 4, "ftyphei", 7) || !memcmp(buf + 4, "ftypmif1", 8)) { +		// HEIC/HEIF - note that this is only supported in imlib2-heic. Ordinary +		// imlib2 releases do not support heic/heif images as of 2021-01. +		return 1; +	} +	buf[15] = 0; +	if (strstr((char *)buf, "XPM")) { +		// XPM +		return 1; +	} +	if (strstr(file->filename, ".bz2") || strstr(file->filename, ".gz")) { +		// Imlib2 supports compressed images. It relies on the filename to +		// determine the appropriate loader and does not use magic bytes here. +		return 1; +	} +	// moved to the end as this variable won't be set in most cases +	if (getenv("FEH_SKIP_MAGIC")) { +		return 1; +	} +	return 0; +} +  int feh_load_image(Imlib_Image * im, feh_file * file)  {  	Imlib_Load_Error err = IMLIB_LOAD_ERROR_NONE; +	enum feh_load_error feh_err = LOAD_ERROR_IMLIB;  	enum { SRC_IMLIB, SRC_HTTP, SRC_MAGICK, SRC_DCRAW } image_source = SRC_IMLIB;  	char *tmpname = NULL;  	char *real_filename = NULL; @@ -230,23 +345,37 @@ int feh_load_image(Imlib_Image * im, feh_file * file)  	if (path_is_url(file->filename)) {  		image_source = SRC_HTTP; -		if ((tmpname = feh_http_load_image(file->filename)) == NULL) +		if ((tmpname = feh_http_load_image(file->filename)) == NULL) { +			feh_err = LOAD_ERROR_CURL;  			err = IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST; +		}  	} -	else if (opt.conversion_timeout >= 0 && feh_file_is_raw(file->filename)) { -		image_source = SRC_DCRAW; -		tmpname = feh_dcraw_load_image(file->filename); -		if (!tmpname) +	else { +		if (feh_is_image(file)) { +			*im = imlib_load_image_with_error_return(file->filename, &err); +		} else { +			feh_err = LOAD_ERROR_MAGICBYTES;  			err = IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT; +		}  	} -	else -		*im = imlib_load_image_with_error_return(file->filename, &err);  	if (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 (feh_file_is_raw(file->filename)) { +			image_source = SRC_DCRAW; +			tmpname = feh_dcraw_load_image(file->filename); +			if (!tmpname) { +				feh_err = LOAD_ERROR_DCRAW; +			} +		} else { +			image_source = SRC_MAGICK; +			feh_err = LOAD_ERROR_IMLIB; +			tmpname = feh_magick_load_image(file->filename); +			if (!tmpname) { +				feh_err = LOAD_ERROR_IMAGEMAGICK; +			} +		}  	}  	if (tmpname) { @@ -269,7 +398,14 @@ int feh_load_image(Imlib_Image * im, feh_file * file)  			file->filename = real_filename;  #ifdef HAVE_LIBEXIF -			file->ed = exif_get_data(tmpname); +			/* +			 * if we're called from within feh_reload_image, file->ed is already +			 * populated. +			 */ +			if (file->ed) { +				exif_data_unref(file->ed); +			} +			file->ed = exif_data_new_from_file(tmpname);  #endif  		}  		if (!opt.use_conversion_cache && ((image_source != SRC_HTTP) || !opt.keep_http)) @@ -279,8 +415,19 @@ int feh_load_image(Imlib_Image * im, feh_file * file)  			// add_file_to_rm_filelist duplicates tmpname  			add_file_to_rm_filelist(tmpname); -		if (image_source != SRC_HTTP && !opt.use_conversion_cache) +		if (!opt.use_conversion_cache)  			free(tmpname); +	} else if (im) { +#ifdef HAVE_LIBEXIF +		/* +			* if we're called from within feh_reload_image, file->ed is already +			* populated. +			*/ +		if (file->ed) { +			exif_data_unref(file->ed); +		} +		file->ed = exif_data_new_from_file(file->filename); +#endif  	}  	if ((err) || (!im)) { @@ -288,7 +435,7 @@ int feh_load_image(Imlib_Image * im, feh_file * file)  			fputs("\n", stderr);  			reset_output = 1;  		} -		feh_imlib_print_load_error(file->filename, NULL, err); +		feh_print_load_error(file->filename, NULL, err, feh_err);  		D(("Load *failed*\n"));  		return(0);  	} @@ -305,14 +452,12 @@ int feh_load_image(Imlib_Image * im, feh_file * file)  #ifdef HAVE_LIBEXIF  	int orientation = 0; -	ExifData *exifData = exif_data_new_from_file(file->filename); -	if (exifData) { -		ExifByteOrder byteOrder = exif_data_get_byte_order(exifData); -		ExifEntry *exifEntry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION); +	if (file->ed) { +		ExifByteOrder byteOrder = exif_data_get_byte_order(file->ed); +		ExifEntry *exifEntry = exif_data_get_entry(file->ed, EXIF_TAG_ORIENTATION);  		if (exifEntry && opt.auto_rotate)  			orientation = exif_get_short(exifEntry->data, byteOrder);  	} -	file->ed = exifData;  	if (orientation == 2)  		gib_imlib_image_flip_horizontal(*im); @@ -430,11 +575,9 @@ static int feh_file_is_raw(char *filename)  	}  	if (childpid == 0) { -		if (opt.quiet) { -			int devnull = open("/dev/null", O_WRONLY); -			dup2(devnull, 1); -			dup2(devnull, 2); -		} +		int devnull = open("/dev/null", O_WRONLY); +		dup2(devnull, 1); +		dup2(devnull, 2);  		execlp("dcraw", "dcraw", "-i", filename, NULL);  		_exit(1);  	} else { @@ -1085,6 +1228,7 @@ void feh_draw_exif(winwidget w)  				info_buf[i], IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);  		gib_imlib_text_draw(im, fn, NULL, 1, (i * line_height) + 1,  				info_buf[i], IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); +		free(info_buf[i]);  	} @@ -1392,8 +1536,10 @@ void feh_edit_inplace(winwidget w, int op)  		return;  	} -	if (!strcmp(gib_imlib_image_format(w->im), "jpeg") && -			!path_is_url(FEH_FILE(w->file->data)->filename)) { +	// Imlib2 <= 1.5 returns "jpeg", Imlib2 >= 1.6 uses "jpg" +	if ((!strcmp(gib_imlib_image_format(w->im), "jpeg") +				|| !strcmp(gib_imlib_image_format(w->im), "jpg")) +			&& !path_is_url(FEH_FILE(w->file->data)->filename)) {  		feh_edit_inplace_lossless(w, op);  		feh_reload_image(w, 1, 1);  		return; @@ -1413,8 +1559,8 @@ void feh_edit_inplace(winwidget w, int op)  			FEH_FILE(w->file->data)->filename, &err);  		gib_imlib_free_image(old);  		if (err) -			feh_imlib_print_load_error(FEH_FILE(w->file->data)->filename, -				w, err); +			feh_print_load_error(FEH_FILE(w->file->data)->filename, +				w, err, LOAD_ERROR_IMLIB);  		feh_reload_image(w, 1, 1);  	} else {  		/* diff --git a/src/index.c b/src/index.c index 3633c96..85a3ee8 100644 --- a/src/index.c +++ b/src/index.c @@ -332,7 +332,7 @@ void init_index_mode(void)  		gib_imlib_save_image_with_error_return(im_main, output_buf, &err);  		if (err) { -			feh_imlib_print_load_error(output_buf, im_main, err); +			feh_print_load_error(output_buf, im_main, err, LOAD_ERROR_IMLIB);  		}  		else if (opt.verbose) {  			int tw, th; diff --git a/src/keyevents.c b/src/keyevents.c index 0da07d3..0ca489a 100644 --- a/src/keyevents.c +++ b/src/keyevents.c @@ -319,7 +319,7 @@ 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) { +	if (read(STDIN_FILENO, &stdin_buf, 1) <= 0) {  		control_via_stdin = 0;  		if (isatty(STDIN_FILENO) && getpgrp() == (tcgetpgrp(STDIN_FILENO))) {  			weprintf("reading a command from stdin failed - disabling control via stdin"); @@ -710,6 +710,8 @@ void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysy  	}  	else if (feh_is_kp(EVENT_toggle_pause, state, keysym, button)) {  		slideshow_pause_toggle(winwid); +		/* We need to re-render the image to update the info string immediately. */ +		winwidget_render_image(winwid, 0, 0);  	}  	else if (feh_is_kp(EVENT_save_image, state, keysym, button)) {  		slideshow_save_image(winwid); @@ -80,6 +80,8 @@ enum {  	CB_OPT_FREEZE_WINDOW,  	CB_OPT_FULLSCREEN,  	CB_EDIT_ROTATE, +	CB_EDIT_MIRROR, +	CB_EDIT_FLIP,  	CB_OPT_AUTO_ZOOM,  	CB_OPT_KEEP_ZOOM_VP  }; @@ -920,7 +922,12 @@ void feh_menu_init_main(void)  	feh_menu_add_entry(m, "Reload", NULL, CB_RELOAD, 0, NULL);  	feh_menu_add_entry(m, "Save Image", NULL, CB_SAVE_IMAGE, 0, NULL);  	feh_menu_add_entry(m, "Save List", NULL, CB_SAVE_FILELIST, 0, NULL); -	feh_menu_add_entry(m, "Edit in Place", "EDIT", 0, 0, NULL); +	if (opt.edit) { +		feh_menu_add_entry(m, "Edit in Place", "EDIT", 0, 0, NULL); +	} +	else { +		feh_menu_add_entry(m, "Change View", "EDIT", 0, 0, NULL); +	}  	feh_menu_add_entry(m, "Background", "BACKGROUND", 0, 0, NULL);  	feh_menu_add_entry(m, NULL, NULL, 0, 0, NULL);  	feh_menu_add_entry(m, "Hide", NULL, CB_REMOVE, 0, NULL); @@ -963,6 +970,8 @@ void feh_menu_init_common()  	feh_menu_add_entry(m, "Rotate 90 CW", NULL, CB_EDIT_ROTATE, 1, NULL);  	feh_menu_add_entry(m, "Rotate 180", NULL, CB_EDIT_ROTATE, 2, NULL);  	feh_menu_add_entry(m, "Rotate 90 CCW", NULL, CB_EDIT_ROTATE, 3, NULL); +	feh_menu_add_entry(m, "Mirror", NULL, CB_EDIT_MIRROR, 0, NULL); +	feh_menu_add_entry(m, "Flip", NULL, CB_EDIT_FLIP, 0, NULL);  	menu_bg = feh_menu_new();  	menu_bg->name = estrdup("BACKGROUND"); @@ -1309,6 +1318,12 @@ void feh_menu_cb(feh_menu * m, feh_menu_item * i, int action, unsigned short dat  		case CB_EDIT_ROTATE:  			feh_edit_inplace(m->fehwin, data);  			break; +		case CB_EDIT_MIRROR: +			feh_edit_inplace(m->fehwin, INPLACE_EDIT_MIRROR); +			break; +		case CB_EDIT_FLIP: +			feh_edit_inplace(m->fehwin, INPLACE_EDIT_FLIP); +			break;  		case CB_SAVE_IMAGE:  			slideshow_save_image(m->fehwin);  			break; diff --git a/src/options.c b/src/options.c index 2cd7a9e..4744bb0 100644 --- a/src/options.c +++ b/src/options.c @@ -433,6 +433,7 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun)  #endif  		{"class"         , 1, 0, 249},  		{"no-conversion-cache", 0, 0, 250}, +		{"window-id", 1, 0, 251},  		{0, 0, 0, 0}  	};  	int optch = 0, cmdx = 0; @@ -833,6 +834,9 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun)  		case 250:  			opt.use_conversion_cache = 0;  			break; +		case 251: +			opt.x11_windowid = atol(optarg); +			break;  		default:  			break;  		} @@ -855,11 +859,26 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun)  		 */  		if (opt.start_list_at && path_is_url(opt.start_list_at) && (strlen(opt.start_list_at) <= 8 || strncmp(opt.start_list_at, "file:///", 8) != 0)) {  			add_file_to_filelist_recursively(opt.start_list_at, FILELIST_FIRST); +		/* +		 * Otherwise, make "feh --start-at dir/file.jpg" behave like +		 * "feh --start-at dir/file.jpg dir". +		 */  		} else if (opt.start_list_at && strrchr(opt.start_list_at, '/')) { +			/* +			 * feh can't candle urlencoded path components ("some%20dir" etc). +			 * Use libcurl to unescape them if --start-at is file://... +			 */  			if (strlen(opt.start_list_at) > 8 && strncmp(opt.start_list_at, "file:///", 8) == 0) { -				char *start_at_path = estrdup(opt.start_list_at + 7); -				free(opt.start_list_at); -				opt.start_list_at = start_at_path; +				char *unescaped_path = feh_http_unescape(opt.start_list_at); +				if (unescaped_path != NULL) { +					free(opt.start_list_at); +					opt.start_list_at = estrdup(unescaped_path + 7); +					free(unescaped_path); +				} else { +					char *new_path = estrdup(opt.start_list_at + 7); +					free(opt.start_list_at); +					opt.start_list_at = new_path; +				}  			}  			char *target_directory = estrdup(opt.start_list_at);  			char *filename_start = strrchr(target_directory, '/'); @@ -943,8 +962,8 @@ static void show_version(void)  		"stat64 "  #endif -#ifdef HAVE_VERSCMP -		"verscmp " +#ifdef HAVE_STRVERSCMP +         "verscmp "  #endif  #ifdef HAVE_LIBXINERAMA diff --git a/src/options.h b/src/options.h index 4906004..7ca699b 100644 --- a/src/options.h +++ b/src/options.h @@ -130,6 +130,7 @@ struct __fehoptions {  	unsigned char adjust_reload;  	int xinerama_index;  	char *x11_class; +	unsigned long int x11_windowid;  	/* signed in case someone wants to invert scrolling real quick */  	int scroll_step; diff --git a/src/slideshow.c b/src/slideshow.c index 591848c..87ec4cb 100644 --- a/src/slideshow.c +++ b/src/slideshow.c @@ -57,6 +57,7 @@ void init_slideshow_mode(void)  	// Try finding an exact filename match first  	for (; l && opt.start_list_at; l = l->next) {  		if (!strcmp(opt.start_list_at, FEH_FILE(l->data)->filename)) { +			free(opt.start_list_at);  			opt.start_list_at = NULL;  			break;  		} @@ -83,6 +84,7 @@ void init_slideshow_mode(void)  				current_filename = FEH_FILE(l->data)->filename;  			}  			if (!strcmp(start_at_filename, current_filename)) { +				free(opt.start_list_at);  				opt.start_list_at = NULL;  				break;  			} @@ -135,47 +137,54 @@ void cb_reload_timer(void *data)  	winwidget w = (winwidget) data; -	/* save the current filename for refinding it in new list */ -	current_filename = estrdup(FEH_FILE(current_file->data)->filename); +	/* +	 * multi-window mode has no concept of a "current file" and +	 * dynamically adding/removing windows is not implemented at the moment. +	 * So don't reload filelists in multi-window mode. +	 */ +	if (current_file != NULL) { +		/* save the current filename for refinding it in new list */ +		current_filename = estrdup(FEH_FILE(current_file->data)->filename); -	for (l = filelist; l; l = l->next) { -		feh_file_free(l->data); -		l->data = NULL; -	} -	gib_list_free_and_data(filelist); -	filelist = NULL; -	filelist_len = 0; -	current_file = NULL; - -	/* rebuild filelist from original_file_items */ -	if (gib_list_length(original_file_items) > 0) -		for (l = gib_list_last(original_file_items); l; l = l->prev) -			add_file_to_filelist_recursively(l->data, FILELIST_FIRST); -	else if (!opt.filelistfile && !opt.bgmode) -		add_file_to_filelist_recursively(".", FILELIST_FIRST); - -	if (opt.filelistfile) { -		filelist = gib_list_cat(filelist, feh_read_filelist(opt.filelistfile)); -	} -	 -	if (!(filelist_len = gib_list_length(filelist))) { -		eprintf("No files found to reload."); -	} +		for (l = filelist; l; l = l->next) { +			feh_file_free(l->data); +			l->data = NULL; +		} +		gib_list_free_and_data(filelist); +		filelist = NULL; +		filelist_len = 0; +		current_file = NULL; + +		/* rebuild filelist from original_file_items */ +		if (gib_list_length(original_file_items) > 0) +			for (l = gib_list_last(original_file_items); l; l = l->prev) +				add_file_to_filelist_recursively(l->data, FILELIST_FIRST); +		else if (!opt.filelistfile && !opt.bgmode) +			add_file_to_filelist_recursively(".", FILELIST_FIRST); + +		if (opt.filelistfile) { +			filelist = gib_list_cat(filelist, feh_read_filelist(opt.filelistfile)); +		} +		 +		if (!(filelist_len = gib_list_length(filelist))) { +			eprintf("No files found to reload."); +		} -	feh_prepare_filelist(); +		feh_prepare_filelist(); -	/* find the previously current file */ -	for (l = filelist; l; l = l->next) -		if (strcmp(FEH_FILE(l->data)->filename, current_filename) == 0) { -			current_file = l; -			break; -		} +		/* find the previously current file */ +		for (l = filelist; l; l = l->next) +			if (strcmp(FEH_FILE(l->data)->filename, current_filename) == 0) { +				current_file = l; +				break; +			} -	free(current_filename); +		free(current_filename); -	if (!current_file) -		current_file = filelist; -	w->file = current_file; +		if (!current_file) +			current_file = filelist; +		w->file = current_file; +	}  	feh_reload_image(w, 1, 0);  	feh_add_unique_timer(cb_reload_timer, w, opt.reload); @@ -218,6 +227,18 @@ void slideshow_change_image(winwidget winwid, int change, int render)  	/* The for loop prevents us looping infinitely */  	for (i = 0; i < our_filelist_len; i++) {  		winwidget_free_image(winwid); +#ifdef HAVE_LIBEXIF +		/* +		 * An EXIF data chunk requires up to 50 kB of space. For large and +		 * long-running slideshows, this would acculumate gigabytes of +		 * EXIF data after a few days. We therefore do not cache EXIF data +		 * in slideshows. +		 */ +		if (FEH_FILE(winwid->file->data)->ed) { +			exif_data_unref(FEH_FILE(winwid->file->data)->ed); +			FEH_FILE(winwid->file->data)->ed = NULL; +		} +#endif  		switch (change) {  		case SLIDE_NEXT:  			current_file = feh_list_jump(filelist, current_file, FORWARD, 1); @@ -398,6 +419,14 @@ char *feh_printf(char *str, feh_file * file, winwidget winwid)  		if ((*c == '%') && (*(c+1) != '\0')) {  			c++;  			switch (*c) { +			case 'a': +				if (opt.paused == 1) { +				   strncat(ret, "paused", sizeof(ret) - strlen(ret) - 1); +				} +				else { +				   strncat(ret, "playing", sizeof(ret) - strlen(ret) - 1); +				} +				break;  			case 'f':  				if (file)  					strncat(ret, file->filename, sizeof(ret) - strlen(ret) - 1); @@ -612,7 +641,7 @@ void slideshow_save_image(winwidget win)  	gib_imlib_save_image_with_error_return(win->im, tmpname, &err);  	if (err) -		feh_imlib_print_load_error(tmpname, win, err); +		feh_print_load_error(tmpname, win, err, LOAD_ERROR_IMLIB);  	free(tmpname);  	return; diff --git a/src/strverscmp.c b/src/strverscmp.c new file mode 100644 index 0000000..92cd877 --- /dev/null +++ b/src/strverscmp.c @@ -0,0 +1,57 @@ +/* + * Copyright © 2005-2020 Rich Felker, et al. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#define _GNU_SOURCE +#include <ctype.h> +#include <string.h> + +int strverscmp(const char *l0, const char *r0) +{ +	const unsigned char *l = (const void *)l0; +	const unsigned char *r = (const void *)r0; +	size_t i, dp, j; +	int z = 1; + +	/* Find maximal matching prefix and track its maximal digit +	 * suffix and whether those digits are all zeros. */ +	for (dp=i=0; l[i]==r[i]; i++) { +		int c = l[i]; +		if (!c) return 0; +		if (!isdigit(c)) dp=i+1, z=1; +		else if (c!='0') z=0; +	} + +	if (l[dp]!='0' && r[dp]!='0') { +		/* If we're not looking at a digit sequence that began +		 * with a zero, longest digit string is greater. */ +		for (j=i; isdigit(l[j]); j++) +			if (!isdigit(r[j])) return 1; +		if (isdigit(r[j])) return -1; +	} else if (z && dp<i && (isdigit(l[i]) || isdigit(r[i]))) { +		/* Otherwise, if common prefix of digit sequence is +		 * all zeros, digits order less than non-digits. */ +		return (unsigned char)(l[i]-'0') - (unsigned char)(r[i]-'0'); +	} + +	return l[i] - r[i]; +} diff --git a/src/thumbnail.c b/src/thumbnail.c index 41bd1a0..9781027 100644 --- a/src/thumbnail.c +++ b/src/thumbnail.c @@ -392,7 +392,7 @@ void init_thumbnail_mode(void)  		}  		gib_imlib_save_image_with_error_return(td.im_main, output_buf, &err);  		if (err) { -			feh_imlib_print_load_error(output_buf, td.im_main, err); +			feh_print_load_error(output_buf, td.im_main, err, LOAD_ERROR_IMLIB);  		}  		else if (opt.verbose) {  			int tw, th; @@ -411,6 +411,7 @@ void init_thumbnail_mode(void)  	else if (opt.start_list_at) {  		for (l = thumbnails; l; l = l->next) {  			if (!strcmp(opt.start_list_at, FEH_THUMB(l->data)->file->filename)) { +				free(opt.start_list_at);  				opt.start_list_at = NULL;  				feh_thumbnail_select(winwid, FEH_THUMB(l->data));  				break; diff --git a/src/timers.c b/src/timers.c index 4bdd64e..b2cdbc7 100644 --- a/src/timers.c +++ b/src/timers.c @@ -58,7 +58,37 @@ double feh_get_time(void)  	return((double) timev.tv_sec + (((double) timev.tv_usec) / 1000000));  } -void feh_remove_timer(char *name) +void feh_remove_timer_by_data(void *data) +{ +	fehtimer ft, ptr, pptr; + +	D(("removing timer for %p\n", data)); +	pptr = NULL; +	ptr = first_timer; +	while (ptr) { +		D(("Stepping through event list\n")); +		ft = ptr; +		if (ft->data == data) { +			D(("Found it. Removing\n")); +			if (pptr) +				pptr->next = ft->next; +			else +				first_timer = ft->next; +			if (ft->next) +				ft->next->in += ft->in; +			if (ft->name) +				free(ft->name); +			if (ft) +				free(ft); +			return; +		} +		pptr = ptr; +		ptr = ptr->next; +	} +	return; +} + +static void feh_remove_timer(char *name)  {  	fehtimer ft, ptr, pptr; @@ -88,6 +118,7 @@ void feh_remove_timer(char *name)  	return;  } +  void feh_add_timer(void (*func) (void *data), void *data, double in, char *name)  {  	fehtimer ft, ptr, pptr; diff --git a/src/timers.h b/src/timers.h index a4243ca..e95d9b5 100644 --- a/src/timers.h +++ b/src/timers.h @@ -37,7 +37,7 @@ struct __fehtimer {  void feh_handle_timer(void);  double feh_get_time(void); -void feh_remove_timer(char *name); +void feh_remove_timer_by_data(void *data);  void feh_add_timer(void (*func) (void *data), void *data, double in, char *name);  void feh_add_unique_timer(void (*func) (void *data), void *data, double in); diff --git a/src/wallpaper.c b/src/wallpaper.c index 62b36a4..5adb3ca 100644 --- a/src/wallpaper.c +++ b/src/wallpaper.c @@ -245,6 +245,106 @@ static void feh_wm_set_bg_maxed(Pixmap pmap, Imlib_Image im, int use_filelist,  	return;  } +/* +** Creates a script that can be used to create the same background +** as the last time the program was called. +*/ +void feh_wm_gen_bg_script(char* fil, int centered, int scaled, int filled, int use_filelist) { +	char * home = getenv("HOME"); + +	if (!home) +		return; + +	FILE *fp; +	int fd; +	char *path; +	char *exec_method; +	char *absolute_path; +	struct stat s; +	gib_list *filelist_pos = filelist; + +	if (strchr(cmdargv[0], '/')) +		exec_method = feh_absolute_path(cmdargv[0]); +	else +		exec_method = cmdargv[0]; + +	path = estrjoin("/", home, ".fehbg", NULL); + +	if ((fp = fopen(path, "w")) == NULL) { +		weprintf("Can't write to %s", path); +	} else { +		fputs("#!/bin/sh\n", fp); +		fputs(exec_method, fp); +		fputs(" --no-fehbg --bg-", fp); +		if (centered) +			fputs("center", fp); +		else if (scaled) +			fputs("scale", fp); +		else if (filled == 1) +			fputs("fill", fp); +		else if (filled == 2) +			fputs("max", fp); +		else +			fputs("tile", fp); +		if (opt.image_bg) { +			fputs(" --image-bg ", fp); +			fputs(shell_escape(opt.image_bg), fp); +		} +#ifdef HAVE_LIBXINERAMA +		if (opt.xinerama) { +			if (opt.xinerama_index >= 0) { +				fprintf(fp, " --xinerama-index %d", opt.xinerama_index); +			} +		} +		else { +			fputs(" --no-xinerama", fp); +		} +#endif			/* HAVE_LIBXINERAMA */ +		if (opt.geom_flags & XValue) { +			fprintf(fp, " --geometry %c%d", +					opt.geom_flags & XNegative ? '-' : '+', +					opt.geom_flags & XNegative ? abs(opt.geom_x) : opt.geom_x); +			if (opt.geom_flags & YValue) { +				fprintf(fp, "%c%d", +						opt.geom_flags & YNegative ? '-' : '+', +						opt.geom_flags & YNegative ? abs(opt.geom_y) : opt.geom_y); +			} +		} +		if (opt.force_aliasing) { +			fputs(" --force-aliasing", fp); +		} +		fputc(' ', fp); +		if (use_filelist) { +#ifdef HAVE_LIBXINERAMA +			for (int i = 0; (i < opt.xinerama ? num_xinerama_screens : 1) && filelist_pos; i++) +#else +				for (int i = 0; (i < 1                   ) && filelist_pos; i++) +#endif +				{ +					absolute_path = feh_absolute_path(FEH_FILE(filelist_pos->data)->filename); +					fputs(shell_escape(absolute_path), fp); +					filelist_pos = filelist_pos->next; +					free(absolute_path); +					fputc(' ', fp); +				} +		} else if (fil) { +			absolute_path = feh_absolute_path(fil); +			fputs(shell_escape(absolute_path), fp); +			free(absolute_path); +		} +		fputc('\n', fp); +		fd = fileno(fp); +		if (fstat(fd, &s) != 0 || fchmod(fd, s.st_mode | S_IXUSR | S_IXGRP) != 0) { +			weprintf("Can't set %s as executable", path); +		} +		fclose(fp); +	} +	free(path); + +	if(exec_method != cmdargv[0]) +		free(exec_method); +} +  void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,  		int filled, int desktop, int use_filelist)  { @@ -325,8 +425,6 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,  		unsigned char *data_root = NULL, *data_esetroot = NULL;  		Pixmap pmap_d1, pmap_d2; -		char *home; -  		/* local display to set closedownmode on */  		Display *disp2;  		Window root2; @@ -451,87 +549,8 @@ void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled,  			gib_imlib_render_image_on_drawable(pmap_d1, im, 0, 0, 1, 1, 0);  		} -		if (!opt.no_fehbg) { -			home = getenv("HOME"); -			if (home) { -				FILE *fp; -				int fd; -				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 { -					fputs("#!/bin/sh\n", fp); -					fputs(cmdargv[0], fp); -					fputs(" --no-fehbg --bg-", fp); -					if (centered) -						fputs("center", fp); -					else if (scaled) -						fputs("scale", fp); -					else if (filled == 1) -						fputs("fill", fp); -					else if (filled == 2) -						fputs("max", fp); -					else -						fputs("tile", fp); -					if (opt.image_bg) { -						fputs(" --image-bg ", fp); -						fputs(shell_escape(opt.image_bg), fp); -					} -#ifdef HAVE_LIBXINERAMA -					if (opt.xinerama) { -						if (opt.xinerama_index >= 0) { -							fprintf(fp, " --xinerama-index %d", opt.xinerama_index); -						} -					} -					else { -						fputs(" --no-xinerama", fp); -					} -#endif			/* HAVE_LIBXINERAMA */ -					if (opt.geom_flags & XValue) { -						fprintf(fp, " --geometry %c%d", -								opt.geom_flags & XNegative ? '-' : '+', -								opt.geom_flags & XNegative ? abs(opt.geom_x) : opt.geom_x); -						if (opt.geom_flags & YValue) { -							fprintf(fp, "%c%d", -									opt.geom_flags & YNegative ? '-' : '+', -									opt.geom_flags & YNegative ? abs(opt.geom_y) : opt.geom_y); -						} -					} -					if (opt.force_aliasing) { -						fputs(" --force-aliasing", fp); -					} -					fputc(' ', fp); -					if (use_filelist) { -#ifdef HAVE_LIBXINERAMA -						for (int i = 0; (i < opt.xinerama ? num_xinerama_screens : 1) && filelist_pos; i++) { -#else -						for (int i = 0; (i < 1                   ) && filelist_pos; i++) { -#endif -							absolute_path = feh_absolute_path(FEH_FILE(filelist_pos->data)->filename); -							fputs(shell_escape(absolute_path), fp); -							filelist_pos = filelist_pos->next; -							free(absolute_path); -							fputc(' ', fp); -						} -					} else if (fil) { -						absolute_path = feh_absolute_path(fil); -						fputs(shell_escape(absolute_path), fp); -						free(absolute_path); -					} -					fputc('\n', fp); -					fd = fileno(fp); -					if (fstat(fd, &s) != 0 || fchmod(fd, s.st_mode | S_IXUSR | S_IXGRP) != 0) { -						weprintf("Can't set %s as executable", path); -					} -					fclose(fp); -				} -				free(path); -			} -		} +		if (!opt.no_fehbg) +			feh_wm_gen_bg_script(fil, centered, scaled, filled, use_filelist);  		/* create new display, copy pixmap to new display */  		disp2 = XOpenDisplay(NULL); diff --git a/src/wallpaper.h b/src/wallpaper.h index cf52d2d..77769f8 100644 --- a/src/wallpaper.h +++ b/src/wallpaper.h @@ -42,7 +42,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  extern Window ipc_win;  extern Atom ipc_atom; -_XFUNCPROTOBEGIN extern Window enl_ipc_get_win(void); +extern Window enl_ipc_get_win(void);  extern void enl_ipc_send(char *);  extern char *enl_wait_for_reply(void);  extern char *enl_ipc_get(const char *); @@ -53,5 +53,4 @@ extern int feh_wm_get_num_desks(void);  extern signed char feh_wm_get_wm_is_e(void);  void feh_wm_set_bg_filelist(unsigned char bgmode); -_XFUNCPROTOEND  #endif diff --git a/src/winwidget.c b/src/winwidget.c index 6ff90ed..37031ec 100644 --- a/src/winwidget.c +++ b/src/winwidget.c @@ -29,6 +29,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  #include "winwidget.h"  #include "options.h"  #include "events.h" +#include "timers.h"  #ifdef HAVE_INOTIFY  #include <sys/inotify.h> @@ -143,6 +144,7 @@ winwidget winwidget_create_from_file(gib_list * list, char type)  void winwidget_create_window(winwidget ret, int w, int h)  { +	XWindowAttributes window_attr;  	XSetWindowAttributes attr;  	XEvent ev;  	XClassHint *xch; @@ -249,11 +251,31 @@ void winwidget_create_window(winwidget ret, int w, int h)  		}  	} -	ret->win = -	    XCreateWindow(disp, DefaultRootWindow(disp), x, y, w, h, 0, -			  depth, InputOutput, vis, -			  CWOverrideRedirect | CWSaveUnder | CWBackingStore -			  | CWColormap | CWBackPixel | CWBorderPixel | CWEventMask, &attr); +	if (opt.x11_windowid == 0) { +		ret->win = +		  XCreateWindow(disp, DefaultRootWindow(disp), x, y, w, h, 0, +		                depth, InputOutput, vis, +		                CWOverrideRedirect | CWSaveUnder | CWBackingStore +		                | CWColormap | CWBackPixel | CWBorderPixel | CWEventMask, +		                &attr); +	} else { +		/* x11_windowid is a pointer to the window */ +		ret->win = (Window) opt.x11_windowid; + +		/* set our attributes on the window */ +		XChangeWindowAttributes(disp, ret->win, +                            CWOverrideRedirect | CWSaveUnder | CWBackingStore +                            | CWColormap | CWBackPixel | CWBorderPixel +                            | CWEventMask, &attr); + +		/* determine the size and visibility of the window */ +		XGetWindowAttributes(disp, ret->win, &window_attr); +		ret->visible = (window_attr.map_state == IsViewable); +		ret->x = window_attr.x; +		ret->y = window_attr.y; +		ret->w = window_attr.width; +		ret->h = window_attr.height; +	}  	if (mwmhints.flags) {  		XChangeProperty(disp, ret->win, prop, prop, 32, @@ -673,6 +695,9 @@ void winwidget_destroy(winwidget winwid)  #ifdef HAVE_INOTIFY      winwidget_inotify_remove(winwid);  #endif +	if (opt.reload > 0 && opt.multiwindow) { +		feh_remove_timer_by_data(winwid); +	}  	winwidget_destroy_xwin(winwid);  	if (winwid->name)  		free(winwid->name); @@ -1010,8 +1035,9 @@ void winwidget_rename(winwidget winwid, char *newname)  void winwidget_free_image(winwidget w)  { -	if (w->im) +	if (w->im) {  		gib_imlib_free_image(w->im); +	}  	w->im = NULL;  	w->im_w = 0;  	w->im_h = 0; @@ -47,7 +47,7 @@ if ( $version =~ m{ Compile-time \s switches : \s .* help }ox ) {  }  my $re_warning -  = qr{${feh_name} WARNING: test/fail/... \- No Imlib2 loader for that file format\n}; +  = qr{${feh_name} WARNING: test/fail/... \- Does not look like an image \(magic bytes missing\)\n};  my $re_loadable    = qr{test/ok/...};  my $re_unloadable  = qr{test/fail/...};  my $re_list_action = qr{test/ok/... 16x16}; | 
