diff options
| -rw-r--r-- | man/feh.pre | 17 | ||||
| -rw-r--r-- | src/imlib.c | 103 | ||||
| -rw-r--r-- | test/feh.t | 2 | 
3 files changed, 118 insertions, 4 deletions
| diff --git a/man/feh.pre b/man/feh.pre index ba5c409..7f7ef6b 100644 --- a/man/feh.pre +++ b/man/feh.pre @@ -162,6 +162,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  . diff --git a/src/imlib.c b/src/imlib.c index e06a813..4e8e790 100644 --- a/src/imlib.c +++ b/src/imlib.c @@ -181,7 +181,11 @@ void feh_imlib_print_load_error(char *file, winwidget w, Imlib_Load_Error err)  			break;  		case IMLIB_LOAD_ERROR_UNKNOWN:  		case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT: -			im_weprintf(w, "%s - No Imlib2 loader for that file format", file); +			if (getenv("FEH_SKIP_MAGIC")) { +				im_weprintf(w, "%s - No Imlib2 loader for that file format", file); +			} else { +				im_weprintf(w, "%s - Does not look like an image (magic bytes missing)", file); +			}  			break;  		case IMLIB_LOAD_ERROR_PATH_TOO_LONG:  			im_weprintf(w, "%s - Path specified is too long", file); @@ -215,6 +219,94 @@ 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) { +		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; +	} +	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; @@ -239,8 +331,13 @@ int feh_load_image(Imlib_Image * im, feh_file * file)  		if (!tmpname)  			err = IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT;  	} -	else -		*im = imlib_load_image_with_error_return(file->filename, &err); +	else { +		if (feh_is_image(file)) { +			*im = imlib_load_image_with_error_return(file->filename, &err); +		} else { +			err = IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT; +		} +	}  	if (opt.conversion_timeout >= 0 && (  			(err == IMLIB_LOAD_ERROR_UNKNOWN) || @@ -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}; | 
