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