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