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