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 Image *tofree;
22 int page;
23 int angle = 0;
24 int showbottom = 0; /* on the next showpage, move the image so the bottom is visible. */
26 Rectangle ulrange; /* the upper left corner of the image must be in this rectangle */
27 Point ul; /* the upper left corner of the image is at this point on the screen */
29 Point pclip(Point, Rectangle);
30 Rectangle mkrange(Rectangle screenr, Rectangle imr);
31 void redraw(Image*);
32 void plumbproc(void*);
34 Cursor reading={
35 {-1, -1},
36 {0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00,
37 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0,
38 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0,
39 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, },
40 {0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00,
41 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0,
42 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40,
43 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, }
44 };
46 Cursor query = {
47 {-7,-7},
48 {0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe,
49 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8,
50 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0,
51 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, },
52 {0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c,
53 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0,
54 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80,
55 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
56 };
58 enum {
59 Left = 1,
60 Middle = 2,
61 Right = 4,
63 RMenu = 3,
64 };
66 static void
67 delayfreeimage(Image *m)
68 {
69 if(m == tofree)
70 return;
71 if(tofree)
72 freeimage(tofree);
73 tofree = m;
74 }
76 void
77 unhide(void)
78 {
79 USED(nil);
80 }
82 int
83 max(int a, int b)
84 {
85 return a > b ? a : b;
86 }
88 int
89 min(int a, int b)
90 {
91 return a < b ? a : b;
92 }
95 char*
96 menugen(int n)
97 {
98 static char menustr[32];
99 char *p;
100 int len;
102 if(n == doc->npage)
103 return "exit";
104 if(n > doc->npage)
105 return nil;
107 if(reverse)
108 n = doc->npage-1-n;
110 p = doc->pagename(doc, n);
111 len = (sizeof menustr)-2;
113 if(strlen(p) > len && strrchr(p, '/'))
114 p = strrchr(p, '/')+1;
115 if(strlen(p) > len)
116 p = p+strlen(p)-len;
118 strcpy(menustr+1, p);
119 if(page == n)
120 menustr[0] = '>';
121 else
122 menustr[0] = ' ';
123 return menustr;
126 void
127 showpage(int page, Menu *m)
129 if(doc->fwdonly)
130 m->lasthit = 0; /* this page */
131 else
132 m->lasthit = reverse ? doc->npage-1-page : page;
134 setcursor(mc, &reading);
135 delayfreeimage(nil);
136 im = cachedpage(doc, angle, page);
137 if(im == nil)
138 wexits(0);
139 if(resizing)
140 resize(Dx(im->r), Dy(im->r));
142 setcursor(mc, nil);
143 if(showbottom){
144 ul.y = screen->r.max.y - Dy(im->r);
145 showbottom = 0;
148 if((doc->type == Tgfx) && fitwin)
149 fit();
150 else{
151 redraw(screen);
152 flushimage(display, 1);
156 char*
157 writebitmap(void)
159 char basename[64];
160 char name[64+30];
161 static char result[200];
162 char *p, *q;
163 int fd = -1;
165 if(im == nil)
166 return "no image";
168 memset(basename, 0, sizeof basename);
169 if(doc->docname)
170 strncpy(basename, doc->docname, sizeof(basename)-1);
171 else if((p = menugen(page)) && p[0] != '\0')
172 strncpy(basename, p+1, sizeof(basename)-1);
174 if(basename[0]) {
175 if(q = strrchr(basename, '/'))
176 q++;
177 else
178 q = basename;
179 if(p = strchr(q, '.'))
180 *p = 0;
182 memset(name, 0, sizeof name);
183 snprint(name, sizeof(name)-1, "%s.%d.bit", q, page+1);
184 if(access(name, 0) >= 0) {
185 strcat(name, "XXXX");
186 fd = mkstemp(name);
188 if(fd < 0)
189 return "couldn't think of a name for bitmap";
190 } else {
191 strcpy(name, "bitXXXX");
192 mkstemp(name);
193 if(fd < 0)
194 return "couldn't think of a name for bitmap";
197 if(fd < 0) {
198 snprint(result, sizeof result, "cannot create %s: %r", name);
199 return result;
202 if(writeimage(fd, im, 0) < 0) {
203 snprint(result, sizeof result, "cannot writeimage: %r");
204 close(fd);
205 return result;
207 close(fd);
209 snprint(result, sizeof result, "wrote %s", name);
210 return result;
213 static void translate(Point);
215 static int
216 showdata(Plumbmsg *msg)
218 char *s;
220 s = plumblookup(msg->attr, "action");
221 return s && strcmp(s, "showdata")==0;
224 /* correspond to entries in miditems[] below,
225 * changing one means you need to change
226 */
227 enum{
228 Restore = 0,
229 Zin,
230 Fit,
231 Rot,
232 Upside,
233 Empty1,
234 Next,
235 Prev,
236 Zerox,
237 Empty2,
238 Reverse,
239 Del,
240 Write,
241 Empty3,
242 Exit,
243 };
245 void
246 viewer(Document *dd)
248 int i, fd, n, oldpage;
249 int nxt, a;
250 Channel *cp;
251 Menu menu, midmenu;
252 Mouse m;
253 Keyboardctl *kc;
254 Point dxy, oxy, xy0;
255 Rune run;
256 Rectangle r;
257 int size[2];
258 Image *tmp;
259 PDFInfo *pdf;
260 PSInfo *ps;
261 static char *fwditems[] = { "this page", "next page", "exit", 0 };
262 static char *miditems[] = {
263 "orig size",
264 "zoom in",
265 "fit window",
266 "rotate 90",
267 "upside down",
268 "",
269 "next",
270 "prev",
271 "zerox",
272 "",
273 "reverse",
274 "discard",
275 "write",
276 "",
277 "quit",
279 };
280 char *s;
281 enum {
282 CMouse,
283 CResize,
284 CKeyboard,
285 CPlumb,
286 CN
287 };
288 Alt alts[CN+1];
289 Plumbmsg *pm;
291 cp = chancreate(sizeof pm, 0);
292 assert(cp);
294 doc = dd; /* save global for menuhit */
295 ul = screen->r.min;
296 mc = initmouse(nil, screen);
297 kc = initkeyboard(nil);
298 alts[CMouse].c = mc->c;
299 alts[CMouse].v = &m;
300 alts[CMouse].op = CHANRCV;
301 alts[CResize].c = mc->resizec;
302 alts[CResize].v = &size;
303 alts[CResize].op = CHANRCV;
304 alts[CKeyboard].c = kc->c;
305 alts[CKeyboard].v = &run;
306 alts[CKeyboard].op = CHANRCV;
307 alts[CPlumb].c = cp;
308 alts[CPlumb].v = &pm;
309 alts[CPlumb].op = CHANNOP;
310 alts[CN].op = CHANEND;
312 /* XXX: Event */
313 if(doc->addpage != nil) {
314 alts[CPlumb].op = CHANRCV;
315 proccreate(plumbproc, cp, 16384);
318 setcursor(mc, &reading);
319 r.min = ZP;
321 /*
322 * im is a global pointer to the current image.
323 * eventually, i think we will have a layer between
324 * the display routines and the ps/pdf/whatever routines
325 * to perhaps cache and handle images of different
326 * sizes, etc.
327 */
328 im = 0;
329 page = reverse ? doc->npage-1 : 0;
331 if(doc->fwdonly) {
332 menu.item = fwditems;
333 menu.gen = 0;
334 menu.lasthit = 0;
335 } else {
336 menu.item = 0;
337 menu.gen = menugen;
338 menu.lasthit = 0;
341 midmenu.item = miditems;
342 midmenu.gen = 0;
343 midmenu.lasthit = Next;
345 showpage(page, &menu);
346 setcursor(mc, nil);
348 nxt = 0;
349 for(;;) {
350 /*
351 * throughout, if doc->fwdonly is set, we restrict the functionality
352 * a fair amount. we don't care about doc->npage anymore, and
353 * all that can be done is select the next page.
354 */
355 unlockdisplay(display);
356 a = alt(alts);
357 lockdisplay(display);
358 switch(a) {
359 case CKeyboard:
360 if(run <= 0xFF && isdigit(run)) {
361 nxt = nxt*10+run-'0';
362 break;
363 } else if(run != '\n')
364 nxt = 0;
365 switch(run) {
366 case 'r': /* reverse page order */
367 if(doc->fwdonly)
368 break;
369 reverse = !reverse;
370 menu.lasthit = doc->npage-1-menu.lasthit;
372 /*
373 * the theory is that if we are reversing the
374 * document order and are on the first or last
375 * page then we're just starting and really want
376 * to view the other end. maybe the if
377 * should be dropped and this should happen always.
378 */
379 if(page == 0 || page == doc->npage-1) {
380 page = doc->npage-1-page;
381 showpage(page, &menu);
383 break;
384 case 'w': /* write bitmap of current screen */
385 setcursor(mc, &reading);
386 s = writebitmap();
387 if(s)
388 string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
389 display->defaultfont, s);
390 setcursor(mc, nil);
391 flushimage(display, 1);
392 break;
393 case 'd': /* remove image from working set */
394 if(doc->rmpage && page < doc->npage) {
395 if(doc->rmpage(doc, page) >= 0) {
396 if(doc->npage < 0)
397 wexits(0);
398 if(page >= doc->npage)
399 page = doc->npage-1;
400 showpage(page, &menu);
403 break;
404 case 'q':
405 case 0x04: /* ctrl-d */
406 wexits(0);
407 case 'u':
408 if(im==nil)
409 break;
410 setcursor(mc, &reading);
411 rot180(im);
412 setcursor(mc, nil);
413 angle = (angle+180) % 360;
414 redraw(screen);
415 flushimage(display, 1);
416 break;
417 case '-':
418 case '\b':
419 case Kleft:
420 if(page > 0 && !doc->fwdonly) {
421 --page;
422 showpage(page, &menu);
424 break;
425 case '\n':
426 if(nxt) {
427 nxt--;
428 if(nxt >= 0 && nxt < doc->npage && !doc->fwdonly)
429 showpage(page=nxt, &menu);
430 nxt = 0;
431 break;
433 goto Gotonext;
434 case Kright:
435 case ' ':
436 Gotonext:
437 if(doc->npage && ++page >= doc->npage && !doc->fwdonly)
438 wexits(0);
439 showpage(page, &menu);
440 break;
442 /*
443 * The upper y coordinate of the image is at ul.y in screen->r.
444 * Panning up means moving the upper left corner down. If the
445 * upper left corner is currently visible, we need to go back a page.
446 */
447 case Kup:
448 if(screen->r.min.y <= ul.y && ul.y < screen->r.max.y){
449 if(page > 0 && !doc->fwdonly){
450 --page;
451 showbottom = 1;
452 showpage(page, &menu);
454 } else {
455 i = Dy(screen->r)/2;
456 if(i > 10)
457 i -= 10;
458 if(i+ul.y > screen->r.min.y)
459 i = screen->r.min.y - ul.y;
460 translate(Pt(0, i));
462 break;
464 /*
465 * If the lower y coordinate is on the screen, we go to the next page.
466 * The lower y coordinate is at ul.y + Dy(im->r).
467 */
468 case Kdown:
469 i = ul.y + Dy(im->r);
470 if(screen->r.min.y <= i && i <= screen->r.max.y){
471 ul.y = screen->r.min.y;
472 goto Gotonext;
473 } else {
474 i = -Dy(screen->r)/2;
475 if(i < -10)
476 i += 10;
477 if(i+ul.y+Dy(im->r) <= screen->r.max.y)
478 i = screen->r.max.y - Dy(im->r) - ul.y - 1;
479 translate(Pt(0, i));
481 break;
482 default:
483 setcursor(mc, &query);
484 sleep(1000);
485 setcursor(mc, nil);
486 break;
488 break;
490 case CMouse:
491 switch(m.buttons){
492 case Left:
493 oxy = m.xy;
494 xy0 = oxy;
495 do {
496 dxy = subpt(m.xy, oxy);
497 oxy = m.xy;
498 translate(dxy);
499 recv(mc->c, &m);
500 } while(m.buttons == Left);
501 if(m.buttons) {
502 dxy = subpt(xy0, oxy);
503 translate(dxy);
505 break;
507 case Middle:
508 if(doc->npage == 0)
509 break;
511 n = menuhit(Middle, mc, &midmenu, nil);
512 if(n == -1)
513 break;
514 switch(n){
515 case Next: /* next */
516 if(reverse)
517 page--;
518 else
519 page++;
520 if(page < 0) {
521 if(reverse) return;
522 else page = 0;
525 if((page >= doc->npage) && !doc->fwdonly)
526 return;
528 showpage(page, &menu);
529 nxt = 0;
530 break;
531 case Prev: /* prev */
532 if(reverse)
533 page++;
534 else
535 page--;
536 if(page < 0) {
537 if(reverse) return;
538 else page = 0;
541 if((page >= doc->npage) && !doc->fwdonly && !reverse)
542 return;
544 showpage(page, &menu);
545 nxt = 0;
546 break;
547 case Zerox: /* prev */
548 zerox();
549 break;
550 case Zin: /* zoom in */
551 if (dd->type == Tpdf){ /* pdf */
552 pdf = (PDFInfo *) dd->extra;
553 if (pdf != nil){
554 ppi+= 50;
555 setdim(&pdf->gs, Rect(0,0,0,0), ppi, 0);
556 showpage(page, &menu);
558 break;
560 if (dd->type == Tps){ /* ps */
561 ps = (PSInfo *) dd->extra;
562 if (ps != nil){
563 ppi+= 50;
564 setdim(&ps->gs, Rect(0,0,0,0), ppi, 0);
565 showpage(page, &menu);
567 break;
569 else{ /* image */
570 double delta;
571 Rectangle r;
573 r = getrect(Middle, mc);
574 if((rectclip(&r, rectaddpt(im->r, ul)) == 0) ||
575 Dx(r) == 0 || Dy(r) == 0)
576 break;
577 /* use the smaller side to expand */
578 if(Dx(r) < Dy(r))
579 delta = (double)Dx(im->r)/(double)Dx(r);
580 else
581 delta = (double)Dy(im->r)/(double)Dy(r);
583 setcursor(mc, &reading);
584 tmp = xallocimage(display,
585 Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)),
586 im->chan, 0, DBlack);
587 if(tmp == nil) {
588 fprint(2, "out of memory during zoom: %r\n");
589 wexits("memory");
591 resample(im, tmp);
592 im = tmp;
593 delayfreeimage(tmp);
594 setcursor(mc, nil);
595 ul = screen->r.min;
596 redraw(screen);
597 flushimage(display, 1);
598 break;
600 case Fit: /* fit */
601 /* no op if pdf or ps*/
602 if (dd->type == Tgfx){
603 fitwin = 1;
604 fit();
606 break;
607 case Rot: /* rotate 90 */
608 angle = (angle+90) % 360;
609 showpage(page, &menu);
610 break;
611 case Upside: /* upside-down */
612 angle = (angle+180) % 360;
613 showpage(page, &menu);
614 break;
615 case Restore: /* restore */
616 if (dd->type == Tpdf){ /* pdf */
617 pdf = (PDFInfo *) dd->extra;
618 if (pdf != nil){
619 ppi = 100;
620 setdim(&pdf->gs, Rect(0,0,0,0), ppi, 0);
622 showpage(page, &menu);
623 break;
625 if (dd->type == Tps){ /* ps */
626 ps = (PSInfo *) dd->extra;
627 if (ps != nil){
628 ppi = 100;
629 setdim(&ps->gs, Rect(0,0,0,0), ppi, 0);
631 showpage(page, &menu);
632 break;
634 fitwin = 0;
635 showpage(page, &menu);
636 break;
637 case Reverse: /* reverse */
638 if(doc->fwdonly)
639 break;
640 reverse = !reverse;
641 menu.lasthit = doc->npage-1-menu.lasthit;
643 if(page == 0 || page == doc->npage-1) {
644 page = doc->npage-1-page;
645 showpage(page, &menu);
647 break;
648 case Write: /* write */
649 setcursor(mc, &reading);
650 s = writebitmap();
651 if(s)
652 string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
653 display->defaultfont, s);
654 setcursor(mc, nil);
655 flushimage(display, 1);
656 break;
657 case Del: /* delete */
658 if(doc->rmpage && page < doc->npage) {
659 if(doc->rmpage(doc, page) >= 0) {
660 if(doc->npage < 0)
661 wexits(0);
662 if(page >= doc->npage)
663 page = doc->npage-1;
664 showpage(page, &menu);
667 break;
668 case Exit: /* exit */
669 return;
670 case Empty1:
671 case Empty2:
672 case Empty3:
673 break;
675 };
679 case Right:
680 if(doc->npage == 0)
681 break;
683 oldpage = page;
684 n = menuhit(RMenu, mc, &menu, nil);
685 if(n == -1)
686 break;
688 if(doc->fwdonly) {
689 switch(n){
690 case 0: /* this page */
691 break;
692 case 1: /* next page */
693 showpage(++page, &menu);
694 break;
695 case 2: /* exit */
696 return;
698 break;
701 if(n == doc->npage)
702 return;
703 else
704 page = reverse ? doc->npage-1-n : n;
706 if(oldpage != page)
707 showpage(page, &menu);
708 nxt = 0;
709 break;
711 break;
712 case CResize:
713 r = screen->r;
714 if(getwindow(display, Refnone) < 0)
715 fprint(2,"can't reattach to window");
716 ul = addpt(ul, subpt(screen->r.min, r.min));
717 redraw(screen);
718 flushimage(display, 1);
719 break;
720 case CPlumb:
721 if(pm->ndata <= 0){
722 plumbfree(pm);
723 break;
725 if(showdata(pm)) {
726 s = estrdup("/tmp/pageplumbXXXXXXX");
727 fd = opentemp(s, ORDWR|ORCLOSE);
728 write(fd, pm->data, pm->ndata);
729 /* lose fd reference on purpose; the file is open ORCLOSE */
730 } else if(pm->data[0] == '/') {
731 s = estrdup(pm->data);
732 } else {
733 s = emalloc(strlen(pm->wdir)+1+pm->ndata+1);
734 sprint(s, "%s/%s", pm->wdir, pm->data);
735 cleanname(s);
737 if((i = doc->addpage(doc, s)) >= 0) {
738 page = i;
739 unhide();
740 showpage(page, &menu);
742 free(s);
743 plumbfree(pm);
744 break;
749 Image *gray;
751 /*
752 * A draw operation that touches only the area contained in bot but not in top.
753 * mp and sp get aligned with bot.min.
754 */
755 static void
756 gendrawdiff(Image *dst, Rectangle bot, Rectangle top,
757 Image *src, Point sp, Image *mask, Point mp, int op)
759 Rectangle r;
760 Point origin;
761 Point delta;
763 USED(op);
765 if(Dx(bot)*Dy(bot) == 0)
766 return;
768 /* no points in bot - top */
769 if(rectinrect(bot, top))
770 return;
772 /* bot - top ≡ bot */
773 if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
774 gendrawop(dst, bot, src, sp, mask, mp, op);
775 return;
778 origin = bot.min;
779 /* split bot into rectangles that don't intersect top */
780 /* left side */
781 if(bot.min.x < top.min.x){
782 r = Rect(bot.min.x, bot.min.y, top.min.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.min.x = top.min.x;
788 /* right side */
789 if(bot.max.x > top.max.x){
790 r = Rect(top.max.x, bot.min.y, bot.max.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.max.x = top.max.x;
796 /* top */
797 if(bot.min.y < top.min.y){
798 r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
799 delta = subpt(r.min, origin);
800 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
801 bot.min.y = top.min.y;
804 /* bottom */
805 if(bot.max.y > top.max.y){
806 r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
807 delta = subpt(r.min, origin);
808 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
809 bot.max.y = top.max.y;
813 static void
814 drawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Image *mask, Point p, int op)
816 gendrawdiff(dst, bot, top, src, p, mask, p, op);
819 /*
820 * Translate the image in the window by delta.
821 */
822 static void
823 translate(Point delta)
825 Point u;
826 Rectangle r, or;
828 if(im == nil)
829 return;
831 u = pclip(addpt(ul, delta), ulrange);
832 delta = subpt(u, ul);
833 if(delta.x == 0 && delta.y == 0)
834 return;
836 /*
837 * The upper left corner of the image is currently at ul.
838 * We want to move it to u.
839 */
840 or = rectaddpt(Rpt(ZP, Pt(Dx(im->r), Dy(im->r))), ul);
841 r = rectaddpt(or, delta);
843 drawop(screen, r, screen, nil, ul, S);
844 ul = u;
846 /* fill in gray where image used to be but isn't. */
847 drawdiff(screen, insetrect(or, -2), insetrect(r, -2), gray, nil, ZP, S);
849 /* fill in black border */
850 drawdiff(screen, insetrect(r, -2), r, display->black, nil, ZP, S);
852 /* fill in image where it used to be off the screen. */
853 if(rectclip(&or, screen->r))
854 drawdiff(screen, r, rectaddpt(or, delta), im, nil, im->r.min, S);
855 else
856 drawop(screen, r, im, nil, im->r.min, S);
857 flushimage(display, 1);
860 void
861 redraw(Image *screen)
863 Rectangle r;
865 if(im == nil)
866 return;
868 ulrange.max = screen->r.max;
869 ulrange.min = subpt(screen->r.min, Pt(Dx(im->r), Dy(im->r)));
871 ul = pclip(ul, ulrange);
872 drawop(screen, screen->r, im, nil, subpt(im->r.min, subpt(ul, screen->r.min)), S);
874 if(im->repl)
875 return;
877 /* fill in any outer edges */
878 /* black border */
879 r = rectaddpt(im->r, subpt(ul, im->r.min));
880 border(screen, r, -2, display->black, ZP);
881 r.min = subpt(r.min, Pt(2,2));
882 r.max = addpt(r.max, Pt(2,2));
884 /* gray for the rest */
885 if(gray == nil) {
886 gray = xallocimage(display, Rect(0,0,1,1), RGB24, 1, 0x888888FF);
887 if(gray == nil) {
888 fprint(2, "g out of memory: %r\n");
889 wexits("mem");
892 border(screen, r, -4000, gray, ZP);
893 // flushimage(display, 0);
896 /* clip p to be in r */
897 Point
898 pclip(Point p, Rectangle r)
900 if(p.x < r.min.x)
901 p.x = r.min.x;
902 else if(p.x >= r.max.x)
903 p.x = r.max.x-1;
905 if(p.y < r.min.y)
906 p.y = r.min.y;
907 else if(p.y >= r.max.y)
908 p.y = r.max.y-1;
910 return p;
913 /*
914 * resize is perhaps a misnomer.
915 * this really just grows the window to be at least dx across
916 * and dy high. if the window hits the bottom or right edge,
917 * it is backed up until it hits the top or left edge.
918 */
919 void
920 resize(int dx, int dy)
922 static Rectangle sr;
923 Rectangle r, or;
925 r = screen->r;
926 if(Dx(sr)*Dy(sr) == 0) {
927 sr = screenrect();
928 /* Start with the size of the first image */
929 r.max.x = r.min.x;
930 r.max.y = r.min.y;
933 if(Dx(r) >= dx && Dy(r) >= dy)
934 return;
936 or = r;
938 r.max.x = max(r.min.x+dx, r.max.x);
939 r.max.y = max(r.min.y+dy, r.max.y);
940 if(r.max.x > sr.max.x){
941 if(Dx(r) > Dx(sr)){
942 r.min.x = 0;
943 r.max.x = sr.max.x;
944 }else
945 r = rectaddpt(r, Pt(sr.max.x-r.max.x, 0));
947 if(r.max.y > sr.max.y){
948 if(Dy(r) > Dy(sr)){
949 r.min.y = 0;
950 r.max.y = sr.max.y;
951 }else
952 r = rectaddpt(r, Pt(0, sr.max.y-r.max.y));
955 /*
956 * Sometimes we can't actually grow the window big enough,
957 * and resizing it to the same shape makes it flash.
958 */
959 if(Dx(r) == Dx(or) && Dy(r) == Dy(or))
960 return;
962 drawresizewindow(r);
965 /*
966 * If we allocimage after a resize but before flushing the draw buffer,
967 * we won't have seen the reshape event, and we won't have called
968 * getwindow, and allocimage will fail. So we flushimage before every alloc.
969 */
970 Image*
971 xallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val)
973 flushimage(display, 0);
974 return allocimage(d, r, chan, repl, val);
977 void
978 plumbproc(void *c)
980 Channel *cp;
981 CFid *fd;
983 cp = c;
984 fd = plumbopenfid("image", OREAD|OCEXEC);
985 if(fd == nil) {
986 fprint(2, "Cannot connect to the plumber");
987 threadexits("plumber");
989 for(;;) {
990 send(cp, plumbrecvfid(fd));
994 /* XXX: This function is ugly and hacky. There may be a better way... or not */
995 Rectangle
996 screenrect(void)
998 int fd[3], pfd[2];
999 int n, w, h;
1000 char buf[64];
1001 char *p, *pr;
1003 if(pipe(pfd) < 0)
1004 wexits("pipe failed");
1006 fd[0] = open("/dev/null", OREAD);
1007 fd[1] = pfd[1];
1008 fd[2] = dup(2, -1);
1009 if(threadspawnl(fd, "rc", "rc", "-c", "xdpyinfo | grep 'dimensions:'", nil) == -1)
1010 wexits("threadspawnl failed");
1012 if((n = read(pfd[0], buf, 63)) <= 0)
1013 wexits("read xdpyinfo failed");
1014 close(fd[0]);
1016 buf[n] = '\0';
1017 for(p = buf; *p; p++)
1018 if(*p >= '0' && *p <= '9') break;
1019 if(*p == '\0')
1020 wexits("xdpyinfo parse failed");
1022 w = strtoul(p, &pr, 10);
1023 if(p == pr || *pr == '\0' || *(++pr) == '\0')
1024 wexits("xdpyinfo parse failed");
1025 h = strtoul(pr, &p, 10);
1026 if(p == pr)
1027 wexits("xdpyinfo parse failed");
1029 return Rect(0, 0, w, h);
1032 void
1033 zerox(void)
1035 int pfd[2];
1036 int fd[3];
1038 pipe(pfd);
1039 fd[0] = pfd[0];
1040 fd[1] = dup(1, -1);
1041 fd[2] = dup(2, -1);
1042 threadspawnl(fd, "page", "page", "-R", nil);
1044 writeimage(pfd[1], im, 0);
1045 close(pfd[1]);
1048 void
1049 fit()
1051 double delta;
1052 Rectangle r;
1053 Image* tmp;
1055 delta = (double)Dx(screen->r)/(double)Dx(im->r);
1056 if((double)Dy(im->r)*delta > Dy(screen->r))
1057 delta = (double)Dy(screen->r)/(double)Dy(im->r);
1058 r = Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta));
1059 setcursor(mc, &reading);
1060 tmp = xallocimage(display, r, im->chan, 0, DBlack);
1061 if(tmp == nil) {
1062 fprint(2, "out of memory during fit: %r\n");
1063 wexits("memory");
1065 resample(im, tmp);
1066 im = tmp;
1067 delayfreeimage(tmp);
1068 setcursor(mc, nil);
1069 ul = screen->r.min;
1070 redraw(screen);
1071 flushimage(display, 1);