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