xref: /inferno-os/emu/Plan9/win.c (revision 1aff7a0a7dab24c5871eb95737c86616c9fd848b)
1 #include	"dat.h"
2 #include	"fns.h"
3 #include	"kernel.h"
4 #include	"error.h"
5 
6 #include	<draw.h>
7 #include	<memdraw.h>
8 #include	<cursor.h>
9 #include	"keyboard.h"
10 
11 enum
12 {
13 	Margin	= 4,
14 	Lsize		= 100,
15 };
16 
17 extern Memimage *screenimage;
18 
19 extern int kproc1(char*, void (*)(void*), void*, int);
20 
21 static	ulong*	attachwindow(Rectangle*, ulong*, int*, int*);
22 
23 static void	plan9readmouse(void*);
24 static void	plan9readkeybd(void*);
25 static int	mapspecials(char *s1, char *s2, int *n);
26 
27 int	usenewwin = 1;
28 int	kbdiscons;
29 static int	truedepth;
30 
31 static int		datafd;
32 static int		ctlfd;
33 static int		mousefd = -1;
34 static int		keybdfd;
35 static int		mousepid = -1;
36 static int		keybdpid = -1;
37 static int		cursfd;
38 static char	winname[64];
39 
40 /* Following updated by attachwindow() asynchronously */
41 static QLock		ql;
42 static Rectangle	tiler;
43 static ulong*		data;
44 static uchar*		loadbuf;
45 static int		cursfd;
46 static int		imageid;
47 static Rectangle	imager;
48 static uchar	*chunk;
49 static int	chunksize;
50 static	int	dispbufsize;
51 
52 #define	NINFO	12*12
53 #define	HDR		21
54 
55 
56 void
57 killrefresh(void)
58 {
59 	if(mousepid < 0)
60 		return;
61 	close(mousefd);
62 	close(ctlfd);
63 	close(datafd);
64 	postnote(PNPROC, mousepid, Eintr);
65 	postnote(PNPROC, keybdpid, Eintr);
66 }
67 
68 uchar*
69 attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen)
70 {
71 	int fd;
72 	char *p, buf[128], info[NINFO+1];
73 
74 	if(usenewwin){
75 		p = getenv("wsys");
76 		if(p == nil)
77 			return nil;
78 
79 		fd = open(p, ORDWR);
80 		if(fd < 0) {
81 			fprint(2, "attachscreen: can't open window manager: %r\n");
82 			return nil;
83 		}
84 		sprint(buf, "new -dx %d -dy %d", Xsize+2*Margin, Ysize+2*Margin);
85 		if(mount(fd, -1, "/mnt/wsys", MREPL, buf) < 0) {
86 			fprint(2, "attachscreen: can't mount window manager: %r\n");
87 			return nil;
88 		}
89 		if(bind("/mnt/wsys", "/dev", MBEFORE) < 0){
90 			fprint(2, "attachscreen: can't bind /mnt/wsys before /dev: %r\n");
91 			return nil;
92 		}
93 	}
94 
95 	cursfd = open("/dev/cursor", OWRITE);
96 	if(cursfd < 0) {
97 		fprint(2, "attachscreen: open cursor: %r\n");
98 		return nil;
99 	}
100 
101 	/* Set up graphics window console (chars->gkbdq) */
102 	keybdfd = open("/dev/cons", OREAD);
103 	if(keybdfd < 0) {
104 		fprint(2, "attachscreen: open keyboard: %r\n");
105 		return nil;
106 	}
107 	mousefd = open("/dev/mouse", ORDWR);
108 	if(mousefd < 0){
109 		fprint(2, "attachscreen: can't open mouse: %r\n");
110 		return nil;
111 	}
112 	if(usenewwin || 1){
113 		fd = open("/dev/consctl", OWRITE);
114 		if(fd < 0)
115 			fprint(2, "attachscreen: open /dev/consctl: %r\n");
116 		if(write(fd, "rawon", 5) != 5)
117 			fprint(2, "attachscreen: write /dev/consctl: %r\n");
118 	}
119 
120 	/* Set up graphics files */
121 	ctlfd = open("/dev/draw/new", ORDWR);
122 	if(ctlfd < 0){
123 		fprint(2, "attachscreen: can't open graphics control file: %r\n");
124 		return nil;
125 	}
126 	if(read(ctlfd, info, sizeof info) < NINFO){
127 		close(ctlfd);
128 		fprint(2, "attachscreen: can't read graphics control file: %r\n");
129 		return nil;
130 	}
131 	sprint(buf, "/dev/draw/%d/data", atoi(info+0*12));
132 	datafd = open(buf, ORDWR|OCEXEC);
133 	if(datafd < 0){
134 		close(ctlfd);
135 		fprint(2, "attachscreen: can't read graphics data file: %r\n");
136 		return nil;
137 	}
138 	dispbufsize = iounit(datafd);
139 	if(dispbufsize <= 0)
140 		dispbufsize = 8000;
141 	if(dispbufsize < 512){
142 		close(ctlfd);
143 		close(datafd);
144 		fprint(2, "attachscreen: iounit %d too small\n", dispbufsize);
145 		return nil;
146 	}
147 	chunksize = dispbufsize - 64;
148 
149 	if(attachwindow(r, chan, d, width) == nil){
150 		close(ctlfd);
151 		close(datafd);
152 		return nil;
153 	}
154 
155 	mousepid = kproc1("readmouse", plan9readmouse, nil, 0);
156 	keybdpid = kproc1("readkbd", plan9readkeybd, nil, 0);
157 
158 	fd = open("/dev/label", OWRITE);
159 	if(fd >= 0){
160 		snprint(buf, sizeof(buf), "inferno %d", getpid());
161 		write(fd, buf, strlen(buf));
162 		close(fd);
163 	}
164 
165 	*softscreen = 1;
166 	return (uchar*)data;
167 }
168 
169 static ulong*
170 attachwindow(Rectangle *r, ulong *chan, int *d, int *width)
171 {
172 	int n, fd, nb;
173 	char buf[256];
174 	uchar ubuf[128];
175 	ulong imagechan;
176 
177 	/*
178 	 * Discover name of window
179 	 */
180 	fd = open("/mnt/wsys/winname", OREAD);
181 	if(fd<0 || (n=read(fd, winname, sizeof winname))<=0){
182 		fprint(2, "attachwindow: can only run inferno under rio, not stand-alone\n");
183 		return nil;
184 	}
185 	close(fd);
186 	/*
187 	 * If had previous window, release it
188 	 */
189 	if(imageid > 0){
190 		ubuf[0] = 'f';
191 		BPLONG(ubuf+1, imageid);
192 		if(write(datafd, ubuf, 1+4) != 1+4)
193 			fprint(2, "attachwindow: cannot free old window: %r\n");
194 	}
195 	/*
196 	 * Allocate image pointing to window, and discover its ID
197 	 */
198 	ubuf[0] = 'n';
199 	++imageid;
200 	BPLONG(ubuf+1, imageid);
201 	ubuf[5] = n;
202 	memmove(ubuf+6, winname, n);
203 	if(write(datafd, ubuf, 6+n) != 6+n){
204 		fprint(2, "attachwindow: cannot bind %d to window id '%s': %r\n", imageid, winname);
205 		return nil;
206 	}
207 	if(read(ctlfd, buf, sizeof buf) < 12*12){
208 		fprint(2, "attachwindow: cannot read window id: %r\n");
209 		return nil;
210 	}
211 	imagechan = strtochan(buf+2*12);
212 	truedepth = chantodepth(imagechan);
213 	if(truedepth == 0){
214 		fprint(2, "attachwindow: cannot handle window depth specifier %.12s\n", buf+2*12);
215 		return nil;
216 	}
217 
218 	/*
219 	 * Report back
220 	 */
221 	if(chan != nil)
222 		*chan = imagechan;
223 	if(d != nil)
224 		*d = chantodepth(imagechan);
225 	nb = 0;
226 	if(r != nil){
227 		Xsize = atoi(buf+6*12)-atoi(buf+4*12)-2*Margin;
228 		Ysize = atoi(buf+7*12)-atoi(buf+5*12)-2*Margin;
229 		r->min.x = 0;
230 		r->min.y = 0;
231 		r->max.x = Xsize;
232 		r->max.y = Ysize;
233 		nb = bytesperline(*r, truedepth);
234 		data = malloc(nb*Ysize);
235 		loadbuf = malloc(nb*Lsize+1);
236 		chunk = malloc(HDR+chunksize+5);	/* +5 for flush (1 old, 5 new) */
237 	}
238 	imager.min.x = atoi(buf+4*12);
239 	imager.min.y = atoi(buf+5*12);
240 	imager.max.x = atoi(buf+6*12);
241 	imager.max.y = atoi(buf+7*12);
242 
243 	if(width != nil)
244 		*width = nb/4;
245 
246 	tiler.min.x = atoi(buf+4*12)+Margin;
247 	tiler.min.y = atoi(buf+5*12)+Margin;
248 	tiler.max.x = atoi(buf+6*12)-Margin;
249 	tiler.max.y = atoi(buf+7*12)-Margin;
250 
251 	return data;
252 }
253 
254 static int
255 plan9loadimage(Rectangle r, uchar *data, int ndata)
256 {
257 	long dy;
258 	int n, bpl;
259 
260 	if(!rectinrect(r, imager)){
261 		werrstr("loadimage: bad rectangle");
262 		return -1;
263 	}
264 	bpl = bytesperline(r, truedepth);
265 	n = bpl*Dy(r);
266 	if(n > ndata){
267 		werrstr("loadimage: insufficient data");
268 		return -1;
269 	}
270 	ndata = 0;
271 	while(r.max.y > r.min.y){
272 		dy = r.max.y - r.min.y;
273 		if(dy*bpl> chunksize)
274 			dy = chunksize/bpl;
275 		n = dy*bpl;
276 		chunk[0] = 'y';
277 		BPLONG(chunk+1, imageid);
278 		BPLONG(chunk+5, r.min.x);
279 		BPLONG(chunk+9, r.min.y);
280 		BPLONG(chunk+13, r.max.x);
281 		BPLONG(chunk+17, r.min.y+dy);
282 		memmove(chunk+21, data, n);
283 		ndata += n;
284 		data += n;
285 		r.min.y += dy;
286 		n += 21;
287 		if(r.min.y >= r.max.y)	/* flush to screen */
288 			chunk[n++] = 'v';
289 		if(write(datafd, chunk, n) != n)
290 			return -1;
291 	}
292 	return ndata;
293 }
294 
295 static void
296 _flushmemscreen(Rectangle r)
297 {
298 	int n, dy, l;
299 	Rectangle rr;
300 
301 	if(data == nil || loadbuf == nil || chunk==nil)
302 		return;
303 	if(!rectclip(&r, Rect(0, 0, Xsize, Ysize)))
304 		return;
305 	if(!rectclip(&r, Rect(0, 0, Dx(tiler), Dy(tiler))))
306 		return;
307 	if(Dx(r)<=0 || Dy(r)<=0)
308 		return;
309 	l = bytesperline(r, truedepth);
310 	while(r.min.y < r.max.y){
311 		dy = Dy(r);
312 		if(dy > Lsize)
313 			dy = Lsize;
314 		rr = r;
315 		rr.max.y = rr.min.y+dy;
316 		n = unloadmemimage(screenimage, rr, loadbuf, l*dy);
317 		/* offset from (0,0) to window */
318 		rr.min.x += tiler.min.x;
319 		rr.min.y += tiler.min.y;
320 		rr.max.x += tiler.min.x;
321 		rr.max.y += tiler.min.y;
322 		if(plan9loadimage(rr, loadbuf, n) != n)
323 			fprint(2, "flushmemscreen: %d bytes: %r\n", n);
324 		r.min.y += dy;
325 	}
326 }
327 
328 void
329 flushmemscreen(Rectangle r)
330 {
331 	qlock(&ql);
332 	_flushmemscreen(r);
333 	qunlock(&ql);
334 }
335 
336 void
337 drawcursor(Drawcursor *c)
338 {
339 	int j, i, h, w, bpl;
340 	uchar *bc, *bs, *cclr, *cset, curs[2*4+2*2*16];
341 
342 	/* Set the default system cursor */
343 	if(c->data == nil) {
344 		write(cursfd, curs, 0);
345 		return;
346 	}
347 
348 	BPLONG(curs+0*4, c->hotx);
349 	BPLONG(curs+1*4, c->hoty);
350 
351 	w = (c->maxx-c->minx);
352 	h = (c->maxy-c->miny)/2;
353 
354 	cclr = curs+2*4;
355 	cset = curs+2*4+2*16;
356 	bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1);
357 	bc = c->data;
358 	bs = c->data + h*bpl;
359 
360 	if(h > 16)
361 		h = 16;
362 	if(w > 16)
363 		w = 16;
364 	w /= 8;
365 	for(i = 0; i < h; i++) {
366 		for(j = 0; j < w; j++) {
367 			cclr[j] = bc[j];
368 			cset[j] = bs[j];
369 		}
370 		bc += bpl;
371 		bs += bpl;
372 		cclr += 2;
373 		cset += 2;
374 	}
375 	write(cursfd, curs, sizeof curs);
376 }
377 
378 static int
379 checkmouse(char *buf, int n)
380 {
381 	int x, y, tick, b;
382 	static int lastb, lastt, lastx, lasty, lastclick;
383 
384 	switch(n){
385 	default:
386 		kwerrstr("atomouse: bad count");
387 		return -1;
388 
389 	case 1+4*12:
390 		if(buf[0] == 'r'){
391 			qlock(&ql);
392 			if(attachwindow(nil, nil, nil, nil) == nil) {
393 				qunlock(&ql);
394 				return -1;
395 			}
396 			_flushmemscreen(Rect(0, 0, Xsize, Ysize));
397 			qunlock(&ql);
398 		}
399 		x = atoi(buf+1+0*12) - tiler.min.x;
400 		y = atoi(buf+1+1*12) - tiler.min.y;
401 		b = atoi(buf+1+2*12);
402 		tick = atoi(buf+1+3*12);
403 		if(b && lastb == 0){	/* button newly pressed */
404 			if(b==lastclick && tick-lastt<400
405 			   && abs(x-lastx)<10 && abs(y-lasty)<10)
406 				b |= (1<<8);
407 			lastt = tick;
408 			lastclick = b&0xff;
409 			lastx = x;
410 			lasty = y;
411 		}
412 		lastb = b&0xff;
413 		//mouse.msec = tick;
414 		mousetrack(b, x, y, 0);
415 		return n;
416 	}
417 }
418 
419 static void
420 plan9readmouse(void *v)
421 {
422 	int n;
423 	char buf[128];
424 
425 	USED(v);
426 	for(;;){
427 		n = read(mousefd, buf, sizeof(buf));
428 		if(n < 0)	/* probably interrupted */
429 			_exits(0);
430 		checkmouse(buf, n);
431 	}
432 }
433 
434 static void
435 plan9readkeybd(void*)
436 {
437 	int n, partial;
438 	char buf[32];
439 	char dbuf[32 * 3];		/* overestimate but safe */
440 
441 	partial = 0;
442 	for(;;){
443 		n = read(keybdfd, buf + partial, sizeof(buf) - partial);
444 		if(n < 0)	/* probably interrupted */
445 			_exits(0);
446 		partial += n;
447 		n = mapspecials(dbuf, buf, &partial);
448 		qproduce(gkbdq, dbuf, n);
449 	}
450 }
451 
452 void
453 setpointer(int x, int y)
454 {
455 	char buf[50];
456 	int n;
457 
458 	if(mousefd < 0)
459 		return;
460 	x += tiler.min.x;
461 	y += tiler.min.y;
462 	n = snprint(buf, sizeof buf, "m%11d %11d ", x, y);
463 	write(mousefd, buf, n);
464 }
465 
466 /*
467  * plan9 keyboard codes; from /sys/include/keyboard.h; can't include directly
468  * because constant names clash.
469  */
470 enum {
471 	P9KF=	0xF000,	/* Rune: beginning of private Unicode space */
472 	P9Spec=	0xF800,
473 	/* KF|1, KF|2, ..., KF|0xC is F1, F2, ..., F12 */
474 	Khome=	P9KF|0x0D,
475 	Kup=	P9KF|0x0E,
476 	Kpgup=	P9KF|0x0F,
477 	Kprint=	P9KF|0x10,
478 	Kleft=	P9KF|0x11,
479 	Kright=	P9KF|0x12,
480 	Kdown=	P9Spec|0x00,
481 	Kview=	P9Spec|0x00,
482 	Kpgdown=	P9KF|0x13,
483 	Kins=	P9KF|0x14,
484 	Kend=	KF|0x18,
485 
486 	Kalt=		P9KF|0x15,
487 	Kshift=	P9KF|0x16,
488 	Kctl=		P9KF|0x17,
489 };
490 
491 /*
492  * translate plan 9 special characters from s2 (of length *n) into s1;
493  * return number of chars placed into s1.
494  * any trailing incomplete chars are moved to the beginning of s2,
495  * and *n set to the number moved there.
496  */
497 static int
498 mapspecials(char *s1, char *s2, int *n)
499 {
500 	char *s, *d, *es2;
501 	Rune r;
502 	d = s1;
503 	s = s2;
504 	es2 = s2 + *n;
505 	while (fullrune(s, es2 - s)) {
506 		s += chartorune(&r, s);
507 		switch (r) {
508 		case Kshift:
509 			r = LShift;
510 			break;
511 		case Kctl:
512 			r = LCtrl;
513 			break;
514 		case Kalt:
515 			r = LAlt;
516 			break;
517 		case Khome:
518 			r = Home;
519 			break;
520 		case Kend:
521 			r = End;
522 			break;
523 		case Kup:
524 			r = Up;
525 			break;
526 		case Kdown:
527 			r = Down;
528 			break;
529 		case Kleft:
530 			r = Left;
531 			break;
532 		case Kright:
533 			r = Right;
534 			break;
535 		case Kpgup:
536 			r = Pgup;
537 			break;
538 		case Kpgdown:
539 			r = Pgdown;
540 			break;
541 		case Kins:
542 			r = Ins;
543 			break;
544 		/*
545 		 * function keys
546 		 */
547 		case P9KF|1:
548 		case P9KF|2:
549 		case P9KF|3:
550 		case P9KF|4:
551 		case P9KF|5:
552 		case P9KF|6:
553 		case P9KF|7:
554 		case P9KF|8:
555 		case P9KF|9:
556 		case P9KF|10:
557 		case P9KF|11:
558 		case P9KF|12:
559 			r = (r - P9KF) + KF;
560 		}
561 		d += runetochar(d, &r);
562 	}
563 	*n = es2 - s;
564 	memmove(s2, s, *n);
565 	return d - s1;
566 }
567