xref: /plan9/sys/src/libdraw/init.c (revision 5d9de2d38d2503efca29e12e0e32036368a7a75f)
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
drawshutdown(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
geninitdraw(char * devdir,void (* error)(Display *,char *),char * fontname,char * label,char * windir,int ref)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
initdraw(void (* error)(Display *,char *),char * fontname,char * label)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
gengetwindow(Display * d,char * winname,Image ** winp,Screen ** scrp,int ref)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
getwindow(Display * d,int ref)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*
initdisplay(char * dev,char * win,void (* error)(Display *,char *))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 	info[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
closedisplay(Display * disp)339 closedisplay(Display *disp)
340 {
341 	_closedisplay(disp, 0);
342 }
343 
344 static void
_closedisplay(Display * disp,int isshutdown)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
lockdisplay(Display * disp)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
unlockdisplay(Display * disp)398 unlockdisplay(Display *disp)
399 {
400 	qunlock(&disp->qlock);
401 }
402 
403 void
drawerror(Display * d,char * s)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
doflush(Display * d)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
flushimage(Display * d,int visible)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*
bufimage(Display * d,int n)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 
469