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