| ---
tview.c (22223B)
---
1 /*
2 * the actual viewer that handles screen stuff
3 */
4
5 #include
6 #include
7 #include <9pclient.h>
8 #include
9 #include
10 #include
11 #include
12 #include
13 #include
14 #include
15 #include
16 #include "page.h"
17
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. */
25
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 */
28
29 Point pclip(Point, Rectangle);
30 Rectangle mkrange(Rectangle screenr, Rectangle imr);
31 void redraw(Image*);
32 void plumbproc(void*);
33
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 };
45
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 };
57
58 enum {
59 Left = 1,
60 Middle = 2,
61 Right = 4,
62
63 RMenu = 3,
64 };
65
66 static void
67 delayfreeimage(Image *m)
68 {
69 if(m == tofree)
70 return;
71 if(tofree)
72 freeimage(tofree);
73 tofree = m;
74 }
75
76 void
77 unhide(void)
78 {
79 USED(nil);
80 }
81
82 int
83 max(int a, int b)
84 {
85 return a > b ? a : b;
86 }
87
88 int
89 min(int a, int b)
90 {
91 return a < b ? a : b;
92 }
93
94
95 char*
96 menugen(int n)
97 {
98 static char menustr[32];
99 char *p;
100 int len;
101
102 if(n == doc->npage)
103 return "exit";
104 if(n > doc->npage)
105 return nil;
106
107 if(reverse)
108 n = doc->npage-1-n;
109
110 p = doc->pagename(doc, n);
111 len = (sizeof menustr)-2;
112
113 if(strlen(p) > len && strrchr(p, '/'))
114 p = strrchr(p, '/')+1;
115 if(strlen(p) > len)
116 p = p+strlen(p)-len;
117
118 strcpy(menustr+1, p);
119 if(page == n)
120 menustr[0] = '>';
121 else
122 menustr[0] = ' ';
123 return menustr;
124 }
125
126 void
127 showpage(int page, Menu *m)
128 {
129 if(doc->fwdonly)
130 m->lasthit = 0; /* this page */
131 else
132 m->lasthit = reverse ? doc->npage-1-page : page;
133
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));
141
142 setcursor(mc, nil);
143 if(showbottom){
144 ul.y = screen->r.max.y - Dy(im->r);
145 showbottom = 0;
146 }
147
148 if((doc->type == Tgfx) && fitwin)
149 fit();
150 else{
151 redraw(screen);
152 flushimage(display, 1);
153 }
154 }
155
156 char*
157 writebitmap(void)
158 {
159 char basename[64];
160 char name[64+30];
161 static char result[200];
162 char *p, *q;
163 int fd = -1;
164
165 if(im == nil)
166 return "no image";
167
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);
173
174 if(basename[0]) {
175 if(q = strrchr(basename, '/'))
176 q++;
177 else
178 q = basename;
179 if(p = strchr(q, '.'))
180 *p = 0;
181
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);
187 }
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";
195 }
196
197 if(fd < 0) {
198 snprint(result, sizeof result, "cannot create %s: %r", name);
199 return result;
200 }
201
202 if(writeimage(fd, im, 0) < 0) {
203 snprint(result, sizeof result, "cannot writeimage: %r");
204 close(fd);
205 return result;
206 }
207 close(fd);
208
209 snprint(result, sizeof result, "wrote %s", name);
210 return result;
211 }
212
213 static void translate(Point);
214
215 static int
216 showdata(Plumbmsg *msg)
217 {
218 char *s;
219
220 s = plumblookup(msg->attr, "action");
221 return s && strcmp(s, "showdata")==0;
222 }
223
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 };
244
245 void
246 viewer(Document *dd)
247 {
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",
278 0
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;
290
291 cp = chancreate(sizeof pm, 0);
292 assert(cp);
293
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 = ±
309 alts[CPlumb].op = CHANNOP;
310 alts[CN].op = CHANEND;
311
312 /* XXX: Event */
313 if(doc->addpage != nil) {
314 alts[CPlumb].op = CHANRCV;
315 proccreate(plumbproc, cp, 16384);
316 }
317
318 setcursor(mc, &reading);
319 r.min = ZP;
320
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;
330
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;
339 }
340
341 midmenu.item = miditems;
342 midmenu.gen = 0;
343 midmenu.lasthit = Next;
344
345 showpage(page, &menu);
346 setcursor(mc, nil);
347
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;
371
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);
382 }
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);
401 }
402 }
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);
423 }
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;
432 }
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;
441
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);
453 }
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));
461 }
462 break;
463
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));
480 }
481 break;
482 default:
483 setcursor(mc, &query);
484 sleep(1000);
485 setcursor(mc, nil);
486 break;
487 }
488 break;
489
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);
504 }
505 break;
506
507 case Middle:
508 if(doc->npage == 0)
509 break;
510
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;
523 }
524
525 if((page >= doc->npage) && !doc->fwdonly)
526 return;
527
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;
539 }
540
541 if((page >= doc->npage) && !doc->fwdonly && !reverse)
542 return;
543
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);
557 }
558 break;
559 }
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);
566 }
567 break;
568 }
569 else{ /* image */
570 double delta;
571 Rectangle r;
572
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);
582
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");
590 }
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;
599 }
600 case Fit: /* fit */
601 /* no op if pdf or ps*/
602 if (dd->type == Tgfx){
603 fitwin = 1;
604 fit();
605 }
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);
621 }
622 showpage(page, &menu);
623 break;
624 }
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);
630 }
631 showpage(page, &menu);
632 break;
633 }
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;
642
643 if(page == 0 || page == doc->npage-1) {
644 page = doc->npage-1-page;
645 showpage(page, &menu);
646 }
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);
665 }
666 }
667 break;
668 case Exit: /* exit */
669 return;
670 case Empty1:
671 case Empty2:
672 case Empty3:
673 break;
674
675 };
676
677
678
679 case Right:
680 if(doc->npage == 0)
681 break;
682
683 oldpage = page;
684 n = menuhit(RMenu, mc, &menu, nil);
685 if(n == -1)
686 break;
687
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;
697 }
698 break;
699 }
700
701 if(n == doc->npage)
702 return;
703 else
704 page = reverse ? doc->npage-1-n : n;
705
706 if(oldpage != page)
707 showpage(page, &menu);
708 nxt = 0;
709 break;
710 }
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;
724 }
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);
736 }
737 if((i = doc->addpage(doc, s)) >= 0) {
738 page = i;
739 unhide();
740 showpage(page, &menu);
741 }
742 free(s);
743 plumbfree(pm);
744 break;
745 }
746 }
747 }
748
749 Image *gray;
750
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)
758 {
759 Rectangle r;
760 Point origin;
761 Point delta;
762
763 USED(op);
764
765 if(Dx(bot)*Dy(bot) == 0)
766 return;
767
768 /* no points in bot - top */
769 if(rectinrect(bot, top))
770 return;
771
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;
776 }
777
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;
786 }
787
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;
794 }
795
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;
802 }
803
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;
810 }
811 }
812
813 static void
814 drawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Image *mask, Point p, int op)
815 {
816 gendrawdiff(dst, bot, top, src, p, mask, p, op);
817 }
818
819 /*
820 * Translate the image in the window by delta.
821 */
822 static void
823 translate(Point delta)
824 {
825 Point u;
826 Rectangle r, or;
827
828 if(im == nil)
829 return;
830
831 u = pclip(addpt(ul, delta), ulrange);
832 delta = subpt(u, ul);
833 if(delta.x == 0 && delta.y == 0)
834 return;
835
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);
842
843 drawop(screen, r, screen, nil, ul, S);
844 ul = u;
845
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);
848
849 /* fill in black border */
850 drawdiff(screen, insetrect(r, -2), r, display->black, nil, ZP, S);
851
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);
858 }
859
860 void
861 redraw(Image *screen)
862 {
863 Rectangle r;
864
865 if(im == nil)
866 return;
867
868 ulrange.max = screen->r.max;
869 ulrange.min = subpt(screen->r.min, Pt(Dx(im->r), Dy(im->r)));
870
871 ul = pclip(ul, ulrange);
872 drawop(screen, screen->r, im, nil, subpt(im->r.min, subpt(ul, screen->r.min)), S);
873
874 if(im->repl)
875 return;
876
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));
883
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");
890 }
891 }
892 border(screen, r, -4000, gray, ZP);
893 // flushimage(display, 0);
894 }
895
896 /* clip p to be in r */
897 Point
898 pclip(Point p, Rectangle r)
899 {
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;
904
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;
909
910 return p;
911 }
912
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)
921 {
922 static Rectangle sr;
923 Rectangle r, or;
924
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;
931 }
932
933 if(Dx(r) >= dx && Dy(r) >= dy)
934 return;
935
936 or = r;
937
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));
946 }
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));
953 }
954
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;
961
962 drawresizewindow(r);
963 }
964
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)
972 {
973 flushimage(display, 0);
974 return allocimage(d, r, chan, repl, val);
975 }
976
977 void
978 plumbproc(void *c)
979 {
980 Channel *cp;
981 CFid *fd;
982
983 cp = c;
984 fd = plumbopenfid("image", OREAD|OCEXEC);
985 if(fd == nil) {
986 fprint(2, "Cannot connect to the plumber");
987 threadexits("plumber");
988 }
989 for(;;) {
990 send(cp, plumbrecvfid(fd));
991 }
992 }
993
994 /* XXX: This function is ugly and hacky. There may be a better way... or not */
995 Rectangle
996 screenrect(void)
997 {
998 int fd[3], pfd[2];
999 int n, w, h;
1000 char buf[64];
1001 char *p, *pr;
1002
1003 if(pipe(pfd) < 0)
1004 wexits("pipe failed");
1005
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");
1011
1012 if((n = read(pfd[0], buf, 63)) <= 0)
1013 wexits("read xdpyinfo failed");
1014 close(fd[0]);
1015
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");
1021
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");
1028
1029 return Rect(0, 0, w, h);
1030 }
1031
1032 void
1033 zerox(void)
1034 {
1035 int pfd[2];
1036 int fd[3];
1037
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);
1043
1044 writeimage(pfd[1], im, 0);
1045 close(pfd[1]);
1046 }
1047
1048 void
1049 fit()
1050 {
1051 double delta;
1052 Rectangle r;
1053 Image* tmp;
1054
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");
1064 }
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);
1072 } |