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