Merge branch 'userinfo'

This commit is contained in:
Bert Münnich 2013-01-27 18:03:07 +01:00
commit aa6ccf42b8
8 changed files with 221 additions and 158 deletions

View file

@ -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

View file

@ -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

17
image-info Normal file
View file

@ -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"

149
main.c
View file

@ -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);
} 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);
}
win_set_title(&win, "sxiv");
win_set_bar_info(&win, win_bar_l, NULL);
} else {
size_readable(&size, &size_unit);
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);
if (tns.cnt == filecnt) {
n = snprintf(t, len, "%0*d/%d", fw, sel + 1, filecnt);
ow_info = true;
} else {
frame_info[0] = '\0';
snprintf(bar.l, sizeof(bar.l), "Loading... %0*d/%d",
fw, tns.cnt, filecnt);
bar.r[0] = '\0';
ow_info = false;
}
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);
} else {
snprintf(title, sizeof(title), "sxiv - %s", files[sel].name);
win_set_title(&win, title);
if (win_textwidth(win_bar_l, ln, true) +
win_textwidth(win_bar_r, rn, true) > win.w)
{
snprintf(win_bar_l + fi, sizeof win_bar_l - fi, "%s",
files[fileidx].base);
n = snprintf(t, len, "%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) ",
fn, img.multi.sel + 1, img.multi.cnt);
}
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);
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)
{
strncpy(bar.l, files[sel].name, sizeof(bar.l));
} else {
strncpy(bar.l, files[sel].base, sizeof(bar.l));
}
}
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);

30
sxiv.1
View file

@ -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)

View file

@ -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");
}

141
window.c
View file

@ -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)
@ -250,7 +250,7 @@ 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) {
x = win->w - tw - H_TEXT_PAD;
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, rt, len);
XmbDrawString(e->dpy, win->pm, font.set, gc, x, y, win->bar.r, len);
else
XDrawString(e->dpy, win->pm, gc, x, y, rt, len);
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) {
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);
else
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;
}
}

View file

@ -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 */