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