ref: ad9d494cf1e59520cf05c68d0ef8cba86ec8c34f
parent: a2d6c698b2607561e11c2e155888b064e4f40098
author: halfwit <michaelmisch1985@gmail.com>
date: Sun Oct 11 10:56:33 PDT 2020
Some work on porting draw
--- a/libdraw/draw.h
+++ b/libdraw/draw.h
@@ -1,3 +1,5 @@
+#include <pthread.h>
+
typedef struct Cachefont Cachefont;
typedef struct Cacheinfo Cacheinfo;
typedef struct Cachesubf Cachesubf;
@@ -16,7 +18,7 @@
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int Rune;
-typedef void* nil;
+#define nil ((void*)0)
enum
{
@@ -204,6 +206,7 @@
Image *windows;
Image *screenimage;
int _isnewdisplay;
+ pthread_mutex_t qlock;
};
struct Image
@@ -338,8 +341,8 @@
extern int flushimage(Display*, int);
extern int freeimage(Image*);
extern int _freeimage1(Image*);
-extern int geninitdraw(char*, void(*)(Display*, char*), char*, char*, char*, int);
-extern int initdraw(void(*)(Display*, char*), char*, char*);
+extern int geninitdraw(char*, void(*)(Display*, char*), char*, char*, int);
+extern int initdraw(void(*)(Display*, char*), char*);
extern int newwindow(char*);
extern Display* initdisplay(char*, char*, void(*)(Display*, char*));
extern int loadimage(Image*, Rectangle, uchar*, int);
--- /dev/null
+++ b/libdraw/init.c
@@ -1,0 +1,459 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include "draw.h"
+#include "../c9/c9.h"
+
+Display *_display;
+_Font *font;
+Image *screen;
+C9ctx *ctx;
+
+static char deffontname[] = "*default*";
+Screen *_screen;
+
+int debuglockdisplay = 0;
+
+static void _closedisplay(Display*, int);
+
+/* note handler */
+static void
+drawshutdown(void)
+{
+ Display *d;
+
+ d = _display;
+ if(d != nil){
+ _display = nil;
+ _closedisplay(d, 1);
+ }
+}
+
+int
+geninitdraw(char *devdir, void(*error)(Display*, char*), char *label, char *windir, int ref)
+{
+ C9tag ftag, ltag, wtag;
+ int fd, n;
+ char *fontname;
+ char path[256]; /* Maxpath */
+ Subfont *df;
+
+ _display = initdisplay(devdir, windir, error);
+ if(_display == nil)
+ return -1;
+
+ /*
+ * Set up default font
+ */
+ df = getdefont(_display);
+ _display->defaultsubfont = df;
+ if(df == nil){
+ Error:
+ closedisplay(_display);
+ _display = nil;
+ return -1;
+ }
+
+ sprintf(path, "/env/font");
+ c9walk(ctx, &ftag, 1, fd, &path);
+ c9open(ctx, &ftag, fd, O_RDONLY);
+ if(fd >= 0){
+ c9read(ctx, &ftag, fd, 0, 128);
+ //fontname = buf;
+ }
+ }
+
+ /*
+ * Build fonts with caches==depth of screen, for speed.
+ * If conversion were faster, we'd use 0 and save memory.
+ */
+ if(fontname == nil){
+ snprintf(buf, sizeof buf, "%d %d\n0 %d\t%s\n", df->height, df->ascent,
+ df->n-1, deffontname);
+//BUG: Need something better for this installsubfont("*default*", df);
+ font = buildfont(_display, buf, deffontname);
+ if(font == nil)
+ goto Error;
+ }else{
+ font = openfont(_display, fontname); /* BUG: grey fonts */
+ if(font == nil)
+ goto Error;
+ }
+ _display->defaultfont = font;
+
+ /*
+ * Write label; ignore errors (we might not be running under rio)
+ */
+ if(label != nil){
+ snprintf(buf, sizeof buf, "%s/label", _display->windir);
+ fd = open(buf, OREAD);
+ if(fd >= 0){
+ read(fd, _display->oldlabel, (sizeof _display->oldlabel)-1);
+ close(fd);
+ fd = create(buf, OWRITE, 0666);
+ if(fd >= 0){
+ write(fd, label, strlen(label));
+ close(fd);
+ }
+ }
+ }
+
+ snprintf(buf, sizeof buf, "%s/winname", _display->windir);
+ if(gengetwindow(_display, buf, &screen, &_screen, ref) < 0)
+ goto Error;
+
+ atexit(drawshutdown);
+
+ return 1;
+}
+
+int
+initdraw(void(*error)(Display*, char*), char *label)
+{
+ static char dev[] = "/dev";
+
+ return geninitdraw(dev, error, label, dev, Refnone);
+}
+
+/*
+ * Attach, or possibly reattach, to window.
+ * If reattaching, maintain value of screen pointer.
+ */
+int
+gengetwindow(Display *d, char *winname, Image **winp, Screen **scrp, int ref)
+{
+ int n, fd;
+ char buf[64+1], obuf[64+1];
+ Image *image;
+ Rectangle r;
+
+ obuf[0] = 0;
+retry:
+ fd = open(winname, OREAD);
+ if(fd<0 || (n=read(fd, buf, sizeof buf-1))<=0){
+ if((image=d->image) == nil){
+ *winp = nil;
+ d->screenimage = nil;
+ return -1;
+ }
+ strcpy(buf, "noborder");
+ }else{
+ close(fd);
+ buf[n] = '\0';
+ image = namedimage(d, buf);
+ if(image == nil){
+ /*
+ * theres a race where the winname can change after
+ * we read it, so keep trying as long as the name
+ * keeps changing.
+ */
+ if(strcmp(buf, obuf) != 0){
+ strcpy(obuf, buf);
+ goto retry;
+ }
+ }
+ if(*winp != nil){
+ _freeimage1(*winp);
+ freeimage((*scrp)->image);
+ freescreen(*scrp);
+ *scrp = nil;
+ }
+ if(image == nil){
+ *winp = nil;
+ d->screenimage = nil;
+ return -1;
+ }
+ }
+
+ d->screenimage = image;
+ *scrp = allocscreen(image, d->white, 0);
+ if(*scrp == nil){
+ *winp = nil;
+ d->screenimage = nil;
+ freeimage(image);
+ return -1;
+ }
+
+ r = image->r;
+ if(strncmp(buf, "noborder", 8) != 0)
+ r = insetrect(image->r, Borderwidth);
+ *winp = _allocwindow(*winp, *scrp, r, ref, DWhite);
+ if(*winp == nil){
+ freescreen(*scrp);
+ *scrp = nil;
+ d->screenimage = nil;
+ freeimage(image);
+ return -1;
+ }
+ d->screenimage = *winp;
+ return 1;
+}
+
+int
+getwindow(Display *d, int ref)
+{
+ char winname[128];
+
+ snprintf(winname, sizeof winname, "%s/winname", d->windir);
+ return gengetwindow(d, winname, &screen, &_screen, ref);
+}
+
+#define NINFO 12*12
+
+Display*
+initdisplay(char *dev, char *win, void(*error)(Display*, char*))
+{
+ char buf[128], info[NINFO+1], *t, isnew;
+ int n, datafd, ctlfd, reffd;
+ Display *disp;
+ Image *image;
+
+ if(dev == nil)
+ dev = "/dev";
+ if(win == nil)
+ win = "/dev";
+ if(strlen(dev)>sizeof buf-25 || strlen(win)>sizeof buf-25){
+ return nil;
+ }
+ t = strdup(win);
+ if(t == nil)
+ return nil;
+
+ sprintf(buf, "%s/draw/new", dev);
+ ctlfd = open(buf, ORDWR|OCEXEC);
+ if(ctlfd < 0){
+ Error1:
+ free(t);
+
+ return nil;
+ }
+ if((n=read(ctlfd, info, sizeof info)) < 12){
+ Error2:
+ close(ctlfd);
+ goto Error1;
+ }
+ if(n==NINFO+1)
+ n = NINFO;
+ info[n] = '\0';
+ isnew = 0;
+ if(n < NINFO) /* this will do for now, we need something better here */
+ isnew = 1;
+ sprint(buf, "%s/draw/%d/data", dev, atoi(info+0*12));
+ datafd = open(buf, ORDWR|OCEXEC);
+ if(datafd < 0)
+ goto Error2;
+ sprint(buf, "%s/draw/%d/refresh", dev, atoi(info+0*12));
+ reffd = open(buf, OREAD|OCEXEC);
+ if(reffd < 0){
+ Error3:
+ close(datafd);
+ goto Error2;
+ }
+ disp = mallocz(sizeof(Display), 1);
+ if(disp == nil){
+ Error4:
+ close(reffd);
+ goto Error3;
+ }
+ image = nil;
+ if(0){
+ Error5:
+ free(image);
+ free(disp);
+ goto Error4;
+ }
+ if(n >= NINFO){
+ image = mallocz(sizeof(Image), 1);
+ if(image == nil)
+ goto Error5;
+ image->display = disp;
+ image->id = 0;
+ image->chan = strtochan(info+2*12);
+ image->depth = chantodepth(image->chan);
+ image->repl = atoi(info+3*12);
+ image->r.min.x = atoi(info+4*12);
+ image->r.min.y = atoi(info+5*12);
+ image->r.max.x = atoi(info+6*12);
+ image->r.max.y = atoi(info+7*12);
+ image->clipr.min.x = atoi(info+8*12);
+ image->clipr.min.y = atoi(info+9*12);
+ image->clipr.max.x = atoi(info+10*12);
+ image->clipr.max.y = atoi(info+11*12);
+ }
+
+ disp->_isnewdisplay = isnew;
+ disp->bufsize = iounit(datafd);
+ if(disp->bufsize <= 0)
+ disp->bufsize = 8000;
+ if(disp->bufsize < 512){
+ goto Error5;
+ }
+ disp->buf = malloc(disp->bufsize+5); /* +5 for flush message */
+ if(disp->buf == nil)
+ goto Error5;
+
+ disp->image = image;
+ disp->dirno = atoi(info+0*12);
+ disp->fd = datafd;
+ disp->ctlfd = ctlfd;
+ disp->reffd = reffd;
+ disp->bufp = disp->buf;
+ disp->error = error;
+ disp->windir = t;
+ disp->devdir = strdup(dev);
+ pthread_mutex_lock(&disp->qlock);
+ disp->white = allocimage(disp, Rect(0, 0, 1, 1), GREY1, 1, DWhite);
+ disp->black = allocimage(disp, Rect(0, 0, 1, 1), GREY1, 1, DBlack);
+ if(disp->white == nil || disp->black == nil){
+ free(disp->devdir);
+ free(disp->white);
+ free(disp->black);
+ goto Error5;
+ }
+ disp->opaque = disp->white;
+ disp->transparent = disp->black;
+
+ //dir = dirfstat(ctlfd);
+ //if(dir!=nil && dir->type=='i'){
+ // disp->local = 1;
+ // disp->dataqid = dir->qid.path;
+ //}
+ //if(dir!=nil && dir->qid.vers==1) /* other way to tell */
+ // disp->_isnewdisplay = 1;
+ //free(dir);
+
+ return disp;
+}
+
+/*
+ * Call with d unlocked.
+ * Note that disp->defaultfont and defaultsubfont are not freed here.
+ */
+void
+closedisplay(Display *disp)
+{
+ _closedisplay(disp, 0);
+}
+
+static void
+_closedisplay(Display *disp, int isshutdown)
+{
+ int fd;
+ char buf[128];
+
+ if(disp == nil)
+ return;
+ if(disp == _display)
+ _display = nil;
+ if(disp->oldlabel[0]){
+ snprint(buf, sizeof buf, "%s/label", disp->windir);
+ fd = open(buf, OWRITE);
+ if(fd >= 0){
+ write(fd, disp->oldlabel, strlen(disp->oldlabel));
+ close(fd);
+ }
+ }
+
+ /*
+ * if we're shutting down, don't free all the resources.
+ * if other procs are getting shot down by notes too,
+ * one might get shot down while holding the malloc lock.
+ * just let the kernel clean things up when we exit.
+ */
+ if(isshutdown)
+ return;
+
+ free(disp->devdir);
+ free(disp->windir);
+ freeimage(disp->white);
+ freeimage(disp->black);
+ close(disp->fd);
+ close(disp->ctlfd);
+ /* should cause refresh slave to shut down */
+ close(disp->reffd);
+ pthread_mutex_unlock(&disp->qlock);
+ free(disp);
+}
+
+void
+lockdisplay(Display *disp)
+{
+ if(debuglockdisplay){
+ /* avoid busy looping; it's rare we collide anyway */
+ while(!pthread_mutex_trylock(&disp->qlock))
+ sleep(1000);
+ }else
+ pthread_mutex_lock(&disp->qlock);
+}
+
+void
+unlockdisplay(Display *disp)
+{
+ pthread_mutex_unlock(&disp->qlock);
+}
+
+void
+drawerror(Display *d, char *s)
+{
+ char err[128]; /* ERRMAX */
+
+ if(d != nil && d->error != nil)
+ (*d->error)(d, s);
+ else{
+ errstr(err, sizeof err);
+ fprint(2, "draw: %s: %s\n", s, err);
+ exits(s);
+ }
+}
+
+static
+int
+doflush(Display *d)
+{
+ int n;
+
+ n = d->bufp-d->buf;
+ if(n <= 0)
+ return 1;
+
+ if(write(d->fd, d->buf, n) != n){
+ d->bufp = d->buf; /* might as well; chance of continuing */
+ return -1;
+ }
+ d->bufp = d->buf;
+ return 1;
+}
+
+int
+flushimage(Display *d, int visible)
+{
+ if(d == nil)
+ return 0;
+ if(visible){
+ *d->bufp++ = 'v'; /* five bytes always reserved for this */
+ if(d->_isnewdisplay){
+ BPLONG(d->bufp, d->screenimage->id);
+ d->bufp += 4;
+ }
+ }
+ return doflush(d);
+}
+
+uchar*
+bufimage(Display *d, int n)
+{
+ uchar *p;
+
+ if(n<0 || n>d->bufsize){
+ return nil;
+ }
+ if(d->bufp+n > d->buf+d->bufsize)
+ if(doflush(d) < 0)
+ return nil;
+ p = d->bufp;
+ d->bufp += n;
+ return p;
+}
+
--- /dev/null
+++ b/libdraw/loadimage.c
@@ -1,0 +1,53 @@
+#include <stdio.h>
+#include <string.h>
+#include "draw.h"
+
+int
+loadimage(Image *i, Rectangle r, uchar *data, int ndata)
+{
+ long dx, dy;
+ int n, bpl;
+ uchar *a;
+ int chunk;
+
+ chunk = i->display->bufsize - 64;
+
+ if(!rectinrect(r, i->r)){
+ return -1;
+ }
+ bpl = bytesperline(r, i->depth);
+ n = bpl*Dy(r);
+ if(n > ndata){
+ return -1;
+ }
+ ndata = 0;
+ while(r.max.y > r.min.y){
+ dy = Dy(r);
+ dx = Dx(r);
+ if(dy*bpl > chunk)
+ dy = chunk/bpl;
+ if(dy <= 0){
+ dy = 1;
+ dx = ((chunk*dx)/bpl) & ~7;
+ n = bytesperline(Rect(r.min.x, r.min.y, r.min.x+dx, r.min.y+dy), i->depth);
+ if(loadimage(i, Rect(r.min.x+dx, r.min.y, r.max.x, r.min.y+dy), data+n, bpl-n) < 0)
+ return -1;
+ } else
+ n = dy*bpl;
+ a = bufimage(i->display, 21+n);
+ if(a == nil){
+ return -1;
+ }
+ a[0] = 'y';
+ BPLONG(a+1, i->id);
+ BPLONG(a+5, r.min.x);
+ BPLONG(a+9, r.min.y);
+ BPLONG(a+13, r.min.x+dx);
+ BPLONG(a+17, r.min.y+dy);
+ memmove(a+21, data, n);
+ ndata += dy*bpl;
+ data += dy*bpl;
+ r.min.y += dy;
+ }
+ return ndata;
+}
--- a/screen.c
+++ b/screen.c
@@ -49,7 +49,6 @@
p += x9di.bpl;
r.min.y++;
}
-
End:
flushimage(_display, 1);
}
--- a/x9dev.c
+++ b/x9dev.c
@@ -161,8 +161,7 @@
c->ctx->r = x9r;
/* We have 9p, we can init */
- //if(initdraw(c->ctx, "x9dev")< 0)
- if(initdraw(NULL, 0, "x9dev") < 0)
+ if(initdraw(NULL, "x9dev") < 0)
FatalError("can't open display");
x9di.depth = screen->depth;