URI:
       tlibdraw: autoscale fonts when moving between low and high dpi screens - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 213fc4f6fb26bb5781ea3e489bf4cc5c2aca591e
   DIR parent 77f23268f7073b254e91748d4764768bab6d6f1f
  HTML Author: Russ Cox <rsc@swtch.com>
       Date:   Tue, 17 Feb 2015 12:16:20 -0500
       
       libdraw: autoscale fonts when moving between low and high dpi screens
       
       Change-Id: I6093955b222db89dfe437fb723593b173d888d01
       Reviewed-on: https://plan9port-review.googlesource.com/1170
       Reviewed-by: Russ Cox <rsc@swtch.com>
       
       Diffstat:
         M include/draw.h                      |      17 ++++++++++++++++-
         M man/man3/graphics.3                 |      15 +++++++++++++++
         M src/libdraw/buildfont.c             |      18 ++++++++++++++++++
         M src/libdraw/getsubfont.c            |      19 +++++++++++--------
         M src/libdraw/init.c                  |      12 ++++++++++++
         M src/libdraw/openfont.c              |     165 ++++++++++++++++++++++++++++---
         M src/libdraw/string.c                |       2 +-
         M src/libdraw/stringwidth.c           |       2 +-
         M src/libdraw/subfontname.c           |      21 +++++++++++++++++----
       
       9 files changed, 243 insertions(+), 28 deletions(-)
       ---
   DIR diff --git a/include/draw.h b/include/draw.h
       t@@ -206,6 +206,9 @@ struct Display
                struct Mux        *mux;
                int                srvfd;
                int                dpi;
       +        
       +        Font        *firstfont;
       +        Font        *lastfont;
        };
        
        struct Image
       t@@ -319,6 +322,15 @@ struct Font
                Cachesubf        *subf;
                Cachefont        **sub;        /* as read from file */
                Image                *cacheimage;
       +        
       +        /* doubly linked list of fonts known to display */
       +        int ondisplaylist;
       +        Font *next;
       +        Font *prev;
       +        
       +        /* on hi-dpi systems, one of these is set to f and the other is the other-dpi version of f */
       +        Font        *lodpi;
       +        Font        *hidpi;
        };
        
        #define        Dx(r)        ((r).max.x-(r).min.x)
       t@@ -460,6 +472,7 @@ extern void        borderop(Image*, Rectangle, int, Image*, Point, Drawop);
         * Font management
         */
        extern Font*        openfont(Display*, char*);
       +extern int        parsefontscale(char*, char**);
        extern Font*        buildfont(Display*, char*, char*);
        extern void        freefont(Font*);
        extern Font*        mkfont(Subfont*, Rune);
       t@@ -483,11 +496,13 @@ extern int        runestringnwidth(Font*, Rune*, int);
        extern Point        strsubfontwidth(Subfont*, char*);
        extern int        loadchar(Font*, Rune, Cacheinfo*, int, int, char**);
        extern char*        subfontname(char*, char*, int);
       -extern Subfont*        _getsubfont(Display*, Font*, char*);
       +extern Subfont*        _getsubfont(Display*, char*);
        extern Subfont*        getdefont(Display*);
        extern void                lockdisplay(Display*);
        extern void        unlockdisplay(Display*);
        extern int                drawlsetrefresh(u32int, int, void*, void*);
       +extern void        loadhidpi(Font*);
       +extern void        swapfont(Font*, Font**, Font**);
        
        /*
         * Predefined 
   DIR diff --git a/man/man3/graphics.3 b/man/man3/graphics.3
       t@@ -487,6 +487,21 @@ point to the portion of the window inside the border;
        sophisticated clients may use
        .B _screen
        to make further subwindows.
       +If
       +.I getwindow
       +is being called due to a resizing of the window,
       +the resize may be accompanied by a change in screen pixel density (DPI),
       +in which case the value of the
       +.BR Display 's
       +.B dpi
       +field and any open
       +.BR Font 's
       +.B height
       +and
       +.B ascent
       +fields may be updated during the call to
       +.IR getwindow .
       +Programs should discard any cached information about display or font sizes.
        .\" Programs desiring multiple independent windows
        .\" may use the mechanisms of
        .\" .IR rio (4)
   DIR diff --git a/src/libdraw/buildfont.c b/src/libdraw/buildfont.c
       t@@ -138,5 +138,23 @@ freefont(Font *f)
                free(f->cache);
                free(f->subf);
                free(f->sub);
       +
       +        if(f->ondisplaylist) {
       +                f->ondisplaylist = 0;
       +                if(f->next)
       +                        f->next->prev = f->prev;
       +                else
       +                        f->display->lastfont = f->prev;
       +                if(f->prev)
       +                        f->prev->next = f->next;
       +                else
       +                        f->display->firstfont = f->next;
       +        }
       +
       +        if(f->lodpi != f)        
       +                freefont(f->lodpi);
       +        if(f->hidpi != f)
       +                freefont(f->hidpi);
       +
                free(f);
        }
   DIR diff --git a/src/libdraw/getsubfont.c b/src/libdraw/getsubfont.c
       t@@ -11,17 +11,20 @@ int _fontpipe(char*);
        static void scalesubfont(Subfont*, int);
        
        Subfont*
       -_getsubfont(Display *d, Font *ff, char *name)
       +_getsubfont(Display *d, char *name)
        {
                int fd;
                Subfont *f;
       -
       -        fd = open(name, OREAD);
       -        if(fd < 0 && strncmp(name, "/mnt/font/", 10) == 0)
       -                fd = _fontpipe(name+10);
       +        int scale;
       +        char *fname;
       +        
       +        scale = parsefontscale(name, &fname);
       +        fd = open(fname, OREAD);
       +        if(fd < 0 && strncmp(fname, "/mnt/font/", 10) == 0)
       +                fd = _fontpipe(fname+10);
        
                if(fd < 0){
       -                fprint(2, "getsubfont: can't open %s: %r\n", name);
       +                fprint(2, "getsubfont: can't open %s: %r\n", fname);
                        return 0;
                }
                /*
       t@@ -38,8 +41,8 @@ _getsubfont(Display *d, Font *ff, char *name)
                if(f == 0)
                        fprint(2, "getsubfont: can't read %s: %r\n", name);
                close(fd);
       -        if(ff->scale != 1 && ff->scale != 0)
       -                scalesubfont(f, ff->scale);
       +        if(scale > 1)
       +                scalesubfont(f, scale);
                return f;
        }
        
   DIR diff --git a/src/libdraw/init.c b/src/libdraw/init.c
       t@@ -199,6 +199,7 @@ int
        getwindow(Display *d, int ref)
        {
                Image *i, *oi;
       +        Font *f;
                
                /* XXX check for destroyed? */
                
       t@@ -219,6 +220,17 @@ getwindow(Display *d, int ref)
                _freeimage1(screen);
                screen = _allocwindow(screen, _screen, i->r, ref, DWhite);
                d->screenimage = screen;
       +
       +
       +        if(d->dpi >= DefaultDPI*3/2) {
       +                for(f=d->firstfont; f != nil; f=f->next)
       +                        loadhidpi(f);
       +        } else {
       +                for(f=d->firstfont; f != nil; f=f->next)
       +                        if(f->lodpi != nil && f->lodpi != f)
       +                                swapfont(f, &f->hidpi, &f->lodpi);
       +        }
       +
                return 0;
        }
        
   DIR diff --git a/src/libdraw/openfont.c b/src/libdraw/openfont.c
       t@@ -5,23 +5,41 @@
        extern vlong _drawflength(int);
        int _fontpipe(char*);
        
       +int
       +parsefontscale(char *name, char **base)
       +{
       +        char *p;
       +        int scale;
       +        
       +        p = name;
       +        scale = 0;
       +        while('0' <= *p && *p <= '9') {
       +                scale = scale*10 + *p - '0';
       +                p++;
       +        }
       +        if(*p == '*' && scale > 0)
       +                *base = p+1;
       +        else {
       +                *base = name;
       +                scale = 1;
       +        }
       +        return scale;
       +}        
       +
        Font*
       -openfont(Display *d, char *name)
       +openfont1(Display *d, char *name)
        {
                Font *fnt;
                int fd, i, n, scale;
       -        char *buf, *nambuf;
       +        char *buf, *nambuf, *fname, *freename;
        
                nambuf = 0;
       -        scale = 1;
       -        if('1' <= name[0] && name[0] <= '9' && name[1] == '*') {
       -                scale = name[0] - '0';
       -                name += 2;
       -        }
       -        fd = open(name, OREAD);
       +        freename = nil;
       +        scale = parsefontscale(name, &fname);
        
       -        if(fd < 0 && strncmp(name, "/lib/font/bit/", 14) == 0){
       -                nambuf = smprint("#9/font/%s", name+14);
       +        fd = open(fname, OREAD);
       +        if(fd < 0 && strncmp(fname, "/lib/font/bit/", 14) == 0){
       +                nambuf = smprint("#9/font/%s", fname+14);
                        if(nambuf == nil)
                                return 0;
                        nambuf = unsharp(nambuf);
       t@@ -31,12 +49,18 @@ openfont(Display *d, char *name)
                                free(nambuf);
                                return 0;
                        }
       -                name = nambuf;
       +                fname = nambuf;
       +                if(scale > 1) {
       +                        name = smprint("%d*%s", scale, fname);
       +                        freename = name;
       +                } else {
       +                        name = fname;
       +                }
                }
                if(fd >= 0)
                        n = _drawflength(fd);
       -        if(fd < 0 && strncmp(name, "/mnt/font/", 10) == 0) {
       -                fd = _fontpipe(name+10);
       +        if(fd < 0 && strncmp(fname, "/mnt/font/", 10) == 0) {
       +                fd = _fontpipe(fname+10);
                        n = 8192;
                }
                if(fd < 0)
       t@@ -59,6 +83,7 @@ openfont(Display *d, char *name)
                fnt = buildfont(d, buf, name);
                free(buf);
                free(nambuf);
       +        free(freename);
                if(scale != 1) {
                        fnt->scale = scale;
                        fnt->height *= scale;
       t@@ -68,6 +93,120 @@ openfont(Display *d, char *name)
                return fnt;
        }
        
       +void
       +swapfont(Font *targ, Font **oldp, Font **newp)
       +{
       +        Font f, *old, *new;
       +
       +        if(targ != *oldp)
       +                sysfatal("bad swapfont %p %p %p", targ, *oldp, *newp);
       +        
       +        old = *oldp;
       +        new = *newp;
       +
       +        f.name = old->name;
       +        f.display = old->display;
       +        f.height = old->height;
       +        f.ascent = old->ascent;
       +        f.width = old->width;
       +        f.nsub = old->nsub;
       +        f.age = old->age;
       +        f.maxdepth = old->maxdepth;
       +        f.ncache = old->ncache;
       +        f.nsubf = old->nsubf;
       +        f.scale = old->scale;
       +        f.cache = old->cache;
       +        f.subf = old->subf;
       +        f.sub = old->sub;
       +        f.cacheimage = old->cacheimage;
       +
       +        old->name = new->name;
       +        old->display = new->display;
       +        old->height = new->height;
       +        old->ascent = new->ascent;
       +        old->width = new->width;
       +        old->nsub = new->nsub;
       +        old->age = new->age;
       +        old->maxdepth = new->maxdepth;
       +        old->ncache = new->ncache;
       +        old->nsubf = new->nsubf;
       +        old->scale = new->scale;
       +        old->cache = new->cache;
       +        old->subf = new->subf;
       +        old->sub = new->sub;
       +        old->cacheimage = new->cacheimage;
       +
       +        new->name = f.name;
       +        new->display = f.display;
       +        new->height = f.height;
       +        new->ascent = f.ascent;
       +        new->width = f.width;
       +        new->nsub = f.nsub;
       +        new->age = f.age;
       +        new->maxdepth = f.maxdepth;
       +        new->ncache = f.ncache;
       +        new->nsubf = f.nsubf;
       +        new->scale = f.scale;
       +        new->cache = f.cache;
       +        new->subf = f.subf;
       +        new->sub = f.sub;
       +        new->cacheimage = f.cacheimage;
       +
       +        *oldp = new;
       +        *newp = old;
       +}
       +
       +void
       +loadhidpi(Font *f)
       +{
       +        char *name;
       +        Font *fnew;
       +
       +        if(f->hidpi == f)
       +                return;
       +        if(f->hidpi != nil) {
       +                swapfont(f, &f->lodpi, &f->hidpi);
       +                return;
       +        }
       +        
       +        name = smprint("%d*%s", f->scale*2, f->name);
       +        fnew = openfont1(f->display, name);
       +        if(fnew == nil)
       +                return;
       +        f->hidpi = fnew;
       +        free(name);
       +
       +        swapfont(f, &f->lodpi, &f->hidpi);
       +}
       +
       +Font*
       +openfont(Display *d, char *name)
       +{
       +        Font *f;
       +        
       +        f = openfont1(d, name);
       +        f->lodpi = f;
       +        
       +        /* add to display list for when dpi changes */
       +        /* d can be nil when invoked from mc. */
       +        if(d != nil) {
       +                f->ondisplaylist = 1;
       +                f->prev = d->lastfont;
       +                f->next = nil;
       +                if(f->prev)
       +                        f->prev->next = f;
       +                else
       +                        d->firstfont = f;
       +                d->lastfont = f;
       +        
       +                /* if this is a hi-dpi display, find hi-dpi version and swap */
       +                if(d->dpi >= DefaultDPI*3/2)
       +                        loadhidpi(f);
       +        }
       +
       +        return f;
       +}
       +
        int
        _fontpipe(char *name)
        {
   DIR diff --git a/src/libdraw/string.c b/src/libdraw/string.c
       t@@ -130,7 +130,7 @@ _string(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, Rune *r, i
                        }
                        if(subfontname){
                                freesubfont(sf);
       -                        if((sf=_getsubfont(f->display, f, subfontname)) == 0){
       +                        if((sf=_getsubfont(f->display, subfontname)) == 0){
                                        def = f->display ? f->display->defaultfont : nil;
                                        if(def && f!=def)
                                                f = def;
   DIR diff --git a/src/libdraw/stringwidth.c b/src/libdraw/stringwidth.c
       t@@ -48,7 +48,7 @@ _stringnwidth(Font *f, char *s, Rune *r, int len)
                                }
                                if(subfontname){
                                        freesubfont(sf);
       -                                if((sf=_getsubfont(f->display, f, subfontname)) == 0){
       +                                if((sf=_getsubfont(f->display, subfontname)) == 0){
                                                def = f->display ? f->display->defaultfont : nil;
                                                if(def && f!=def)
                                                        f = def;
   DIR diff --git a/src/libdraw/subfontname.c b/src/libdraw/subfontname.c
       t@@ -9,14 +9,16 @@
        char*
        subfontname(char *cfname, char *fname, int maxdepth)
        {
       -        char *t, *u, *tmp1, *tmp2;
       -        int i;
       +        char *t, *u, *tmp1, *tmp2, *base;
       +        int i, scale;
       +        
       +        scale = parsefontscale(fname, &base);
        
                t = strdup(cfname);  /* t is the return string */
                if(strcmp(cfname, "*default*") == 0)
                        return t;
                if(t[0] != '/'){
       -                tmp2 = strdup(fname);
       +                tmp2 = strdup(base);
                        u = utfrrune(tmp2, '/');
                        if(u)
                                u[0] = 0;
       t@@ -38,13 +40,24 @@ subfontname(char *cfname, char *fname, int maxdepth)
                        tmp2 = smprint("%s.%d", t, i);
                        if(access(tmp2, AREAD) == 0) {
                                free(t);
       +                        if(scale > 1) {
       +                                t = smprint("%d*%s", scale, tmp2);
       +                                free(tmp2);
       +                                tmp2 = t;
       +                        }
                                return tmp2;
                        }
                }
        
                /* try default */
       -        if(strncmp(t, "/mnt/font/", 10) == 0 || access(t, AREAD) == 0)
       +        if(strncmp(t, "/mnt/font/", 10) == 0 || access(t, AREAD) == 0) {
       +                if(scale > 1) {
       +                        tmp2 = smprint("%d*%s", scale, t);
       +                        free(t);
       +                        t = tmp2;
       +                }
                        return t;
       +        }
        
                return nil;
        }