diff --git a/Makefile b/Makefile index 6c26d3c..304f2a9 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ LIB_EXIF_0 = LIB_EXIF_1 = -lexif LIB_GIF_0 = LIB_GIF_1 = -lgif -LDLIBS = -lImlib2 -lX11 -lXft \ +LDLIBS = -lImlib2 -lX11 -lXft -lfontconfig \ $(LIB_EXIF_$(HAVE_LIBEXIF)) $(LIB_GIF_$(HAVE_GIFLIB)) OBJS = autoreload_$(AUTORELOAD).o commands.o image.o main.o options.o \ diff --git a/main.c b/main.c index ebe14c9..0bafac5 100644 --- a/main.c +++ b/main.c @@ -390,8 +390,8 @@ void update_info(void) if (ow_info) { fn = strlen(files[fileidx].name); if (fn < l->size && - win_textwidth(&win.env, files[fileidx].name, fn, true) + - win_textwidth(&win.env, r->buf, r->p - r->buf, true) < win.w) + win_textwidth(&win.env, files[fileidx].name, fn, true, NULL) + + win_textwidth(&win.env, r->buf, r->p - r->buf, true, NULL) < win.w) { strncpy(l->buf, files[fileidx].name, l->size); } else { diff --git a/sxiv.h b/sxiv.h index 25443ea..2d026b1 100644 --- a/sxiv.h +++ b/sxiv.h @@ -362,6 +362,7 @@ int r_opendir(r_dir_t*, const char*, bool); int r_closedir(r_dir_t*); char* r_readdir(r_dir_t*); int r_mkdir(char*); +void* utf8codepoint(const void * __restrict__ str, long * __restrict__ out_codepoint); /* window.c */ @@ -442,7 +443,7 @@ void win_toggle_bar(win_t*); void win_clear(win_t*); void win_draw(win_t*); void win_draw_rect(win_t*, int, int, int, int, bool, int, unsigned long); -int win_textwidth(const win_env_t*, const char*, unsigned int, bool); +int win_textwidth(const win_env_t*, const char*, unsigned int, bool, XftFont*); void win_set_title(win_t*, const char*); void win_set_cursor(win_t*, cursor_t); void win_cursor_pos(win_t*, int*, int*); diff --git a/util.c b/util.c index 6a236c6..973bc80 100644 --- a/util.c +++ b/util.c @@ -204,3 +204,32 @@ int r_mkdir(char *path) } return 0; } + +/* copied from sheredom's utf8.h (public domain) https://github.com/sheredom/utf8.h */ + +void* utf8codepoint(const void* __restrict__ str, long* __restrict__ out_codepoint) +{ + const char *s = (const char *)str; + + if (0xf0 == (0xf8 & s[0])) { + // 4 byte utf8 codepoint + *out_codepoint = ((0x07 & s[0]) << 18) | ((0x3f & s[1]) << 12) | + ((0x3f & s[2]) << 6) | (0x3f & s[3]); + s += 4; + } else if (0xe0 == (0xf0 & s[0])) { + // 3 byte utf8 codepoint + *out_codepoint = ((0x0f & s[0]) << 12) | ((0x3f & s[1]) << 6) | (0x3f & s[2]); + s += 3; + } else if (0xc0 == (0xe0 & s[0])) { + // 2 byte utf8 codepoint + *out_codepoint = ((0x1f & s[0]) << 6) | (0x3f & s[1]); + s += 2; + } else { + // 1 byte utf8 codepoint otherwise + *out_codepoint = s[0]; + s += 1; + } + + return (void *)s; +} + diff --git a/window.c b/window.c index 71d439f..46c3c6d 100644 --- a/window.c +++ b/window.c @@ -360,9 +360,40 @@ void win_clear(win_t *win) XFillRectangle(e->dpy, win->buf.pm, gc, 0, 0, win->buf.w, win->buf.h); } +void win_draw_bar_text(win_t *win, XftDraw *d, XftColor *color, XftFont *font, int x, int y, char *text, int maxlen, int maximum_x) +{ + size_t len = 0; + int xshift = 0, newshift; + long codep; + char *p, *nextp; + FcCharSet* fccharset; + XftFont* fallback = NULL; + + for (p = text; *p && (len < maxlen); p = nextp, len++) { + nextp = utf8codepoint(p, &codep); + if (!XftCharExists(win->env.dpy, font, codep)) { + fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, codep); + fallback = XftFontOpen(win->env.dpy, win->env.scr, + FC_CHARSET, FcTypeCharSet, fccharset, + FC_SCALABLE, FcTypeBool, FcTrue, + NULL); + FcCharSetDestroy(fccharset); + } + newshift = win_textwidth(&win->env, p, (int) (nextp-p), false, (fallback ? fallback : font)); + if (xshift + newshift <= maximum_x) + XftDrawStringUtf8(d, color, (fallback ? fallback : font), x + xshift, y, (XftChar8*)p, (int) (nextp-p)); + xshift += newshift; + if (fallback) { + XftFontClose(win->env.dpy, fallback); + fallback = NULL; + } + } +} + void win_draw_bar(win_t *win) { - int len, olen, x, y, w, tw; + int len, olen, x, y, w, tw, maximum_x; char rest[3]; const char *dots = "..."; win_env_t *e; @@ -385,7 +416,7 @@ void win_draw_bar(win_t *win) XSetBackground(e->dpy, gc, win->bar.bgcol.pixel); if ((len = strlen(r->buf)) > 0) { - if ((tw = win_textwidth(e, r->buf, len, true)) > w) + if ((tw = win_textwidth(e, r->buf, len, true, font)) > w) return; x = win->w - tw + H_TEXT_PAD; w -= tw; @@ -393,9 +424,10 @@ void win_draw_bar(win_t *win) } if ((len = strlen(l->buf)) > 0) { olen = len; - while (len > 0 && (tw = win_textwidth(e, l->buf, len, true)) > w) + while (len > 0 && (tw = win_textwidth(e, l->buf, len, true, font)) > w) len--; if (len > 0) { + maximum_x = w; if (len != olen) { w = strlen(dots); if (len <= w) @@ -404,7 +436,7 @@ void win_draw_bar(win_t *win) memcpy(l->buf + len - w, dots, w); } x = H_TEXT_PAD; - XftDrawStringUtf8(d, &win->bar.fgcol, font, x, y, (XftChar8*)l->buf, len); + win_draw_bar_text(win, d, &win->bar.fgcol, font, x, y, l->buf, len, maximum_x); if (len != olen) memcpy(l->buf + len - w, rest, w); } @@ -437,11 +469,14 @@ void win_draw_rect(win_t *win, int x, int y, int w, int h, bool fill, int lw, XDrawRectangle(win->env.dpy, win->buf.pm, gc, x, y, w, h); } -int win_textwidth(const win_env_t *e, const char *text, unsigned int len, bool with_padding) +int win_textwidth(const win_env_t *e, const char *text, unsigned int len, bool with_padding, XftFont *fnt) { XGlyphInfo ext; - XftTextExtentsUtf8(e->dpy, font, (XftChar8*)text, len, &ext); + if(!fnt) + fnt = font; + + XftTextExtentsUtf8(e->dpy, fnt, (XftChar8*)text, len, &ext); return ext.xOff + (with_padding ? 2 * H_TEXT_PAD : 0); }