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