If $font is not set, the default font is constructed from font data linked into every libdraw binary. That process was different from the usual openfont code, and so it was not hidpi-aware, resulting in very tiny fonts out of the box on hidpi systems, until users set $font. Fix this by using openfont to construct the default font, by recognizing the name *default* when looking for font and subfont file contents. Then all the hidpi scaling applies automatically. As a side effect, the concept of a 'default subfont' is gone, as are display->defaultsubfont, getdefont, and memgetdefont.
604 lines
10 KiB
C
604 lines
10 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include <draw.h>
|
|
#include <memdraw.h>
|
|
#include <thread.h>
|
|
#include <fcall.h>
|
|
#include <9p.h>
|
|
/*
|
|
* we included thread.h in order to include 9p.h,
|
|
* but we don't use threads, so exits is ok.
|
|
*/
|
|
#undef exits
|
|
|
|
#include "a.h"
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fprint(2, "usage: fontsrv [-m mtpt]\n");
|
|
fprint(2, "or fontsrv -p path\n");
|
|
exits("usage");
|
|
}
|
|
|
|
static
|
|
void
|
|
packinfo(Fontchar *fc, uchar *p, int n)
|
|
{
|
|
int j;
|
|
|
|
for(j=0; j<=n; j++){
|
|
p[0] = fc->x;
|
|
p[1] = fc->x>>8;
|
|
p[2] = fc->top;
|
|
p[3] = fc->bottom;
|
|
p[4] = fc->left;
|
|
p[5] = fc->width;
|
|
fc++;
|
|
p += 6;
|
|
}
|
|
}
|
|
|
|
enum
|
|
{
|
|
Qroot = 0,
|
|
Qfontdir,
|
|
Qsizedir,
|
|
Qfontfile,
|
|
Qsubfontfile,
|
|
};
|
|
|
|
#define QTYPE(p) ((p) & 0xF)
|
|
#define QFONT(p) (((p) >> 4) & 0xFFFF)
|
|
#define QSIZE(p) (((p) >> 20) & 0xFF)
|
|
#define QANTIALIAS(p) (((p) >> 28) & 0x1)
|
|
#define QRANGE(p) (((p) >> 29) & SubfontMask)
|
|
static int sizes[] = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 28 };
|
|
|
|
static vlong
|
|
qpath(int type, int font, int size, int antialias, int range)
|
|
{
|
|
return type | (font << 4) | (size << 20) | (antialias << 28) | ((vlong)range << 29);
|
|
}
|
|
|
|
static void
|
|
dostat(vlong path, Qid *qid, Dir *dir)
|
|
{
|
|
char *name;
|
|
Qid q;
|
|
ulong mode;
|
|
vlong length;
|
|
XFont *f;
|
|
char buf[100];
|
|
|
|
q.type = 0;
|
|
q.vers = 0;
|
|
q.path = path;
|
|
mode = 0444;
|
|
length = 0;
|
|
name = "???";
|
|
|
|
switch(QTYPE(path)) {
|
|
default:
|
|
sysfatal("dostat %#llux", path);
|
|
|
|
case Qroot:
|
|
q.type = QTDIR;
|
|
name = "/";
|
|
break;
|
|
|
|
case Qfontdir:
|
|
q.type = QTDIR;
|
|
f = &xfont[QFONT(path)];
|
|
name = f->name;
|
|
break;
|
|
|
|
case Qsizedir:
|
|
q.type = QTDIR;
|
|
snprint(buf, sizeof buf, "%lld%s", QSIZE(path), QANTIALIAS(path) ? "a" : "");
|
|
name = buf;
|
|
break;
|
|
|
|
case Qfontfile:
|
|
f = &xfont[QFONT(path)];
|
|
load(f);
|
|
length = 11+1+11+1+f->nrange*(6+1+6+1+9+1);
|
|
name = "font";
|
|
break;
|
|
|
|
case Qsubfontfile:
|
|
snprint(buf, sizeof buf, "x%04x.bit", (int)QRANGE(path)*SubfontSize);
|
|
name = buf;
|
|
break;
|
|
}
|
|
|
|
if(qid)
|
|
*qid = q;
|
|
if(dir) {
|
|
memset(dir, 0, sizeof *dir);
|
|
dir->name = estrdup9p(name);
|
|
dir->muid = estrdup9p("");
|
|
dir->uid = estrdup9p("font");
|
|
dir->gid = estrdup9p("font");
|
|
dir->qid = q;
|
|
if(q.type == QTDIR)
|
|
mode |= DMDIR | 0111;
|
|
dir->mode = mode;
|
|
dir->length = length;
|
|
}
|
|
}
|
|
|
|
static char*
|
|
xwalk1(Fid *fid, char *name, Qid *qid)
|
|
{
|
|
int i, dotdot;
|
|
vlong path;
|
|
char *p;
|
|
int a, n;
|
|
XFont *f;
|
|
|
|
path = fid->qid.path;
|
|
dotdot = strcmp(name, "..") == 0;
|
|
switch(QTYPE(path)) {
|
|
default:
|
|
NotFound:
|
|
return "file not found";
|
|
|
|
case Qroot:
|
|
if(dotdot)
|
|
break;
|
|
for(i=0; i<nxfont; i++) {
|
|
if(strcmp(xfont[i].name, name) == 0) {
|
|
path = qpath(Qfontdir, i, 0, 0, 0);
|
|
goto Found;
|
|
}
|
|
}
|
|
goto NotFound;
|
|
|
|
case Qfontdir:
|
|
if(dotdot) {
|
|
path = Qroot;
|
|
break;
|
|
}
|
|
n = strtol(name, &p, 10);
|
|
if(n == 0)
|
|
goto NotFound;
|
|
a = 0;
|
|
if(*p == 'a') {
|
|
a = 1;
|
|
p++;
|
|
}
|
|
if(*p != 0)
|
|
goto NotFound;
|
|
path += Qsizedir - Qfontdir + qpath(0, 0, n, a, 0);
|
|
break;
|
|
|
|
case Qsizedir:
|
|
if(dotdot) {
|
|
path = qpath(Qfontdir, QFONT(path), 0, 0, 0);
|
|
break;
|
|
}
|
|
if(strcmp(name, "font") == 0) {
|
|
path += Qfontfile - Qsizedir;
|
|
break;
|
|
}
|
|
f = &xfont[QFONT(path)];
|
|
load(f);
|
|
p = name;
|
|
if(*p != 'x')
|
|
goto NotFound;
|
|
p++;
|
|
n = strtoul(p, &p, 16);
|
|
if(p != name+5 || n%SubfontSize != 0 || strcmp(p, ".bit") != 0 || !f->range[(n/SubfontSize) & SubfontMask])
|
|
goto NotFound;
|
|
path += Qsubfontfile - Qsizedir + qpath(0, 0, 0, 0, (n/SubfontSize) & SubfontMask);
|
|
break;
|
|
}
|
|
Found:
|
|
dostat(path, qid, nil);
|
|
fid->qid = *qid;
|
|
return nil;
|
|
}
|
|
|
|
static int
|
|
rootgen(int i, Dir *d, void *v)
|
|
{
|
|
if(i >= nxfont)
|
|
return -1;
|
|
dostat(qpath(Qfontdir, i, 0, 0, 0), nil, d);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
fontgen(int i, Dir *d, void *v)
|
|
{
|
|
vlong path;
|
|
Fid *f;
|
|
|
|
f = v;
|
|
path = f->qid.path;
|
|
if(i >= 2*nelem(sizes))
|
|
return -1;
|
|
dostat(qpath(Qsizedir, QFONT(path), sizes[i/2], i&1, 0), nil, d);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
sizegen(int i, Dir *d, void *v)
|
|
{
|
|
vlong path;
|
|
Fid *fid;
|
|
XFont *f;
|
|
int j;
|
|
|
|
fid = v;
|
|
path = fid->qid.path;
|
|
if(i == 0) {
|
|
path += Qfontfile - Qsizedir;
|
|
goto Done;
|
|
}
|
|
i--;
|
|
f = &xfont[QFONT(path)];
|
|
load(f);
|
|
for(j=0; j<nelem(f->range); j++) {
|
|
if(f->range[j] == 0)
|
|
continue;
|
|
if(i == 0) {
|
|
path += Qsubfontfile - Qsizedir;
|
|
path += qpath(0, 0, 0, 0, j);
|
|
goto Done;
|
|
}
|
|
i--;
|
|
}
|
|
return -1;
|
|
|
|
Done:
|
|
dostat(path, nil, d);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
xattach(Req *r)
|
|
{
|
|
dostat(0, &r->ofcall.qid, nil);
|
|
r->fid->qid = r->ofcall.qid;
|
|
respond(r, nil);
|
|
}
|
|
|
|
static void
|
|
xopen(Req *r)
|
|
{
|
|
if(r->ifcall.mode != OREAD) {
|
|
respond(r, "permission denied");
|
|
return;
|
|
}
|
|
r->ofcall.qid = r->fid->qid;
|
|
respond(r, nil);
|
|
}
|
|
|
|
void
|
|
responderrstr(Req *r)
|
|
{
|
|
char err[ERRMAX];
|
|
|
|
rerrstr(err, sizeof err);
|
|
respond(r, err);
|
|
}
|
|
|
|
static void
|
|
xread(Req *r)
|
|
{
|
|
int i, size, height, ascent;
|
|
vlong path;
|
|
Fmt fmt;
|
|
XFont *f;
|
|
char *data;
|
|
Memsubfont *sf;
|
|
Memimage *m;
|
|
|
|
path = r->fid->qid.path;
|
|
switch(QTYPE(path)) {
|
|
case Qroot:
|
|
dirread9p(r, rootgen, nil);
|
|
break;
|
|
case Qfontdir:
|
|
dirread9p(r, fontgen, r->fid);
|
|
break;
|
|
case Qsizedir:
|
|
dirread9p(r, sizegen, r->fid);
|
|
break;
|
|
case Qfontfile:
|
|
fmtstrinit(&fmt);
|
|
f = &xfont[QFONT(path)];
|
|
load(f);
|
|
if(f->unit == 0 && f->loadheight == nil) {
|
|
readstr(r, "font missing\n");
|
|
break;
|
|
}
|
|
height = 0;
|
|
ascent = 0;
|
|
if(f->unit > 0) {
|
|
height = f->height * (int)QSIZE(path)/f->unit + 0.99999999;
|
|
ascent = height - (int)(-f->originy * (int)QSIZE(path)/f->unit + 0.99999999);
|
|
}
|
|
if(f->loadheight != nil)
|
|
f->loadheight(f, QSIZE(path), &height, &ascent);
|
|
fmtprint(&fmt, "%11d %11d\n", height, ascent);
|
|
for(i=0; i<nelem(f->range); i++) {
|
|
if(f->range[i] == 0)
|
|
continue;
|
|
fmtprint(&fmt, "0x%04x 0x%04x x%04x.bit\n", i*SubfontSize, ((i+1)*SubfontSize) - 1, i*SubfontSize);
|
|
}
|
|
data = fmtstrflush(&fmt);
|
|
readstr(r, data);
|
|
free(data);
|
|
break;
|
|
case Qsubfontfile:
|
|
f = &xfont[QFONT(path)];
|
|
load(f);
|
|
if(r->fid->aux == nil) {
|
|
r->fid->aux = mksubfont(f, f->name, QRANGE(path)*SubfontSize, ((QRANGE(path)+1)*SubfontSize)-1, QSIZE(path), QANTIALIAS(path));
|
|
if(r->fid->aux == nil) {
|
|
responderrstr(r);
|
|
return;
|
|
}
|
|
}
|
|
sf = r->fid->aux;
|
|
m = sf->bits;
|
|
if(r->ifcall.offset < 5*12) {
|
|
char *chan;
|
|
if(QANTIALIAS(path))
|
|
chan = "k8";
|
|
else
|
|
chan = "k1";
|
|
data = smprint("%11s %11d %11d %11d %11d ", chan, m->r.min.x, m->r.min.y, m->r.max.x, m->r.max.y);
|
|
readstr(r, data);
|
|
free(data);
|
|
break;
|
|
}
|
|
r->ifcall.offset -= 5*12;
|
|
size = bytesperline(m->r, chantodepth(m->chan)) * Dy(m->r);
|
|
if(r->ifcall.offset < size) {
|
|
readbuf(r, byteaddr(m, m->r.min), size);
|
|
break;
|
|
}
|
|
r->ifcall.offset -= size;
|
|
data = emalloc9p(3*12+6*(sf->n+1));
|
|
sprint(data, "%11d %11d %11d ", sf->n, sf->height, sf->ascent);
|
|
packinfo(sf->info, (uchar*)data+3*12, sf->n);
|
|
readbuf(r, data, 3*12+6*(sf->n+1));
|
|
free(data);
|
|
break;
|
|
}
|
|
respond(r, nil);
|
|
}
|
|
|
|
static void
|
|
xdestroyfid(Fid *fid)
|
|
{
|
|
Memsubfont *sf;
|
|
|
|
sf = fid->aux;
|
|
if(sf == nil)
|
|
return;
|
|
|
|
freememimage(sf->bits);
|
|
free(sf->info);
|
|
free(sf);
|
|
fid->aux = nil;
|
|
}
|
|
|
|
static void
|
|
xstat(Req *r)
|
|
{
|
|
dostat(r->fid->qid.path, nil, &r->d);
|
|
respond(r, nil);
|
|
}
|
|
|
|
Srv xsrv;
|
|
|
|
int
|
|
proccreate(void (*f)(void*), void *a, unsigned i)
|
|
{
|
|
abort();
|
|
}
|
|
|
|
int pflag;
|
|
|
|
static long dirpackage(uchar*, long, Dir**);
|
|
|
|
void
|
|
dump(char *path)
|
|
{
|
|
char *elem, *p, *path0, *err;
|
|
uchar buf[4096];
|
|
Fid fid;
|
|
Qid qid;
|
|
Dir *d;
|
|
Req r;
|
|
int off, i, n;
|
|
|
|
// root
|
|
memset(&fid, 0, sizeof fid);
|
|
dostat(0, &fid.qid, nil);
|
|
qid = fid.qid;
|
|
|
|
path0 = path;
|
|
while(path != nil) {
|
|
p = strchr(path, '/');
|
|
if(p != nil)
|
|
*p = '\0';
|
|
elem = path;
|
|
if(strcmp(elem, "") != 0 && strcmp(elem, ".") != 0) {
|
|
err = xwalk1(&fid, elem, &qid);
|
|
if(err != nil) {
|
|
fprint(2, "%s: %s\n", path0, err);
|
|
exits(err);
|
|
}
|
|
}
|
|
if(p)
|
|
*p++ = '/';
|
|
path = p;
|
|
}
|
|
|
|
memset(&r, 0, sizeof r);
|
|
xsrv.fake = 1;
|
|
|
|
// read and display
|
|
off = 0;
|
|
for(;;) {
|
|
r.srv = &xsrv;
|
|
r.fid = &fid;
|
|
r.ifcall.type = Tread;
|
|
r.ifcall.count = sizeof buf;
|
|
r.ifcall.offset = off;
|
|
r.ofcall.data = (char*)buf;
|
|
r.ofcall.count = 0;
|
|
xread(&r);
|
|
if(r.ofcall.type != Rread) {
|
|
fprint(2, "reading %s: %s\n", path0, r.ofcall.ename);
|
|
exits(r.ofcall.ename);
|
|
}
|
|
n = r.ofcall.count;
|
|
if(n == 0)
|
|
break;
|
|
if(off == 0 && pflag > 1) {
|
|
print("\001");
|
|
}
|
|
off += n;
|
|
if(qid.type & QTDIR) {
|
|
n = dirpackage(buf, n, &d);
|
|
for(i=0; i<n; i++)
|
|
print("%s%s\n", d[i].name, (d[i].mode&DMDIR) ? "/" : "");
|
|
free(d);
|
|
} else
|
|
write(1, buf, n);
|
|
}
|
|
}
|
|
|
|
int
|
|
fontcmp(const void *va, const void *vb)
|
|
{
|
|
XFont *a, *b;
|
|
|
|
a = (XFont*)va;
|
|
b = (XFont*)vb;
|
|
return strcmp(a->name, b->name);
|
|
}
|
|
|
|
void
|
|
main(int argc, char **argv)
|
|
{
|
|
char *mtpt, *srvname;
|
|
|
|
mtpt = nil;
|
|
srvname = "font";
|
|
|
|
ARGBEGIN{
|
|
case 'D':
|
|
chatty9p++;
|
|
break;
|
|
case 'F':
|
|
chattyfuse++;
|
|
break;
|
|
case 'm':
|
|
mtpt = EARGF(usage());
|
|
break;
|
|
case 's':
|
|
srvname = EARGF(usage());
|
|
break;
|
|
case 'p':
|
|
pflag++;
|
|
break;
|
|
default:
|
|
usage();
|
|
}ARGEND
|
|
|
|
xsrv.attach = xattach;
|
|
xsrv.open = xopen;
|
|
xsrv.read = xread;
|
|
xsrv.stat = xstat;
|
|
xsrv.walk1 = xwalk1;
|
|
xsrv.destroyfid = xdestroyfid;
|
|
|
|
fmtinstall('R', Rfmt);
|
|
fmtinstall('P', Pfmt);
|
|
memimageinit();
|
|
loadfonts();
|
|
qsort(xfont, nxfont, sizeof xfont[0], fontcmp);
|
|
|
|
if(pflag) {
|
|
if(argc != 1 || chatty9p || chattyfuse)
|
|
usage();
|
|
dump(argv[0]);
|
|
exits(0);
|
|
}
|
|
|
|
if(pflag || argc != 0)
|
|
usage();
|
|
|
|
/*
|
|
* Check twice -- if there is an exited instance
|
|
* mounted there, the first access will fail but unmount it.
|
|
*/
|
|
if(mtpt && access(mtpt, AEXIST) < 0 && access(mtpt, AEXIST) < 0)
|
|
sysfatal("mountpoint %s does not exist", mtpt);
|
|
|
|
xsrv.foreground = 1;
|
|
threadpostmountsrv(&xsrv, srvname, mtpt, 0);
|
|
}
|
|
|
|
/*
|
|
/sys/src/libc/9sys/dirread.c
|
|
*/
|
|
static
|
|
long
|
|
dirpackage(uchar *buf, long ts, Dir **d)
|
|
{
|
|
char *s;
|
|
long ss, i, n, nn, m;
|
|
|
|
*d = nil;
|
|
if(ts <= 0)
|
|
return 0;
|
|
|
|
/*
|
|
* first find number of all stats, check they look like stats, & size all associated strings
|
|
*/
|
|
ss = 0;
|
|
n = 0;
|
|
for(i = 0; i < ts; i += m){
|
|
m = BIT16SZ + GBIT16(&buf[i]);
|
|
if(statcheck(&buf[i], m) < 0)
|
|
break;
|
|
ss += m;
|
|
n++;
|
|
}
|
|
|
|
if(i != ts)
|
|
return -1;
|
|
|
|
*d = malloc(n * sizeof(Dir) + ss);
|
|
if(*d == nil)
|
|
return -1;
|
|
|
|
/*
|
|
* then convert all buffers
|
|
*/
|
|
s = (char*)*d + n * sizeof(Dir);
|
|
nn = 0;
|
|
for(i = 0; i < ts; i += m){
|
|
m = BIT16SZ + GBIT16((uchar*)&buf[i]);
|
|
if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){
|
|
free(*d);
|
|
*d = nil;
|
|
return -1;
|
|
}
|
|
nn++;
|
|
s += m;
|
|
}
|
|
|
|
return nn;
|
|
}
|
|
|