summaryrefslogtreecommitdiff
path: root/src/imlib.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/imlib.c')
-rw-r--r--src/imlib.c1678
1 files changed, 1194 insertions, 484 deletions
diff --git a/src/imlib.c b/src/imlib.c
index 8196340..d2352fd 100644
--- a/src/imlib.c
+++ b/src/imlib.c
@@ -1,6 +1,7 @@
/* imlib.c
Copyright (C) 1999-2003 Tom Gilbert.
+Copyright (C) 2010-2024 Birte Kristina Friesel.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
@@ -25,6 +26,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "feh.h"
#include "filelist.h"
+#include "signals.h"
#include "winwidget.h"
#include "options.h"
@@ -34,6 +36,20 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <arpa/inet.h>
#include <netdb.h>
+#ifdef HAVE_LIBCURL
+#include <curl/curl.h>
+#endif
+
+#ifdef HAVE_LIBEXIF
+#include "exif.h"
+#endif
+
+#ifdef HAVE_LIBMAGIC
+#include <magic.h>
+
+magic_t magic = NULL;
+#endif
+
Display *disp = NULL;
Visual *vis = NULL;
Screen *scr = NULL;
@@ -50,17 +66,45 @@ int xinerama_screen;
int num_xinerama_screens;
#endif /* HAVE_LIBXINERAMA */
+gib_hash* conversion_cache = NULL;
+
+int childpid = 0;
+
+static int feh_file_is_raw(char *filename);
+static char *feh_http_load_image(char *url);
+static char *feh_dcraw_load_image(char *filename);
+static char *feh_magick_load_image(char *filename);
+
#ifdef HAVE_LIBXINERAMA
void init_xinerama(void)
{
if (opt.xinerama && XineramaIsActive(disp)) {
- int major, minor;
- if (getenv("XINERAMA_SCREEN"))
- xinerama_screen = atoi(getenv("XINERAMA_SCREEN"));
- else
- xinerama_screen = 0;
+ int major, minor, px, py, i;
+
+ /* discarded */
+ Window dw;
+ int di;
+ unsigned int du;
+
XineramaQueryVersion(disp, &major, &minor);
xinerama_screens = XineramaQueryScreens(disp, &num_xinerama_screens);
+
+ if (opt.xinerama_index >= 0)
+ xinerama_screen = opt.xinerama_index;
+ else {
+ xinerama_screen = 0;
+ XQueryPointer(disp, root, &dw, &dw, &px, &py, &di, &di, &du);
+ for (i = 0; i < num_xinerama_screens; i++) {
+ if (XY_IN_RECT(px, py,
+ xinerama_screens[i].x_org,
+ xinerama_screens[i].y_org,
+ xinerama_screens[i].width,
+ xinerama_screens[i].height)) {
+ xinerama_screen = i;
+ break;
+ }
+ }
+ }
}
}
#endif /* HAVE_LIBXINERAMA */
@@ -98,12 +142,23 @@ void init_x_and_imlib(void)
imlib_context_set_operation(IMLIB_OP_COPY);
wmDeleteWindow = XInternAtom(disp, "WM_DELETE_WINDOW", False);
- /* Initialise random numbers */
- srand(getpid() * time(NULL) % ((unsigned int) -1));
+ imlib_set_cache_size(opt.cache_size * 1024 * 1024);
return;
}
+int feh_should_ignore_image(Imlib_Image * im)
+{
+ if (opt.filter_by_dimensions) {
+ unsigned int w = gib_imlib_image_get_width(im);
+ unsigned int h = gib_imlib_image_get_height(im);
+ if (w < opt.min_width || w > opt.max_width || h < opt.min_height || h > opt.max_height) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
int feh_load_image_char(Imlib_Image * im, char *filename)
{
feh_file *file;
@@ -115,421 +170,852 @@ int feh_load_image_char(Imlib_Image * im, char *filename)
return(i);
}
-int feh_load_image(Imlib_Image * im, feh_file * file)
+void feh_print_load_error(char *file, winwidget w, Imlib_Load_Error err, enum feh_load_error feh_err)
{
- Imlib_Load_Error err;
-
- D(("filename is %s, image is %p\n", file->filename, im));
-
- if (!file || !file->filename)
- return(0);
-
- /* Handle URLs */
- if ((!strncmp(file->filename, "http://", 7)) || (!strncmp(file->filename, "https://", 8))
- || (!strncmp(file->filename, "ftp://", 6))) {
- char *tmpname = NULL;
- char *tempcpy;
-
- tmpname = feh_http_load_image(file->filename);
- if (tmpname == NULL)
- return(0);
- *im = imlib_load_image_with_error_return(tmpname, &err);
- if (im) {
- /* load the info now, in case it's needed after we delete the
- temporary image file */
- tempcpy = file->filename;
- file->filename = tmpname;
- feh_file_info_load(file, *im);
- file->filename = tempcpy;
- }
- if ((opt.slideshow) && (opt.reload == 0)) {
- /* Http, no reload, slideshow. Let's keep this image on hand... */
- free(file->filename);
- file->filename = estrdup(tmpname);
- } else {
- /* Don't cache the image if we're doing reload + http (webcams etc) */
- if (!opt.keep_http)
- unlink(tmpname);
+ 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 (!opt.keep_http)
- add_file_to_rm_filelist(tmpname);
- free(tmpname);
- } else {
- *im = imlib_load_image_with_error_return(file->filename, &err);
- }
-
- if ((err) || (!im)) {
- if (opt.verbose && !opt.quiet) {
- fprintf(stdout, "\n");
- reset_output = 1;
+ if (feh_err != LOAD_ERROR_IMLIB) {
+ return;
}
- /* Check error code */
switch (err) {
case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST:
- if (!opt.quiet)
- weprintf("%s - File does not exist", file->filename);
+ im_weprintf(w, "%s - File does not exist", file);
break;
case IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY:
- if (!opt.quiet)
- weprintf("%s - Directory specified for image filename", file->filename);
+ im_weprintf(w, "%s - Directory specified for image filename", file);
break;
case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ:
- if (!opt.quiet)
- weprintf("%s - No read access to directory", file->filename);
+ im_weprintf(w, "%s - No read access", file);
break;
case IMLIB_LOAD_ERROR_UNKNOWN:
case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT:
- if (!opt.quiet)
- weprintf("%s - No Imlib2 loader for that file format", file->filename);
+ im_weprintf(w, "%s - No Imlib2 loader for that file format", file);
break;
case IMLIB_LOAD_ERROR_PATH_TOO_LONG:
- if (!opt.quiet)
- weprintf("%s - Path specified is too long", file->filename);
+ im_weprintf(w, "%s - Path specified is too long", file);
break;
case IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT:
- if (!opt.quiet)
- weprintf("%s - Path component does not exist", file->filename);
+ im_weprintf(w, "%s - Path component does not exist", file);
break;
case IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY:
- if (!opt.quiet)
- weprintf("%s - Path component is not a directory", file->filename);
+ im_weprintf(w, "%s - Path component is not a directory", file);
break;
case IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE:
- if (!opt.quiet)
- weprintf("%s - Path points outside address space", file->filename);
+ im_weprintf(w, "%s - Path points outside address space", file);
break;
case IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS:
- if (!opt.quiet)
- weprintf("%s - Too many levels of symbolic links", file->filename);
+ im_weprintf(w, "%s - Too many levels of symbolic links", file);
break;
case IMLIB_LOAD_ERROR_OUT_OF_MEMORY:
- if (!opt.quiet)
- weprintf("While loading %s - Out of memory", file->filename);
- break;
- case IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS:
- eprintf("While loading %s - Out of file descriptors", file->filename);
+ im_weprintf(w, "While loading %s - Out of memory", file);
break;
case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE:
- if (!opt.quiet)
- weprintf("%s - Cannot write to directory", file->filename);
+ im_weprintf(w, "%s - Cannot write to directory", file);
break;
case IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE:
- if (!opt.quiet)
- weprintf("%s - Cannot write - out of disk space", file->filename);
+ im_weprintf(w, "%s - Cannot write - out of disk space", file);
break;
+#if defined(IMLIB2_VERSION_MAJOR) && defined(IMLIB2_VERSION_MINOR) && (IMLIB2_VERSION_MAJOR > 1 || IMLIB2_VERSION_MINOR > 7)
+ case IMLIB_LOAD_ERROR_IMAGE_READ:
+ im_weprintf(w, "%s - Invalid image file", file);
+ break;
+ case IMLIB_LOAD_ERROR_IMAGE_FRAME:
+ im_weprintf(w, "%s - Requested frame not in image", file);
+ break;
+#endif
default:
- if (!opt.quiet)
- weprintf("While loading %s - Unknown error (%d). Attempting to continue",
- file->filename, err);
+ im_weprintf(w, "While loading %s - Unknown error (%d)",
+ file, err);
break;
}
- D(("Load *failed*\n"));
- return(0);
}
+}
- D(("Loaded ok\n"));
- return(1);
+#ifdef HAVE_LIBMAGIC
+void uninit_magic(void)
+{
+ if (!magic) {
+ return;
+ }
+
+ magic_close(magic);
+ magic = NULL;
}
+void init_magic(void)
+{
+ if (getenv("FEH_SKIP_MAGIC")) {
+ return;
+ }
-char *feh_http_load_image(char *url)
+ if (!(magic = magic_open(MAGIC_NONE))) {
+ weprintf("unable to initialize magic library\n");
+ return;
+ }
+
+ if (magic_load(magic, NULL) != 0) {
+ weprintf("cannot load magic database: %s\n", magic_error(magic));
+ uninit_magic();
+ }
+}
+
+/*
+ * This is a workaround for an Imlib2 regression, causing unloadable image
+ * detection to be excessively slow (and, thus, causing feh to hang for a while
+ * when encountering an unloadable image). We use magic byte detection to
+ * avoid calling Imlib2 for files it probably cannot handle. See
+ * <https://phab.enlightenment.org/T8739> and
+ * <https://github.com/derf/feh/issues/505>.
+ */
+int feh_is_image(feh_file * file, int magic_flags)
{
- char *tmpname;
- char *basename;
- char *path = NULL;
+ const char * mime_type = NULL;
- if (opt.keep_http) {
- if (opt.output_dir)
- path = opt.output_dir;
- else
- path = "";
- } else
- path = "/tmp/";
+ if (!magic) {
+ return 1;
+ }
- basename = strrchr(url, '/') + 1;
- tmpname = feh_unique_filename(path, basename);
-
- if (opt.builtin_http) {
- /* state for HTTP header parser */
-#define SAW_NONE 1
-#define SAW_ONE_CM 2
-#define SAW_ONE_CJ 3
-#define SAW_TWO_CM 4
-#define IN_BODY 5
-
-#define OUR_BUF_SIZE 1024
-#define EOL "\015\012"
-
- int sockno = 0;
- int size;
- int body = SAW_NONE;
- struct sockaddr_in addr;
- struct hostent *hptr;
- char *hostname;
- char *get_string;
- char *host_string;
- char *query_string;
- char *get_url;
- static char buf[OUR_BUF_SIZE];
- char ua_string[] = "User-Agent: feh image viewer";
- char accept_string[] = "Accept: image/*";
- FILE *fp;
-
- D(("using builtin http collection\n"));
- fp = fopen(tmpname, "w");
- if (!fp) {
- weprintf("couldn't write to file %s:", tmpname);
- free(tmpname);
- return(NULL);
- }
+ magic_setflags(magic, MAGIC_MIME_TYPE | MAGIC_SYMLINK | magic_flags);
+ mime_type = magic_file(magic, file->filename);
- hostname = feh_strip_hostname(url);
- if (!hostname) {
- weprintf("couldn't work out hostname from %s:", url);
- fclose(fp);
- unlink(tmpname);
- free(tmpname);
- return(NULL);
- }
+ if (!mime_type) {
+ return 0;
+ }
- D(("trying hostname %s\n", hostname));
+ D(("file %s has mime type: %s\n", file->filename, mime_type));
- if (!(hptr = feh_gethostbyname(hostname))) {
- weprintf("error resolving host %s:", hostname);
- fclose(fp);
- unlink(tmpname);
- free(hostname);
- free(tmpname);
- return(NULL);
+ if (strncmp(mime_type, "image/", 6) == 0) {
+ return 1;
+ }
+
+ /* no infinite loop on compressed content, please */
+ if (magic_flags) {
+ return 0;
+ }
+
+ /* imlib2 supports loading compressed images, let's have a look inside */
+ if (strcmp(mime_type, "application/gzip") == 0 ||
+ strcmp(mime_type, "application/x-bzip2") == 0 ||
+ strcmp(mime_type, "application/x-xz") == 0) {
+ return feh_is_image(file, MAGIC_COMPRESS);
+ }
+
+ return 0;
+}
+#else
+int feh_is_image(__attribute__((unused)) feh_file * file, __attribute__((unused)) int magic_flags)
+{
+ return 1;
+}
+#endif
+
+int feh_load_image(Imlib_Image * im, feh_file * file)
+{
+ Imlib_Load_Error err = IMLIB_LOAD_ERROR_NONE;
+ enum 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;
+
+ D(("filename is %s, image is %p\n", file->filename, im));
+
+ if (!file || !file->filename)
+ return 0;
+
+ if (path_is_url(file->filename)) {
+ image_source = SRC_HTTP;
+
+ if ((tmpname = feh_http_load_image(file->filename)) == NULL) {
+ feh_err = LOAD_ERROR_CURL;
+ err = IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST;
+ }
+ }
+ else {
+ if (feh_is_image(file, 0)) {
+ *im = imlib_load_image_with_error_return(file->filename, &err);
+ } else {
+ feh_err = LOAD_ERROR_MAGICBYTES;
+ err = IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT;
}
+ }
- /* Copy the address of the host to socket description. */
- memcpy(&addr.sin_addr, hptr->h_addr, hptr->h_length);
+ if (opt.conversion_timeout >= 0 && (
+ (err == IMLIB_LOAD_ERROR_UNKNOWN) ||
+ (err == IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT))) {
+ if (feh_file_is_raw(file->filename)) {
+ image_source = SRC_DCRAW;
+ tmpname = feh_dcraw_load_image(file->filename);
+ if (!tmpname) {
+ feh_err = LOAD_ERROR_DCRAW;
+ }
+ } else {
+ image_source = SRC_MAGICK;
+ feh_err = LOAD_ERROR_IMLIB;
+ tmpname = feh_magick_load_image(file->filename);
+ if (!tmpname) {
+ feh_err = LOAD_ERROR_IMAGEMAGICK;
+ }
+ }
+ }
- /* Set port and protocol */
- addr.sin_family = AF_INET;
- addr.sin_port = htons(80);
+ if (tmpname) {
+ *im = imlib_load_image_with_error_return(tmpname, &err);
+ if (!err && im) {
+ real_filename = file->filename;
+ file->filename = tmpname;
- if ((sockno = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
- weprintf("error opening socket:");
- fclose(fp);
- unlink(tmpname);
- free(tmpname);
- free(hostname);
- return(NULL);
+ /*
+ * feh does not associate a non-native image with its temporary
+ * filename and may delete the temporary file right after loading.
+ * To ensure that it is still aware of image size, dimensions, etc.,
+ * file_info is preloaded here. To avoid a memory leak when loading
+ * a non-native file multiple times in a slideshow, the file_info
+ * struct is freed first. If file->info is not set, feh_file_info_free
+ * is a no-op.
+ */
+ feh_file_info_free(file->info);
+ feh_file_info_load(file, *im);
+
+ file->filename = real_filename;
+#ifdef HAVE_LIBEXIF
+ /*
+ * 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 (connect(sockno, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
- weprintf("error connecting socket:");
- fclose(fp);
+ if (!opt.use_conversion_cache && ((image_source != SRC_HTTP) || !opt.keep_http))
unlink(tmpname);
+ // keep_http already performs an add_file_to_rm_filelist call
+ else if (opt.use_conversion_cache && !opt.keep_http)
+ // add_file_to_rm_filelist duplicates tmpname
+ add_file_to_rm_filelist(tmpname);
+
+ if (!opt.use_conversion_cache)
free(tmpname);
- free(hostname);
- return(NULL);
+ } 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
+ }
- get_url = strchr(url, '/') + 2;
- get_url = strchr(get_url, '/');
-
- get_string = estrjoin(" ", "GET", get_url, "HTTP/1.0", NULL);
- host_string = estrjoin(" ", "Host:", hostname, NULL);
- query_string = estrjoin(EOL, get_string, host_string, accept_string, ua_string, "", "", NULL);
- /* At this point query_string looks something like
- **
- ** GET /dir/foo.jpg?123456 HTTP/1.0^M^J
- ** Host: www.example.com^M^J
- ** Accept: image/ *^M^J
- ** User-Agent: feh image viewer^M^J
- ** ^M^J
- **
- ** Host: is required by HTTP/1.1 and very important for some sites,
- ** even with HTTP/1.0
- **
- ** -- BEG
- */
- if ((send(sockno, query_string, strlen(query_string), 0)) == -1) {
- free(get_string);
- free(host_string);
- free(query_string);
- free(tmpname);
- free(hostname);
- weprintf("error sending over socket:");
- return(NULL);
+ if ((err) || (!im)) {
+ if (opt.verbose && !opt.quiet) {
+ fputs("\n", stderr);
+ reset_output = 1;
}
- free(get_string);
- free(host_string);
- free(query_string);
- free(hostname);
-
- while ((size = read(sockno, &buf, OUR_BUF_SIZE))) {
- if (body == IN_BODY) {
- fwrite(buf, 1, size, fp);
- } else {
- int i;
-
- for (i = 0; i < size; i++) {
- /* We are looking for ^M^J^M^J, but will accept
- ** ^J^J from broken servers. Stray ^Ms will be
- ** ignored.
- **
- ** TODO:
- ** Checking the headers for a
- ** Content-Type: image/ *
- ** header would help detect problems with results.
- ** Maybe look at the response code too? But there is
- ** no fundamental reason why a 4xx or 5xx response
- ** could not return an image, it is just the 3xx
- ** series we need to worry about.
- **
- ** Also, grabbing the size from the Content-Length
- ** header and killing the connection after that
- ** many bytes where read would speed up closing the
- ** socket.
- ** -- BEG
- */
+ feh_print_load_error(file->filename, NULL, err, feh_err);
+ D(("Load *failed*\n"));
+ return(0);
+ }
- switch (body) {
+ /*
+ * 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;
+ 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);
+ }
+ }
- case IN_BODY:
- fwrite(buf + i, 1, size - i, fp);
- i = size;
- break;
+ if (orientation == 2)
+ gib_imlib_image_flip_horizontal(*im);
+ else if (orientation == 3)
+ gib_imlib_image_orientate(*im, 2);
+ else if (orientation == 4)
+ gib_imlib_image_flip_vertical(*im);
+ else if (orientation == 5) {
+ gib_imlib_image_orientate(*im, 3);
+ gib_imlib_image_flip_vertical(*im);
+ }
+ else if (orientation == 6)
+ gib_imlib_image_orientate(*im, 1);
+ else if (orientation == 7) {
+ gib_imlib_image_orientate(*im, 3);
+ gib_imlib_image_flip_horizontal(*im);
+ }
+ else if (orientation == 8)
+ gib_imlib_image_orientate(*im, 3);
+#endif
- case SAW_ONE_CM:
- if (buf[i] == '\012') {
- body = SAW_ONE_CJ;
- } else {
- body = SAW_NONE;
- }
- break;
-
- case SAW_ONE_CJ:
- if (buf[i] == '\015') {
- body = SAW_TWO_CM;
- } else {
- if (buf[i] == '\012') {
- body = IN_BODY;
- } else {
- body = SAW_NONE;
- }
- }
- break;
+ D(("Loaded ok\n"));
+ return(1);
+}
- case SAW_TWO_CM:
- if (buf[i] == '\012') {
- body = IN_BODY;
- } else {
- body = SAW_NONE;
- }
- break;
-
- case SAW_NONE:
- if (buf[i] == '\015') {
- body = SAW_ONE_CM;
- } else {
- if (buf[i] == '\012') {
- body = SAW_ONE_CJ;
- }
- }
- break;
+void feh_reload_image(winwidget w, int resize, int force_new)
+{
+ char *new_title;
+ int len;
+ Imlib_Image tmp;
+ int old_w, old_h;
- } /* switch */
- } /* for i */
- }
- } /* while read */
- close(sockno);
- fclose(fp);
+ if (!w->file) {
+ im_weprintf(w, "couldn't reload, this image has no file associated with it.");
+ winwidget_render_image(w, 0, 0);
+ return;
+ }
+
+ D(("resize %d, force_new %d\n", resize, force_new));
+
+ free(FEH_FILE(w->file->data)->caption);
+ FEH_FILE(w->file->data)->caption = NULL;
+
+ len = strlen(w->name) + sizeof("Reloading: ") + 1;
+ new_title = emalloc(len);
+ snprintf(new_title, len, "Reloading: %s", w->name);
+ winwidget_rename(w, new_title);
+ free(new_title);
+
+ old_w = gib_imlib_image_get_width(w->im);
+ old_h = gib_imlib_image_get_height(w->im);
+
+ /*
+ * If we don't free the old image before loading the new one, Imlib2's
+ * caching will get in our way.
+ * However, if --reload is used (force_new == 0), we want to continue if
+ * the new image cannot be loaded, so we must not free the old image yet.
+ */
+ if (force_new)
+ winwidget_free_image(w);
+
+ // if it's an external image, our own cache will also get in your way
+ char *sfn;
+ if (opt.use_conversion_cache && conversion_cache && (sfn = gib_hash_get(conversion_cache, FEH_FILE(w->file->data)->filename)) != NULL) {
+ free(sfn);
+ gib_hash_set(conversion_cache, FEH_FILE(w->file->data)->filename, NULL);
+ }
+
+ if ((feh_load_image(&tmp, FEH_FILE(w->file->data))) == 0) {
+ if (force_new)
+ eprintf("failed to reload image\n");
+ else {
+ im_weprintf(w, "Couldn't reload image. Is it still there?");
+ winwidget_render_image(w, 0, 0);
+ }
+ return;
+ }
+
+ if (!resize && ((old_w != gib_imlib_image_get_width(tmp)) ||
+ (old_h != gib_imlib_image_get_height(tmp))))
+ resize = 1;
+
+ if (!force_new)
+ winwidget_free_image(w);
+
+ w->im = tmp;
+ winwidget_reset_image(w);
+
+ w->mode = MODE_NORMAL;
+ if ((w->im_w != gib_imlib_image_get_width(w->im))
+ || (w->im_h != gib_imlib_image_get_height(w->im)))
+ w->had_resize = 1;
+ if (w->has_rotated) {
+ Imlib_Image temp;
+
+ temp = gib_imlib_create_rotated_image(w->im, 0.0);
+ w->im_w = gib_imlib_image_get_width(temp);
+ w->im_h = gib_imlib_image_get_height(temp);
+ gib_imlib_free_image_and_decache(temp);
+ } else {
+ w->im_w = gib_imlib_image_get_width(w->im);
+ w->im_h = gib_imlib_image_get_height(w->im);
+ }
+ winwidget_render_image(w, resize, 0);
+
+ return;
+}
+
+static int feh_file_is_raw(char *filename)
+{
+ childpid = fork();
+ if (childpid == -1) {
+ perror("fork");
+ return 0;
+ }
+
+ if (childpid == 0) {
+ int devnull = open("/dev/null", O_WRONLY);
+ dup2(devnull, 1);
+ dup2(devnull, 2);
+ execlp("dcraw", "dcraw", "-i", filename, NULL);
+ _exit(1);
} else {
- int pid;
int status;
+ do {
+ waitpid(childpid, &status, WUNTRACED);
+ if (WIFEXITED(status)) {
+ return !WEXITSTATUS(status);
+ }
+ } while (!WIFEXITED(status) && !WIFSIGNALED(status));
+ }
- if ((pid = fork()) < 0) {
- weprintf("open url: fork failed:");
- free(tmpname);
- return(NULL);
- } else if (pid == 0) {
- char *quiet = NULL;
+ return 0;
+}
- if (!opt.verbose)
- quiet = estrdup("-q");
+static char *feh_dcraw_load_image(char *filename)
+{
+ char *basename;
+ char *tmpname;
+ char *sfn;
+ int fd = -1;
+
+ if (opt.use_conversion_cache) {
+ if (!conversion_cache)
+ conversion_cache = gib_hash_new();
+ if ((sfn = gib_hash_get(conversion_cache, filename)) != NULL)
+ return sfn;
+ }
- execlp("wget", "wget", "--cache=off", "-O", tmpname, url, quiet, NULL);
- eprintf("url: Is 'wget' installed? Failed to exec wget:");
+ basename = strrchr(filename, '/');
+
+ if (basename == NULL)
+ basename = filename;
+ else
+ basename++;
+
+ tmpname = feh_unique_filename("/tmp/", basename);
+
+ if (strlen(tmpname) > (NAME_MAX-6))
+ tmpname[NAME_MAX-7] = '\0';
+
+ sfn = estrjoin("_", tmpname, "XXXXXX", NULL);
+ free(tmpname);
+
+ fd = mkstemp(sfn);
+
+ if (fd == -1) {
+ free(sfn);
+ return NULL;
+ }
+
+ childpid = fork();
+ if (childpid == -1) {
+ weprintf("%s: Can't load with dcraw. Fork failed:", filename);
+ unlink(sfn);
+ free(sfn);
+ close(fd);
+ return NULL;
+ } else if (childpid == 0) {
+ dup2(fd, STDOUT_FILENO);
+ close(fd);
+
+ alarm(opt.conversion_timeout);
+ execlp("dcraw", "dcraw", "-c", "-e", filename, NULL);
+ _exit(1);
+ }
+
+ int status;
+ waitpid(-1, &status, 0);
+ if (WIFSIGNALED(status)) {
+ unlink(sfn);
+ free(sfn);
+ sfn = NULL;
+ if (!opt.quiet)
+ weprintf("%s - Conversion took too long, skipping", filename);
+ }
+
+ if ((sfn != NULL) && opt.use_conversion_cache)
+ gib_hash_set(conversion_cache, filename, sfn);
+
+ return sfn;
+}
+
+static char *feh_magick_load_image(char *filename)
+{
+ char *argv_fn;
+ char *basename;
+ char *tmpname;
+ char *sfn;
+ char tempdir[] = "/tmp/.feh-magick-tmp-XXXXXX";
+ int fd = -1, devnull = -1;
+ int status;
+ char created_tempdir = 0;
+
+ if (opt.use_conversion_cache) {
+ if (!conversion_cache)
+ conversion_cache = gib_hash_new();
+ if ((sfn = gib_hash_get(conversion_cache, filename)) != NULL)
+ return sfn;
+ }
+
+ basename = strrchr(filename, '/');
+
+ if (basename == NULL)
+ basename = filename;
+ else
+ basename++;
+
+ tmpname = feh_unique_filename("/tmp/", basename);
+
+ if (strlen(tmpname) > (NAME_MAX-6))
+ tmpname[NAME_MAX-7] = '\0';
+
+ sfn = estrjoin("_", tmpname, "XXXXXX", NULL);
+ free(tmpname);
+
+ fd = mkstemp(sfn);
+
+ if (fd == -1) {
+ free(sfn);
+ return NULL;
+ }
+
+ /*
+ * 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 {
- waitpid(pid, &status, 0);
+ created_tempdir = 1;
+ }
+ }
+
+ if ((childpid = fork()) < 0) {
+ weprintf("%s: Can't load with imagemagick. Fork failed:", filename);
+ unlink(sfn);
+ free(sfn);
+ sfn = NULL;
+ }
+ else if (childpid == 0) {
+
+ devnull = open("/dev/null", O_WRONLY);
+ dup2(devnull, 0);
+ if (opt.quiet) {
+ /* discard convert output */
+ dup2(devnull, 1);
+ dup2(devnull, 2);
+ }
+
+ /*
+ * convert only accepts SIGINT via killpg, a normal kill doesn't work
+ */
+ setpgid(0, 0);
+
+ if (created_tempdir) {
+ // no error checking - this is a best-effort code path
+ setenv("MAGICK_TMPDIR", tempdir, 0);
+ }
+
+ execlp("convert", "convert", filename, argv_fn, NULL);
+ _exit(1);
+ }
+ else {
+ alarm(opt.conversion_timeout);
+ waitpid(childpid, &status, 0);
+ kill(childpid, SIGKILL);
+ if (opt.conversion_timeout > 0 && !alarm(0)) {
+ unlink(sfn);
+ free(sfn);
+ sfn = NULL;
+
+ if (!opt.quiet) {
+ weprintf("%s: Conversion took too long, skipping", filename);
+ }
+ }
+ close(fd);
+ childpid = 0;
+ }
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- weprintf("url: wget failed to load URL %s\n", url);
- unlink(tmpname);
- free(tmpname);
- return(NULL);
+ 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);
}
- return(tmpname);
+ free(argv_fn);
+
+ if ((sfn != NULL) && opt.use_conversion_cache)
+ gib_hash_set(conversion_cache, filename, sfn);
+
+ return sfn;
}
-struct hostent *feh_gethostbyname(const char *name)
+#ifdef HAVE_LIBCURL
+
+#if LIBCURL_VERSION_NUM >= 0x072000 /* 07.32.0 */
+static int curl_quit_function(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
+#else
+static int curl_quit_function(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
+#endif
{
- struct hostent *hp;
- unsigned long addr;
+ // ignore "unused parameter" warnings
+ (void)clientp;
+ (void)dltotal;
+ (void)dlnow;
+ (void)ultotal;
+ (void)ulnow;
+ if (sig_exit) {
+ /*
+ * The user wants to quit feh. Tell libcurl to abort the transfer and
+ * return control to the main loop, where we can quit gracefully.
+ */
+ return 1;
+ }
+ return 0;
+}
- addr = (unsigned long) inet_addr(name);
- if ((int) addr != -1)
- hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
- else
- hp = gethostbyname(name);
- return(hp);
+static char *feh_http_load_image(char *url)
+{
+ CURL *curl;
+ CURLcode res;
+ char *sfn;
+ FILE *sfp;
+ int fd = -1;
+ char *ebuff;
+ char *tmpname;
+ char *basename;
+ char *path = NULL;
+
+ if (opt.use_conversion_cache) {
+ if (!conversion_cache)
+ conversion_cache = gib_hash_new();
+ if ((sfn = gib_hash_get(conversion_cache, url)) != NULL)
+ return sfn;
+ }
+
+ if (opt.keep_http) {
+ if (opt.output_dir)
+ path = opt.output_dir;
+ else
+ path = "";
+ } else
+ path = "/tmp/";
+
+ curl = curl_easy_init();
+ if (!curl) {
+ weprintf("open url: libcurl initialization failure");
+ return NULL;
+ }
+
+ basename = strrchr(url, '/') + 1;
+
+#ifdef HAVE_MKSTEMPS
+ tmpname = estrjoin("_", "feh_curl_XXXXXX", basename, NULL);
+
+ if (strlen(tmpname) > NAME_MAX) {
+ tmpname[NAME_MAX] = '\0';
+ }
+#else
+ if (strlen(basename) > NAME_MAX-7) {
+ tmpname = estrdup("feh_curl_XXXXXX");
+ } else {
+ tmpname = estrjoin("_", "feh_curl", basename, "XXXXXX", NULL);
+ }
+#endif
+
+ sfn = estrjoin("", path, tmpname, NULL);
+ free(tmpname);
+
+ D(("sfn is %s\n", sfn))
+
+#ifdef HAVE_MKSTEMPS
+ fd = mkstemps(sfn, strlen(basename) + 1);
+#else
+ fd = mkstemp(sfn);
+#endif
+
+ if (fd != -1) {
+ sfp = fdopen(fd, "w+");
+ if (sfp != NULL) {
+#ifdef DEBUG
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
+#endif
+ /*
+ * Do not allow requests to take longer than 30 minutes.
+ * This should be sufficiently high to accommodate use cases with
+ * unusually high latencies, while at the same time avoiding
+ * feh hanging indefinitely in unattended slideshows.
+ */
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1800);
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, sfp);
+ ebuff = emalloc(CURL_ERROR_SIZE);
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, ebuff);
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, PACKAGE "/" VERSION);
+ curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
+#if LIBCURL_VERSION_NUM >= 0x072000 /* 07.32.0 */
+ curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, curl_quit_function);
+#else
+ curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, curl_quit_function);
+#endif
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
+ if (opt.insecure_ssl) {
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
+ } else if (getenv("CURL_CA_BUNDLE") != NULL) {
+ // Allow the user to specify custom CA certificates.
+ curl_easy_setopt(curl, CURLOPT_CAINFO,
+ getenv("CURL_CA_BUNDLE"));
+ }
+
+ res = curl_easy_perform(curl);
+ curl_easy_cleanup(curl);
+ if (res != CURLE_OK) {
+ if (res != CURLE_ABORTED_BY_CALLBACK) {
+ weprintf("open url: %s", ebuff);
+ }
+ unlink(sfn);
+ close(fd);
+ free(sfn);
+ sfn = NULL;
+ }
+
+ free(ebuff);
+ fclose(sfp);
+ if (opt.use_conversion_cache)
+ gib_hash_set(conversion_cache, url, sfn);
+ return sfn;
+ } else {
+ weprintf("open url: fdopen failed:");
+ unlink(sfn);
+ free(sfn);
+ close(fd);
+ }
+ } else {
+#ifdef HAVE_MKSTEMPS
+ weprintf("open url: mkstemps failed:");
+#else
+ weprintf("open url: mkstemp failed:");
+#endif
+ free(sfn);
+ }
+ curl_easy_cleanup(curl);
+ return NULL;
}
-char *feh_strip_hostname(char *url)
+#else /* HAVE_LIBCURL */
+
+char *feh_http_load_image(char *url)
{
- char *ret;
- char *start;
- char *finish;
- int len;
+ weprintf(
+ "Cannot load image %s\nPlease recompile feh with libcurl support",
+ url
+ );
+ return NULL;
+}
- start = strchr(url, '/');
- if (!start)
- return(NULL);
+#endif /* HAVE_LIBCURL */
- start += 2;
+void feh_imlib_image_fill_text_bg(Imlib_Image im, int w, int h)
+{
+ gib_imlib_image_set_has_alpha(im, 1);
- finish = strchr(start, '/');
- if (!finish)
- return(NULL);
+ imlib_context_set_blend(0);
- len = finish - start;
+ if (opt.text_bg == TEXT_BG_CLEAR)
+ gib_imlib_image_fill_rectangle(im, 0, 0, w, h, 0, 0, 0, 0);
+ else
+ gib_imlib_image_fill_rectangle(im, 0, 0, w, h, 0, 0, 0, 127);
- ret = emalloc(len + 1);
- strncpy(ret, start, len);
- ret[len] = '\0';
- return(ret);
+ imlib_context_set_blend(1);
}
-void feh_draw_zoom(winwidget w)
+static Imlib_Font feh_load_font(winwidget w)
{
static Imlib_Font fn = NULL;
- int tw = 0, th = 0;
- Imlib_Image im = NULL;
- char buf[100];
- static DATA8 atab[256];
-
- if (!w->im)
- return;
if (opt.font)
fn = gib_imlib_load_font(opt.font);
if (!fn) {
- fn = gib_imlib_load_font(DEFAULT_FONT);
+ if (w && w->full_screen)
+ fn = gib_imlib_load_font(DEFAULT_FONT_BIG);
+ else
+ fn = gib_imlib_load_font(DEFAULT_FONT);
}
if (!fn) {
- weprintf("Couldn't load font for zoom printing");
- return;
+ eprintf("Couldn't load font to draw a message");
}
- memset(atab, 0, sizeof(atab));
+ return fn;
+}
+
+
+void feh_draw_zoom(winwidget w)
+{
+ static Imlib_Font fn = NULL;
+ int tw = 0, th = 0;
+ Imlib_Image im = NULL;
+ char buf[100];
+
+ if (!w->im)
+ return;
+
+ fn = feh_load_font(w);
snprintf(buf, sizeof(buf), "%.0f%%, %dx%d", w->zoom * 100,
(int) (w->im_w * w->zoom), (int) (w->im_h * w->zoom));
@@ -543,9 +1029,7 @@ void feh_draw_zoom(winwidget w)
if (!im)
eprintf("Couldn't create image. Out of memory?");
- gib_imlib_image_set_has_alpha(im, 1);
- gib_imlib_apply_color_modifier_to_rectangle(im, 0, 0, tw, th, NULL, NULL, NULL, atab);
- gib_imlib_image_fill_rectangle(im, 0, 0, tw, th, 0, 0, 0, 0);
+ feh_imlib_image_fill_text_bg(im, tw, th);
gib_imlib_text_draw(im, fn, NULL, 2, 2, buf, IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
gib_imlib_text_draw(im, fn, NULL, 1, 1, buf, IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
@@ -554,12 +1038,65 @@ void feh_draw_zoom(winwidget w)
return;
}
-void feh_draw_filename(winwidget w)
+void im_weprintf(winwidget w, char *fmt, ...)
+{
+ va_list args;
+ char *errstr = emalloc(1024);
+
+ fflush(stdout);
+ fputs(PACKAGE " WARNING: ", stderr);
+
+ va_start(args, fmt);
+ vsnprintf(errstr, 1024, fmt, args);
+ va_end(args);
+
+ if (w)
+ w->errstr = errstr;
+
+ fputs(errstr, stderr);
+ if (fmt[0] != '\0' && fmt[strlen(fmt) - 1] == ':')
+ fprintf(stderr, " %s", strerror(errno));
+ fputs("\n", stderr);
+ if (!w)
+ free(errstr);
+}
+
+
+void feh_draw_errstr(winwidget w)
{
static Imlib_Font fn = NULL;
int tw = 0, th = 0;
Imlib_Image im = NULL;
- static DATA8 atab[256];
+
+ if (!w->im)
+ return;
+
+ fn = feh_load_font(NULL);
+
+ /* Work out how high the font is */
+ gib_imlib_get_text_size(fn, w->errstr, NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT);
+
+ tw += 3;
+ th += 3;
+ im = imlib_create_image(tw, th);
+ if (!im)
+ eprintf("Couldn't create errstr image. Out of memory?");
+
+ feh_imlib_image_fill_text_bg(im, tw, th);
+
+ gib_imlib_text_draw(im, fn, NULL, 2, 2, w->errstr, IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
+ gib_imlib_text_draw(im, fn, NULL, 1, 1, w->errstr, IMLIB_TEXT_TO_RIGHT, 255, 0, 0, 255);
+ free(w->errstr);
+ w->errstr = NULL;
+ gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0, w->h - th, 1, 1, 0);
+ gib_imlib_free_image_and_decache(im);
+}
+
+void feh_draw_filename(winwidget w)
+{
+ static Imlib_Font fn = NULL;
+ int tw = 0, th = 0, nw = 0;
+ Imlib_Image im = NULL;
char *s = NULL;
int len = 0;
@@ -567,49 +1104,43 @@ void feh_draw_filename(winwidget w)
|| (!FEH_FILE(w->file->data)->filename))
return;
- if (opt.font)
- fn = gib_imlib_load_font(opt.font);
+ fn = feh_load_font(w);
- if (!fn) {
- if (w->full_screen)
- fn = gib_imlib_load_font(DEFAULT_FONT_BIG);
- else
- fn = gib_imlib_load_font(DEFAULT_FONT);
- }
+ /* Work out how high the font is */
+ gib_imlib_get_text_size(fn, FEH_FILE(w->file->data)->filename, NULL, &tw,
+ &th, IMLIB_TEXT_TO_RIGHT);
- if (!fn) {
- weprintf("Couldn't load font for filename printing");
- return;
- }
+ if (gib_list_length(filelist) > 1) {
+ len = snprintf(NULL, 0, "%d of %d", gib_list_length(filelist),
+ gib_list_length(filelist)) + 1;
+ s = emalloc(len);
+ if (w->file)
+ snprintf(s, len, "%d of %d", gib_list_num(filelist, w->file) +
+ 1, gib_list_length(filelist));
+ else
+ snprintf(s, len, "%d of %d", gib_list_num(filelist, current_file) +
+ 1, gib_list_length(filelist));
- memset(atab, 0, sizeof(atab));
+ gib_imlib_get_text_size(fn, s, NULL, &nw, NULL, IMLIB_TEXT_TO_RIGHT);
- /* Work out how high the font is */
- gib_imlib_get_text_size(fn, FEH_FILE(w->file->data)->filename, NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT);
+ if (nw > tw)
+ tw = nw;
+ }
- /* tw is no longer correct, if the filename is shorter than
- * the string "%d of %d" used below in fullscreen mode */
tw += 3;
th += 3;
im = imlib_create_image(tw, 2 * th);
if (!im)
eprintf("Couldn't create image. Out of memory?");
- gib_imlib_image_set_has_alpha(im, 1);
- gib_imlib_apply_color_modifier_to_rectangle(im, 0, 0, tw, 2 * th, NULL, NULL, NULL, atab);
- gib_imlib_image_fill_rectangle(im, 0, 0, tw, 2 * th, 0, 0, 0, 0);
+ feh_imlib_image_fill_text_bg(im, tw, 2 * th);
gib_imlib_text_draw(im, fn, NULL, 2, 2, FEH_FILE(w->file->data)->filename,
IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
gib_imlib_text_draw(im, fn, NULL, 1, 1, FEH_FILE(w->file->data)->filename,
IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
- /* Print the position in the filelist, if we have >=2 files */
- if (gib_list_length(filelist) > 1) {
- /* sic! */
- len = snprintf(NULL, 0, "%d of %d", gib_list_length(filelist), gib_list_length(filelist)) + 1;
- s = emalloc(len);
- snprintf(s, len, "%d of %d", gib_list_num(filelist, current_file) + 1, gib_list_length(filelist));
- /* This should somehow be right-aligned */
+
+ if (s) {
gib_imlib_text_draw(im, fn, NULL, 2, th + 1, s, IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
gib_imlib_text_draw(im, fn, NULL, 1, th, s, IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
free(s);
@@ -621,81 +1152,188 @@ void feh_draw_filename(winwidget w)
return;
}
-void feh_draw_info(winwidget w)
+#ifdef HAVE_LIBEXIF
+void feh_draw_exif(winwidget w)
{
static Imlib_Font fn = NULL;
- int tw = 0, th = 0;
+ int width = 0, height = 0, line_width = 0, line_height = 0;
Imlib_Image im = NULL;
- static DATA8 atab[256];
- int no_lines = 0;
- char *info_cmd;
- char info_buf[256];
- FILE *info_pipe;
-
- if ((!w->file) || (!FEH_FILE(w->file->data))
- || (!FEH_FILE(w->file->data)->filename))
+ int no_lines = 0, i;
+ int pos = 0;
+ int pos2 = 0;
+ char info_line[256];
+ char *info_buf[128];
+ char buffer[EXIF_MAX_DATA];
+
+ if ( (!w->file) || (!FEH_FILE(w->file->data))
+ || (!FEH_FILE(w->file->data)->filename) )
+ {
return;
+ }
- if (opt.font)
- fn = gib_imlib_load_font(opt.font);
- if (!fn) {
- if (w->full_screen)
- fn = gib_imlib_load_font(DEFAULT_FONT_BIG);
- else
- fn = gib_imlib_load_font(DEFAULT_FONT);
- }
+ buffer[0] = '\0';
+ exif_get_info(FEH_FILE(w->file->data)->ed, buffer, EXIF_MAX_DATA);
- if (!fn) {
- weprintf("Couldn't load font for filename printing");
- return;
+ fn = feh_load_font(w);
+
+ if (buffer[0] == '\0')
+ {
+ snprintf(buffer, EXIF_MAX_DATA, "%s", "Failed to run exif command");
+ gib_imlib_get_text_size(fn, buffer, NULL, &width, &height, IMLIB_TEXT_TO_RIGHT);
+ info_buf[no_lines] = estrdup(buffer);
+ no_lines++;
}
+ else
+ {
+
+ while ( (no_lines < 128) && (pos < EXIF_MAX_DATA) )
+ {
+ /* max 128 lines */
+ pos2 = 0;
+ while ( pos2 < 255 ) /* max 255 chars + 1 null byte per line */
+ {
+ if ( (buffer[pos] != '\n')
+ && (buffer[pos] != '\0') )
+ {
+ info_line[pos2] = buffer[pos];
+ }
+ else if ( buffer[pos] == '\0' )
+ {
+ pos = EXIF_MAX_DATA; /* all data seen */
+ break;
+ }
+ else
+ {
+ pos++; /* line finished, continue with next line*/
+ break;
+ }
- info_cmd = feh_printf(opt.info_cmd, FEH_FILE(w->file->data));
+ pos++;
+ pos2++;
+ }
+ info_line[pos2] = '\0';
- memset(atab, 0, sizeof(atab));
+ gib_imlib_get_text_size(fn, info_line, NULL, &line_width,
+ &line_height, IMLIB_TEXT_TO_RIGHT);
- gib_imlib_get_text_size(fn, "w", NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT);
+ if (line_height > height)
+ height = line_height;
+ if (line_width > width)
+ width = line_width;
+ info_buf[no_lines] = estrdup(info_line);
- info_pipe = popen(info_cmd, "r");
+ no_lines++;
+ }
+ }
+
+ if (no_lines == 0)
+ return;
- im = imlib_create_image(290 * tw, 20 * th);
+ height *= no_lines;
+ width += 4;
+
+ im = imlib_create_image(width, height);
if (!im)
+ {
eprintf("Couldn't create image. Out of memory?");
+ }
- gib_imlib_image_set_has_alpha(im, 1);
- gib_imlib_apply_color_modifier_to_rectangle(im, 0, 0, 290 * tw, 20 * th, NULL, NULL, NULL, atab);
- gib_imlib_image_fill_rectangle(im, 0, 0, 290 * tw, 20 * th, 0, 0, 0, 0);
+ feh_imlib_image_fill_text_bg(im, width, height);
+
+ for (i = 0; i < no_lines; i++)
+ {
+ gib_imlib_text_draw(im, fn, NULL, 2, (i * line_height) + 2,
+ 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]);
+
+ }
+
+ gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0, w->h - height, 1, 1, 0);
+
+ gib_imlib_free_image_and_decache(im);
+ return;
+
+}
+#endif
+
+void feh_draw_info(winwidget w)
+{
+ static Imlib_Font fn = NULL;
+ int width = 0, height = 0, line_width = 0, line_height = 0;
+ Imlib_Image im = NULL;
+ int no_lines = 0, i;
+ char *info_cmd;
+ char info_line[256];
+ char *info_buf[128];
+ FILE *info_pipe;
+
+ if ((!w->file) || (!FEH_FILE(w->file->data))
+ || (!FEH_FILE(w->file->data)->filename))
+ return;
+
+ fn = feh_load_font(w);
+
+ info_cmd = feh_printf(opt.info_cmd, FEH_FILE(w->file->data), w);
+
+ info_pipe = popen(info_cmd, "r");
if (!info_pipe) {
- gib_imlib_text_draw(im, fn, NULL, 2, 2,
- "Error runnig info command", IMLIB_TEXT_TO_RIGHT,
- 255, 0, 0, 255);
- gib_imlib_get_text_size(fn, "Error running info command", NULL, &tw, &th,
- IMLIB_TEXT_TO_RIGHT);
+ info_buf[0] = estrdup("Failed to run info command");
+ gib_imlib_get_text_size(fn, info_buf[0], NULL, &width, &height, IMLIB_TEXT_TO_RIGHT);
no_lines = 1;
}
else {
- while ((no_lines < 20) && fgets(info_buf, 256, info_pipe)) {
- info_buf[strlen(info_buf)-1] = '\0';
- gib_imlib_text_draw(im, fn, NULL, 2, (no_lines*th)+2, info_buf,
- IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
- gib_imlib_text_draw(im, fn, NULL, 1, (no_lines*th)+1, info_buf,
- IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
+ while ((no_lines < 128) && fgets(info_line, 256, info_pipe)) {
+ if (info_line[strlen(info_line)-1] == '\n')
+ info_line[strlen(info_line)-1] = '\0';
+
+ gib_imlib_get_text_size(fn, info_line, NULL, &line_width,
+ &line_height, IMLIB_TEXT_TO_RIGHT);
+
+ if (line_height > height)
+ height = line_height;
+ if (line_width > width)
+ width = line_width;
+
+ info_buf[no_lines] = estrdup(info_line);
+
no_lines++;
}
pclose(info_pipe);
}
+ if (no_lines == 0)
+ return;
+
+ height *= no_lines;
+ width += 4;
+
+ im = imlib_create_image(width, height);
+ if (!im)
+ eprintf("Couldn't create image. Out of memory?");
+
+ feh_imlib_image_fill_text_bg(im, width, height);
+
+ for (i = 0; i < no_lines; i++) {
+ gib_imlib_text_draw(im, fn, NULL, 2, (i * line_height) + 2,
+ 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]);
+ }
- gib_imlib_render_image_on_drawable(w->bg_pmap, im, 2,
- w->h - (th * no_lines) - 2, 1, 1, 0);
+ gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0,
+ w->h - height, 1, 1, 0);
gib_imlib_free_image_and_decache(im);
return;
}
-char *build_caption_filename(feh_file * file)
+char *build_caption_filename(feh_file * file, short create_dir)
{
char *caption_filename;
char *s, *dir, *caption_dir;
@@ -714,6 +1352,8 @@ char *build_caption_filename(feh_file * file)
D(("dir %s, cp %s, cdir %s\n", dir, opt.caption_path, caption_dir))
if (stat(caption_dir, &cdir_stat) == -1) {
+ if (!create_dir)
+ return NULL;
if (mkdir(caption_dir, 0755) == -1)
eprintf("Failed to create caption directory %s:", caption_dir);
} else if (!S_ISDIR(cdir_stat.st_mode))
@@ -733,7 +1373,6 @@ void feh_draw_caption(winwidget w)
int tw = 0, th = 0, ww, hh;
int x, y;
Imlib_Image im = NULL;
- static DATA8 atab[256];
char *p;
gib_list *lines, *l;
static gib_style *caption_style = NULL;
@@ -749,9 +1388,12 @@ void feh_draw_caption(winwidget w)
if (!file->caption) {
char *caption_filename;
- caption_filename = build_caption_filename(file);
- /* read caption from file */
- file->caption = ereadfile(caption_filename);
+ caption_filename = build_caption_filename(file, 0);
+ if (caption_filename)
+ /* read caption from file */
+ file->caption = ereadfile(caption_filename);
+ else
+ file->caption = estrdup("");
free(caption_filename);
}
@@ -776,22 +1418,7 @@ void feh_draw_caption(winwidget w)
caption_style->bits = gib_list_add_front(caption_style->bits,
gib_style_bit_new(1, 1, 0, 0, 0, 255));
- if (opt.font)
- fn = gib_imlib_load_font(opt.font);
-
- if (!fn) {
- if (w->full_screen)
- fn = gib_imlib_load_font(DEFAULT_FONT_BIG);
- else
- fn = gib_imlib_load_font(DEFAULT_FONT);
- }
-
- if (!fn) {
- weprintf("Couldn't load font for caption printing");
- return;
- }
-
- memset(atab, 0, sizeof(atab));
+ fn = feh_load_font(w);
if (*(file->caption) == '\0') {
p = estrdup("Caption entry mode - Hit ESC to cancel");
@@ -826,12 +1453,9 @@ void feh_draw_caption(winwidget w)
if (!im)
eprintf("Couldn't create image. Out of memory?");
- gib_imlib_image_set_has_alpha(im, 1);
- gib_imlib_apply_color_modifier_to_rectangle(im, 0, 0, tw, th, NULL, NULL, NULL, atab);
- gib_imlib_image_fill_rectangle(im, 0, 0, tw, th, 0, 0, 0, 0);
+ feh_imlib_image_fill_text_bg(im, tw, th);
l = lines;
- x = 0;
y = 0;
while (l) {
p = (char *) l->data;
@@ -867,6 +1491,13 @@ void feh_display_status(char stat)
D(("filelist %p, filelist->next %p\n", filelist, filelist->next));
+ if (!stat) {
+ putc('\n', stderr);
+ init_len = 0;
+ i = 0;
+ return;
+ }
+
if (!init_len)
init_len = gib_list_length(filelist);
@@ -874,49 +1505,106 @@ void feh_display_status(char stat)
if (reset_output) {
/* There's just been an error message. Unfortunate ;) */
for (j = 0; j < (((i % 50) + ((i % 50) / 10)) + 7); j++)
- fprintf(stdout, " ");
+ putc(' ', stderr);
}
if (!(i % 50)) {
int len = gib_list_length(filelist);
- fprintf(stdout, " %5d/%d (%d)\n[%3d%%] ",
+ fprintf(stderr, " %5d/%d (%d)\n[%3d%%] ",
i, init_len, len, ((int) ((float) i / init_len * 100)));
} else if ((!(i % 10)) && (!reset_output))
- fprintf(stdout, " ");
+ putc(' ', stderr);
reset_output = 0;
} else
- fprintf(stdout, "[ 0%%] ");
+ fputs("[ 0%] ", stderr);
- fprintf(stdout, "%c", stat);
- fflush(stdout);
+ fprintf(stderr, "%c", stat);
+ fflush(stderr);
i++;
return;
}
-void feh_edit_inplace_orient(winwidget w, int orientation)
+void feh_edit_inplace(winwidget w, int op)
{
- int ret;
- Imlib_Image old;
+ int tmp;
+ Imlib_Image old = NULL;
+ Imlib_Load_Error err = IMLIB_LOAD_ERROR_NONE;
if (!w->file || !w->file->data || !FEH_FILE(w->file->data)->filename)
return;
- if (!strcmp(gib_imlib_image_format(w->im), "jpeg")) {
- feh_edit_inplace_lossless_rotate(w, orientation);
+ if (!opt.edit) {
+ imlib_context_set_image(w->im);
+ if (op == INPLACE_EDIT_FLIP)
+ imlib_image_flip_vertical();
+ else if (op == INPLACE_EDIT_MIRROR)
+ imlib_image_flip_horizontal();
+ else {
+ imlib_image_orientate(op);
+ if(op != 2) {
+ tmp = w->im_w;
+ w->im_w = w->im_h;
+ w->im_h = tmp;
+ }
+ if (FEH_FILE(w->file->data)->info) {
+ FEH_FILE(w->file->data)->info->width = w->im_w;
+ FEH_FILE(w->file->data)->info->height = w->im_h;
+ }
+ }
+ winwidget_render_image(w, 1, 0);
+ return;
+ }
+
+ // Imlib2 <= 1.5 returns "jpeg", Imlib2 >= 1.6 uses "jpg"
+ if ((!strcmp(gib_imlib_image_format(w->im), "jpeg")
+ || !strcmp(gib_imlib_image_format(w->im), "jpg"))
+ && !path_is_url(FEH_FILE(w->file->data)->filename)) {
+ feh_edit_inplace_lossless(w, op);
feh_reload_image(w, 1, 1);
return;
}
- ret = feh_load_image(&old, FEH_FILE(w->file->data));
- if (ret) {
- gib_imlib_image_orientate(old, orientation);
- gib_imlib_save_image(old, FEH_FILE(w->file->data)->filename);
+ old = imlib_load_image_with_error_return(FEH_FILE(w->file->data)->filename, &err);
+
+ if ((old != NULL) && (err == IMLIB_LOAD_ERROR_NONE)) {
+ imlib_context_set_image(old);
+ if (op == INPLACE_EDIT_FLIP)
+ imlib_image_flip_vertical();
+ else if (op == INPLACE_EDIT_MIRROR)
+ imlib_image_flip_horizontal();
+ else
+ imlib_image_orientate(op);
+ gib_imlib_save_image_with_error_return(old,
+ FEH_FILE(w->file->data)->filename, &err);
gib_imlib_free_image(old);
+ if (err)
+ feh_print_load_error(FEH_FILE(w->file->data)->filename,
+ w, err, LOAD_ERROR_IMLIB);
feh_reload_image(w, 1, 1);
} else {
- weprintf("failed to load image from disk to edit it in place\n");
+ /*
+ * Image was opened using curl/magick or has been deleted after
+ * opening it
+ */
+ imlib_context_set_image(w->im);
+ if (op == INPLACE_EDIT_FLIP)
+ imlib_image_flip_vertical();
+ else if (op == INPLACE_EDIT_MIRROR)
+ imlib_image_flip_horizontal();
+ else {
+ imlib_image_orientate(op);
+ tmp = w->im_w;
+ w->im_w = w->im_h;
+ w->im_h = tmp;
+ if (FEH_FILE(w->file->data)->info) {
+ FEH_FILE(w->file->data)->info->width = w->im_w;
+ FEH_FILE(w->file->data)->info->height = w->im_h;
+ }
+ }
+ im_weprintf(w, "unable to edit in place. Changes have not been saved.");
+ winwidget_render_image(w, 1, 0);
}
return;
@@ -1007,7 +1695,6 @@ gib_list *feh_wrap_string(char *text, int wrap_width, Imlib_Font fn, gib_style *
list = gib_list_add_end(list, estrdup(line));
free(line);
line = NULL;
- line_width = 0;
}
gib_list_free_and_data(words);
}
@@ -1020,38 +1707,79 @@ gib_list *feh_wrap_string(char *text, int wrap_width, Imlib_Font fn, gib_style *
return lines;
}
-void feh_edit_inplace_lossless_rotate(winwidget w, int orientation)
+void feh_edit_inplace_lossless(winwidget w, int op)
{
char *filename = FEH_FILE(w->file->data)->filename;
- char rotate_str[4];
int len = strlen(filename) + 1;
char *file_str = emalloc(len);
- int rotatearg = 90 * orientation;
int pid, status;
+ int devnull = -1;
+ char op_name[] = "rotate"; /* message */
+ char op_op[] = "-rotate"; /* jpegtran option */
+ char op_value[] = "horizontal"; /* jpegtran option's value */
+
+ if (op == INPLACE_EDIT_FLIP) {
+ sprintf(op_name, "flip");
+ sprintf(op_op, "-flip");
+ sprintf(op_value, "vertical");
+ } else if (op == INPLACE_EDIT_MIRROR) {
+ sprintf(op_name, "mirror");
+ sprintf(op_op, "-flip");
+ } else
+ snprintf(op_value, 4, "%d", 90 * op);
- snprintf(rotate_str, 4, "%d", rotatearg);
snprintf(file_str, len, "%s", filename);
if ((pid = fork()) < 0) {
- weprintf("lossless rotate: fork failed:");
+ im_weprintf(w, "lossless %s: fork failed:", op_name);
+ free(file_str);
return;
- } else if (pid == 0) {
+ }
+ else if (pid == 0) {
- execlp("jpegtran", "jpegtran", "-copy", "all", "-rotate",
- rotate_str, "-outfile", file_str, file_str, NULL);
+ execlp("jpegtran", "jpegtran", "-copy", "all", op_op, op_value,
+ "-outfile", file_str, file_str, NULL);
- eprintf("lossless rotate: Is 'jpegtran' installed? Failed to exec:");
- } else {
+ weprintf("lossless %s: Is 'jpegtran' installed? Failed to exec:", op_name);
+ _exit(1);
+ }
+ else {
waitpid(pid, &status, 0);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- weprintf("lossless rotate: Got exitcode %d from jpegtran."
- " Commandline was:\n"
- "jpegtran -copy all -rotate %d -outfile %s %s\n",
- status >> 8, rotate_str, file_str, file_str);
+ im_weprintf(w,
+ "lossless %s: Got exitcode %d from jpegtran."
+ " Commandline was: "
+ "jpegtran -copy all %s %s -outfile %s %s",
+ op_name, status >> 8, op_op, op_value, file_str, file_str);
+ free(file_str);
return;
}
}
+ if ((pid = fork()) < 0) {
+ im_weprintf(w, "lossless %s: fork failed while updating EXIF tags:", op_name);
+ }
+ else if (pid == 0) {
+
+ /* discard normal output */
+ devnull = open("/dev/null", O_WRONLY);
+ dup2(devnull, 1);
+
+ execlp("jpegexiforient", "jpegexiforient", "-1", file_str, NULL);
+ weprintf("lossless %s: Failed to exec jpegexiforient:", op_name);
+ _exit(1);
+ }
+ else {
+ waitpid(pid, &status, 0);
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ im_weprintf(w,
+ "lossless %s: Failed to update EXIF orientation tag:"
+ " jpegexiforient returned %d",
+ op_name, status >> 8);
+ }
+ }
+ free(file_str);
}
void feh_draw_actions(winwidget w)
@@ -1062,11 +1790,10 @@ void feh_draw_actions(winwidget w)
int max_tw = 0;
int line_th = 0;
Imlib_Image im = NULL;
- static DATA8 atab[256];
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
@@ -1085,22 +1812,7 @@ void feh_draw_actions(winwidget w)
|| (!FEH_FILE(w->file->data)->filename))
return;
- if (opt.font)
- fn = gib_imlib_load_font(opt.font);
-
- if (!fn) {
- if (w->full_screen)
- fn = gib_imlib_load_font(DEFAULT_FONT_BIG);
- else
- fn = gib_imlib_load_font(DEFAULT_FONT);
- }
-
- if (!fn) {
- weprintf("Couldn't load font for actions printing");
- return;
- }
-
- memset(atab, 0, sizeof(atab));
+ fn = feh_load_font(w);
gib_imlib_get_text_size(fn, "defined actions:", NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT);
/* Check for the widest line */
@@ -1108,9 +1820,9 @@ void feh_draw_actions(winwidget w)
for (i = 0; i < 10; i++) {
if (opt.actions[i]) {
- line = emalloc(strlen(opt.actions[i]) + 5);
+ line = emalloc(strlen(opt.action_titles[i]) + 5);
strcpy(line, "0: ");
- line = strcat(line, opt.actions[i]);
+ line = strcat(line, opt.action_titles[i]);
gib_imlib_get_text_size(fn, line, NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT);
free(line);
if (tw > max_tw)
@@ -1134,21 +1846,19 @@ void feh_draw_actions(winwidget w)
if (!im)
eprintf("Couldn't create image. Out of memory?");
- gib_imlib_image_set_has_alpha(im, 1);
- gib_imlib_apply_color_modifier_to_rectangle(im, 0, 0, tw, th, NULL, NULL, NULL, atab);
- gib_imlib_image_fill_rectangle(im, 0, 0, tw, th, 0, 0, 0, 0);
+ feh_imlib_image_fill_text_bg(im, tw, th);
gib_imlib_text_draw(im, fn, NULL, 2, 2, "defined actions:", IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
gib_imlib_text_draw(im, fn, NULL, 1, 1, "defined actions:", IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
for (i = 0; i < 10; i++) {
- if (opt.actions[i]) {
+ if (opt.action_titles[i]) {
cur_action++;
- line = emalloc(strlen(opt.actions[i]) + 5);
+ line = emalloc(strlen(opt.action_titles[i]) + 5);
sprintf(index, "%d", i);
strcpy(line, index);
strcat(line, ": ");
- strcat(line, opt.actions[i]);
+ strcat(line, opt.action_titles[i]);
gib_imlib_text_draw(im, fn, NULL, 2,
(cur_action * line_th) + 2, line,