1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <mouse.h>
5 
6 Display	*display;
7 Font	*font;
8 Image	*screen;
9 int	_drawdebug;
10 
11 Screen	*_screen;
12 
13 int		debuglockdisplay = 1;
14 char	*winsize;
15 
16 int		visibleclicks = 0;
17 Image	*mousebuttons;
18 Image	*mousesave;
19 Mouse	_drawmouse;
20 
21 void
needdisplay(void)22 needdisplay(void)
23 {
24 }
25 
26 /*
27 static void
28 drawshutdown(void)
29 {
30 	Display *d;
31 
32 	d = display;
33 	if(d){
34 		display = nil;
35 		closedisplay(d);
36 	}
37 }
38 */
39 
40 int
geninitdraw(char * devdir,void (* error)(Display *,char *),char * fontname,char * label,char * windir,int ref)41 geninitdraw(char *devdir, void(*error)(Display*, char*), char *fontname, char *label, char *windir, int ref)
42 {
43 	char *p;
44 
45 	if(label == nil)
46 		label = argv0;
47 	display = _initdisplay(error, label);
48 	if(display == nil)
49 		return -1;
50 
51 	/*
52 	 * Set up default font
53 	 */
54 	if(openfont(display, "*default*") == 0) {
55 		fprint(2, "imageinit: can't open default subfont: %r\n");
56     Error:
57 		closedisplay(display);
58 		display = nil;
59 		return -1;
60 	}
61 	if(fontname == nil)
62 		fontname = getenv("font");
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 		fontname = strdup("*default*");
70 
71 	font = openfont(display, fontname);
72 	if(font == nil){
73 		fprint(2, "imageinit: can't open font %s: %r\n", fontname);
74 		goto Error;
75 	}
76 	display->defaultfont = font;
77 
78 	_screen = allocscreen(display->image, display->white, 0);
79 	display->screenimage = display->image;	/* _allocwindow wants screenimage->chan */
80 	screen = _allocwindow(nil, _screen, display->image->r, Refnone, DWhite);
81 	if(screen == nil){
82 		fprint(2, "_allocwindow: %r\n");
83 		goto Error;
84 	}
85 	display->screenimage = screen;
86 	draw(screen, screen->r, display->white, nil, ZP);
87 	flushimage(display, 1);
88 
89 	p = getenv("visibleclicks");
90 	visibleclicks = p != nil && *p == '1';
91 	if(visibleclicks) {
92 		Font *f;
93 
94 		f = display->defaultfont;
95 		mousebuttons = allocimage(display, Rect(0,0,64,22), screen->chan, 0, DWhite);
96 		border(mousebuttons, mousebuttons->r, 1, display->black, ZP);
97 		border(mousebuttons, Rect(0, 0, 22, 22), 1, display->black, ZP);
98 		border(mousebuttons, Rect(42, 0, 64, 22), 1, display->black, ZP);
99 		string(mousebuttons, Pt(10-stringwidth(display->defaultfont, "1")/2, 11-f->height/2), display->black, ZP, display->defaultfont, "1");
100 		string(mousebuttons, Pt(21+10-stringwidth(display->defaultfont, "2")/2, 11-f->height/2), display->black, ZP, display->defaultfont, "2");
101 		string(mousebuttons, Pt(42+10-stringwidth(display->defaultfont, "3")/2, 11-f->height/2), display->black, ZP, display->defaultfont, "3");
102 		mousesave = allocimage(display, Rect(0,0,64,22), screen->chan, 0, 0);
103 	}
104 
105 	/*
106 	 * I don't see any reason to go away gracefully,
107 	 * and if some other proc exits holding the display
108 	 * lock, this atexit call never finishes.
109 	 *
110 	 * atexit(drawshutdown);
111 	 */
112 	return 1;
113 }
114 
115 int
initdraw(void (* error)(Display *,char *),char * fontname,char * label)116 initdraw(void (*error)(Display*, char*), char *fontname, char *label)
117 {
118 	return geninitdraw("/dev", error, fontname, label, "/dev", Refnone);
119 }
120 
121 extern int _freeimage1(Image*);
122 
123 static Image*
getimage0(Display * d,Image * image)124 getimage0(Display *d, Image *image)
125 {
126 	char info[12*12+1];
127 	uchar *a;
128 	int n;
129 
130 	/*
131 	 * If there's an old screen, it has id 0.  The 'J' request below
132 	 * will try to install the new screen as id 0, so the old one
133 	 * must be freed first.
134 	 */
135 	if(image){
136 		_freeimage1(image);
137 		memset(image, 0, sizeof(Image));
138 	}
139 
140 	a = bufimage(d, 2);
141 	a[0] = 'J';
142 	a[1] = 'I';
143 	if(flushimage(d, 0) < 0){
144 		fprint(2, "cannot read screen info: %r\n");
145 		return nil;
146 	}
147 
148 	n = _displayrddraw(d, info, sizeof info);
149 	if(n != 12*12){
150 		fprint(2, "short screen info\n");
151 		return nil;
152 	}
153 
154 	if(image == nil){
155 		image = mallocz(sizeof(Image), 1);
156 		if(image == nil){
157 			fprint(2, "cannot allocate image: %r\n");
158 			return nil;
159 		}
160 	}
161 
162 	image->display = d;
163 	image->id = 0;
164 	image->chan = strtochan(info+2*12);
165 	image->depth = chantodepth(image->chan);
166 	image->repl = atoi(info+3*12);
167 	image->r.min.x = atoi(info+4*12);
168 	image->r.min.y = atoi(info+5*12);
169 	image->r.max.x = atoi(info+6*12);
170 	image->r.max.y = atoi(info+7*12);
171 	image->clipr.min.x = atoi(info+8*12);
172 	image->clipr.min.y = atoi(info+9*12);
173 	image->clipr.max.x = atoi(info+10*12);
174 	image->clipr.max.y = atoi(info+11*12);
175 
176 	a = bufimage(d, 3);
177 	a[0] = 'q';
178 	a[1] = 1;
179 	a[2] = 'd';
180 	d->dpi = 100;
181 	if(flushimage(d, 0) >= 0 && _displayrddraw(d, info, 12) == 12)
182 		d->dpi = atoi(info);
183 
184 	return image;
185 }
186 
187 /*
188  * Attach, or possibly reattach, to window.
189  * If reattaching, maintain value of screen pointer.
190  */
191 int
getwindow(Display * d,int ref)192 getwindow(Display *d, int ref)
193 {
194 	Image *i, *oi;
195 	Font *f;
196 
197 	/* XXX check for destroyed? */
198 
199 	/*
200 	 * Libdraw promises not to change the value of "screen",
201 	 * so we have to reuse the image structure
202 	 * memory we already have.
203 	 */
204 	oi = d->image;
205 	i = getimage0(d, oi);
206 	if(i == nil)
207 		sysfatal("getwindow failed");
208 	d->image = i;
209 	/* fprint(2, "getwindow %p -> %p\n", oi, i); */
210 
211 	freescreen(_screen);
212 	_screen = allocscreen(i, d->white, 0);
213 	_freeimage1(screen);
214 	screen = _allocwindow(screen, _screen, i->r, ref, DWhite);
215 	d->screenimage = screen;
216 
217 
218 	if(d->dpi >= DefaultDPI*3/2) {
219 		for(f=d->firstfont; f != nil; f=f->next)
220 			loadhidpi(f);
221 	} else {
222 		for(f=d->firstfont; f != nil; f=f->next)
223 			if(f->lodpi != nil && f->lodpi != f)
224 				swapfont(f, &f->hidpi, &f->lodpi);
225 	}
226 
227 	return 0;
228 }
229 
230 Display*
_initdisplay(void (* error)(Display *,char *),char * label)231 _initdisplay(void (*error)(Display*, char*), char *label)
232 {
233 	Display *disp;
234 	Image *image;
235 
236 	fmtinstall('P', Pfmt);
237 	fmtinstall('R', Rfmt);
238 
239 	disp = mallocz(sizeof(Display), 1);
240 	if(disp == nil){
241     Error1:
242 		return nil;
243 	}
244 	disp->srvfd = -1;
245 	image = nil;
246 	if(0){
247     Error2:
248 		free(image);
249 		free(disp);
250 		goto Error1;
251 	}
252 	disp->bufsize = 65500;
253 	disp->buf = malloc(disp->bufsize+5);	/* +5 for flush message */
254 	disp->bufp = disp->buf;
255 	disp->error = error;
256 	qlock(&disp->qlock);
257 
258 	if(disp->buf == nil)
259 		goto Error2;
260 	if(0){
261     Error3:
262 		free(disp->buf);
263 		goto Error2;
264 	}
265 
266 	if(_displaymux(disp) < 0
267 	|| _displayconnect(disp) < 0
268 	|| _displayinit(disp, label, winsize) < 0)
269 		goto Error3;
270 	if(0){
271     Error4:
272 		close(disp->srvfd);
273 		goto Error3;
274 	}
275 
276 	image = getimage0(disp, nil);
277 	if(image == nil)
278 		goto Error4;
279 
280 	disp->image = image;
281 	disp->white = allocimage(disp, Rect(0, 0, 1, 1), GREY1, 1, DWhite);
282 	disp->black = allocimage(disp, Rect(0, 0, 1, 1), GREY1, 1, DBlack);
283 	if(disp->white == nil || disp->black == nil){
284 		free(disp->white);
285 		free(disp->black);
286 		goto Error4;
287 	}
288 
289 	disp->opaque = disp->white;
290 	disp->transparent = disp->black;
291 
292 	return disp;
293 }
294 
295 /*
296  * Call with d unlocked.
297  * Note that disp->defaultfont is not freed here.
298  */
299 void
closedisplay(Display * disp)300 closedisplay(Display *disp)
301 {
302 	int fd;
303 	char buf[128];
304 
305 	if(disp == nil)
306 		return;
307 	if(disp == display)
308 		display = nil;
309 	if(disp->oldlabel[0]){
310 		snprint(buf, sizeof buf, "%s/label", disp->windir);
311 		fd = open(buf, OWRITE);
312 		if(fd >= 0){
313 			write(fd, disp->oldlabel, strlen(disp->oldlabel));
314 			close(fd);
315 		}
316 	}
317 
318 	free(disp->devdir);
319 	free(disp->windir);
320 	if(disp->white)
321 		freeimage(disp->white);
322 	if(disp->black)
323 		freeimage(disp->black);
324 	if(disp->srvfd >= 0)
325 		close(disp->srvfd);
326 	free(disp);
327 }
328 
329 void
lockdisplay(Display * disp)330 lockdisplay(Display *disp)
331 {
332 	if(debuglockdisplay){
333 		/* avoid busy looping; it's rare we collide anyway */
334 		while(!canqlock(&disp->qlock)){
335 			fprint(1, "proc %d waiting for display lock...\n", getpid());
336 			sleep(1000);
337 		}
338 	}else
339 		qlock(&disp->qlock);
340 }
341 
342 void
unlockdisplay(Display * disp)343 unlockdisplay(Display *disp)
344 {
345 	qunlock(&disp->qlock);
346 }
347 
348 void
drawerror(Display * d,char * s)349 drawerror(Display *d, char *s)
350 {
351 	char err[ERRMAX];
352 
353 	if(d->error)
354 		d->error(d, s);
355 	else{
356 		errstr(err, sizeof err);
357 		fprint(2, "draw: %s: %s\n", s, err);
358 		exits(s);
359 	}
360 }
361 
362 static
363 int
doflush(Display * d)364 doflush(Display *d)
365 {
366 	int n;
367 
368 	n = d->bufp-d->buf;
369 	if(n <= 0)
370 		return 1;
371 
372 	if(_displaywrdraw(d, d->buf, n) != n){
373 		if(_drawdebug)
374 			fprint(2, "flushimage fail: d=%p: %r\n", d); /**/
375 		d->bufp = d->buf;	/* might as well; chance of continuing */
376 		return -1;
377 	}
378 	d->bufp = d->buf;
379 	return 1;
380 }
381 
382 int
flushimage(Display * d,int visible)383 flushimage(Display *d, int visible)
384 {
385 	if(visible == 1 && visibleclicks && mousebuttons && _drawmouse.buttons) {
386 		Rectangle r, r1;
387 		int ret;
388 
389 		r = mousebuttons->r;
390 		r = rectaddpt(r, _drawmouse.xy);
391 		r = rectaddpt(r, Pt(-Dx(mousebuttons->r)/2, -Dy(mousebuttons->r)-3));
392 		drawop(mousesave, mousesave->r, screen, nil, r.min, S);
393 
394 		r1 = rectaddpt(Rect(0, 0, 22, 22), r.min);
395 		if(_drawmouse.buttons & 1)
396 			drawop(screen, r1, mousebuttons, nil, ZP, S);
397 		r1 = rectaddpt(r1, Pt(21, 0));
398 		if(_drawmouse.buttons & 2)
399 			drawop(screen, r1, mousebuttons, nil, Pt(21, 0), S);
400 		r1 = rectaddpt(r1, Pt(21, 0));
401 		if(_drawmouse.buttons & 4)
402 			drawop(screen, r1, mousebuttons, nil, Pt(42, 0), S);
403 		ret = flushimage(d, 2);
404 		drawop(screen, r, mousesave, nil, ZP, S);
405 		return ret;
406 	}
407 
408 	if(visible){
409 		*d->bufp++ = 'v';	/* five bytes always reserved for this */
410 		if(d->_isnewdisplay){
411 			BPLONG(d->bufp, d->screenimage->id);
412 			d->bufp += 4;
413 		}
414 	}
415 	return doflush(d);
416 }
417 
418 uchar*
bufimage(Display * d,int n)419 bufimage(Display *d, int n)
420 {
421 	uchar *p;
422 
423 	if(n<0 || d == nil || n>d->bufsize){
424 		abort();
425 		werrstr("bad count in bufimage");
426 		return 0;
427 	}
428 	if(d->bufp+n > d->buf+d->bufsize)
429 		if(doflush(d) < 0)
430 			return 0;
431 	p = d->bufp;
432 	d->bufp += n;
433 	return p;
434 }
435 
436 int
scalesize(Display * d,int n)437 scalesize(Display *d, int n)
438 {
439 	if(d == nil || d->dpi <= DefaultDPI)
440 		return n;
441 	return (n*d->dpi+DefaultDPI/2)/DefaultDPI;
442 }
443