URI: 
       drw_text: improve both performance and correctness - dmenu - dynamic menu
  HTML git clone git://git.suckless.org/dmenu
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 41fdabbf7c517f8d524b70cbd78238cc319ccef3
   DIR parent 3a505cebe8adab204e5619357e0bfe3f9f3a92ff
  HTML Author: NRK <nrk@disroot.org>
       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 <bakkeby@gmail.com>, 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(-)
       ---
   DIR 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 h, int filled, int
        int
        drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
        {
       -        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 int h, unsigned int lp
                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 int h, unsigned int lp
                }
        
                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 int h, unsigned int lp
                                for (curfont = drw->fonts; curfont; curfont = curfont->next) {
                                        charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
                                        if (charexists) {
       -                                        if (curfont == usedfont) {
       +                                        drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL);
       +                                        if (ew + ellipsis_width <= w) {
       +                                                /* keep track where the ellipsis still fits */
       +                                                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 int h, unsigned int lp
                                        }
                                }
        
       -                        if (!charexists || nextfont)
       +                        if (overflow || !charexists || nextfont)
                                        break;
                                else
                                        charexists = 0;
                        }
        
                        if (utf8strlen) {
       -                        drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
       -                        /* shorten text if necessary */
       -                        for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--)
       -                                drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
       -
       -                        if (len) {
       -                                memcpy(buf, utf8str, len);
       -                                buf[len] = '\0';
       -                                if (len < utf8strlen)
       -                                        for (i = len; i && i > len - 3; buf[--i] = '.')
       -                                                ; /* NOP */
       -
       -                                if (render) {
       -                                        ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
       -                                        XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
       -                                                          usedfont->xfont, x, ty, (XftChar8 *)buf, len);
       -                                }
       -                                x += ew;
       -                                w -= ew;
       +                        if (render) {
       +                                ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
       +                                XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
       +                                                  usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen);
                                }
       +                        x += ew;
       +                        w -= ew;
                        }
       +                if (render && overflow)
       +                        drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert);
        
       -                if (!*text) {
       +                if (!*text || overflow) {
                                break;
                        } else if (nextfont) {
                                charexists = 0;