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