From f3298400e6704844aeb1d3e0951e84b4236d2302 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bert=20M=C3=BCnnich?= <be.muennich@gmail.com>
Date: Mon, 11 Feb 2013 23:05:26 +0100
Subject: [PATCH 1/4] Spawn and read from info script without blocking

---
 main.c   | 144 +++++++++++++++++++++++++++++++++++--------------------
 window.c |  59 ++++++++++++-----------
 window.h |  12 +++--
 3 files changed, 131 insertions(+), 84 deletions(-)

diff --git a/main.c b/main.c
index 9ff3c1e..f648dab 100644
--- a/main.c
+++ b/main.c
@@ -22,10 +22,14 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <fcntl.h>
 #include <unistd.h>
+#include <errno.h>
+#include <signal.h>
 #include <sys/select.h>
 #include <sys/stat.h>
 #include <sys/time.h>
+#include <sys/wait.h>
 #include <X11/keysym.h>
 
 #include "types.h"
@@ -38,8 +42,6 @@
 #include "config.h"
 
 enum {
-	BAR_L_LEN    = 512,
-	BAR_R_LEN    = 64,
 	FILENAME_CNT = 1024,
 	TITLE_LEN    = 256
 };
@@ -70,12 +72,11 @@ int prefix;
 bool resized = false;
 
 const char * const INFO_SCRIPT = ".sxiv/exec/image-info";
-char *info_script;
-
 struct {
-	char l[BAR_L_LEN];
-	char r[BAR_R_LEN];
-} bar;
+  char *script;
+  int fd;
+  unsigned int i, lastsep;
+} info;
 
 timeout_t timeouts[] = {
 	{ { 0, 0 }, false, redraw },
@@ -212,35 +213,66 @@ bool check_timeouts(struct timeval *t)
 	return tmin > 0;
 }
 
+void open_info(void)
+{
+	static pid_t pid;
+	int pfd[2];
+
+	win.bar.l[0] = '\0';
+
+	if (info.fd != -1) {
+		close(info.fd);
+		kill(pid, SIGTERM);
+		while (waitpid(-1, NULL, WNOHANG) > 0);
+		info.fd = -1;
+	}
+	if (info.script == NULL || pipe(pfd) < 0)
+		return;
+
+	pid = fork();
+	if (pid > 0) {
+		close(pfd[1]);
+		fcntl(pfd[0], F_SETFL, O_NONBLOCK);
+		info.fd = pfd[0];
+		info.i = info.lastsep = 0;
+	} else if (pid == 0) {
+		close(pfd[0]);
+		dup2(pfd[1], 1);
+		execl(info.script, info.script, files[fileidx].name, NULL);
+	}
+}
+
 void read_info(void)
 {
-	char cmd[4096];
-	FILE *outp;
-	int c, i = 0, n = sizeof(bar.l) - 1;
-	bool lastsep = false;
-	
-	if (info_script != NULL) {
-		snprintf(cmd, sizeof(cmd), "%s \"%s\"", info_script, files[fileidx].name);
-		outp = popen(cmd, "r");
-		if (outp == NULL)
+	ssize_t i, n;
+	char buf[BAR_L_LEN];
+
+	while (true) {
+		n = read(info.fd, buf, sizeof(buf));
+		if (n < 0 && errno == EAGAIN)
+			return;
+		else if (n == 0)
 			goto end;
-		while (i < n && (c = fgetc(outp)) != EOF) {
-			if (c == '\n') {
-				if (!lastsep) {
-					bar.l[i++] = ' ';
-					lastsep = true;
+		for (i = 0; i < n; i++) {
+			if (buf[i] == '\n') {
+				if (info.lastsep == 0) {
+					win.bar.l[info.i++] = ' ';
+					info.lastsep = 1;
 				}
 			} else {
-				bar.l[i++] = c;
-				lastsep = false;
+				win.bar.l[info.i++] = buf[i];
+				info.lastsep = 0;
 			}
+			if (info.i + 1 == sizeof(win.bar.l))
+				goto end;
 		}
-		pclose(outp);
 	}
 end:
-	if (lastsep)
-		i--;
-	bar.l[i] = '\0';
+	info.i -= info.lastsep;
+	win.bar.l[info.i] = '\0';
+	win_update_bar(&win);
+	info.fd = -1;
+	while (waitpid(-1, NULL, WNOHANG) > 0);
 }
 
 void load_image(int new)
@@ -261,7 +293,7 @@ void load_image(int new)
 	alternate = fileidx;
 	fileidx = new;
 
-	read_info();
+	open_info();
 
 	if (img.multi.cnt > 0 && img.multi.animate)
 		set_timeout(animate, img.multi.frames[img.multi.sel].delay, true);
@@ -271,9 +303,10 @@ void load_image(int new)
 
 void update_info(void)
 {
-	unsigned int i, fn, fw, n, len = sizeof(bar.r);
 	int sel;
-	char *t = bar.r, title[TITLE_LEN];
+	unsigned int i, fn, fw, n;
+	unsigned int llen = sizeof(win.bar.l), rlen = sizeof(win.bar.r);
+	char *lt = win.bar.l, *rt = win.bar.r, title[TITLE_LEN];
 	bool ow_info;
 
 	for (fw = 0, i = filecnt; i > 0; fw++, i /= 10);
@@ -283,39 +316,37 @@ void update_info(void)
 		win_set_title(&win, "sxiv");
 
 		if (tns.cnt == filecnt) {
-			n = snprintf(t, len, "%0*d/%d", fw, sel + 1, filecnt);
+			n = snprintf(rt, rlen, "%0*d/%d", fw, sel + 1, filecnt);
 			ow_info = true;
 		} else {
-			snprintf(bar.l, sizeof(bar.l), "Loading... %0*d/%d",
-			         fw, tns.cnt, filecnt);
-			bar.r[0] = '\0';
+			snprintf(lt, llen, "Loading... %0*d/%d", fw, tns.cnt, filecnt);
+			rt[0] = '\0';
 			ow_info = false;
 		}
 	} else {
 		snprintf(title, sizeof(title), "sxiv - %s", files[sel].name);
 		win_set_title(&win, title);
 
-		n = snprintf(t, len, "%3d%% ", (int) (img.zoom * 100.0));
+		n = snprintf(rt, rlen, "%3d%% ", (int) (img.zoom * 100.0));
 		if (img.multi.cnt > 0) {
 			for (fn = 0, i = img.multi.cnt; i > 0; fn++, i /= 10);
-			n += snprintf(t + n, len - n, "(%0*d/%d) ",
+			n += snprintf(rt + n, rlen - n, "(%0*d/%d) ",
 			              fn, img.multi.sel + 1, img.multi.cnt);
 		}
-		n += snprintf(t + n, len - n, "%0*d/%d", fw, sel + 1, filecnt);
-		ow_info = bar.l[0] == '\0';
+		n += snprintf(rt + n, rlen - n, "%0*d/%d", fw, sel + 1, filecnt);
+		ow_info = info.script == NULL;
 	}
 	if (ow_info) {
 		fn = strlen(files[sel].name);
-		if (fn < sizeof(bar.l) &&
+		if (fn < llen &&
 		    win_textwidth(files[sel].name, fn, true) +
-		    win_textwidth(bar.r, n, true) < win.w)
+		    win_textwidth(rt, n, true) < win.w)
 		{
-			strncpy(bar.l, files[sel].name, sizeof(bar.l));
+			strncpy(lt, files[sel].name, llen);
 		} else {
-			strncpy(bar.l, files[sel].base, sizeof(bar.l));
+			strncpy(lt, files[sel].base, llen);
 		}
 	}
-	win_set_bar_info(&win, bar.l, bar.r);
 }
 
 void redraw(void)
@@ -458,8 +489,8 @@ void run(void)
 	int xfd;
 	fd_set fds;
 	struct timeval timeout;
+	bool discard, to_set;
 	XEvent ev, nextev;
-	bool discard;
 
 	redraw();
 
@@ -482,12 +513,20 @@ void run(void)
 				check_timeouts(NULL);
 		}
 
-		while (XPending(win.env.dpy) == 0 && check_timeouts(&timeout)) {
-			/* wait for timeouts */
+		while (XPending(win.env.dpy) == 0
+		       && ((to_set = check_timeouts(&timeout)) || info.fd != -1))
+		{
+			/* check for timeouts & input */
 			xfd = ConnectionNumber(win.env.dpy);
 			FD_ZERO(&fds);
 			FD_SET(xfd, &fds);
-			select(xfd + 1, &fds, 0, 0, &timeout);
+			if (info.fd != -1) {
+				FD_SET(info.fd, &fds);
+				xfd = MAX(xfd, info.fd);
+			}
+			select(xfd + 1, &fds, 0, 0, to_set ? &timeout : NULL);
+			if (FD_ISSET(info.fd, &fds))
+				read_info();
 		}
 
 		do {
@@ -641,13 +680,14 @@ int main(int argc, char **argv)
 		warn("could not locate home directory");
 	} else {
 		len = strlen(homedir) + strlen(INFO_SCRIPT) + 2;
-		info_script = (char*) s_malloc(len);
-		snprintf(info_script, len, "%s/%s", homedir, INFO_SCRIPT);
-		if (access(info_script, X_OK) != 0) {
-			free(info_script);
-			info_script = NULL;
+		info.script = (char*) s_malloc(len);
+		snprintf(info.script, len, "%s/%s", homedir, INFO_SCRIPT);
+		if (access(info.script, X_OK) != 0) {
+			free(info.script);
+			info.script = NULL;
 		}
 	}
+	info.fd = -1;
 
 	if (options->thumb_mode) {
 		mode = MODE_THUMB;
diff --git a/window.c b/window.c
index 0b1ce65..a51499e 100644
--- a/window.c
+++ b/window.c
@@ -414,31 +414,28 @@ void win_draw_bar(win_t *win)
 	XSetForeground(e->dpy, gc, win->bar.fgcol);
 	XSetBackground(e->dpy, gc, win->bar.bgcol);
 
-	if (win->bar.r != NULL) {
-		len = strlen(win->bar.r);
-		if (len > 0) {
-			if ((tw = win_textwidth(win->bar.r, len, true)) > w)
-				return;
-			x = win->w - tw + H_TEXT_PAD;
-			w -= tw;
-			if (font.set)
-				XmbDrawString(e->dpy, win->pm, font.set, gc, x, y, win->bar.r, len);
-			else
-				XDrawString(e->dpy, win->pm, gc, x, y, win->bar.r, len);
-		}
+	if ((len = strlen(win->bar.r)) > 0) {
+		if ((tw = win_textwidth(win->bar.r, len, true)) > w)
+			return;
+		x = win->w - tw + H_TEXT_PAD;
+		w -= tw;
+		if (font.set)
+			XmbDrawString(e->dpy, win->pm, font.set, gc, x, y, win->bar.r, len);
+		else
+			XDrawString(e->dpy, win->pm, gc, x, y, win->bar.r, len);
 	}
-	if (win->bar.l != NULL) {
-		olen = len = strlen(win->bar.l);
+	if ((len = strlen(win->bar.l)) > 0) {
+		olen = len;
 		while (len > 0 && (tw = win_textwidth(win->bar.l, len, true)) > w)
 			len--;
 		if (len > 0) {
-      if (len != olen) {
-        w = strlen(dots);
-        if (len <= w)
-          return;
-        memcpy(rest, win->bar.l + len - w, w);
-        memcpy(win->bar.l + len - w, dots, w);
-      }
+			if (len != olen) {
+				w = strlen(dots);
+				if (len <= w)
+					return;
+				memcpy(rest, win->bar.l + len - w, w);
+				memcpy(win->bar.l + len - w, dots, w);
+			}
 			x = H_TEXT_PAD;
 			if (font.set)
 				XmbDrawString(e->dpy, win->pm, font.set, gc, x, y, win->bar.l, len);
@@ -480,6 +477,18 @@ void win_draw_rect(win_t *win, Pixmap pm, int x, int y, int w, int h,
 		XDrawRectangle(win->env.dpy, pm, gc, x, y, w, h);
 }
 
+void win_update_bar(win_t *win)
+{
+	if (win == NULL || win->xwin == None || win->pm == None)
+		return;
+
+	if (win->bar.h > 0) {
+		win_draw_bar(win);
+		XCopyArea(win->env.dpy, win->pm, win->xwin, gc,
+		          0, win->h, win->w, win->bar.h, 0, win->h);
+	}
+}
+
 int win_textwidth(const char *text, unsigned int len, bool with_padding)
 {
 	XRectangle r;
@@ -514,14 +523,6 @@ void win_set_title(win_t *win, const char *title)
 	                PropModeReplace, (unsigned char *) title, strlen(title));
 }
 
-void win_set_bar_info(win_t *win, char *linfo, char *rinfo)
-{
-	if (win != NULL) {
-		win->bar.l = linfo;
-		win->bar.r = rinfo;
-	}
-}
-
 void win_set_cursor(win_t *win, cursor_t cursor)
 {
 	if (win == NULL || win->xwin == None)
diff --git a/window.h b/window.h
index dfb39aa..19a7171 100644
--- a/window.h
+++ b/window.h
@@ -24,6 +24,11 @@
 
 #include "types.h"
 
+enum {
+	BAR_L_LEN = 512,
+	BAR_R_LEN = 64
+};
+
 typedef struct {
 	Display *dpy;
 	int scr;
@@ -55,8 +60,8 @@ typedef struct {
 
 	struct {
 		unsigned int h;
-		char *l;
-		char *r;
+		char l[BAR_L_LEN];
+		char r[BAR_R_LEN];
 		unsigned long bgcol;
 		unsigned long fgcol;
 	} bar;
@@ -80,10 +85,11 @@ void win_draw(win_t*);
 void win_draw_rect(win_t*, Pixmap, int, int, int, int, bool, int,
                    unsigned long);
 
+void win_update_bar(win_t*);
+
 int win_textwidth(const char*, unsigned int, bool);
 
 void win_set_title(win_t*, const char*);
-void win_set_bar_info(win_t*, char*, char*);
 void win_set_cursor(win_t*, cursor_t);
 
 #endif /* WINDOW_H */

From 30802cec0f233aa9977256684cb749df6c7e28c0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bert=20M=C3=BCnnich?= <be.muennich@gmail.com>
Date: Tue, 12 Feb 2013 17:55:47 +0100
Subject: [PATCH 2/4] Spawn info script & update bar contents only when needed

---
 commands.c |  8 ++++++--
 main.c     | 24 +++++++++++++++++-------
 window.c   |  5 +++--
 3 files changed, 26 insertions(+), 11 deletions(-)

diff --git a/commands.c b/commands.c
index f358ed3..3e108a4 100644
--- a/commands.c
+++ b/commands.c
@@ -33,6 +33,7 @@
 void cleanup(void);
 void remove_file(int, bool);
 void load_image(int);
+void open_info(void);
 void redraw(void);
 void reset_cursor(void);
 void animate(void);
@@ -92,10 +93,13 @@ bool it_toggle_fullscreen(arg_t a)
 bool it_toggle_bar(arg_t a)
 {
 	win_toggle_bar(&win);
-	if (mode == MODE_IMAGE)
+	if (mode == MODE_IMAGE) {
 		img.checkpan = img.dirty = true;
-	else
+		if (win.bar.h > 0)
+			open_info();
+	} else {
 		tns.dirty = true;
+	}
 	return true;
 }
 
diff --git a/main.c b/main.c
index f648dab..d6cdcd4 100644
--- a/main.c
+++ b/main.c
@@ -76,6 +76,7 @@ struct {
   char *script;
   int fd;
   unsigned int i, lastsep;
+  bool open;
 } info;
 
 timeout_t timeouts[] = {
@@ -218,23 +219,25 @@ void open_info(void)
 	static pid_t pid;
 	int pfd[2];
 
-	win.bar.l[0] = '\0';
-
+	if (info.script == NULL || info.open || win.bar.h == 0)
+		return;
 	if (info.fd != -1) {
 		close(info.fd);
 		kill(pid, SIGTERM);
 		while (waitpid(-1, NULL, WNOHANG) > 0);
 		info.fd = -1;
 	}
-	if (info.script == NULL || pipe(pfd) < 0)
-		return;
+	win.bar.l[0] = '\0';
 
+	if (pipe(pfd) < 0)
+		return;
 	pid = fork();
 	if (pid > 0) {
 		close(pfd[1]);
 		fcntl(pfd[0], F_SETFL, O_NONBLOCK);
 		info.fd = pfd[0];
 		info.i = info.lastsep = 0;
+		info.open = true;
 	} else if (pid == 0) {
 		close(pfd[0]);
 		dup2(pfd[1], 1);
@@ -293,6 +296,7 @@ void load_image(int new)
 	alternate = fileidx;
 	fileidx = new;
 
+	info.open = false;
 	open_info();
 
 	if (img.multi.cnt > 0 && img.multi.animate)
@@ -312,9 +316,18 @@ void update_info(void)
 	for (fw = 0, i = filecnt; i > 0; fw++, i /= 10);
 	sel = mode == MODE_IMAGE ? fileidx : tns.sel;
 
+	/* update window title */
 	if (mode == MODE_THUMB) {
 		win_set_title(&win, "sxiv");
+	} else {
+		snprintf(title, sizeof(title), "sxiv - %s", files[sel].name);
+		win_set_title(&win, title);
+	}
 
+	/* update bar contents */
+	if (win.bar.h == 0)
+		return;
+	if (mode == MODE_THUMB) {
 		if (tns.cnt == filecnt) {
 			n = snprintf(rt, rlen, "%0*d/%d", fw, sel + 1, filecnt);
 			ow_info = true;
@@ -324,9 +337,6 @@ void update_info(void)
 			ow_info = false;
 		}
 	} else {
-		snprintf(title, sizeof(title), "sxiv - %s", files[sel].name);
-		win_set_title(&win, title);
-
 		n = snprintf(rt, rlen, "%3d%% ", (int) (img.zoom * 100.0));
 		if (img.multi.cnt > 0) {
 			for (fn = 0, i = img.multi.cnt; i > 0; fn++, i /= 10);
diff --git a/window.c b/window.c
index a51499e..30081de 100644
--- a/window.c
+++ b/window.c
@@ -119,12 +119,15 @@ void win_init(win_t *win)
 	e->cmap = DefaultColormap(e->dpy, e->scr);
 	e->depth = DefaultDepth(e->dpy, e->scr);
 
+	win_init_font(e->dpy, BAR_FONT);
+
 	win->white     = WhitePixel(e->dpy, e->scr);
 	win->bgcol     = win_alloc_color(win, WIN_BG_COLOR);
 	win->fscol     = win_alloc_color(win, WIN_FS_COLOR);
 	win->selcol    = win_alloc_color(win, SEL_COLOR);
 	win->bar.bgcol = win_alloc_color(win, BAR_BG_COLOR);
 	win->bar.fgcol = win_alloc_color(win, BAR_FG_COLOR);
+	win->bar.h     = options->hide_bar ? 0 : barheight;
 
 	win->sizehints.flags = PWinGravity;
 	win->sizehints.win_gravity = NorthWestGravity;
@@ -135,8 +138,6 @@ void win_init(win_t *win)
 	if (setlocale(LC_CTYPE, "") == NULL || XSupportsLocale() == 0)
 		warn("no locale support");
 
-	win_init_font(e->dpy, BAR_FONT);
-
 	wm_delete_win = XInternAtom(e->dpy, "WM_DELETE_WINDOW", False);
 }
 

From 38ecea3b4d01d1c41a7692d75b888044268a9d02 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bert=20M=C3=BCnnich?= <be.muennich@gmail.com>
Date: Sun, 24 Feb 2013 14:55:49 +0100
Subject: [PATCH 3/4] Polished info script execution

---
 main.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/main.c b/main.c
index d6cdcd4..db95770 100644
--- a/main.c
+++ b/main.c
@@ -224,7 +224,6 @@ void open_info(void)
 	if (info.fd != -1) {
 		close(info.fd);
 		kill(pid, SIGTERM);
-		while (waitpid(-1, NULL, WNOHANG) > 0);
 		info.fd = -1;
 	}
 	win.bar.l[0] = '\0';
@@ -242,6 +241,8 @@ void open_info(void)
 		close(pfd[0]);
 		dup2(pfd[1], 1);
 		execl(info.script, info.script, files[fileidx].name, NULL);
+		warn("could not exec: %s", info.script);
+		exit(EXIT_FAILURE);
 	}
 }
 

From 825c52c33fcaec5dbaa361cb39835987de8312f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bert=20M=C3=BCnnich?= <be.muennich@gmail.com>
Date: Sun, 24 Feb 2013 20:04:55 +0100
Subject: [PATCH 4/4] Made bar fields more distinguishable

---
 image-info | 6 ++++--
 main.c     | 4 ++--
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/image-info b/image-info
index 4a97a89..a160b2b 100644
--- a/image-info
+++ b/image-info
@@ -5,13 +5,15 @@
 # with the name of the image file as its first argument.
 # The output is displayed in sxiv's status bar.
 
+s=" | " # field separator
+
 filename=$(basename "$1")
 filesize=$(du -h "$1" | cut -f 1)
 
 geometry=$(identify -format '%wx%h' "$1[0]")
 
 tags=$(exiv2 -q pr -pi "$1" | awk '$1~"Keywords" { printf("%s,", $4); }')
-tags=${tags:+|}${tags%,}
+tags=${tags%,}
 
-echo "[$filesize|$geometry$tags] $filename"
+echo "${filesize}${s}${geometry}${tags:+$s}${tags}${s}${filename}"
 
diff --git a/main.c b/main.c
index db95770..e6d6700 100644
--- a/main.c
+++ b/main.c
@@ -338,10 +338,10 @@ void update_info(void)
 			ow_info = false;
 		}
 	} else {
-		n = snprintf(rt, rlen, "%3d%% ", (int) (img.zoom * 100.0));
+		n = snprintf(rt, rlen, "%3d%% | ", (int) (img.zoom * 100.0));
 		if (img.multi.cnt > 0) {
 			for (fn = 0, i = img.multi.cnt; i > 0; fn++, i /= 10);
-			n += snprintf(rt + n, rlen - n, "(%0*d/%d) ",
+			n += snprintf(rt + n, rlen - n, "%0*d/%d | ",
 			              fn, img.multi.sel + 1, img.multi.cnt);
 		}
 		n += snprintf(rt + n, rlen - n, "%0*d/%d", fw, sel + 1, filecnt);