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