diff options
author | Bert Münnich <be.muennich@gmail.com> | 2013-01-27 18:03:07 +0100 |
---|---|---|
committer | Bert Münnich <be.muennich@gmail.com> | 2013-01-27 18:03:07 +0100 |
commit | aa6ccf42b8aace05154bf2eebcba8695524b0b17 (patch) | |
tree | 79229fc6dd0bbbf7fde51a4614172cc5677a7c64 | |
parent | 3a0a1ae889bc4de959cfe5079d541b0ec9119089 (diff) | |
parent | 9c0a53bc34c53424613ee203253fee526ba9f049 (diff) |
Merge branch 'userinfo'
-rw-r--r-- | Makefile | 8 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | image-info | 17 | ||||
-rw-r--r-- | main.c | 145 | ||||
-rw-r--r-- | sxiv.1 | 30 | ||||
-rw-r--r-- | thumbs.c | 12 | ||||
-rw-r--r-- | window.c | 141 | ||||
-rw-r--r-- | window.h | 16 |
8 files changed, 218 insertions, 155 deletions
@@ -1,4 +1,4 @@ -VERSION = git-20130112 +VERSION = git-20130127 PREFIX = /usr/local MANPREFIX = $(PREFIX)/share/man @@ -32,9 +32,13 @@ install: all cp sxiv $(DESTDIR)$(PREFIX)/bin/ chmod 755 $(DESTDIR)$(PREFIX)/bin/sxiv mkdir -p $(DESTDIR)$(MANPREFIX)/man1 - sed "s/VERSION/$(VERSION)/g" sxiv.1 > $(DESTDIR)$(MANPREFIX)/man1/sxiv.1 + sed "s!PREFIX!$(PREFIX)!g; s!VERSION!$(VERSION)!g" sxiv.1 > $(DESTDIR)$(MANPREFIX)/man1/sxiv.1 chmod 644 $(DESTDIR)$(MANPREFIX)/man1/sxiv.1 + mkdir -p $(DESTDIR)$(PREFIX)/share/sxiv/exec + cp image-info $(DESTDIR)$(PREFIX)/share/sxiv/exec/image-info + chmod 755 $(DESTDIR)$(PREFIX)/share/sxiv/exec/image-info uninstall: rm -f $(DESTDIR)$(PREFIX)/bin/sxiv rm -f $(DESTDIR)$(MANPREFIX)/man1/sxiv.1 + rm -rf $(DESTDIR)$(PREFIX)/share/sxiv @@ -1,7 +1,7 @@ sxiv ==== -**Simple (or small or suckless) X Image Viewer** +**Simple X Image Viewer** sxiv is an alternative to feh and qiv. Its only dependencies besides xlib are imlib2 and giflib. The primary goal for writing sxiv is to create an image @@ -20,7 +20,7 @@ Features * Ability to cache thumbnails for fast re-loading * Basic support for multi-frame images * Load all frames from GIF files and play GIF animations -* Display image information in window title +* Display image information in status bar Screenshots diff --git a/image-info b/image-info new file mode 100644 index 0000000..3c03a3f --- /dev/null +++ b/image-info @@ -0,0 +1,17 @@ +#!/bin/sh + +# Example for ~/.sxiv/exec/image-info +# Called by sxiv(1) whenever an image gets loaded, +# with the name of the image file as its first argument. +# The output is displayed in sxiv's status bar. + +filename=$(basename "$1") +filesize=$(du -h "$1" | cut -f 1) + +geometry=$(identify -format '%wx%h' "$1") + +tags=$(exiv2 -q pr -pi "$1" | awk '$1~"Keywords" { printf("%s,", $4); }') +tags=${tags:+|}${tags%,} + +echo "[$filesize|$geometry$tags] $filename" + @@ -39,8 +39,10 @@ #include "config.h" enum { - INFO_STR_LEN = 256, - FILENAME_CNT = 1024 + BAR_L_LEN = 512, + BAR_R_LEN = 64, + FILENAME_CNT = 1024, + TITLE_LEN = 256 }; typedef struct { @@ -63,15 +65,18 @@ win_t win; fileinfo_t *files; int filecnt, fileidx; int alternate; -size_t filesize; int prefix; bool resized = false; -char win_bar_l[INFO_STR_LEN]; -char win_bar_r[INFO_STR_LEN]; -char win_title[INFO_STR_LEN]; +const char * const INFO_SCRIPT = ".sxiv/exec/image-info"; +char *info_script; + +struct { + char l[BAR_L_LEN]; + char r[BAR_R_LEN]; +} bar; timeout_t timeouts[] = { { { 0, 0 }, false, redraw }, @@ -202,9 +207,37 @@ bool check_timeouts(struct timeval *t) { return tmin > 0; } -void load_image(int new) { - struct stat fstats; +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) + goto end; + while (i < n && (c = fgetc(outp)) != EOF) { + if (c == '\n') { + if (!lastsep) { + bar.l[i++] = ' '; + lastsep = true; + } + } else { + bar.l[i++] = c; + lastsep = false; + } + } + pclose(outp); + } +end: + if (lastsep) + i--; + bar.l[i] = '\0'; +} +void load_image(int new) { if (new < 0 || new >= filecnt) return; @@ -220,10 +253,8 @@ void load_image(int new) { files[new].loaded = true; alternate = fileidx; fileidx = new; - if (stat(files[new].path, &fstats) == 0) - filesize = fstats.st_size; - else - filesize = 0; + + read_info(); if (img.multi.cnt > 0 && img.multi.animate) set_timeout(animate, img.multi.frames[img.multi.sel].delay, true); @@ -232,60 +263,51 @@ void load_image(int new) { } void update_info(void) { - int i, fw, pw, fi, ln, rn; - char frame_info[16]; - const char *size_unit; - float size = filesize; + unsigned int i, fn, fw, n, len = sizeof(bar.r); + int sel; + char *t = bar.r, title[TITLE_LEN]; + bool ow_info; - pw = 0; - for (i = filecnt; i > 0; i /= 10) - pw++; + for (fw = 0, i = filecnt; i > 0; fw++, i /= 10); + sel = mode == MODE_IMAGE ? fileidx : tns.sel; if (mode == MODE_THUMB) { - if (tns.cnt != filecnt) { - snprintf(win_bar_l, sizeof win_bar_l, "Loading... %0*d/%d", - pw, tns.cnt, filecnt); + win_set_title(&win, "sxiv"); + + if (tns.cnt == filecnt) { + n = snprintf(t, len, "%0*d/%d", fw, sel + 1, filecnt); + ow_info = true; } else { - fi = snprintf(win_bar_l, sizeof win_bar_l, "%0*d/%d%s", - pw, tns.sel + 1, filecnt, BAR_SEPARATOR); - ln = snprintf(win_bar_l + fi, sizeof win_bar_l - fi, "%s", - files[tns.sel].name) + fi; - if (win_textwidth(win_bar_l, ln, true) > win.w) - snprintf(win_bar_l + fi, sizeof win_bar_l - fi, "%s", - files[tns.sel].base); + snprintf(bar.l, sizeof(bar.l), "Loading... %0*d/%d", + fw, tns.cnt, filecnt); + bar.r[0] = '\0'; + ow_info = false; } - win_set_title(&win, "sxiv"); - win_set_bar_info(&win, win_bar_l, NULL); } else { - size_readable(&size, &size_unit); + snprintf(title, sizeof(title), "sxiv - %s", files[sel].name); + win_set_title(&win, title); + + n = snprintf(t, len, "%3d%% ", (int) (img.zoom * 100.0)); if (img.multi.cnt > 0) { - fw = 0; - for (i = img.multi.cnt; i > 0; i /= 10) - fw++; - snprintf(frame_info, sizeof frame_info, "%s%0*d/%d", - BAR_SEPARATOR, fw, img.multi.sel+1, img.multi.cnt); - } else { - frame_info[0] = '\0'; + for (fn = 0, i = img.multi.cnt; i > 0; fn++, i /= 10); + n += snprintf(t + n, len - n, "(%0*d/%d) ", + fn, img.multi.sel + 1, img.multi.cnt); } - fi = snprintf(win_bar_l, sizeof win_bar_l, "%0*d/%d%s", - pw, fileidx + 1, filecnt, BAR_SEPARATOR); - ln = snprintf(win_bar_l + fi, sizeof win_bar_l - fi, "%s", - files[fileidx].name) + fi; - rn = snprintf(win_bar_r, sizeof win_bar_r, "%.2f%s%s%dx%d%s%3d%%%s", - size, size_unit, BAR_SEPARATOR, img.w, img.h, BAR_SEPARATOR, - (int) (img.zoom * 100.0), frame_info); - - if (win_textwidth(win_bar_l, ln, true) + - win_textwidth(win_bar_r, rn, true) > win.w) + n += snprintf(t + n, len - n, "%0*d/%d", fw, sel + 1, filecnt); + ow_info = bar.l[0] == '\0'; + } + if (ow_info) { + fn = strlen(files[sel].name); + if (fn < sizeof(bar.l) && + win_textwidth(files[sel].name, fn, true) + + win_textwidth(bar.r, n, true) < win.w) { - snprintf(win_bar_l + fi, sizeof win_bar_l - fi, "%s", - files[fileidx].base); + strncpy(bar.l, files[sel].name, sizeof(bar.l)); + } else { + strncpy(bar.l, files[sel].base, sizeof(bar.l)); } - win_set_bar_info(&win, win_bar_l, win_bar_r); - - snprintf(win_title, sizeof win_title, "sxiv - %s", files[fileidx].name); - win_set_title(&win, win_title); } + win_set_bar_info(&win, bar.l, bar.r); } void redraw(void) { @@ -519,6 +541,7 @@ int main(int argc, char **argv) { size_t n; ssize_t len; char *filename; + const char *homedir; struct stat fstats; r_dir_t dir; @@ -595,6 +618,18 @@ int main(int argc, char **argv) { win_init(&win); img_init(&img, &win); + if ((homedir = getenv("HOME")) == NULL) { + 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; + } + } + if (options->thumb_mode) { mode = MODE_THUMB; tns_init(&tns, filecnt, &win); @@ -1,6 +1,6 @@ .TH SXIV 1 sxiv\-VERSION .SH NAME -sxiv \- Simple (or small or suckless) X Image Viewer +sxiv \- Simple X Image Viewer .SH SYNOPSIS .B sxiv .RB [ \-bcdFfhpqrstvZ ] @@ -306,9 +306,18 @@ Pan image left. .TP .B Shift+ScrollDown Pan image right. +.SH STATUS BAR +The information displayed on the left side of the status bar can be replaced +with the output of a user-provided script, which is called by sxiv whenever an +image gets loaded. The path of this script is +.I ~/.sxiv/exec/image-info +and the first argument to this script is the path of the loaded image. +.P +There is also an example script installed together with sxiv as +.IR PREFIX/share/sxiv/exec/image-info . .SH THUMBNAIL CACHING To enable thumbnail caching, please make sure to create the directory -.I ~/.sxiv/ +.I ~/.sxiv/cache/ with write permissions. sxiv will then store all thumbnails inside this directory, but it will not create this directory by itself. It rather uses the existance of this directory as an affirmation, that the user wants thumbnails @@ -321,30 +330,23 @@ Additionally, run the following command afterwards inside the cache directory to remove empty subdirectories: .P .RS -find \-type d \-empty \-delete -.RE -.P -If the version of -.I find -installed on your local system does not support the \-delete option, then you -can also try the following command: -.P -.RS find . \-depth \-type d \-empty ! \-name '.' \-exec rmdir {} \\; .RE .SH AUTHOR .EX -Bert Muennich <ber.t at gmx.com> +Bert Muennich <be.muennich @ gmail.com> .EE .SH CONTRIBUTORS .EX Bastien Dejean <nihilhill at gmail.com> Dave Reisner <d at falconindy.com> Fung SzeTat <sthorde at gmail.com> -.EX +.EE .SH HOMEPAGE -.TP +.EX +http://muennich.github.com/sxiv https://github.com/muennich/sxiv +.EE .SH SEE ALSO .BR feh (1), .BR qiv (1) @@ -31,8 +31,10 @@ #include "util.h" #include "config.h" -const int thumb_dim = THUMB_SIZE + 10; -char *cache_dir = NULL; +static const int thumb_dim = THUMB_SIZE + 10; + +static const char * const CACHE_DIR = ".sxiv/cache"; +static char *cache_dir = NULL; bool tns_cache_enabled(void) { struct stat stats; @@ -175,9 +177,9 @@ void tns_init(tns_t *tns, int cnt, win_t *win) { if ((homedir = getenv("HOME")) != NULL) { if (cache_dir != NULL) free(cache_dir); - len = strlen(homedir) + 10; - cache_dir = (char*) s_malloc(len * sizeof(char)); - snprintf(cache_dir, len, "%s/.sxiv", homedir); + len = strlen(homedir) + strlen(CACHE_DIR) + 2; + cache_dir = (char*) s_malloc(len); + snprintf(cache_dir, len, "%s/%s", homedir, CACHE_DIR); } else { warn("could not locate thumbnail cache directory"); } @@ -42,13 +42,16 @@ static GC gc; Atom wm_delete_win; -struct { +static struct { int ascent; int descent; XFontStruct *xfont; XFontSet set; } font; +static int fontheight; +static int barheight; + void win_init_font(Display *dpy, const char *fontstr) { int n; char *def, **missing; @@ -77,6 +80,8 @@ void win_init_font(Display *dpy, const char *fontstr) { font.ascent = font.xfont->ascent; font.descent = font.xfont->descent; } + fontheight = font.ascent + font.descent; + barheight = fontheight + 2 * V_TEXT_PAD; } unsigned long win_alloc_color(win_t *win, const char *name) { @@ -85,8 +90,8 @@ unsigned long win_alloc_color(win_t *win, const char *name) { if (win == NULL) return 0UL; if (XAllocNamedColor(win->env.dpy, - DefaultColormap(win->env.dpy, win->env.scr), - name, &col, &col) == 0) + DefaultColormap(win->env.dpy, win->env.scr), + name, &col, &col) == 0) { die("could not allocate color: %s", name); } @@ -99,6 +104,8 @@ void win_init(win_t *win) { if (win == NULL) return; + memset(win, 0, sizeof(win_t)); + e = &win->env; if ((e->dpy = XOpenDisplay(NULL)) == NULL) die("could not open display"); @@ -110,19 +117,12 @@ void win_init(win_t *win) { e->cmap = DefaultColormap(e->dpy, e->scr); e->depth = DefaultDepth(e->dpy, e->scr); - 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->barbgcol = win_alloc_color(win, BAR_BG_COLOR); - win->barfgcol = win_alloc_color(win, BAR_FG_COLOR); - - win->xwin = 0; - win->pm = 0; - win->fullscreen = false; - win->barh = 0; - win->lbar = NULL; - win->rbar = NULL; + 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); if (setlocale(LC_CTYPE, "") == NULL || XSupportsLocale() == 0) warn("no locale support"); @@ -141,8 +141,8 @@ void win_set_sizehints(win_t *win) { sizehints.flags = PMinSize | PMaxSize; sizehints.min_width = win->w; sizehints.max_width = win->w; - sizehints.min_height = win->h + win->barh; - sizehints.max_height = win->h + win->barh; + sizehints.min_height = win->h + win->bar.h; + sizehints.max_height = win->h + win->bar.h; XSetWMNormalHints(win->env.dpy, win->xwin, &sizehints); } @@ -215,8 +215,8 @@ void win_open(win_t *win) { XSetWMProtocols(e->dpy, win->xwin, &wm_delete_win, 1); if (!options->hide_bar) { - win->barh = font.ascent + font.descent + 2 * V_TEXT_PAD; - win->h -= win->barh; + win->bar.h = barheight; + win->h -= win->bar.h; } if (options->fixed_win) @@ -249,8 +249,8 @@ bool win_configure(win_t *win, XConfigureEvent *c) { if (win == NULL || c == NULL) return false; - - if ((changed = win->w != c->width || win->h + win->barh != c->height)) { + + if ((changed = win->w != c->width || win->h + win->bar.h != c->height)) { if (win->pm != None) { XFreePixmap(win->env.dpy, win->pm); win->pm = None; @@ -260,7 +260,7 @@ bool win_configure(win_t *win, XConfigureEvent *c) { win->x = c->x; win->y = c->y; win->w = c->width; - win->h = c->height - win->barh; + win->h = c->height - win->bar.h; win->bw = c->border_width; return changed; @@ -283,13 +283,13 @@ bool win_moveresize(win_t *win, int x, int y, unsigned int w, unsigned int h) { w = MIN(w, win->env.scrw - 2 * win->bw); h = MIN(h, win->env.scrh - 2 * win->bw); - if (win->x == x && win->y == y && win->w == w && win->h + win->barh == h) + if (win->x == x && win->y == y && win->w == w && win->h + win->bar.h == h) return false; win->x = x; win->y = y; win->w = w; - win->h = h - win->barh; + win->h = h - win->bar.h; if (options->fixed_win) win_set_sizehints(win); @@ -327,12 +327,12 @@ void win_toggle_bar(win_t *win) { if (win == NULL || win->xwin == None) return; - if (win->barh != 0) { - win->h += win->barh; - win->barh = 0; + if (win->bar.h != 0) { + win->h += win->bar.h; + win->bar.h = 0; } else { - win->barh = font.ascent + font.descent + 2 * V_TEXT_PAD; - win->h -= win->barh; + win->bar.h = barheight; + win->h -= win->bar.h; } } @@ -343,7 +343,7 @@ void win_clear(win_t *win) { if (win == NULL || win->xwin == None) return; - h = win->h + win->barh; + h = win->h + win->bar.h; e = &win->env; if (win->pm == None) @@ -354,53 +354,56 @@ void win_clear(win_t *win) { } void win_draw_bar(win_t *win) { + int len, olen, x, y, w, tw; + char rest[3]; + const char *dots = "..."; win_env_t *e; - int len, x, y, w, tw = 0, seplen; - const char *rt; if (win == NULL || win->xwin == None || win->pm == None) return; e = &win->env; - x = H_TEXT_PAD; y = win->h + font.ascent + V_TEXT_PAD; - w = win->w - 2 * H_TEXT_PAD; + w = win->w; - XSetForeground(e->dpy, gc, win->barbgcol); - XFillRectangle(e->dpy, win->pm, gc, 0, win->h, win->w, win->barh); + XSetForeground(e->dpy, gc, win->bar.bgcol); + XFillRectangle(e->dpy, win->pm, gc, 0, win->h, win->w, win->bar.h); - XSetForeground(e->dpy, gc, win->barfgcol); - XSetBackground(e->dpy, gc, win->barbgcol); + XSetForeground(e->dpy, gc, win->bar.fgcol); + XSetBackground(e->dpy, gc, win->bar.bgcol); - if (win->lbar != NULL) { - len = strlen(win->lbar); - while (len > 0 && (tw = win_textwidth(win->lbar, len, false)) > w) - len--; - w -= tw + 2 * H_TEXT_PAD; - if (font.set) - XmbDrawString(e->dpy, win->pm, font.set, gc, x, y, win->lbar, len); - else - XDrawString(e->dpy, win->pm, gc, x, y, win->lbar, len); - } - if (win->rbar != NULL) { - len = strlen(win->rbar); - seplen = strlen(BAR_SEPARATOR); - rt = win->rbar; - while (len > 0 && (tw = win_textwidth(rt, len, false)) > w) { - rt = strstr(rt, BAR_SEPARATOR); - if (rt != NULL) { - rt += seplen; - len = strlen(rt); - } else { - len = 0; - } + 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 (win->bar.l != NULL) { + olen = len = strlen(win->bar.l); + while (len > 0 && (tw = win_textwidth(win->bar.l, len, true)) > w) + len--; if (len > 0) { - x = win->w - tw - H_TEXT_PAD; + 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, rt, len); + XmbDrawString(e->dpy, win->pm, font.set, gc, x, y, win->bar.l, len); else - XDrawString(e->dpy, win->pm, gc, x, y, rt, len); + XDrawString(e->dpy, win->pm, gc, x, y, win->bar.l, len); + if (len != olen) + memcpy(win->bar.l + len - w, rest, w); } } } @@ -409,11 +412,11 @@ void win_draw(win_t *win) { if (win == NULL || win->xwin == None || win->pm == None) return; - if (win->barh > 0) + if (win->bar.h > 0) win_draw_bar(win); XCopyArea(win->env.dpy, win->pm, win->xwin, gc, - 0, 0, win->w, win->h + win->barh, 0, 0); + 0, 0, win->w, win->h + win->bar.h, 0, 0); } void win_draw_rect(win_t *win, Pixmap pm, int x, int y, int w, int h, @@ -466,10 +469,10 @@ void win_set_title(win_t *win, const char *title) { PropModeReplace, (unsigned char *) title, strlen(title)); } -void win_set_bar_info(win_t *win, const char *li, const char *ri) { +void win_set_bar_info(win_t *win, char *linfo, char *rinfo) { if (win != NULL) { - win->lbar = li; - win->rbar = ri; + win->bar.l = linfo; + win->bar.r = rinfo; } } @@ -23,8 +23,6 @@ #include "types.h" -#define BAR_SEPARATOR " | " - typedef struct { Display *dpy; int scr; @@ -42,21 +40,23 @@ typedef struct { unsigned long bgcol; unsigned long fscol; unsigned long selcol; - unsigned long barbgcol; - unsigned long barfgcol; Pixmap pm; int x; int y; unsigned int w; unsigned int h; /* = win height - bar height */ - unsigned int barh; unsigned int bw; bool fullscreen; - const char *lbar; - const char *rbar; + struct { + unsigned int h; + char *l; + char *r; + unsigned long bgcol; + unsigned long fgcol; + } bar; } win_t; extern Atom wm_delete_win; @@ -80,7 +80,7 @@ void win_draw_rect(win_t*, Pixmap, int, int, int, int, bool, int, int win_textwidth(const char*, unsigned int, bool); void win_set_title(win_t*, const char*); -void win_set_bar_info(win_t*, const char*, const char*); +void win_set_bar_info(win_t*, char*, char*); void win_set_cursor(win_t*, cursor_t); #endif /* WINDOW_H */ |