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 <event.h>
10 #include <bio.h>
11 #include <plumb.h>
12 #include <ctype.h>
13 #include <keyboard.h>
14 #include "page.h"
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. */
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 */
25 Point pclip(Point, Rectangle);
26 Rectangle mkrange(Rectangle screenr, Rectangle imr);
27 void redraw(Image*);
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 };
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 };
53 enum {
54 Left = 1,
55 Middle = 2,
56 Right = 4,
58 RMenu = 3,
59 };
61 void
62 unhide(void)
63 {
64 static int wctl = -1;
66 if(wctl < 0)
67 wctl = open("/dev/wctl", OWRITE);
68 if(wctl < 0)
69 return;
71 write(wctl, "unhide", 6);
72 }
74 int
75 max(int a, int b)
76 {
77 return a > b ? a : b;
78 }
80 int
81 min(int a, int b)
82 {
83 return a < b ? a : b;
84 }
87 char*
88 menugen(int n)
89 {
90 static char menustr[32];
91 char *p;
92 int len;
94 if(n == doc->npage)
95 return "exit";
96 if(n > doc->npage)
97 return nil;
99 if(reverse)
100 n = doc->npage-1-n;
102 p = doc->pagename(doc, n);
103 len = (sizeof menustr)-2;
105 if(strlen(p) > len && strrchr(p, '/'))
106 p = strrchr(p, '/')+1;
107 if(strlen(p) > len)
108 p = p+strlen(p)-len;
110 strcpy(menustr+1, p);
111 if(page == n)
112 menustr[0] = '>';
113 else
114 menustr[0] = ' ';
115 return menustr;
118 void
119 showpage(int page, Menu *m)
121 Image *tmp;
123 if(doc->fwdonly)
124 m->lasthit = 0; /* this page */
125 else
126 m->lasthit = reverse ? doc->npage-1-page : page;
128 esetcursor(&reading);
129 freeimage(im);
130 if((page < 0 || page >= doc->npage) && !doc->fwdonly){
131 im = nil;
132 return;
134 im = doc->drawpage(doc, page);
135 if(im == nil) {
136 if(doc->fwdonly) /* this is how we know we're out of pages */
137 wexits(0);
139 im = xallocimage(display, Rect(0,0,50,50), GREY1, 1, DBlack);
140 if(im == nil) {
141 fprint(2, "out of memory: %r\n");
142 wexits("memory");
144 string(im, ZP, display->white, ZP, display->defaultfont, "?");
145 }else if(resizing){
146 resize(Dx(im->r), Dy(im->r));
148 if(im->r.min.x > 0 || im->r.min.y > 0) {
149 tmp = xallocimage(display, Rect(0, 0, Dx(im->r), Dy(im->r)), im->chan, 0, DNofill);
150 if(tmp == nil) {
151 fprint(2, "out of memory during showpage: %r\n");
152 wexits("memory");
154 drawop(tmp, tmp->r, im, nil, im->r.min, S);
155 freeimage(im);
156 im = tmp;
159 if(upside)
160 rot180(im);
162 esetcursor(nil);
163 if(showbottom){
164 ul.y = screen->r.max.y - Dy(im->r);
165 showbottom = 0;
168 redraw(screen);
169 flushimage(display, 1);
172 char*
173 writebitmap(void)
175 char basename[64];
176 char name[64+30];
177 static char result[200];
178 char *p, *q;
179 int fd;
181 if(im == nil)
182 return "no image";
184 memset(basename, 0, sizeof basename);
185 if(doc->docname)
186 strncpy(basename, doc->docname, sizeof(basename)-1);
187 else if((p = menugen(page)) && p[0] != '\0')
188 strncpy(basename, p+1, sizeof(basename)-1);
190 if(basename[0]) {
191 if(q = strrchr(basename, '/'))
192 q++;
193 else
194 q = basename;
195 if(p = strchr(q, '.'))
196 *p = 0;
198 memset(name, 0, sizeof name);
199 snprint(name, sizeof(name)-1, "%s.%d.bit", q, page+1);
200 if(access(name, 0) >= 0) {
201 strcat(name, "XXXX");
202 mktemp(name);
204 if(access(name, 0) >= 0)
205 return "couldn't think of a name for bitmap";
206 } else {
207 strcpy(name, "bitXXXX");
208 mktemp(name);
209 if(access(name, 0) >= 0)
210 return "couldn't think of a name for bitmap";
213 if((fd = create(name, OWRITE, 0666)) < 0) {
214 snprint(result, sizeof result, "cannot create %s: %r", name);
215 return result;
218 if(writeimage(fd, im, 0) < 0) {
219 snprint(result, sizeof result, "cannot writeimage: %r");
220 close(fd);
221 return result;
223 close(fd);
225 snprint(result, sizeof result, "wrote %s", name);
226 return result;
229 static void translate(Point);
231 static int
232 showdata(Plumbmsg *msg)
234 char *s;
236 s = plumblookup(msg->attr, "action");
237 return s && strcmp(s, "showdata")==0;
240 /* correspond to entries in miditems[] below,
241 * changing one means you need to change
242 */
243 enum{
244 Restore = 0,
245 Zin,
246 Fit,
247 Rot,
248 Upside,
249 Empty1,
250 Next,
251 Prev,
252 Zerox,
253 Empty2,
254 Reverse,
255 Del,
256 Write,
257 Empty3,
258 Exit,
259 };
261 void
262 viewer(Document *dd)
264 int i, fd, n, oldpage;
265 int nxt;
266 Menu menu, midmenu;
267 Mouse m;
268 Event e;
269 Point dxy, oxy, xy0;
270 Rectangle r;
271 Image *tmp;
272 static char *fwditems[] = { "this page", "next page", "exit", 0 };
273 static char *miditems[] = {
274 "orig size",
275 "zoom in",
276 "fit window",
277 "rotate 90",
278 "upside down",
279 "",
280 "next",
281 "prev",
282 "zerox",
283 "",
284 "reverse",
285 "discard",
286 "write",
287 "",
288 "quit",
290 };
291 char *s;
292 enum { Eplumb = 4 };
293 Plumbmsg *pm;
295 doc = dd; /* save global for menuhit */
296 ul = screen->r.min;
297 einit(Emouse|Ekeyboard);
298 if(doc->addpage != nil)
299 eplumb(Eplumb, "image");
301 esetcursor(&reading);
302 r.min = ZP;
304 /*
305 * im is a global pointer to the current image.
306 * eventually, i think we will have a layer between
307 * the display routines and the ps/pdf/whatever routines
308 * to perhaps cache and handle images of different
309 * sizes, etc.
310 */
311 im = 0;
312 page = reverse ? doc->npage-1 : 0;
314 if(doc->fwdonly) {
315 menu.item = fwditems;
316 menu.gen = 0;
317 menu.lasthit = 0;
318 } else {
319 menu.item = 0;
320 menu.gen = menugen;
321 menu.lasthit = 0;
324 midmenu.item = miditems;
325 midmenu.gen = 0;
326 midmenu.lasthit = Next;
328 showpage(page, &menu);
329 esetcursor(nil);
331 nxt = 0;
332 for(;;) {
333 /*
334 * throughout, if doc->fwdonly is set, we restrict the functionality
335 * a fair amount. we don't care about doc->npage anymore, and
336 * all that can be done is select the next page.
337 */
338 switch(eread(Emouse|Ekeyboard|Eplumb, &e)){
339 case Ekeyboard:
340 if(e.kbdc <= 0xFF && isdigit(e.kbdc)) {
341 nxt = nxt*10+e.kbdc-'0';
342 break;
343 } else if(e.kbdc != '\n')
344 nxt = 0;
345 switch(e.kbdc) {
346 case 'r': /* reverse page order */
347 if(doc->fwdonly)
348 break;
349 reverse = !reverse;
350 menu.lasthit = doc->npage-1-menu.lasthit;
352 /*
353 * the theory is that if we are reversing the
354 * document order and are on the first or last
355 * page then we're just starting and really want
356 * to view the other end. maybe the if
357 * should be dropped and this should happen always.
358 */
359 if(page == 0 || page == doc->npage-1) {
360 page = doc->npage-1-page;
361 showpage(page, &menu);
363 break;
364 case 'w': /* write bitmap of current screen */
365 esetcursor(&reading);
366 s = writebitmap();
367 if(s)
368 string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
369 display->defaultfont, s);
370 esetcursor(nil);
371 flushimage(display, 1);
372 break;
373 case 'd': /* remove image from working set */
374 if(doc->rmpage && page < doc->npage) {
375 if(doc->rmpage(doc, page) >= 0) {
376 if(doc->npage < 0)
377 wexits(0);
378 if(page >= doc->npage)
379 page = doc->npage-1;
380 showpage(page, &menu);
383 break;
384 case 'q':
385 case 0x04: /* ctrl-d */
386 wexits(0);
387 case 'u':
388 if(im==nil)
389 break;
390 esetcursor(&reading);
391 rot180(im);
392 esetcursor(nil);
393 upside = !upside;
394 redraw(screen);
395 flushimage(display, 1);
396 break;
397 case '-':
398 case '\b':
399 case Kleft:
400 if(page > 0 && !doc->fwdonly) {
401 --page;
402 showpage(page, &menu);
404 break;
405 case '\n':
406 if(nxt) {
407 nxt--;
408 if(nxt >= 0 && nxt < doc->npage && !doc->fwdonly)
409 showpage(page=nxt, &menu);
410 nxt = 0;
411 break;
413 goto Gotonext;
414 case Kright:
415 case ' ':
416 Gotonext:
417 if(doc->npage && ++page >= doc->npage && !doc->fwdonly)
418 wexits(0);
419 showpage(page, &menu);
420 break;
422 /*
423 * The upper y coordinate of the image is at ul.y in screen->r.
424 * Panning up means moving the upper left corner down. If the
425 * upper left corner is currently visible, we need to go back a page.
426 */
427 case Kup:
428 if(screen->r.min.y <= ul.y && ul.y < screen->r.max.y){
429 if(page > 0 && !doc->fwdonly){
430 --page;
431 showbottom = 1;
432 showpage(page, &menu);
434 } else {
435 i = Dy(screen->r)/2;
436 if(i > 10)
437 i -= 10;
438 if(i+ul.y > screen->r.min.y)
439 i = screen->r.min.y - ul.y;
440 translate(Pt(0, i));
442 break;
444 /*
445 * If the lower y coordinate is on the screen, we go to the next page.
446 * The lower y coordinate is at ul.y + Dy(im->r).
447 */
448 case Kdown:
449 i = ul.y + Dy(im->r);
450 if(screen->r.min.y <= i && i <= screen->r.max.y){
451 ul.y = screen->r.min.y;
452 goto Gotonext;
453 } else {
454 i = -Dy(screen->r)/2;
455 if(i < -10)
456 i += 10;
457 if(i+ul.y+Dy(im->r) <= screen->r.max.y)
458 i = screen->r.max.y - Dy(im->r) - ul.y - 1;
459 translate(Pt(0, i));
461 break;
462 default:
463 esetcursor(&query);
464 sleep(1000);
465 esetcursor(nil);
466 break;
468 break;
470 case Emouse:
471 m = e.mouse;
472 switch(m.buttons){
473 case Left:
474 oxy = m.xy;
475 xy0 = oxy;
476 do {
477 dxy = subpt(m.xy, oxy);
478 oxy = m.xy;
479 translate(dxy);
480 m = emouse();
481 } while(m.buttons == Left);
482 if(m.buttons) {
483 dxy = subpt(xy0, oxy);
484 translate(dxy);
486 break;
488 case Middle:
489 if(doc->npage == 0)
490 break;
492 n = emenuhit(Middle, &m, &midmenu);
493 if(n == -1)
494 break;
495 switch(n){
496 case Next: /* next */
497 if(reverse)
498 page--;
499 else
500 page++;
501 if(page < 0) {
502 if(reverse) return;
503 else page = 0;
506 if((page >= doc->npage) && !doc->fwdonly)
507 return;
509 showpage(page, &menu);
510 nxt = 0;
511 break;
512 case Prev: /* prev */
513 if(reverse)
514 page++;
515 else
516 page--;
517 if(page < 0) {
518 if(reverse) return;
519 else page = 0;
522 if((page >= doc->npage) && !doc->fwdonly && !reverse)
523 return;
525 showpage(page, &menu);
526 nxt = 0;
527 break;
528 case Zerox: /* prev */
529 zerox();
530 break;
531 case Zin: /* zoom in */
533 double delta;
534 Rectangle r;
536 r = egetrect(Middle, &m);
537 if((rectclip(&r, rectaddpt(im->r, ul)) == 0) ||
538 Dx(r) == 0 || Dy(r) == 0)
539 break;
540 /* use the smaller side to expand */
541 if(Dx(r) < Dy(r))
542 delta = (double)Dx(im->r)/(double)Dx(r);
543 else
544 delta = (double)Dy(im->r)/(double)Dy(r);
546 esetcursor(&reading);
547 tmp = xallocimage(display,
548 Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)),
549 im->chan, 0, DBlack);
550 if(tmp == nil) {
551 fprint(2, "out of memory during zoom: %r\n");
552 wexits("memory");
554 resample(im, tmp);
555 freeimage(im);
556 im = tmp;
557 esetcursor(nil);
558 ul = screen->r.min;
559 redraw(screen);
560 flushimage(display, 1);
561 break;
563 case Fit: /* fit */
565 double delta;
566 Rectangle r;
568 delta = (double)Dx(screen->r)/(double)Dx(im->r);
569 if((double)Dy(im->r)*delta > Dy(screen->r))
570 delta = (double)Dy(screen->r)/(double)Dy(im->r);
572 r = Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta));
573 esetcursor(&reading);
574 tmp = xallocimage(display, r, im->chan, 0, DBlack);
575 if(tmp == nil) {
576 fprint(2, "out of memory during fit: %r\n");
577 wexits("memory");
579 resample(im, tmp);
580 freeimage(im);
581 im = tmp;
582 esetcursor(nil);
583 ul = screen->r.min;
584 redraw(screen);
585 flushimage(display, 1);
586 break;
588 case Rot: /* rotate 90 */
589 esetcursor(&reading);
590 im = rot90(im);
591 esetcursor(nil);
592 redraw(screen);
593 flushimage(display, 1);
594 break;
595 case Upside: /* upside-down */
596 if(im==nil)
597 break;
598 esetcursor(&reading);
599 rot180(im);
600 esetcursor(nil);
601 upside = !upside;
602 redraw(screen);
603 flushimage(display, 1);
604 break;
605 case Restore: /* restore */
606 showpage(page, &menu);
607 break;
608 case Reverse: /* reverse */
609 if(doc->fwdonly)
610 break;
611 reverse = !reverse;
612 menu.lasthit = doc->npage-1-menu.lasthit;
614 if(page == 0 || page == doc->npage-1) {
615 page = doc->npage-1-page;
616 showpage(page, &menu);
618 break;
619 case Write: /* write */
620 esetcursor(&reading);
621 s = writebitmap();
622 if(s)
623 string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
624 display->defaultfont, s);
625 esetcursor(nil);
626 flushimage(display, 1);
627 break;
628 case Del: /* delete */
629 if(doc->rmpage && page < doc->npage) {
630 if(doc->rmpage(doc, page) >= 0) {
631 if(doc->npage < 0)
632 wexits(0);
633 if(page >= doc->npage)
634 page = doc->npage-1;
635 showpage(page, &menu);
638 break;
639 case Exit: /* exit */
640 return;
641 case Empty1:
642 case Empty2:
643 case Empty3:
644 break;
646 };
650 case Right:
651 if(doc->npage == 0)
652 break;
654 oldpage = page;
655 n = emenuhit(RMenu, &m, &menu);
656 if(n == -1)
657 break;
659 if(doc->fwdonly) {
660 switch(n){
661 case 0: /* this page */
662 break;
663 case 1: /* next page */
664 showpage(++page, &menu);
665 break;
666 case 2: /* exit */
667 return;
669 break;
672 if(n == doc->npage)
673 return;
674 else
675 page = reverse ? doc->npage-1-n : n;
677 if(oldpage != page)
678 showpage(page, &menu);
679 nxt = 0;
680 break;
682 break;
684 case Eplumb:
685 pm = e.v;
686 if(pm->ndata <= 0){
687 plumbfree(pm);
688 break;
690 if(showdata(pm)) {
691 s = estrdup("/tmp/pageplumbXXXXXXX");
692 fd = opentemp(s);
693 write(fd, pm->data, pm->ndata);
694 /* lose fd reference on purpose; the file is open ORCLOSE */
695 } else if(pm->data[0] == '/') {
696 s = estrdup(pm->data);
697 } else {
698 s = emalloc(strlen(pm->wdir)+1+pm->ndata+1);
699 sprint(s, "%s/%s", pm->wdir, pm->data);
700 cleanname(s);
702 if((i = doc->addpage(doc, s)) >= 0) {
703 page = i;
704 unhide();
705 showpage(page, &menu);
707 free(s);
708 plumbfree(pm);
709 break;
714 Image *gray;
716 /*
717 * A draw operation that touches only the area contained in bot but not in top.
718 * mp and sp get aligned with bot.min.
719 */
720 static void
721 gendrawdiff(Image *dst, Rectangle bot, Rectangle top,
722 Image *src, Point sp, Image *mask, Point mp, int op)
724 Rectangle r;
725 Point origin;
726 Point delta;
728 USED(op);
730 if(Dx(bot)*Dy(bot) == 0)
731 return;
733 /* no points in bot - top */
734 if(rectinrect(bot, top))
735 return;
737 /* bot - top ≡ bot */
738 if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
739 gendrawop(dst, bot, src, sp, mask, mp, op);
740 return;
743 origin = bot.min;
744 /* split bot into rectangles that don't intersect top */
745 /* left side */
746 if(bot.min.x < top.min.x){
747 r = Rect(bot.min.x, bot.min.y, top.min.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.min.x = top.min.x;
753 /* right side */
754 if(bot.max.x > top.max.x){
755 r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y);
756 delta = subpt(r.min, origin);
757 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
758 bot.max.x = top.max.x;
761 /* top */
762 if(bot.min.y < top.min.y){
763 r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
764 delta = subpt(r.min, origin);
765 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
766 bot.min.y = top.min.y;
769 /* bottom */
770 if(bot.max.y > top.max.y){
771 r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
772 delta = subpt(r.min, origin);
773 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
774 bot.max.y = top.max.y;
778 static void
779 drawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Image *mask, Point p, int op)
781 gendrawdiff(dst, bot, top, src, p, mask, p, op);
784 /*
785 * Translate the image in the window by delta.
786 */
787 static void
788 translate(Point delta)
790 Point u;
791 Rectangle r, or;
793 if(im == nil)
794 return;
796 u = pclip(addpt(ul, delta), ulrange);
797 delta = subpt(u, ul);
798 if(delta.x == 0 && delta.y == 0)
799 return;
801 /*
802 * The upper left corner of the image is currently at ul.
803 * We want to move it to u.
804 */
805 or = rectaddpt(Rpt(ZP, Pt(Dx(im->r), Dy(im->r))), ul);
806 r = rectaddpt(or, delta);
808 drawop(screen, r, screen, nil, ul, S);
809 ul = u;
811 /* fill in gray where image used to be but isn't. */
812 drawdiff(screen, insetrect(or, -2), insetrect(r, -2), gray, nil, ZP, S);
814 /* fill in black border */
815 drawdiff(screen, insetrect(r, -2), r, display->black, nil, ZP, S);
817 /* fill in image where it used to be off the screen. */
818 if(rectclip(&or, screen->r))
819 drawdiff(screen, r, rectaddpt(or, delta), im, nil, im->r.min, S);
820 else
821 drawop(screen, r, im, nil, im->r.min, S);
822 flushimage(display, 1);
825 void
826 redraw(Image *screen)
828 Rectangle r;
830 if(im == nil)
831 return;
833 ulrange.max = screen->r.max;
834 ulrange.min = subpt(screen->r.min, Pt(Dx(im->r), Dy(im->r)));
836 ul = pclip(ul, ulrange);
837 drawop(screen, screen->r, im, nil, subpt(im->r.min, subpt(ul, screen->r.min)), S);
839 if(im->repl)
840 return;
842 /* fill in any outer edges */
843 /* black border */
844 r = rectaddpt(im->r, subpt(ul, im->r.min));
845 border(screen, r, -2, display->black, ZP);
846 r.min = subpt(r.min, Pt(2,2));
847 r.max = addpt(r.max, Pt(2,2));
849 /* gray for the rest */
850 if(gray == nil) {
851 gray = xallocimage(display, Rect(0,0,1,1), RGB24, 1, 0x888888FF);
852 if(gray == nil) {
853 fprint(2, "g out of memory: %r\n");
854 wexits("mem");
857 border(screen, r, -4000, gray, ZP);
858 // flushimage(display, 0);
861 void
862 eresized(int new)
864 Rectangle r;
865 r = screen->r;
866 if(new && getwindow(display, Refnone) < 0)
867 fprint(2,"can't reattach to window");
868 ul = addpt(ul, subpt(screen->r.min, r.min));
869 redraw(screen);
872 /* clip p to be in r */
873 Point
874 pclip(Point p, Rectangle r)
876 if(p.x < r.min.x)
877 p.x = r.min.x;
878 else if(p.x >= r.max.x)
879 p.x = r.max.x-1;
881 if(p.y < r.min.y)
882 p.y = r.min.y;
883 else if(p.y >= r.max.y)
884 p.y = r.max.y-1;
886 return p;
889 /*
890 * resize is perhaps a misnomer.
891 * this really just grows the window to be at least dx across
892 * and dy high. if the window hits the bottom or right edge,
893 * it is backed up until it hits the top or left edge.
894 */
895 void
896 resize(int dx, int dy)
898 static Rectangle sr;
899 Rectangle r, or;
901 dx += 2*Borderwidth;
902 dy += 2*Borderwidth;
903 if(wctlfd < 0){
904 wctlfd = open("/dev/wctl", OWRITE);
905 if(wctlfd < 0)
906 return;
909 r = insetrect(screen->r, -Borderwidth);
910 if(Dx(r) >= dx && Dy(r) >= dy)
911 return;
913 if(Dx(sr)*Dy(sr) == 0)
914 sr = screenrect();
916 or = r;
918 r.max.x = max(r.min.x+dx, r.max.x);
919 r.max.y = max(r.min.y+dy, r.max.y);
920 if(r.max.x > sr.max.x){
921 if(Dx(r) > Dx(sr)){
922 r.min.x = 0;
923 r.max.x = sr.max.x;
924 }else
925 r = rectaddpt(r, Pt(sr.max.x-r.max.x, 0));
927 if(r.max.y > sr.max.y){
928 if(Dy(r) > Dy(sr)){
929 r.min.y = 0;
930 r.max.y = sr.max.y;
931 }else
932 r = rectaddpt(r, Pt(0, sr.max.y-r.max.y));
935 /*
936 * Sometimes we can't actually grow the window big enough,
937 * and resizing it to the same shape makes it flash.
938 */
939 if(Dx(r) == Dx(or) && Dy(r) == Dy(or))
940 return;
942 fprint(wctlfd, "resize -minx %d -miny %d -maxx %d -maxy %d\n",
943 r.min.x, r.min.y, r.max.x, r.max.y);
946 /*
947 * If we allocimage after a resize but before flushing the draw buffer,
948 * we won't have seen the reshape event, and we won't have called
949 * getwindow, and allocimage will fail. So we flushimage before every alloc.
950 */
951 Image*
952 xallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val)
954 flushimage(display, 0);
955 return allocimage(d, r, chan, repl, val);
958 /* all code below this line should be in the library, but is stolen from colors instead */
959 static char*
960 rdenv(char *name)
962 char *v;
963 int fd, size;
965 fd = open(name, OREAD);
966 if(fd < 0)
967 return 0;
968 size = seek(fd, 0, 2);
969 v = malloc(size+1);
970 if(v == 0){
971 fprint(2, "page: can't malloc: %r\n");
972 wexits("no mem");
974 seek(fd, 0, 0);
975 read(fd, v, size);
976 v[size] = 0;
977 close(fd);
978 return v;
981 Rectangle
982 screenrect(void)
984 int fd;
985 char buf[12*5];
987 fd = open("/dev/screen", OREAD);
988 if(fd == -1)
989 fd=open("/mnt/term/dev/screen", OREAD);
990 if(fd == -1){
991 fprint(2, "page: can't open /dev/screen: %r\n");
992 wexits("window read");
994 if(read(fd, buf, sizeof buf) != sizeof buf){
995 fprint(2, "page: can't read /dev/screen: %r\n");
996 wexits("screen read");
998 close(fd);
999 return Rect(atoi(buf+12), atoi(buf+24), atoi(buf+36), atoi(buf+48));
1002 void
1003 zerox(void)
1005 int pfd[2];
1007 pipe(pfd);
1008 switch(rfork(RFFDG|RFPROC)) {
1009 case -1:
1010 wexits("cannot fork in zerox: %r");
1011 case 0:
1012 dup(pfd[1], 0);
1013 close(pfd[0]);
1014 execl("/bin/page", "page", "-w", 0);
1015 wexits("cannot exec in zerox: %r\n");
1016 default:
1017 close(pfd[1]);
1018 writeimage(pfd[0], im, 0);
1019 close(pfd[0]);
1020 break;