Blob


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