aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarry Jeffery <harry@exec64.co.uk>2016-01-15 16:16:10 +0000
committerHarry Jeffery <harry@exec64.co.uk>2016-01-15 16:16:10 +0000
commit58fe28b650dfcdd3efe78fb0559cab5b7e811bb5 (patch)
tree73d290727b720e39b36c8165e1ea9202374726f3
parentefa33ab844a09f05907a5de2d66ed7331c4f8ec9 (diff)
parent1ae8a7fe6030771352e347e7564639ea181802ba (diff)
downloadimv-58fe28b650dfcdd3efe78fb0559cab5b7e811bb5.tar.gz
Merge pull request #76 from czarkoff/loader
Loading image data from standard input
-rw-r--r--README.md13
-rw-r--r--doc/imv.124
-rw-r--r--src/loader.c116
-rw-r--r--src/loader.h6
-rw-r--r--src/main.c70
-rw-r--r--src/navigator.c3
-rw-r--r--src/util.c29
-rw-r--r--src/util.h3
8 files changed, 192 insertions, 72 deletions
diff --git a/README.md b/README.md
index 73e3082..f48598b 100644
--- a/README.md
+++ b/README.md
@@ -20,13 +20,16 @@ Usage
imv image1.png another_image.jpeg yet_another.TIFF
### Opening images via stdin
- find . "*.png" | imv -
+ find . "*.png" | imv
### Open an image fullscreen
imv -f image.jpeg
### Viewing images in a random order
- find . "*.png" | shuf | imv -
+ find . "*.png" | shuf | imv
+
+### Viewing images from stdin
+ curl http://somesi.te/img.png | imv -
### Image picker
imv can be used to select images in a pipeline by using the `p` hotkey to print
@@ -35,13 +38,13 @@ to list the remaining paths on exit for a "open set of images, close unwanted
ones with `x`, then quit imv to pass the remaining images through" workflow.
#### Picking a wallpaper
- custom-set-wallpaper-script "$(find ./wallpaper -type f -name '*.jpg' | imv - | tail -n1)"
+ custom-set-wallpaper-script "$(find ./wallpaper -type f -name '*.jpg' | imv | tail -n1)"
#### Deleting unwanted images
- find -type f -name '*.jpg' | imv - | xargs rm -v
+ find -type f -name '*.jpg' | imv | xargs rm -v
#### Choosing pictures to email
- find ./holiday_pics -type f -name '*.jpg' | imv - | xargs cp -t ~/outbox
+ find ./holiday_pics -type f -name '*.jpg' | imv | xargs cp -t ~/outbox
### Slideshow
diff --git a/doc/imv.1 b/doc/imv.1
index cbd5c68..d883611 100644
--- a/doc/imv.1
+++ b/doc/imv.1
@@ -21,12 +21,8 @@ It supports a wide variety of image file formats, including animated gif files.
.Nm
reloads the current image if it detects changes to the file.
.Pp
-When run with argument
-.Sq - ,
.Nm
-reads the list of images from standard input.
-.Pp
-The options are as follows:
+accepts following flags:
.Bl -tag -width Ds
.It Fl a
Default to showing images at actual size.
@@ -66,6 +62,17 @@ Setting this to zero disables slideshow mode, which is the default.
.It Fl u
Use nearest neighbour resampling. Recommended for pixel art.
.El
+.Ss Reading from standard input
+When run with argument
+.Sq - ,
+.Nm
+reads image from standard input.
+Argument
+.Sq -
+may occur among other arguments, but only once.
+.Pp
+If no arguments supplied, reads list of files from standard input.
+.Pp
.Sh CONTROLS
.Bl -tag -width Ds
.It Aq Cm left mouse button
@@ -111,6 +118,13 @@ Increase slideshow delay by one second
.It Cm T
Decrease slideshow delay by one second.
When delay is zero, slideshow mode is disabled.
+.Sh EXAMPLES
+Load all files from directory
+.Pa dir :
+.Pp
+.Dl $ ls dir | imv
+or
+.Dl $ ls dir | xargs imv
.Sh LEGAL
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
diff --git a/src/loader.c b/src/loader.c
index 883e857..1250289 100644
--- a/src/loader.c
+++ b/src/loader.c
@@ -17,10 +17,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "loader.h"
#include "texture.h"
+#include <limits.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
+static void block_usr1_signal(void);
+static int is_thread_cancelled(void);
+static void *bg_new_img(void *data);
+static void *bg_next_frame(void *data);
+static void error_occurred(struct imv_loader *ldr);
+
static void block_usr1_signal(void)
{
sigset_t sigmask;
@@ -61,10 +68,8 @@ void imv_destroy_loader(struct imv_loader *ldr)
}
}
-static void *imv_loader_bg_new_img(void *data);
-static void *imv_loader_bg_next_frame(void *data);
-
-void imv_loader_load_path(struct imv_loader *ldr, const char *path)
+void imv_loader_load(struct imv_loader *ldr, const char *path,
+ const void *buffer, const size_t buffer_size)
{
/* cancel existing thread if already running */
if(ldr->bg_thread) {
@@ -77,7 +82,13 @@ void imv_loader_load_path(struct imv_loader *ldr, const char *path)
free(ldr->path);
}
ldr->path = strdup(path);
- pthread_create(&ldr->bg_thread, NULL, &imv_loader_bg_new_img, ldr);
+ if (strncmp(path, "-", 2) == 0) {
+ ldr->buffer = (BYTE *)buffer;
+ ldr->buffer_size = buffer_size;
+ } else if (ldr->fi_buffer != NULL) {
+ FreeImage_CloseMemory(ldr->fi_buffer);
+ }
+ pthread_create(&ldr->bg_thread, NULL, &bg_new_img, ldr);
pthread_mutex_unlock(&ldr->lock);
}
@@ -120,7 +131,7 @@ void imv_loader_load_next_frame(struct imv_loader *ldr)
}
/* kick off a new thread */
- pthread_create(&ldr->bg_thread, NULL, &imv_loader_bg_next_frame, ldr);
+ pthread_create(&ldr->bg_thread, NULL, &bg_next_frame, ldr);
}
void imv_loader_time_passed(struct imv_loader *ldr, double dt)
@@ -140,33 +151,38 @@ void imv_loader_time_passed(struct imv_loader *ldr, double dt)
}
}
-static void imv_loader_error_occurred(struct imv_loader *ldr)
-{
- pthread_mutex_lock(&ldr->lock);
- if(ldr->out_err) {
- free(ldr->out_err);
- }
- ldr->out_err = strdup(ldr->path);
- pthread_mutex_unlock(&ldr->lock);
-}
-
-static void *imv_loader_bg_new_img(void *data)
+static void *bg_new_img(void *data)
{
/* so we can poll for it */
block_usr1_signal();
struct imv_loader *ldr = data;
+ char path[PATH_MAX] = "-";
pthread_mutex_lock(&ldr->lock);
- char *path = strdup(ldr->path);
+ int from_stdin = !strncmp(path, ldr->path, 2);
+ if(!from_stdin) {
+ (void)snprintf(path, PATH_MAX, "%s", ldr->path);
+ }
pthread_mutex_unlock(&ldr->lock);
- FREE_IMAGE_FORMAT fmt = FreeImage_GetFileType(path, 0);
-
+ FREE_IMAGE_FORMAT fmt;
+ if (from_stdin) {
+ pthread_mutex_lock(&ldr->lock);
+ ldr->fi_buffer = FreeImage_OpenMemory(ldr->buffer, ldr->buffer_size);
+ fmt = FreeImage_GetFileTypeFromMemory(ldr->fi_buffer, 0);
+ pthread_mutex_unlock(&ldr->lock);
+ } else {
+ fmt = FreeImage_GetFileType(path, 0);
+ }
if(fmt == FIF_UNKNOWN) {
- imv_loader_error_occurred(ldr);
- free(path);
- return 0;
+ if (from_stdin) {
+ pthread_mutex_lock(&ldr->lock);
+ FreeImage_CloseMemory(ldr->fi_buffer);
+ pthread_mutex_unlock(&ldr->lock);
+ }
+ error_occurred(ldr);
+ return NULL;
}
int num_frames = 1;
@@ -176,15 +192,21 @@ static void *imv_loader_bg_new_img(void *data)
int raw_frame_time = 100; /* default to 100 */
if(fmt == FIF_GIF) {
- mbmp = FreeImage_OpenMultiBitmap(FIF_GIF, path,
+ if(from_stdin) {
+ pthread_mutex_lock(&ldr->lock);
+ mbmp = FreeImage_LoadMultiBitmapFromMemory(FIF_GIF, ldr->fi_buffer,
+ GIF_LOAD256);
+ pthread_mutex_unlock(&ldr->lock);
+ } else {
+ mbmp = FreeImage_OpenMultiBitmap(FIF_GIF, path,
/* don't create file */ 0,
/* read only */ 1,
/* keep in memory */ 1,
/* flags */ GIF_LOAD256);
- free(path);
+ }
if(!mbmp) {
- imv_loader_error_occurred(ldr);
- return 0;
+ error_occurred(ldr);
+ return NULL;
}
num_frames = FreeImage_GetPageCount(mbmp);
@@ -205,17 +227,27 @@ static void *imv_loader_bg_new_img(void *data)
} else {
/* Future TODO: If we load image line-by-line we could stop loading large
* ones before wasting much more time/memory on them. */
- FIBITMAP *image = FreeImage_Load(fmt, path, 0);
- free(path);
+ FIBITMAP *image;
+ if(from_stdin) {
+ pthread_mutex_lock(&ldr->lock);
+ image = FreeImage_LoadFromMemory(fmt, ldr->fi_buffer, 0);
+ pthread_mutex_unlock(&ldr->lock);
+ } else {
+ image = FreeImage_Load(fmt, path, 0);
+ }
if(!image) {
- imv_loader_error_occurred(ldr);
- return 0;
+ error_occurred(ldr);
+ pthread_mutex_lock(&ldr->lock);
+ FreeImage_CloseMemory(ldr->fi_buffer);
+ ldr->fi_buffer = NULL;
+ pthread_mutex_unlock(&ldr->lock);
+ return NULL;
}
/* Check for cancellation before we convert pixel format */
if(is_thread_cancelled()) {
FreeImage_Unload(image);
- return 0;
+ return NULL;
}
width = FreeImage_GetWidth(bmp);
@@ -236,7 +268,7 @@ static void *imv_loader_bg_new_img(void *data)
FreeImage_Unload(bmp);
}
pthread_mutex_unlock(&ldr->lock);
- return 0;
+ return NULL;
}
if(ldr->mbmp) {
@@ -262,10 +294,10 @@ static void *imv_loader_bg_new_img(void *data)
ldr->frame_time = (double)raw_frame_time * 0.0001;
pthread_mutex_unlock(&ldr->lock);
- return 0;
+ return NULL;
}
-static void *imv_loader_bg_next_frame(void *data)
+static void *bg_next_frame(void *data)
{
struct imv_loader *ldr = data;
@@ -273,7 +305,7 @@ static void *imv_loader_bg_next_frame(void *data)
int num_frames = ldr->num_frames;
if(num_frames < 2) {
pthread_mutex_unlock(&ldr->lock);
- return 0;
+ return NULL;
}
FITAG *tag = NULL;
@@ -371,5 +403,15 @@ static void *imv_loader_bg_next_frame(void *data)
ldr->out_is_new_image = 0;
pthread_mutex_unlock(&ldr->lock);
- return 0;
+ return NULL;
+}
+
+static void error_occurred(struct imv_loader *ldr)
+{
+ pthread_mutex_lock(&ldr->lock);
+ if(ldr->out_err) {
+ free(ldr->out_err);
+ }
+ ldr->out_err = strdup(ldr->path);
+ pthread_mutex_unlock(&ldr->lock);
}
diff --git a/src/loader.h b/src/loader.h
index 65c8689..6e20349 100644
--- a/src/loader.h
+++ b/src/loader.h
@@ -28,6 +28,9 @@ struct imv_loader {
pthread_mutex_t lock;
pthread_t bg_thread;
char *path;
+ BYTE *buffer;
+ size_t buffer_size;
+ FIMEMORY *fi_buffer;
FIBITMAP *out_bmp;
int out_is_new_image;
char *out_err;
@@ -48,7 +51,8 @@ void imv_init_loader(struct imv_loader *img);
void imv_destroy_loader(struct imv_loader *img);
/* Asynchronously load the given file */
-void imv_loader_load_path(struct imv_loader *ldr, const char *path);
+void imv_loader_load(struct imv_loader *ldr, const char *path,
+ const void *buffer, const size_t buffer_size);
/* Returns 1 if image data is available. 0 if not. Caller is responsible for
* cleaning up the data returned. Each image is only returned once.
diff --git a/src/main.c b/src/main.c
index 25ffce1..7ba55ec 100644
--- a/src/main.c
+++ b/src/main.c
@@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <FreeImage.h>
#include <getopt.h>
#include <ctype.h>
+#include <errno.h>
#include "loader.h"
#include "texture.h"
@@ -33,7 +34,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
struct {
int fullscreen;
- int sin;
+ int stdin_list;
int recursive;
int actual;
int nearest_neighbour;
@@ -49,7 +50,7 @@ struct {
const char *font;
} g_options = {
.fullscreen = 0,
- .sin = 0,
+ .stdin_list = 0,
.recursive = 0,
.actual = 0,
.nearest_neighbour = 0,
@@ -72,7 +73,7 @@ static void print_usage(const char* name)
"Usage: %s [-rfaudlh] [-n <NUM|PATH>] [-b BG] [-e FONT:SIZE] [-t SECONDS] [-] [images...]\n"
"\n"
"Flags:\n"
- " -: Read paths from stdin. One path per line.\n"
+ " -: Read image from stdin. One path per line.\n"
" -r: Recursively search input paths.\n"
" -f: Start in fullscreen mode\n"
" -a: Default to images' actual size\n"
@@ -137,8 +138,8 @@ static void parse_args(int argc, char** argv)
switch(o) {
case 'f': g_options.fullscreen = 1; break;
case 'i':
- g_options.sin = 1;
- fprintf(stderr, "Warning: '-i' is deprecated. Just use '-' instead.\n");
+ g_options.stdin_list = 1;
+ fprintf(stderr, "Warning: '-i' is deprecated. No flag is needed.\n");
break;
case 'r': g_options.recursive = 1; break;
case 'a': g_options.actual = 1; break;
@@ -193,31 +194,23 @@ static void parse_args(int argc, char** argv)
int main(int argc, char** argv)
{
- if(argc < 2) {
- print_usage(argv[0]);
- exit(1);
- }
-
struct imv_navigator nav;
imv_navigator_init(&nav);
/* parse any command line options given */
parse_args(argc, argv);
- /* handle any image paths given as arguments */
- for(int i = optind; i < argc; ++i) {
- /* special case: '-' is actually an option */
- if(!strcmp("-",argv[i])) {
- g_options.sin = 1;
- continue;
- }
- /* add the given path to the list to load */
- imv_navigator_add(&nav, argv[i], g_options.recursive);
+ argc -= optind;
+ argv += optind;
+
+ /* if no names are given, expect them on stdin */
+ if(argc == 0) {
+ g_options.stdin_list = 1;
}
/* if the user asked us to load paths from stdin, now is the time */
- if(g_options.sin) {
- char buf[512];
+ if(g_options.stdin_list) {
+ char buf[PATH_MAX];
while(fgets(buf, sizeof(buf), stdin)) {
size_t len = strlen(buf);
if(buf[len-1] == '\n') {
@@ -229,6 +222,30 @@ int main(int argc, char** argv)
}
}
+ void *stdin_buffer = NULL;
+ size_t stdin_buffer_size = 0;
+ int stdin_error = 0;
+
+ /* handle any image paths given as arguments */
+ for(int i = 0; i < argc; ++i) {
+ /* special case: '-' is actually an option */
+ if(!strcmp("-",argv[i])) {
+ if (stdin_buffer) {
+ fprintf(stderr, "Can't read from stdin twice\n");
+ exit(1);
+ }
+ stdin_buffer_size = read_from_stdin(&stdin_buffer);
+ if (stdin_buffer_size == 0) {
+ perror(NULL);
+ continue; /* we can't recover from the freed buffer, just ignore it */
+ }
+ stdin_error = errno;
+ errno = 0; /* clear errno */
+ }
+ /* add the given path to the list to load */
+ imv_navigator_add(&nav, argv[i], g_options.recursive);
+ }
+
/* if we weren't given any paths we have nothing to view. exit */
if(!imv_navigator_selection(&nav)) {
fprintf(stderr, "No input files. Exiting.\n");
@@ -435,6 +452,15 @@ int main(int argc, char** argv)
char *err_path = imv_loader_get_error(&ldr);
if(err_path) {
imv_navigator_remove(&nav, err_path);
+ if (strncmp(err_path, "-", 2) == 0) {
+ free(stdin_buffer);
+ stdin_buffer_size = 0;
+ if (stdin_error != 0) {
+ errno = stdin_error;
+ perror("Failed to load image from standard input");
+ errno = 0;
+ }
+ }
free(err_path);
}
@@ -451,7 +477,7 @@ int main(int argc, char** argv)
nav.cur_path + 1, nav.num_paths, current_path);
imv_viewport_set_title(&view, title);
- imv_loader_load_path(&ldr, current_path);
+ imv_loader_load(&ldr, current_path, stdin_buffer, stdin_buffer_size);
view.playing = 1;
}
diff --git a/src/navigator.c b/src/navigator.c
index fb2a3cd..dd5124d 100644
--- a/src/navigator.c
+++ b/src/navigator.c
@@ -17,6 +17,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "navigator.h"
+#include <limits.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdlib.h>
@@ -84,7 +85,7 @@ static void add_item(struct imv_navigator *nav, const char *path,
void imv_navigator_add(struct imv_navigator *nav, const char *path,
int recursive)
{
- char path_buf[512];
+ char path_buf[PATH_MAX];
struct stat path_info;
stat(path, &path_info);
if(S_ISDIR(path_info.st_mode)) {
diff --git a/src/util.c b/src/util.c
index dac23d4..0a15238 100644
--- a/src/util.c
+++ b/src/util.c
@@ -16,9 +16,36 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "util.h"
-
+#include <unistd.h>
+#include <stddef.h>
+#include <errno.h>
#include <fontconfig/fontconfig.h>
+size_t read_from_stdin(void **buffer) {
+ size_t len = 0;
+ ssize_t r;
+ size_t step = 1024; /* Arbitrary value of 1 KiB */
+ void *p;
+
+ errno = 0; /* clear errno */
+
+ for(*buffer = NULL; (*buffer = realloc((p = *buffer), len + step));
+ len += (size_t)r) {
+ if((r = read(STDIN_FILENO, (uint8_t *)*buffer + len, step)) <= 0) {
+ break;
+ }
+ }
+
+ /* realloc(3) leaves old buffer allocated in case of error */
+ if(*buffer == NULL && p != NULL) {
+ int save = errno;
+ free(p);
+ errno = save;
+ len = 0;
+ }
+ return len;
+}
+
TTF_Font *load_font(const char *font_spec)
{
int font_size;
diff --git a/src/util.h b/src/util.h
index 3625a6c..09d5554 100644
--- a/src/util.h
+++ b/src/util.h
@@ -21,6 +21,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
+/* Read binary data from stdin into buffer */
+size_t read_from_stdin(void **buffer);
+
/* Creates a new SDL_Texture* containing a chequeboard texture */
SDL_Texture *create_chequered(SDL_Renderer *renderer);