]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libdraw/init.c
python: arm64 support
[plan9front.git] / sys / src / libdraw / init.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4
5 Display *display;
6 Font    *font;
7 Image   *screen;
8
9 static char deffontname[] = "*default*";
10 Screen  *_screen;
11
12 int     debuglockdisplay = 0;
13
14 static void _closedisplay(Display*, int);
15
16 /* note handler */
17 static void
18 drawshutdown(void)
19 {
20         Display *d;
21
22         d = display;
23         if(d != nil){
24                 display = nil;
25                 _closedisplay(d, 1);
26         }
27 }
28
29 int
30 geninitdraw(char *devdir, void(*error)(Display*, char*), char *fontname, char *label, char *windir, int ref)
31 {
32         int fd, n;
33         Subfont *df;
34         char buf[128];
35
36         display = initdisplay(devdir, windir, error);
37         if(display == nil)
38                 return -1;
39
40         /*
41          * Set up default font
42          */
43         df = getdefont(display);
44         display->defaultsubfont = df;
45         if(df == nil){
46     Error:
47                 closedisplay(display);
48                 display = nil;
49                 return -1;
50         }
51         if(fontname == nil){
52                 fd = open("/env/font", OREAD);
53                 if(fd >= 0){
54                         n = read(fd, buf, sizeof(buf));
55                         if(n>0 && n<sizeof buf-1){
56                                 buf[n] = 0;
57                                 fontname = buf;
58                         }
59                         close(fd);
60                 }
61         }
62         /*
63          * Build fonts with caches==depth of screen, for speed.
64          * If conversion were faster, we'd use 0 and save memory.
65          */
66         if(fontname == nil){
67                 snprint(buf, sizeof buf, "%d %d\n0 %d\t%s\n", df->height, df->ascent,
68                         df->n-1, deffontname);
69 //BUG: Need something better for this   installsubfont("*default*", df);
70                 font = buildfont(display, buf, deffontname);
71                 if(font == nil)
72                         goto Error;
73         }else{
74                 font = openfont(display, fontname);     /* BUG: grey fonts */
75                 if(font == nil)
76                         goto Error;
77         }
78         display->defaultfont = font;
79
80         /*
81          * Write label; ignore errors (we might not be running under rio)
82          */
83         if(label != nil){
84                 snprint(buf, sizeof buf, "%s/label", display->windir);
85                 fd = open(buf, OREAD);
86                 if(fd >= 0){
87                         read(fd, display->oldlabel, (sizeof display->oldlabel)-1);
88                         close(fd);
89                         fd = create(buf, OWRITE, 0666);
90                         if(fd >= 0){
91                                 write(fd, label, strlen(label));
92                                 close(fd);
93                         }
94                 }
95         }
96
97         snprint(buf, sizeof buf, "%s/winname", display->windir);
98         if(gengetwindow(display, buf, &screen, &_screen, ref) < 0)
99                 goto Error;
100
101         atexit(drawshutdown);
102
103         return 1;
104 }
105
106 int
107 initdraw(void(*error)(Display*, char*), char *fontname, char *label)
108 {
109         static char dev[] = "/dev";
110
111         return geninitdraw(dev, error, fontname, label, dev, Refnone);
112 }
113
114 /*
115  * Attach, or possibly reattach, to window.
116  * If reattaching, maintain value of screen pointer.
117  */
118 int
119 gengetwindow(Display *d, char *winname, Image **winp, Screen **scrp, int ref)
120 {
121         int n, fd;
122         char buf[64+1], obuf[64+1];
123         Image *image;
124         Rectangle r;
125
126         obuf[0] = 0;
127 retry:
128         fd = open(winname, OREAD);
129         if(fd<0 || (n=read(fd, buf, sizeof buf-1))<=0){
130                 if(fd >= 0) close(fd);
131                 strcpy(buf, "noborder");
132                 image = d->image;
133         }else{
134                 close(fd);
135                 buf[n] = '\0';
136                 image = namedimage(d, buf);
137                 if(image == nil){
138                         /*
139                          * theres a race where the winname can change after
140                          * we read it, so keep trying as long as the name
141                          * keeps changing.
142                          */
143                         if(strcmp(buf, obuf) != 0){
144                                 strcpy(obuf, buf);
145                                 goto retry;
146                         }
147                 }
148         }
149         if(*winp != nil){
150                 _freeimage1(*winp);
151                 if((*scrp)->image != d->image)
152                         freeimage((*scrp)->image);
153                 freescreen(*scrp);
154                 *scrp = nil;
155         }
156         if(image == nil){
157                 *winp = nil;
158                 d->screenimage = nil;
159                 return -1;
160         }
161         d->screenimage = image;
162         *scrp = allocscreen(image, d->white, 0);
163         if(*scrp == nil){
164                 *winp = nil;
165                 d->screenimage = nil;
166                 if(image != d->image)
167                         freeimage(image);
168                 return -1;
169         }
170         r = image->r;
171         if(strncmp(buf, "noborder", 8) != 0)
172                 r = insetrect(r, Borderwidth);
173         *winp = _allocwindow(*winp, *scrp, r, ref, DWhite);
174         if(*winp == nil){
175                 freescreen(*scrp);
176                 *scrp = nil;
177                 d->screenimage = nil;
178                 if(image != d->image)
179                         freeimage(image);
180                 return -1;
181         }
182         d->screenimage = *winp;
183         return 1;
184 }
185
186 int
187 getwindow(Display *d, int ref)
188 {
189         char winname[128];
190
191         snprint(winname, sizeof winname, "%s/winname", d->windir);
192         return gengetwindow(d, winname, &screen, &_screen, ref);
193 }
194
195 #define NINFO   12*12
196
197 Display*
198 initdisplay(char *dev, char *win, void(*error)(Display*, char*))
199 {
200         char buf[128], info[NINFO+1], *t, isnew;
201         int n, datafd, ctlfd, reffd;
202         Display *disp;
203         Dir *dir;
204         Image *image;
205
206         fmtinstall('P', Pfmt);
207         fmtinstall('R', Rfmt);
208         if(dev == nil)
209                 dev = "/dev";
210         if(win == nil)
211                 win = "/dev";
212         if(strlen(dev)>sizeof buf-25 || strlen(win)>sizeof buf-25){
213                 werrstr("initdisplay: directory name too long");
214                 return nil;
215         }
216         t = strdup(win);
217         if(t == nil)
218                 return nil;
219
220         sprint(buf, "%s/draw/new", dev);
221         ctlfd = open(buf, ORDWR|OCEXEC);
222         if(ctlfd < 0){
223     Error1:
224                 free(t);
225                 werrstr("initdisplay: %s: %r", buf);
226                 return nil;
227         }
228         if((n=read(ctlfd, info, sizeof info)) < 12){
229     Error2:
230                 close(ctlfd);
231                 goto Error1;
232         }
233         if(n==NINFO+1)
234                 n = NINFO;
235         info[n] = '\0';
236         isnew = 0;
237         if(n < NINFO)   /* this will do for now, we need something better here */
238                 isnew = 1;
239         sprint(buf, "%s/draw/%d/data", dev, atoi(info+0*12));
240         datafd = open(buf, ORDWR|OCEXEC);
241         if(datafd < 0)
242                 goto Error2;
243         sprint(buf, "%s/draw/%d/refresh", dev, atoi(info+0*12));
244         reffd = open(buf, OREAD|OCEXEC);
245         if(reffd < 0){
246     Error3:
247                 close(datafd);
248                 goto Error2;
249         }
250         disp = mallocz(sizeof(Display), 1);
251         if(disp == nil){
252     Error4:
253                 close(reffd);
254                 goto Error3;
255         }
256         image = nil;
257         if(0){
258     Error5:
259                 free(image);
260                 free(disp);
261                 goto Error4;
262         }
263         if(n >= NINFO){
264                 image = mallocz(sizeof(Image), 1);
265                 if(image == nil)
266                         goto Error5;
267                 image->display = disp;
268                 image->id = 0;
269                 image->chan = strtochan(info+2*12);
270                 image->depth = chantodepth(image->chan);
271                 image->repl = atoi(info+3*12);
272                 image->r.min.x = atoi(info+4*12);
273                 image->r.min.y = atoi(info+5*12);
274                 image->r.max.x = atoi(info+6*12);
275                 image->r.max.y = atoi(info+7*12);
276                 image->clipr.min.x = atoi(info+8*12);
277                 image->clipr.min.y = atoi(info+9*12);
278                 image->clipr.max.x = atoi(info+10*12);
279                 image->clipr.max.y = atoi(info+11*12);
280         }
281
282         disp->_isnewdisplay = isnew;
283         disp->bufsize = iounit(datafd);
284         if(disp->bufsize <= 0)
285                 disp->bufsize = 8000;
286         if(disp->bufsize < 512){
287                 werrstr("iounit %d too small", disp->bufsize);
288                 goto Error5;
289         }
290         disp->buf = malloc(disp->bufsize+5);    /* +5 for flush message */
291         if(disp->buf == nil)
292                 goto Error5;
293
294         disp->image = image;
295         disp->dirno = atoi(info+0*12);
296         disp->fd = datafd;
297         disp->ctlfd = ctlfd;
298         disp->reffd = reffd;
299         disp->bufp = disp->buf;
300         disp->error = error;
301         disp->windir = t;
302         disp->devdir = strdup(dev);
303         qlock(&disp->qlock);
304         disp->white = allocimage(disp, Rect(0, 0, 1, 1), GREY1, 1, DWhite);
305         disp->black = allocimage(disp, Rect(0, 0, 1, 1), GREY1, 1, DBlack);
306         if(disp->white == nil || disp->black == nil){
307                 free(disp->devdir);
308                 free(disp->white);
309                 free(disp->black);
310                 goto Error5;
311         }
312         disp->opaque = disp->white;
313         disp->transparent = disp->black;
314         dir = dirfstat(ctlfd);
315         if(dir!=nil && dir->type=='i'){
316                 disp->local = 1;
317                 disp->dataqid = dir->qid.path;
318         }
319         if(dir!=nil && dir->qid.vers==1)        /* other way to tell */
320                 disp->_isnewdisplay = 1;
321         free(dir);
322
323         return disp;
324 }
325
326 /*
327  * Call with d unlocked.
328  * Note that disp->defaultfont and defaultsubfont are not freed here.
329  */
330 void
331 closedisplay(Display *disp)
332 {
333         _closedisplay(disp, 0);
334 }
335
336 static void
337 _closedisplay(Display *disp, int isshutdown)
338 {
339         int fd;
340         char buf[128];
341
342         if(disp == nil)
343                 return;
344         if(disp == display)
345                 display = nil;
346         if(disp->oldlabel[0]){
347                 snprint(buf, sizeof buf, "%s/label", disp->windir);
348                 fd = open(buf, OWRITE);
349                 if(fd >= 0){
350                         write(fd, disp->oldlabel, strlen(disp->oldlabel));
351                         close(fd);
352                 }
353         }
354
355         /*
356          * if we're shutting down, don't free all the resources.
357          * if other procs are getting shot down by notes too,
358          * one might get shot down while holding the malloc lock.
359          * just let the kernel clean things up when we exit.
360          */
361         if(isshutdown)
362                 return;
363
364         free(disp->devdir);
365         free(disp->windir);
366         freeimage(disp->white);
367         freeimage(disp->black);
368         close(disp->fd);
369         close(disp->ctlfd);
370         /* should cause refresh slave to shut down */
371         close(disp->reffd);
372         qunlock(&disp->qlock);
373         free(disp);
374 }
375
376 void
377 lockdisplay(Display *disp)
378 {
379         if(debuglockdisplay){
380                 /* avoid busy looping; it's rare we collide anyway */
381                 while(!canqlock(&disp->qlock))
382                         sleep(1000);
383         }else
384                 qlock(&disp->qlock);
385 }
386
387 void
388 unlockdisplay(Display *disp)
389 {
390         qunlock(&disp->qlock);
391 }
392
393 void
394 drawerror(Display *d, char *s)
395 {
396         char err[ERRMAX];
397
398         if(d != nil && d->error != nil)
399                 (*d->error)(d, s);
400         else{
401                 errstr(err, sizeof err);
402                 fprint(2, "draw: %s: %s\n", s, err);
403                 exits(s);
404         }
405 }
406
407 static
408 int
409 doflush(Display *d)
410 {
411         int n;
412
413         n = d->bufp-d->buf;
414         if(n <= 0)
415                 return 1;
416
417         if(write(d->fd, d->buf, n) != n){
418                 d->bufp = d->buf;       /* might as well; chance of continuing */
419                 return -1;
420         }
421         d->bufp = d->buf;
422         return 1;
423 }
424
425 int
426 flushimage(Display *d, int visible)
427 {
428         if(d == nil)
429                 return 0;
430         if(visible){
431                 *d->bufp++ = 'v';       /* five bytes always reserved for this */
432                 if(d->_isnewdisplay){
433                         BPLONG(d->bufp, d->screenimage->id);
434                         d->bufp += 4;
435                 }
436         }
437         return doflush(d);
438 }
439
440 uchar*
441 bufimage(Display *d, int n)
442 {
443         uchar *p;
444
445         if(n<0 || n>d->bufsize){
446                 werrstr("bad count in bufimage");
447                 return nil;
448         }
449         if(d->bufp+n > d->buf+d->bufsize)
450                 if(doflush(d) < 0)
451                         return nil;
452         p = d->bufp;
453         d->bufp += n;
454         return p;
455 }
456