drw_text: improve both performance and correctness - dmenu - dynamic menu | |
git clone git://git.suckless.org/dmenu | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 41fdabbf7c517f8d524b70cbd78238cc319ccef3 | |
parent 3a505cebe8adab204e5619357e0bfe3f9f3a92ff | |
Author: NRK <[email protected]> | |
Date: Thu, 24 Mar 2022 00:37:55 +0600 | |
drw_text: improve both performance and correctness | |
this patch makes some non-trivial changes, which significantly improves | |
the performance of drawing large strings as well as fixes any issues | |
regarding the printing of the ellipsis when string gets truncated. | |
* performance: | |
before there were two O(n) loops, one which finds how long we can go | |
without changing font, and the second loop would (incorrectly) truncate | |
the string if it's too big. | |
this patch merges the overflow calculation into the first loop and exits | |
out when overflow is detected. when dumping lots of emojies into dmenu, | |
i see some noticeable startup time improvement: | |
before -> after | |
460ms -> 360ms | |
input latency when scrolling up/down is also noticeably better and can | |
be tested with the following: | |
for _ in $(seq 20); do | |
cat /dev/urandom | base64 | tr -d '\n' | head -c 1000000 | |
echo | |
done | ./dmenu -l 10 | |
* correctness: | |
the previous version would incorrectly assumed single byte chars and | |
would overwrite them with '.' , this caused a whole bunch of obvious | |
problems, including the ellipsis not getting rendered if then font | |
changed. | |
in addition to exiting out when we detect overflow, this patch also | |
keeps track of the last x-position where the ellipsis would fit. if we | |
detect overflow, we simply make a recursing call to drw_text() at the | |
ellipsis_x position and overwrite what was there. | |
so now the ellipsis will always be printed properly, regardless of | |
weather the font changes or if the string is single byte char or not. | |
the idea of rendering the ellipsis on top incase of overflow was | |
from Bakkeby <[email protected]>, thanks! however the original patch had | |
some issues incorrectly truncating the prompt (-p flag) and cutting off | |
emojies. those have been fixed in here. | |
Diffstat: | |
M drw.c | 56 ++++++++++++++++-------------… | |
1 file changed, 28 insertions(+), 28 deletions(-) | |
--- | |
diff --git a/drw.c b/drw.c | |
@@ -251,12 +251,10 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned… | |
int | |
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int … | |
{ | |
- char buf[1024]; | |
- int ty; | |
- unsigned int ew; | |
+ int ty, ellipsis_x = 0; | |
+ unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, ellipsis_width; | |
XftDraw *d = NULL; | |
Fnt *usedfont, *curfont, *nextfont; | |
- size_t i, len; | |
int utf8strlen, utf8charlen, render = x || y || w || h; | |
long utf8codepoint = 0; | |
const char *utf8str; | |
@@ -264,7 +262,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned i… | |
FcPattern *fcpattern; | |
FcPattern *match; | |
XftResult result; | |
- int charexists = 0; | |
+ int charexists = 0, overflow = 0; | |
if (!drw || (render && !drw->scheme) || !text || !drw->fonts) | |
return 0; | |
@@ -282,8 +280,9 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned i… | |
} | |
usedfont = drw->fonts; | |
+ drw_font_getexts(usedfont, "...", 3, &ellipsis_width, NULL); | |
while (1) { | |
- utf8strlen = 0; | |
+ ew = ellipsis_len = utf8strlen = 0; | |
utf8str = text; | |
nextfont = NULL; | |
while (*text) { | |
@@ -291,9 +290,21 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned … | |
for (curfont = drw->fonts; curfont; curfont = curfont-… | |
charexists = charexists || XftCharExists(drw->… | |
if (charexists) { | |
- if (curfont == usedfont) { | |
+ drw_font_getexts(curfont, text, utf8ch… | |
+ if (ew + ellipsis_width <= w) { | |
+ /* keep track where the ellips… | |
+ ellipsis_x = x + ew; | |
+ ellipsis_w = w - ew; | |
+ ellipsis_len = utf8strlen; | |
+ } | |
+ | |
+ if (ew + tmpw > w) { | |
+ overflow = 1; | |
+ utf8strlen = ellipsis_len; | |
+ } else if (curfont == usedfont) { | |
utf8strlen += utf8charlen; | |
text += utf8charlen; | |
+ ew += tmpw; | |
} else { | |
nextfont = curfont; | |
} | |
@@ -301,36 +312,25 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned… | |
} | |
} | |
- if (!charexists || nextfont) | |
+ if (overflow || !charexists || nextfont) | |
break; | |
else | |
charexists = 0; | |
} | |
if (utf8strlen) { | |
- drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, N… | |
- /* shorten text if necessary */ | |
- for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew… | |
- drw_font_getexts(usedfont, utf8str, len, &ew, … | |
- | |
- if (len) { | |
- memcpy(buf, utf8str, len); | |
- buf[len] = '\0'; | |
- if (len < utf8strlen) | |
- for (i = len; i && i > len - 3; buf[--… | |
- ; /* NOP */ | |
- | |
- if (render) { | |
- ty = y + (h - usedfont->h) / 2 + usedf… | |
- XftDrawStringUtf8(d, &drw->scheme[inve… | |
- usedfont->xfont, x, … | |
- } | |
- x += ew; | |
- w -= ew; | |
+ if (render) { | |
+ ty = y + (h - usedfont->h) / 2 + usedfont->xfo… | |
+ XftDrawStringUtf8(d, &drw->scheme[invert ? Col… | |
+ usedfont->xfont, x, ty, (Xft… | |
} | |
+ x += ew; | |
+ w -= ew; | |
} | |
+ if (render && overflow) | |
+ drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", … | |
- if (!*text) { | |
+ if (!*text || overflow) { | |
break; | |
} else if (nextfont) { | |
charexists = 0; |