Blob


1 /*
2 * the actual viewer that handles screen stuff
3 */
5 #include <u.h>
6 #include <libc.h>
7 #include <9pclient.h>
8 #include <draw.h>
9 #include <cursor.h>
10 #include <mouse.h>
11 #include <keyboard.h>
12 #include <thread.h>
13 #include <bio.h>
14 #include <plumb.h>
15 #include <ctype.h>
16 #include "page.h"
18 Document *doc;
19 Mousectl *mc;
20 Image *im;
21 int page;
22 int angle = 0;
23 int showbottom = 0; /* on the next showpage, move the image so the bottom is visible. */
25 Rectangle ulrange; /* the upper left corner of the image must be in this rectangle */
26 Point ul; /* the upper left corner of the image is at this point on the screen */
28 Point pclip(Point, Rectangle);
29 Rectangle mkrange(Rectangle screenr, Rectangle imr);
30 void redraw(Image*);
31 void plumbproc(void*);
33 Cursor reading={
34 {-1, -1},
35 {0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00,
36 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0,
37 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0,
38 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, },
39 {0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00,
40 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0,
41 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40,
42 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, }
43 };
45 Cursor query = {
46 {-7,-7},
47 {0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe,
48 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8,
49 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0,
50 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, },
51 {0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c,
52 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0,
53 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80,
54 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
55 };
57 enum {
58 Left = 1,
59 Middle = 2,
60 Right = 4,
62 RMenu = 3,
63 };
65 void
66 unhide(void)
67 {
68 USED(nil);
69 }
71 int
72 max(int a, int b)
73 {
74 return a > b ? a : b;
75 }
77 int
78 min(int a, int b)
79 {
80 return a < b ? a : b;
81 }
84 char*
85 menugen(int n)
86 {
87 static char menustr[32];
88 char *p;
89 int len;
91 if(n == doc->npage)
92 return "exit";
93 if(n > doc->npage)
94 return nil;
96 if(reverse)
97 n = doc->npage-1-n;
99 p = doc->pagename(doc, n);
100 len = (sizeof menustr)-2;
102 if(strlen(p) > len && strrchr(p, '/'))
103 p = strrchr(p, '/')+1;
104 if(strlen(p) > len)
105 p = p+strlen(p)-len;
107 strcpy(menustr+1, p);
108 if(page == n)
109 menustr[0] = '>';
110 else
111 menustr[0] = ' ';
112 return menustr;
115 void
116 showpage(int page, Menu *m)
118 Image *tmp;
120 if(doc->fwdonly)
121 m->lasthit = 0; /* this page */
122 else
123 m->lasthit = reverse ? doc->npage-1-page : page;
125 setcursor(mc, &reading);
126 freeimage(im);
127 if((page < 0 || page >= doc->npage) && !doc->fwdonly){
128 im = nil;
129 return;
131 im = doc->drawpage(doc, page);
132 if(im == nil) {
133 if(doc->fwdonly) /* this is how we know we're out of pages */
134 wexits(0);
136 im = xallocimage(display, Rect(0,0,50,50), GREY1, 1, DBlack);
137 if(im == nil) {
138 fprint(2, "out of memory: %r\n");
139 wexits("memory");
141 string(im, ZP, display->white, ZP, display->defaultfont, "?");
142 }else if(resizing){
143 resize(Dx(im->r), Dy(im->r));
145 if(im->r.min.x > 0 || im->r.min.y > 0) {
146 tmp = xallocimage(display, Rect(0, 0, Dx(im->r), Dy(im->r)), im->chan, 0, DNofill);
147 if(tmp == nil) {
148 fprint(2, "out of memory during showpage: %r\n");
149 wexits("memory");
151 drawop(tmp, tmp->r, im, nil, im->r.min, S);
152 freeimage(im);
153 im = tmp;
156 switch(angle){
157 case 90:
158 im = rot90(im);
159 break;
160 case 180:
161 rot180(im);
162 break;
163 case 270:
164 im = rot270(im);
165 break;
168 setcursor(mc, nil);
169 if(showbottom){
170 ul.y = screen->r.max.y - Dy(im->r);
171 showbottom = 0;
174 redraw(screen);
175 flushimage(display, 1);
178 char*
179 writebitmap(void)
181 char basename[64];
182 char name[64+30];
183 static char result[200];
184 char *p, *q;
185 int fd = -1;
187 if(im == nil)
188 return "no image";
190 memset(basename, 0, sizeof basename);
191 if(doc->docname)
192 strncpy(basename, doc->docname, sizeof(basename)-1);
193 else if((p = menugen(page)) && p[0] != '\0')
194 strncpy(basename, p+1, sizeof(basename)-1);
196 if(basename[0]) {
197 if(q = strrchr(basename, '/'))
198 q++;
199 else
200 q = basename;
201 if(p = strchr(q, '.'))
202 *p = 0;
204 memset(name, 0, sizeof name);
205 snprint(name, sizeof(name)-1, "%s.%d.bit", q, page+1);
206 if(access(name, 0) >= 0) {
207 strcat(name, "XXXX");
208 fd = mkstemp(name);
210 if(fd < 0)
211 return "couldn't think of a name for bitmap";
212 } else {
213 strcpy(name, "bitXXXX");
214 mkstemp(name);
215 if(fd < 0)
216 return "couldn't think of a name for bitmap";
219 if(fd < 0) {
220 snprint(result, sizeof result, "cannot create %s: %r", name);
221 return result;
224 if(writeimage(fd, im, 0) < 0) {
225 snprint(result, sizeof result, "cannot writeimage: %r");
226 close(fd);
227 return result;
229 close(fd);
231 snprint(result, sizeof result, "wrote %s", name);
232 return result;
235 static void translate(Point);
237 static int
238 showdata(Plumbmsg *msg)
240 char *s;
242 s = plumblookup(msg->attr, "action");
243 return s && strcmp(s, "showdata")==0;
246 /* correspond to entries in miditems[] below,
247 * changing one means you need to change
248 */
249 enum{
250 Restore = 0,
251 Zin,
252 Fit,
253 Rot,
254 Upside,
255 Empty1,
256 Next,
257 Prev,
258 Zerox,
259 Empty2,
260 Reverse,
261 Del,
262 Write,
263 Empty3,
264 Exit,
265 };
267 void
268 viewer(Document *dd)
270 int i, fd, n, oldpage;
271 int nxt;
272 Channel *cp;
273 Menu menu, midmenu;
274 Mouse m;
275 Keyboardctl *kc;
276 Point dxy, oxy, xy0;
277 Rune run;
278 Rectangle r;
279 int size[2];
280 Image *tmp;
281 static char *fwditems[] = { "this page", "next page", "exit", 0 };
282 static char *miditems[] = {
283 "orig size",
284 "zoom in",
285 "fit window",
286 "rotate 90",
287 "upside down",
288 "",
289 "next",
290 "prev",
291 "zerox",
292 "",
293 "reverse",
294 "discard",
295 "write",
296 "",
297 "quit",
299 };
300 char *s;
301 enum {
302 CMouse,
303 CResize,
304 CKeyboard,
305 CPlumb,
306 CN
307 };
308 Alt alts[CN+1];
309 Plumbmsg *pm;
311 cp = chancreate(sizeof pm, 0);
312 assert(cp);
314 doc = dd; /* save global for menuhit */
315 ul = screen->r.min;
316 mc = initmouse(nil, screen);
317 kc = initkeyboard(nil);
318 alts[CMouse].c = mc->c;
319 alts[CMouse].v = &m;
320 alts[CMouse].op = CHANRCV;
321 alts[CResize].c = mc->resizec;
322 alts[CResize].v = &size;
323 alts[CResize].op = CHANRCV;
324 alts[CKeyboard].c = kc->c;
325 alts[CKeyboard].v = &run;
326 alts[CKeyboard].op = CHANRCV;
327 alts[CPlumb].c = cp;
328 alts[CPlumb].v = &pm;
329 alts[CPlumb].op = CHANNOP;
330 alts[CN].op = CHANEND;
332 /* XXX: Event */
333 if(doc->addpage != nil) {
334 alts[CPlumb].op = CHANRCV;
335 proccreate(plumbproc, cp, 16384);
338 setcursor(mc, &reading);
339 r.min = ZP;
341 /*
342 * im is a global pointer to the current image.
343 * eventually, i think we will have a layer between
344 * the display routines and the ps/pdf/whatever routines
345 * to perhaps cache and handle images of different
346 * sizes, etc.
347 */
348 im = 0;
349 page = reverse ? doc->npage-1 : 0;
351 if(doc->fwdonly) {
352 menu.item = fwditems;
353 menu.gen = 0;
354 menu.lasthit = 0;
355 } else {
356 menu.item = 0;
357 menu.gen = menugen;
358 menu.lasthit = 0;
361 midmenu.item = miditems;
362 midmenu.gen = 0;
363 midmenu.lasthit = Next;
365 showpage(page, &menu);
366 setcursor(mc, nil);
368 nxt = 0;
369 for(;;) {
370 /*
371 * throughout, if doc->fwdonly is set, we restrict the functionality
372 * a fair amount. we don't care about doc->npage anymore, and
373 * all that can be done is select the next page.
374 */
375 switch(alt(alts)) {
376 case CKeyboard:
377 if(run <= 0xFF && isdigit(run)) {
378 nxt = nxt*10+run-'0';
379 break;
380 } else if(run != '\n')
381 nxt = 0;
382 switch(run) {
383 case 'r': /* reverse page order */
384 if(doc->fwdonly)
385 break;
386 reverse = !reverse;
387 menu.lasthit = doc->npage-1-menu.lasthit;
389 /*
390 * the theory is that if we are reversing the
391 * document order and are on the first or last
392 * page then we're just starting and really want
393 * to view the other end. maybe the if
394 * should be dropped and this should happen always.
395 */
396 if(page == 0 || page == doc->npage-1) {
397 page = doc->npage-1-page;
398 showpage(page, &menu);
400 break;
401 case 'w': /* write bitmap of current screen */
402 setcursor(mc, &reading);
403 s = writebitmap();
404 if(s)
405 string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
406 display->defaultfont, s);
407 setcursor(mc, nil);
408 flushimage(display, 1);
409 break;
410 case 'd': /* remove image from working set */
411 if(doc->rmpage && page < doc->npage) {
412 if(doc->rmpage(doc, page) >= 0) {
413 if(doc->npage < 0)
414 wexits(0);
415 if(page >= doc->npage)
416 page = doc->npage-1;
417 showpage(page, &menu);
420 break;
421 case 'q':
422 case 0x04: /* ctrl-d */
423 wexits(0);
424 case 'u':
425 if(im==nil)
426 break;
427 setcursor(mc, &reading);
428 rot180(im);
429 setcursor(mc, nil);
430 angle = (angle+180) % 360;
431 redraw(screen);
432 flushimage(display, 1);
433 break;
434 case '-':
435 case '\b':
436 case Kleft:
437 if(page > 0 && !doc->fwdonly) {
438 --page;
439 showpage(page, &menu);
441 break;
442 case '\n':
443 if(nxt) {
444 nxt--;
445 if(nxt >= 0 && nxt < doc->npage && !doc->fwdonly)
446 showpage(page=nxt, &menu);
447 nxt = 0;
448 break;
450 goto Gotonext;
451 case Kright:
452 case ' ':
453 Gotonext:
454 if(doc->npage && ++page >= doc->npage && !doc->fwdonly)
455 wexits(0);
456 showpage(page, &menu);
457 break;
459 /*
460 * The upper y coordinate of the image is at ul.y in screen->r.
461 * Panning up means moving the upper left corner down. If the
462 * upper left corner is currently visible, we need to go back a page.
463 */
464 case Kup:
465 if(screen->r.min.y <= ul.y && ul.y < screen->r.max.y){
466 if(page > 0 && !doc->fwdonly){
467 --page;
468 showbottom = 1;
469 showpage(page, &menu);
471 } else {
472 i = Dy(screen->r)/2;
473 if(i > 10)
474 i -= 10;
475 if(i+ul.y > screen->r.min.y)
476 i = screen->r.min.y - ul.y;
477 translate(Pt(0, i));
479 break;
481 /*
482 * If the lower y coordinate is on the screen, we go to the next page.
483 * The lower y coordinate is at ul.y + Dy(im->r).
484 */
485 case Kdown:
486 i = ul.y + Dy(im->r);
487 if(screen->r.min.y <= i && i <= screen->r.max.y){
488 ul.y = screen->r.min.y;
489 goto Gotonext;
490 } else {
491 i = -Dy(screen->r)/2;
492 if(i < -10)
493 i += 10;
494 if(i+ul.y+Dy(im->r) <= screen->r.max.y)
495 i = screen->r.max.y - Dy(im->r) - ul.y - 1;
496 translate(Pt(0, i));
498 break;
499 default:
500 setcursor(mc, &query);
501 sleep(1000);
502 setcursor(mc, nil);
503 break;
505 break;
507 case CMouse:
508 switch(m.buttons){
509 case Left:
510 oxy = m.xy;
511 xy0 = oxy;
512 do {
513 dxy = subpt(m.xy, oxy);
514 oxy = m.xy;
515 translate(dxy);
516 recv(mc->c, &m);
517 } while(m.buttons == Left);
518 if(m.buttons) {
519 dxy = subpt(xy0, oxy);
520 translate(dxy);
522 break;
524 case Middle:
525 if(doc->npage == 0)
526 break;
528 n = menuhit(Middle, mc, &midmenu, nil);
529 if(n == -1)
530 break;
531 switch(n){
532 case Next: /* next */
533 if(reverse)
534 page--;
535 else
536 page++;
537 if(page < 0) {
538 if(reverse) return;
539 else page = 0;
542 if((page >= doc->npage) && !doc->fwdonly)
543 return;
545 showpage(page, &menu);
546 nxt = 0;
547 break;
548 case Prev: /* prev */
549 if(reverse)
550 page++;
551 else
552 page--;
553 if(page < 0) {
554 if(reverse) return;
555 else page = 0;
558 if((page >= doc->npage) && !doc->fwdonly && !reverse)
559 return;
561 showpage(page, &menu);
562 nxt = 0;
563 break;
564 case Zerox: /* prev */
565 zerox();
566 break;
567 case Zin: /* zoom in */
569 double delta;
570 Rectangle r;
572 r = getrect(Middle, mc);
573 if((rectclip(&r, rectaddpt(im->r, ul)) == 0) ||
574 Dx(r) == 0 || Dy(r) == 0)
575 break;
576 /* use the smaller side to expand */
577 if(Dx(r) < Dy(r))
578 delta = (double)Dx(im->r)/(double)Dx(r);
579 else
580 delta = (double)Dy(im->r)/(double)Dy(r);
582 setcursor(mc, &reading);
583 tmp = xallocimage(display,
584 Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)),
585 im->chan, 0, DBlack);
586 if(tmp == nil) {
587 fprint(2, "out of memory during zoom: %r\n");
588 wexits("memory");
590 resample(im, tmp);
591 freeimage(im);
592 im = tmp;
593 setcursor(mc, nil);
594 ul = screen->r.min;
595 redraw(screen);
596 flushimage(display, 1);
597 break;
599 case Fit: /* fit */
601 double delta;
602 Rectangle r;
604 delta = (double)Dx(screen->r)/(double)Dx(im->r);
605 if((double)Dy(im->r)*delta > Dy(screen->r))
606 delta = (double)Dy(screen->r)/(double)Dy(im->r);
608 r = Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta));
609 setcursor(mc, &reading);
610 tmp = xallocimage(display, r, im->chan, 0, DBlack);
611 if(tmp == nil) {
612 fprint(2, "out of memory during fit: %r\n");
613 wexits("memory");
615 resample(im, tmp);
616 freeimage(im);
617 im = tmp;
618 setcursor(mc, nil);
619 ul = screen->r.min;
620 redraw(screen);
621 flushimage(display, 1);
622 break;
624 case Rot: /* rotate 90 */
625 setcursor(mc, &reading);
626 im = rot90(im);
627 setcursor(mc, nil);
628 angle = (angle+90) % 360;
629 redraw(screen);
630 flushimage(display, 1);
631 break;
632 case Upside: /* upside-down */
633 if(im==nil)
634 break;
635 setcursor(mc, &reading);
636 rot180(im);
637 setcursor(mc, nil);
638 angle = (angle+180) % 360;
639 redraw(screen);
640 flushimage(display, 1);
641 break;
642 case Restore: /* restore */
643 showpage(page, &menu);
644 break;
645 case Reverse: /* reverse */
646 if(doc->fwdonly)
647 break;
648 reverse = !reverse;
649 menu.lasthit = doc->npage-1-menu.lasthit;
651 if(page == 0 || page == doc->npage-1) {
652 page = doc->npage-1-page;
653 showpage(page, &menu);
655 break;
656 case Write: /* write */
657 setcursor(mc, &reading);
658 s = writebitmap();
659 if(s)
660 string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
661 display->defaultfont, s);
662 setcursor(mc, nil);
663 flushimage(display, 1);
664 break;
665 case Del: /* delete */
666 if(doc->rmpage && page < doc->npage) {
667 if(doc->rmpage(doc, page) >= 0) {
668 if(doc->npage < 0)
669 wexits(0);
670 if(page >= doc->npage)
671 page = doc->npage-1;
672 showpage(page, &menu);
675 break;
676 case Exit: /* exit */
677 return;
678 case Empty1:
679 case Empty2:
680 case Empty3:
681 break;
683 };
687 case Right:
688 if(doc->npage == 0)
689 break;
691 oldpage = page;
692 n = menuhit(RMenu, mc, &menu, nil);
693 if(n == -1)
694 break;
696 if(doc->fwdonly) {
697 switch(n){
698 case 0: /* this page */
699 break;
700 case 1: /* next page */
701 showpage(++page, &menu);
702 break;
703 case 2: /* exit */
704 return;
706 break;
709 if(n == doc->npage)
710 return;
711 else
712 page = reverse ? doc->npage-1-n : n;
714 if(oldpage != page)
715 showpage(page, &menu);
716 nxt = 0;
717 break;
719 break;
720 case CResize:
721 r = screen->r;
722 if(getwindow(display, Refnone) < 0)
723 fprint(2,"can't reattach to window");
724 ul = addpt(ul, subpt(screen->r.min, r.min));
725 redraw(screen);
726 flushimage(display, 1);
727 break;
728 case CPlumb:
729 if(pm->ndata <= 0){
730 plumbfree(pm);
731 break;
733 if(showdata(pm)) {
734 s = estrdup("/tmp/pageplumbXXXXXXX");
735 fd = opentemp(s);
736 write(fd, pm->data, pm->ndata);
737 /* lose fd reference on purpose; the file is open ORCLOSE */
738 } else if(pm->data[0] == '/') {
739 s = estrdup(pm->data);
740 } else {
741 s = emalloc(strlen(pm->wdir)+1+pm->ndata+1);
742 sprint(s, "%s/%s", pm->wdir, pm->data);
743 cleanname(s);
745 if((i = doc->addpage(doc, s)) >= 0) {
746 page = i;
747 unhide();
748 showpage(page, &menu);
750 free(s);
751 plumbfree(pm);
752 break;
757 Image *gray;
759 /*
760 * A draw operation that touches only the area contained in bot but not in top.
761 * mp and sp get aligned with bot.min.
762 */
763 static void
764 gendrawdiff(Image *dst, Rectangle bot, Rectangle top,
765 Image *src, Point sp, Image *mask, Point mp, int op)
767 Rectangle r;
768 Point origin;
769 Point delta;
771 USED(op);
773 if(Dx(bot)*Dy(bot) == 0)
774 return;
776 /* no points in bot - top */
777 if(rectinrect(bot, top))
778 return;
780 /* bot - top ≡ bot */
781 if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
782 gendrawop(dst, bot, src, sp, mask, mp, op);
783 return;
786 origin = bot.min;
787 /* split bot into rectangles that don't intersect top */
788 /* left side */
789 if(bot.min.x < top.min.x){
790 r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y);
791 delta = subpt(r.min, origin);
792 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
793 bot.min.x = top.min.x;
796 /* right side */
797 if(bot.max.x > top.max.x){
798 r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y);
799 delta = subpt(r.min, origin);
800 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
801 bot.max.x = top.max.x;
804 /* top */
805 if(bot.min.y < top.min.y){
806 r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
807 delta = subpt(r.min, origin);
808 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
809 bot.min.y = top.min.y;
812 /* bottom */
813 if(bot.max.y > top.max.y){
814 r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
815 delta = subpt(r.min, origin);
816 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
817 bot.max.y = top.max.y;
821 static void
822 drawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Image *mask, Point p, int op)
824 gendrawdiff(dst, bot, top, src, p, mask, p, op);
827 /*
828 * Translate the image in the window by delta.
829 */
830 static void
831 translate(Point delta)
833 Point u;
834 Rectangle r, or;
836 if(im == nil)
837 return;
839 u = pclip(addpt(ul, delta), ulrange);
840 delta = subpt(u, ul);
841 if(delta.x == 0 && delta.y == 0)
842 return;
844 /*
845 * The upper left corner of the image is currently at ul.
846 * We want to move it to u.
847 */
848 or = rectaddpt(Rpt(ZP, Pt(Dx(im->r), Dy(im->r))), ul);
849 r = rectaddpt(or, delta);
851 drawop(screen, r, screen, nil, ul, S);
852 ul = u;
854 /* fill in gray where image used to be but isn't. */
855 drawdiff(screen, insetrect(or, -2), insetrect(r, -2), gray, nil, ZP, S);
857 /* fill in black border */
858 drawdiff(screen, insetrect(r, -2), r, display->black, nil, ZP, S);
860 /* fill in image where it used to be off the screen. */
861 if(rectclip(&or, screen->r))
862 drawdiff(screen, r, rectaddpt(or, delta), im, nil, im->r.min, S);
863 else
864 drawop(screen, r, im, nil, im->r.min, S);
865 flushimage(display, 1);
868 void
869 redraw(Image *screen)
871 Rectangle r;
873 if(im == nil)
874 return;
876 ulrange.max = screen->r.max;
877 ulrange.min = subpt(screen->r.min, Pt(Dx(im->r), Dy(im->r)));
879 ul = pclip(ul, ulrange);
880 drawop(screen, screen->r, im, nil, subpt(im->r.min, subpt(ul, screen->r.min)), S);
882 if(im->repl)
883 return;
885 /* fill in any outer edges */
886 /* black border */
887 r = rectaddpt(im->r, subpt(ul, im->r.min));
888 border(screen, r, -2, display->black, ZP);
889 r.min = subpt(r.min, Pt(2,2));
890 r.max = addpt(r.max, Pt(2,2));
892 /* gray for the rest */
893 if(gray == nil) {
894 gray = xallocimage(display, Rect(0,0,1,1), RGB24, 1, 0x888888FF);
895 if(gray == nil) {
896 fprint(2, "g out of memory: %r\n");
897 wexits("mem");
900 border(screen, r, -4000, gray, ZP);
901 // flushimage(display, 0);
904 /* clip p to be in r */
905 Point
906 pclip(Point p, Rectangle r)
908 if(p.x < r.min.x)
909 p.x = r.min.x;
910 else if(p.x >= r.max.x)
911 p.x = r.max.x-1;
913 if(p.y < r.min.y)
914 p.y = r.min.y;
915 else if(p.y >= r.max.y)
916 p.y = r.max.y-1;
918 return p;
921 /*
922 * resize is perhaps a misnomer.
923 * this really just grows the window to be at least dx across
924 * and dy high. if the window hits the bottom or right edge,
925 * it is backed up until it hits the top or left edge.
926 */
927 void
928 resize(int dx, int dy)
930 static Rectangle sr;
931 Rectangle r, or;
933 r = screen->r;
934 if(Dx(sr)*Dy(sr) == 0) {
935 sr = screenrect();
936 /* Start with the size of the first image */
937 r.max.x = r.min.x;
938 r.max.y = r.min.y;
941 if(Dx(r) >= dx && Dy(r) >= dy)
942 return;
944 or = r;
946 r.max.x = max(r.min.x+dx, r.max.x);
947 r.max.y = max(r.min.y+dy, r.max.y);
948 if(r.max.x > sr.max.x){
949 if(Dx(r) > Dx(sr)){
950 r.min.x = 0;
951 r.max.x = sr.max.x;
952 }else
953 r = rectaddpt(r, Pt(sr.max.x-r.max.x, 0));
955 if(r.max.y > sr.max.y){
956 if(Dy(r) > Dy(sr)){
957 r.min.y = 0;
958 r.max.y = sr.max.y;
959 }else
960 r = rectaddpt(r, Pt(0, sr.max.y-r.max.y));
963 /*
964 * Sometimes we can't actually grow the window big enough,
965 * and resizing it to the same shape makes it flash.
966 */
967 if(Dx(r) == Dx(or) && Dy(r) == Dy(or))
968 return;
970 drawresizewindow(r);
973 /*
974 * If we allocimage after a resize but before flushing the draw buffer,
975 * we won't have seen the reshape event, and we won't have called
976 * getwindow, and allocimage will fail. So we flushimage before every alloc.
977 */
978 Image*
979 xallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val)
981 flushimage(display, 0);
982 return allocimage(d, r, chan, repl, val);
985 void
986 plumbproc(void *c)
988 Channel *cp;
989 CFid *fd;
991 cp = c;
992 fd = plumbopenfid("image", OREAD|OCEXEC);
993 if(fd == nil) {
994 fprint(2, "Cannot connect to the plumber");
995 threadexits("plumber");
997 for(;;) {
998 send(cp, plumbrecvfid(fd));
1002 /* XXX: This function is ugly and hacky. There may be a better way... or not */
1003 Rectangle
1004 screenrect(void)
1006 int fd[3], pfd[2];
1007 int n, w, h;
1008 char buf[64];
1009 char *p, *pr;
1011 if(pipe(pfd) < 0)
1012 wexits("pipe failed");
1014 fd[0] = open("/dev/null", OREAD);
1015 fd[1] = pfd[1];
1016 fd[2] = dup(2, -1);
1017 if(threadspawnl(fd, "rc", "rc", "-c", "xdpyinfo | grep 'dimensions:'", nil) == -1)
1018 wexits("threadspawnl failed");
1020 if((n = read(pfd[0], buf, 63)) <= 0)
1021 wexits("read xdpyinfo failed");
1022 close(fd[0]);
1024 buf[n] = '\0';
1025 for(p = buf; *p; p++)
1026 if(*p >= '0' && *p <= '9') break;
1027 if(*p == '\0')
1028 wexits("xdpyinfo parse failed");
1030 w = strtoul(p, &pr, 10);
1031 if(p == pr || *pr == '\0' || *(++pr) == '\0')
1032 wexits("xdpyinfo parse failed");
1033 h = strtoul(pr, &p, 10);
1034 if(p == pr)
1035 wexits("xdpyinfo parse failed");
1037 return Rect(0, 0, w, h);
1040 void
1041 zerox(void)
1043 int pfd[2];
1044 int fd[3];
1046 pipe(pfd);
1047 fd[0] = pfd[0];
1048 fd[1] = dup(1, -1);
1049 fd[2] = dup(2, -1);
1050 threadspawnl(fd, "page", "page", "-R", nil);
1052 writeimage(pfd[1], im, 0);
1053 close(pfd[1]);