tview.c - plan9port - [fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port
Log
Files
Refs
README
LICENSE
---
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 }