URI:
       tview.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tview.c (22223B)
       ---
            1 /*
            2  * the actual viewer that handles screen stuff
            3  */
            4 
            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"
           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 = &pm;
          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 }