xref: /plan9/sys/src/cmd/page/view.c (revision 59cc4ca53493a3c6d2349fe2b7f7c40f7dce7294)
1 /*
2  * the actual viewer that handles screen stuff
3  */
4 
5 #include <u.h>
6 #include <libc.h>
7 #include <draw.h>
8 #include <cursor.h>
9 #include <event.h>
10 #include <bio.h>
11 #include <plumb.h>
12 #include <ctype.h>
13 #include <keyboard.h>
14 #include "page.h"
15 
16 Document *doc;
17 Image *im;
18 int page;
19 int upside = 0;
20 int showbottom = 0;		/* on the next showpage, move the image so the bottom is visible. */
21 
22 Rectangle ulrange;	/* the upper left corner of the image must be in this rectangle */
23 Point ul;			/* the upper left corner of the image is at this point on the screen */
24 
25 Point pclip(Point, Rectangle);
26 Rectangle mkrange(Rectangle screenr, Rectangle imr);
27 void redraw(Image*);
28 
29 Cursor reading={
30 	{-1, -1},
31 	{0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00,
32 	 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0,
33 	 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0,
34 	 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, },
35 	{0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00,
36 	 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0,
37 	 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40,
38 	 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, }
39 };
40 
41 Cursor query = {
42 	{-7,-7},
43 	{0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe,
44 	 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8,
45 	 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0,
46 	 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, },
47 	{0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c,
48 	 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0,
49 	 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80,
50 	 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
51 };
52 
53 enum {
54 	Left = 1,
55 	Middle = 2,
56 	Right = 4,
57 
58 	RMenu = 3,
59 };
60 
61 int
62 max(int a, int b)
63 {
64 	return a > b ? a : b;
65 }
66 
67 int
68 min(int a, int b)
69 {
70 	return a < b ? a : b;
71 }
72 
73 
74 char*
75 menugen(int n)
76 {
77 	static char menustr[32];
78 	char *p;
79 	int len;
80 
81 	if(n == doc->npage)
82 		return "exit";
83 	if(n > doc->npage)
84 		return nil;
85 
86 	if(reverse)
87 		n = doc->npage-1-n;
88 
89 	p = doc->pagename(doc, n);
90 	len = (sizeof menustr)-2;
91 
92 	if(strlen(p) > len && strrchr(p, '/'))
93 		p = strrchr(p, '/')+1;
94 	if(strlen(p) > len)
95 		p = p+strlen(p)-len;
96 
97 	strcpy(menustr+1, p);
98 	if(page == n)
99 		menustr[0] = '>';
100 	else
101 		menustr[0] = ' ';
102 	return menustr;
103 }
104 
105 void
106 showpage(int page, Menu *m)
107 {
108 	if(doc->fwdonly)
109 		m->lasthit = 0;	/* this page */
110 	else
111 		m->lasthit = reverse ? doc->npage-1-page : page;
112 
113 	esetcursor(&reading);
114 	freeimage(im);
115 	if((page < 0 || page >= doc->npage) && !doc->fwdonly){
116 		im = nil;
117 		return;
118 	}
119 	im = doc->drawpage(doc, page);
120 	if(im == nil) {
121 		if(doc->fwdonly)	/* this is how we know we're out of pages */
122 			wexits(0);
123 
124 		im = xallocimage(display, Rect(0,0,50,50), GREY1, 1, DBlack);
125 		if(im == nil) {
126 			fprint(2, "out of memory: %r\n");
127 			wexits("memory");
128 		}
129 		string(im, ZP, display->white, ZP, display->defaultfont, "?");
130 	}else if(resizing){
131 		resize(Dx(im->r), Dy(im->r));
132 	}
133 	if(upside)
134 		rot180(im);
135 
136 	esetcursor(nil);
137 	if(showbottom){
138 		ul.y = screen->r.max.y - Dy(im->r);
139 		showbottom = 0;
140 	}
141 
142 	redraw(screen);
143 	flushimage(display, 1);
144 }
145 
146 char*
147 writebitmap(void)
148 {
149 	char basename[64];
150 	char name[64+30];
151 	static char result[200];
152 	char *p, *q;
153 	int fd;
154 
155 	if(im == nil)
156 		return "no image";
157 
158 	memset(basename, 0, sizeof basename);
159 	if(doc->docname)
160 		strncpy(basename, doc->docname, sizeof(basename)-1);
161 	else if((p = menugen(page)) && p[0] != '\0')
162 		strncpy(basename, p+1, sizeof(basename)-1);
163 
164 	if(basename[0]) {
165 		if(q = strrchr(basename, '/'))
166 			q++;
167 		else
168 			q = basename;
169 		if(p = strchr(q, '.'))
170 			*p = 0;
171 
172 		memset(name, 0, sizeof name);
173 		snprint(name, sizeof(name)-1, "%s.%d.bit", q, page+1);
174 		if(access(name, 0) >= 0) {
175 			strcat(name, "XXXX");
176 			mktemp(name);
177 		}
178 		if(access(name, 0) >= 0)
179 			return "couldn't think of a name for bitmap";
180 	} else {
181 		strcpy(name, "bitXXXX");
182 		mktemp(name);
183 		if(access(name, 0) >= 0)
184 			return "couldn't think of a name for bitmap";
185 	}
186 
187 	if((fd = create(name, OWRITE, 0666)) < 0) {
188 		snprint(result, sizeof result, "cannot create %s: %r", name);
189 		return result;
190 	}
191 
192 	if(writeimage(fd, im, 0) < 0) {
193 		snprint(result, sizeof result, "cannot writeimage: %r");
194 		close(fd);
195 		return result;
196 	}
197 	close(fd);
198 
199 	snprint(result, sizeof result, "wrote %s", name);
200 	return result;
201 }
202 
203 static void translate(Point);
204 
205 static int
206 showdata(Plumbmsg *msg)
207 {
208 	char *s;
209 
210 	s = plumblookup(msg->attr, "action");
211 	return s && strcmp(s, "showdata")==0;
212 }
213 
214 void
215 viewer(Document *dd)
216 {
217 	int i, fd, n, oldpage;
218 	int nxt;
219 	Menu menu;
220 	Mouse m;
221 	Event e;
222 	Point dxy, oxy, xy0;
223 	Rectangle r;
224 	char *fwditems[] = { "this page", "next page", "exit", 0 };
225 	char *s;
226 	enum { Eplumb = 4 };
227 	Plumbmsg *pm;
228 
229 	doc = dd;    /* save global for menuhit */
230 	ul = screen->r.min;
231 	einit(Emouse|Ekeyboard);
232 	if(doc->addpage != nil)
233 		eplumb(Eplumb, "image");
234 
235 	esetcursor(&reading);
236 	r.min = ZP;
237 
238 	/*
239 	 * im is a global pointer to the current image.
240 	 * eventually, i think we will have a layer between
241 	 * the display routines and the ps/pdf/whatever routines
242 	 * to perhaps cache and handle images of different
243 	 * sizes, etc.
244 	 */
245 	im = 0;
246 	page = reverse ? doc->npage-1 : 0;
247 
248 	if(doc->fwdonly) {
249 		menu.item = fwditems;
250 		menu.gen = 0;
251 		menu.lasthit = 0;
252 	} else {
253 		menu.item = 0;
254 		menu.gen = menugen;
255 		menu.lasthit = 0;
256 	}
257 
258 	showpage(page, &menu);
259 	esetcursor(nil);
260 
261 	nxt = 0;
262 	for(;;) {
263 		/*
264 		 * throughout, if doc->fwdonly is set, we restrict the functionality
265 		 * a fair amount.  we don't care about doc->npage anymore, and
266 		 * all that can be done is select the next page.
267 		 */
268 		switch(eread(Emouse|Ekeyboard|Eplumb, &e)){
269 		case Ekeyboard:
270 			if(e.kbdc <= 0xFF && isdigit(e.kbdc)) {
271 				nxt = nxt*10+e.kbdc-'0';
272 				break;
273 			} else if(e.kbdc != '\n')
274 				nxt = 0;
275 			switch(e.kbdc) {
276 			case 'r':	/* reverse page order */
277 				if(doc->fwdonly)
278 					break;
279 				reverse = !reverse;
280 				menu.lasthit = doc->npage-1-menu.lasthit;
281 
282 				/*
283 				 * the theory is that if we are reversing the
284 				 * document order and are on the first or last
285 				 * page then we're just starting and really want
286 		 	 	 * to view the other end.  maybe the if
287 				 * should be dropped and this should happen always.
288 				 */
289 				if(page == 0 || page == doc->npage-1) {
290 					page = doc->npage-1-page;
291 					showpage(page, &menu);
292 				}
293 				break;
294 			case 'w':	/* write bitmap of current screen */
295 				esetcursor(&reading);
296 				s = writebitmap();
297 				if(s)
298 					string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
299 						display->defaultfont, s);
300 				esetcursor(nil);
301 				flushimage(display, 1);
302 				break;
303 			case 'd':	/* remove image from working set */
304 				if(doc->rmpage && page < doc->npage) {
305 					if(doc->rmpage(doc, page) >= 0) {
306 						if(doc->npage < 0)
307 							wexits(0);
308 						if(page >= doc->npage)
309 							page = doc->npage-1;
310 						showpage(page, &menu);
311 					}
312 				}
313 				break;
314 			case 'q':
315 			case 0x04: /* ctrl-d */
316 				wexits(0);
317 			case 'u':
318 				if(im==nil)
319 					break;
320 				esetcursor(&reading);
321 				rot180(im);
322 				esetcursor(nil);
323 				upside = !upside;
324 				redraw(screen);
325 				flushimage(display, 1);
326 				break;
327 			case '-':
328 			case '\b':
329 			case Kleft:
330 				if(page > 0 && !doc->fwdonly) {
331 					--page;
332 					showpage(page, &menu);
333 				}
334 				break;
335 			case '\n':
336 				if(nxt) {
337 					nxt--;
338 					if(nxt >= 0 && nxt < doc->npage && !doc->fwdonly)
339 						showpage(page=nxt, &menu);
340 					nxt = 0;
341 					break;
342 				}
343 				goto Gotonext;
344 			case Kright:
345 			case ' ':
346 			Gotonext:
347 				if(doc->npage && ++page >= doc->npage && !doc->fwdonly)
348 					wexits(0);
349 				showpage(page, &menu);
350 				break;
351 
352 			/*
353 			 * The upper y coordinate of the image is at ul.y in screen->r.
354 			 * Panning up means moving the upper left corner down.  If the
355 			 * upper left corner is currently visible, we need to go back a page.
356 			 */
357 			case Kup:
358 				if(screen->r.min.y <= ul.y && ul.y < screen->r.max.y){
359 					if(page > 0 && !doc->fwdonly){
360 						--page;
361 						showbottom = 1;
362 						showpage(page, &menu);
363 					}
364 				} else {
365 					i = Dy(screen->r)/2;
366 					if(i > 10)
367 						i -= 10;
368 					if(i+ul.y > screen->r.min.y)
369 						i = screen->r.min.y - ul.y;
370 					translate(Pt(0, i));
371 				}
372 				break;
373 
374 			/*
375 			 * If the lower y coordinate is on the screen, we go to the next page.
376 			 * The lower y coordinate is at ul.y + Dy(im->r).
377 			 */
378 			case Kdown:
379 				i = ul.y + Dy(im->r);
380 				if(screen->r.min.y <= i && i <= screen->r.max.y){
381 					ul.y = screen->r.min.y;
382 					goto Gotonext;
383 				} else {
384 					i = -Dy(screen->r)/2;
385 					if(i < -10)
386 						i += 10;
387 					if(i+ul.y+Dy(im->r) <= screen->r.max.y)
388 						i = screen->r.max.y - Dy(im->r) - ul.y - 1;
389 					translate(Pt(0, i));
390 				}
391 				break;
392 			default:
393 				esetcursor(&query);
394 				sleep(1000);
395 				esetcursor(nil);
396 				break;
397 			}
398 			break;
399 
400 		case Emouse:
401 			m = e.mouse;
402 			switch(m.buttons){
403 			case Left:
404 				oxy = m.xy;
405 				xy0 = oxy;
406 				do {
407 					dxy = subpt(m.xy, oxy);
408 					oxy = m.xy;
409 					translate(dxy);
410 					m = emouse();
411 				} while(m.buttons == Left);
412 				if(m.buttons) {
413 					dxy = subpt(xy0, oxy);
414 					translate(dxy);
415 				}
416 				break;
417 
418 			case Middle:
419 				do
420 					m = emouse();
421 				while(m.buttons == Middle);
422 				if(m.buttons)
423 					break;
424 
425 				if(doc->npage == 0)
426 					break;
427 
428 				if(reverse)
429 					page--;
430 				else
431 					page++;
432 
433 				if((page >= doc->npage || page < 0) && !doc->fwdonly)
434 					return;
435 
436 				showpage(page, &menu);
437 				nxt = 0;
438 				break;
439 
440 			case Right:
441 				if(doc->npage == 0)
442 					break;
443 
444 				oldpage = page;
445 				n = emenuhit(RMenu, &m, &menu);
446 				if(n == -1)
447 					break;
448 
449 				if(doc->fwdonly) {
450 					switch(n){
451 					case 0:	/* this page */
452 						break;
453 					case 1:	/* next page */
454 						showpage(++page, &menu);
455 						break;
456 					case 2:	/* exit */
457 						return;
458 					}
459 					break;
460 				}
461 
462 				if(n == doc->npage)
463 					return;
464 				else
465 					page = reverse ? doc->npage-1-n : n;
466 
467 				if(oldpage != page)
468 					showpage(page, &menu);
469 				nxt = 0;
470 				break;
471 			}
472 			break;
473 
474 		case Eplumb:
475 			pm = e.v;
476 			if(pm->ndata <= 0){
477 				plumbfree(pm);
478 				break;
479 			}
480 			if(showdata(pm)) {
481 				s = estrdup("/tmp/pageplumbXXXXXXX");
482 				fd = opentemp(s);
483 				write(fd, pm->data, pm->ndata);
484 				/* lose fd reference on purpose; the file is open ORCLOSE */
485 			} else if(pm->data[0] == '/') {
486 				s = estrdup(pm->data);
487 			} else {
488 				s = emalloc(strlen(pm->wdir)+1+pm->ndata+1);
489 				sprint(s, "%s/%s", pm->wdir, pm->data);
490 				cleanname(s);
491 			}
492 			if((i = doc->addpage(doc, s)) >= 0) {
493 				page = i;
494 				showpage(page, &menu);
495 			}
496 			free(s);
497 			plumbfree(pm);
498 			break;
499 		}
500 	}
501 }
502 
503 Image *gray;
504 
505 /*
506  * A draw operation that touches only the area contained in bot but not in top.
507  * mp and sp get aligned with bot.min.
508  */
509 static void
510 gendrawdiff(Image *dst, Rectangle bot, Rectangle top,
511 	Image *src, Point sp, Image *mask, Point mp)
512 {
513 	Rectangle r;
514 	Point origin;
515 	Point delta;
516 
517 	if(Dx(bot)*Dy(bot) == 0)
518 		return;
519 
520 	/* no points in bot - top */
521 	if(rectinrect(bot, top))
522 		return;
523 
524 	/* bot - top ≡ bot */
525 	if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
526 		gendraw(dst, bot, src, sp, mask, mp);
527 		return;
528 	}
529 
530 	origin = bot.min;
531 	/* split bot into rectangles that don't intersect top */
532 	/* left side */
533 	if(bot.min.x < top.min.x){
534 		r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y);
535 		delta = subpt(r.min, origin);
536 		gendraw(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta));
537 		bot.min.x = top.min.x;
538 	}
539 
540 	/* right side */
541 	if(bot.max.x > top.max.x){
542 		r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y);
543 		delta = subpt(r.min, origin);
544 		gendraw(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta));
545 		bot.max.x = top.max.x;
546 	}
547 
548 	/* top */
549 	if(bot.min.y < top.min.y){
550 		r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
551 		delta = subpt(r.min, origin);
552 		gendraw(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta));
553 		bot.min.y = top.min.y;
554 	}
555 
556 	/* bottom */
557 	if(bot.max.y > top.max.y){
558 		r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
559 		delta = subpt(r.min, origin);
560 		gendraw(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta));
561 		bot.max.y = top.max.y;
562 	}
563 }
564 
565 static void
566 drawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Image *mask, Point p)
567 {
568 	gendrawdiff(dst, bot, top, src, p, mask, p);
569 }
570 
571 /*
572  * Translate the image in the window by delta.
573  */
574 static void
575 translate(Point delta)
576 {
577 	Point u;
578 	Rectangle r, or;
579 
580 	if(im == nil)
581 		return;
582 
583 	u = pclip(addpt(ul, delta), ulrange);
584 	delta = subpt(u, ul);
585 	if(delta.x == 0 && delta.y == 0)
586 		return;
587 
588 	/*
589 	 * The upper left corner of the image is currently at ul.
590 	 * We want to move it to u.
591 	 */
592 	or = rectaddpt(Rpt(ZP, Pt(Dx(im->r), Dy(im->r))), ul);
593 	r = rectaddpt(or, delta);
594 
595 	draw(screen, r, screen, nil, ul);
596 	ul = u;
597 
598 	/* fill in gray where image used to be but isn't. */
599 	drawdiff(screen, insetrect(or, -2), insetrect(r, -2), gray, nil, ZP);
600 
601 	/* fill in black border */
602 	drawdiff(screen, insetrect(r, -2), r, display->black, nil, ZP);
603 
604 	/* fill in image where it used to be off the screen. */
605 	if(rectclip(&or, screen->r))
606 		drawdiff(screen, r, rectaddpt(or, delta), im, nil, im->r.min);
607 	else
608 		draw(screen, r, im, nil, im->r.min);
609 	flushimage(display, 1);
610 }
611 
612 void
613 redraw(Image *screen)
614 {
615 	Rectangle r;
616 
617 	if(im == nil)
618 		return;
619 
620 	ulrange.max = screen->r.max;
621 	ulrange.min = subpt(screen->r.min, Pt(Dx(im->r), Dy(im->r)));
622 
623 	ul = pclip(ul, ulrange);
624 	draw(screen, screen->r, im, nil, subpt(im->r.min, subpt(ul, screen->r.min)));
625 
626 	if(im->repl)
627 		return;
628 
629 	/* fill in any outer edges */
630 	/* black border */
631 	r = rectaddpt(im->r, subpt(ul, im->r.min));
632 	border(screen, r, -2, display->black, ZP);
633 	r.min = subpt(r.min, Pt(2,2));
634 	r.max = addpt(r.max, Pt(2,2));
635 
636 	/* gray for the rest */
637 	if(gray == nil) {
638 		gray = xallocimage(display, Rect(0,0,1,1), RGB24, 1, 0x888888FF);
639 		if(gray == nil) {
640 			fprint(2, "g out of memory: %r\n");
641 			wexits("mem");
642 		}
643 	}
644 	border(screen, r, -4000, gray, ZP);
645 //	flushimage(display, 0);
646 }
647 
648 void
649 eresized(int new)
650 {
651 	Rectangle r;
652 	r = screen->r;
653 	if(new && getwindow(display, Refnone) < 0)
654 		fprint(2,"can't reattach to window");
655 	ul = addpt(ul, subpt(screen->r.min, r.min));
656 	redraw(screen);
657 }
658 
659 /* clip p to be in r */
660 Point
661 pclip(Point p, Rectangle r)
662 {
663 	if(p.x < r.min.x)
664 		p.x = r.min.x;
665 	else if(p.x >= r.max.x)
666 		p.x = r.max.x-1;
667 
668 	if(p.y < r.min.y)
669 		p.y = r.min.y;
670 	else if(p.y >= r.max.y)
671 		p.y = r.max.y-1;
672 
673 	return p;
674 }
675 
676 /*
677  * resize is perhaps a misnomer.
678  * this really just grows the window to be at least dx across
679  * and dy high.  if the window hits the bottom or right edge,
680  * it is backed up until it hits the top or left edge.
681  */
682 void
683 resize(int dx, int dy)
684 {
685 	static Rectangle sr;
686 	Rectangle r, or;
687 
688 	dx += 2*Borderwidth;
689 	dy += 2*Borderwidth;
690 	if(wctlfd < 0){
691 		wctlfd = open("/dev/wctl", OWRITE);
692 		if(wctlfd < 0)
693 			return;
694 	}
695 
696 	r = insetrect(screen->r, -Borderwidth);
697 	if(Dx(r) >= dx && Dy(r) >= dy)
698 		return;
699 
700 	if(Dx(sr)*Dy(sr) == 0)
701 		sr = screenrect();
702 
703 	or = r;
704 
705 	r.max.x = max(r.min.x+dx, r.max.x);
706 	r.max.y = max(r.min.y+dy, r.max.y);
707 	if(r.max.x > sr.max.x){
708 		if(Dx(r) > Dx(sr)){
709 			r.min.x = 0;
710 			r.max.x = sr.max.x;
711 		}else
712 			r = rectaddpt(r, Pt(sr.max.x-r.max.x, 0));
713 	}
714 	if(r.max.y > sr.max.y){
715 		if(Dy(r) > Dy(sr)){
716 			r.min.y = 0;
717 			r.max.y = sr.max.y;
718 		}else
719 			r = rectaddpt(r, Pt(0, sr.max.y-r.max.y));
720 	}
721 
722 	/*
723 	 * Sometimes we can't actually grow the window big enough,
724 	 * and resizing it to the same shape makes it flash.
725 	 */
726 	if(Dx(r) == Dx(or) && Dy(r) == Dy(or))
727 		return;
728 
729 	fprint(wctlfd, "resize -minx %d -miny %d -maxx %d -maxy %d\n",
730 		r.min.x, r.min.y, r.max.x, r.max.y);
731 }
732 
733 /*
734  * If we allocimage after a resize but before flushing the draw buffer,
735  * we won't have seen the reshape event, and we won't have called
736  * getwindow, and allocimage will fail.  So we flushimage before every alloc.
737  */
738 Image*
739 xallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val)
740 {
741 	flushimage(display, 0);
742 	return allocimage(d, r, chan, repl, val);
743 }
744 
745 /* all code below this line should be in the library, but is stolen from colors instead */
746 static char*
747 rdenv(char *name)
748 {
749 	char *v;
750 	int fd, size;
751 
752 	fd = open(name, OREAD);
753 	if(fd < 0)
754 		return 0;
755 	size = seek(fd, 0, 2);
756 	v = malloc(size+1);
757 	if(v == 0){
758 		fprint(2, "page: can't malloc: %r\n");
759 		wexits("no mem");
760 	}
761 	seek(fd, 0, 0);
762 	read(fd, v, size);
763 	v[size] = 0;
764 	close(fd);
765 	return v;
766 }
767 
768 void
769 newwin(void)
770 {
771 	char *srv, *mntsrv;
772 	char spec[100];
773 	int srvfd, cons, pid;
774 
775 	switch(rfork(RFFDG|RFPROC|RFNAMEG|RFENVG|RFNOTEG|RFNOWAIT)){
776 	case -1:
777 		fprint(2, "page: can't fork: %r\n");
778 		wexits("no fork");
779 	case 0:
780 		break;
781 	default:
782 		wexits(0);
783 	}
784 
785 	srv = rdenv("/env/wsys");
786 	if(srv == 0){
787 		mntsrv = rdenv("/mnt/term/env/wsys");
788 		if(mntsrv == 0){
789 			fprint(2, "page: can't find $wsys\n");
790 			wexits("srv");
791 		}
792 		srv = malloc(strlen(mntsrv)+10);
793 		sprint(srv, "/mnt/term%s", mntsrv);
794 		free(mntsrv);
795 		pid  = 0;			/* can't send notes to remote processes! */
796 	}else
797 		pid = getpid();
798 	srvfd = open(srv, ORDWR);
799 	free(srv);
800 	if(srvfd == -1){
801 		fprint(2, "page: can't open %s: %r\n", srv);
802 		wexits("no srv");
803 	}
804 	sprint(spec, "new -pid %d", pid);
805 	if(mount(srvfd, "/mnt/wsys", 0, spec) == -1){
806 		fprint(2, "page: can't mount /mnt/wsys: %r (spec=%s)\n", spec);
807 		wexits("no mount");
808 	}
809 	close(srvfd);
810 	unmount("/mnt/acme", "/dev");
811 	bind("/mnt/wsys", "/dev", MBEFORE);
812 	cons = open("/dev/cons", OREAD);
813 	if(cons==-1){
814 	NoCons:
815 		fprint(2, "page: can't open /dev/cons: %r");
816 		wexits("no cons");
817 	}
818 	dup(cons, 0);
819 	close(cons);
820 	cons = open("/dev/cons", OWRITE);
821 	if(cons==-1)
822 		goto NoCons;
823 	dup(cons, 1);
824 	dup(cons, 2);
825 	close(cons);
826 //	wctlfd = open("/dev/wctl", OWRITE);
827 }
828 
829 Rectangle
830 screenrect(void)
831 {
832 	int fd;
833 	char buf[12*5];
834 
835 	fd = open("/dev/screen", OREAD);
836 	if(fd == -1)
837 		fd=open("/mnt/term/dev/screen", OREAD);
838 	if(fd == -1){
839 		fprint(2, "page: can't open /dev/screen: %r\n");
840 		wexits("window read");
841 	}
842 	if(read(fd, buf, sizeof buf) != sizeof buf){
843 		fprint(2, "page: can't read /dev/screen: %r\n");
844 		wexits("screen read");
845 	}
846 	close(fd);
847 	return Rect(atoi(buf+12), atoi(buf+24), atoi(buf+36), atoi(buf+48));
848 }
849 
850