URI:
       tpaint.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
       ---
       tpaint.c (17531B)
       ---
            1 /*
            2 This code was taken from 9front repository (https://code.9front.org/hg/plan9front).
            3 It is subject to license from 9front, below is a reproduction of the license.
            4 
            5 Copyright (c) 20XX 9front
            6 
            7 Permission is hereby granted, free of charge, to any person obtaining a copy
            8 of this software and associated documentation files (the "Software"), to deal
            9 in the Software without restriction, including without limitation the rights
           10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
           11 copies of the Software, and to permit persons to whom the Software is
           12 furnished to do so, subject to the following conditions:
           13 
           14 The above copyright notice and this permission notice shall be included in all
           15 copies or substantial portions of the Software.
           16 
           17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
           18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
           19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
           20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
           21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
           22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
           23 SOFTWARE.
           24 */
           25 #include <u.h>
           26 #include <libc.h>
           27 #include <draw.h>
           28 #include <event.h>
           29 #include <keyboard.h>
           30 
           31 /* additional libdraw function needed - defined here to avoid API change */
           32 extern int             eenter(char*, char*, int, Mouse*);
           33 
           34 char *filename;
           35 int zoom = 1;
           36 int brush = 1;
           37 Point spos;                /* position on screen */
           38 Point cpos;                /* position on canvas */
           39 Image *canvas;
           40 Image *ink;
           41 Image *back;
           42 Image *pal[16];                /* palette */
           43 Rectangle palr;                /* palette rect on screen */
           44 Rectangle penr;                /* pen size rect on screen */
           45 
           46 enum {
           47         NBRUSH = 10+1,
           48 };
           49 
           50 int nundo = 0;
           51 Image *undo[1024];
           52 
           53 int c64[] = {                /* c64 color palette */
           54         0x000000,
           55         0xFFFFFF,
           56         0x68372B,
           57         0x70A4B2,
           58         0x6F3D86,
           59         0x588D43,
           60         0x352879,
           61         0xB8C76F,
           62         0x6F4F25,
           63         0x433900,
           64         0x9A6759,
           65         0x444444,
           66         0x6C6C6C,
           67         0x9AD284,
           68         0x6C5EB5,
           69         0x959595,
           70 };
           71 
           72 /*
           73  * get bounding rectnagle for stroke from r.min to r.max with
           74  * specified brush (size).
           75  */
           76 static Rectangle
           77 strokerect(Rectangle r, int brush)
           78 {
           79         r = canonrect(r);
           80         return Rect(r.min.x-brush, r.min.y-brush, r.max.x+brush+1, r.max.y+brush+1);
           81 }
           82 
           83 /*
           84  * draw stroke from r.min to r.max to dst with color ink and
           85  * brush (size).
           86  */
           87 static void
           88 strokedraw(Image *dst, Rectangle r, Image *ink, int brush)
           89 {
           90         if(!eqpt(r.min, r.max))
           91                 line(dst, r.min, r.max, Enddisc, Enddisc, brush, ink, ZP);
           92         fillellipse(dst, r.max, brush, brush, ink, ZP);
           93 }
           94 
           95 /*
           96  * A draw operation that touches only the area contained in bot but not in top.
           97  * mp and sp get aligned with bot.min.
           98  */
           99 static void
          100 gendrawdiff(Image *dst, Rectangle bot, Rectangle top,
          101         Image *src, Point sp, Image *mask, Point mp, int op)
          102 {
          103         Rectangle r;
          104         Point origin;
          105         Point delta;
          106 
          107         if(Dx(bot)*Dy(bot) == 0)
          108                 return;
          109 
          110         /* no points in bot - top */
          111         if(rectinrect(bot, top))
          112                 return;
          113 
          114         /* bot - top ≡ bot */
          115         if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
          116                 gendrawop(dst, bot, src, sp, mask, mp, op);
          117                 return;
          118         }
          119 
          120         origin = bot.min;
          121         /* split bot into rectangles that don't intersect top */
          122         /* left side */
          123         if(bot.min.x < top.min.x){
          124                 r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y);
          125                 delta = subpt(r.min, origin);
          126                 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
          127                 bot.min.x = top.min.x;
          128         }
          129 
          130         /* right side */
          131         if(bot.max.x > top.max.x){
          132                 r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y);
          133                 delta = subpt(r.min, origin);
          134                 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
          135                 bot.max.x = top.max.x;
          136         }
          137 
          138         /* top */
          139         if(bot.min.y < top.min.y){
          140                 r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
          141                 delta = subpt(r.min, origin);
          142                 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
          143                 bot.min.y = top.min.y;
          144         }
          145 
          146         /* bottom */
          147         if(bot.max.y > top.max.y){
          148                 r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
          149                 delta = subpt(r.min, origin);
          150                 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
          151                 bot.max.y = top.max.y;
          152         }
          153 }
          154 
          155 int
          156 alphachan(ulong chan)
          157 {
          158         for(; chan; chan >>= 8)
          159                 if(TYPE(chan) == CAlpha)
          160                         return 1;
          161         return 0;
          162 }
          163 
          164 void
          165 zoomdraw(Image *d, Rectangle r, Rectangle top, Image *b, Image *s, Point sp, int f)
          166 {
          167         Rectangle dr;
          168         Image *t;
          169         Point a;
          170         int w;
          171 
          172         a = ZP;
          173         if(r.min.x < d->r.min.x){
          174                 sp.x += (d->r.min.x - r.min.x)/f;
          175                 a.x = (d->r.min.x - r.min.x)%f;
          176                 r.min.x = d->r.min.x;
          177         }
          178         if(r.min.y < d->r.min.y){
          179                 sp.y += (d->r.min.y - r.min.y)/f;
          180                 a.y = (d->r.min.y - r.min.y)%f;
          181                 r.min.y = d->r.min.y;
          182         }
          183         rectclip(&r, d->r);
          184         w = s->r.max.x - sp.x;
          185         if(w > Dx(r))
          186                 w = Dx(r);
          187         dr = r;
          188         dr.max.x = dr.min.x+w;
          189         if(!alphachan(s->chan))
          190                 b = nil;
          191         if(f <= 1){
          192                 if(b) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD);
          193                 gendrawdiff(d, dr, top, s, sp, nil, ZP, SoverD);
          194                 return;
          195         }
          196         if((t = allocimage(display, dr, s->chan, 0, 0)) == nil)
          197                 return;
          198         for(; dr.min.y < r.max.y; dr.min.y++){
          199                 dr.max.y = dr.min.y+1;
          200                 draw(t, dr, s, nil, sp);
          201                 if(++a.y == f){
          202                         a.y = 0;
          203                         sp.y++;
          204                 }
          205         }
          206         dr = r;
          207         for(sp=dr.min; dr.min.x < r.max.x; sp.x++){
          208                 dr.max.x = dr.min.x+1;
          209                 if(b) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD);
          210                 gendrawdiff(d, dr, top, t, sp, nil, ZP, SoverD);
          211                 for(dr.min.x++; ++a.x < f && dr.min.x < r.max.x; dr.min.x++){
          212                         dr.max.x = dr.min.x+1;
          213                         gendrawdiff(d, dr, top, d, Pt(dr.min.x-1, dr.min.y), nil, ZP, SoverD);
          214                 }
          215                 a.x = 0;
          216         }
          217         freeimage(t);
          218 }
          219 
          220 Point
          221 s2c(Point p){
          222         p = subpt(p, spos);
          223         if(p.x < 0) p.x -= zoom-1;
          224         if(p.y < 0) p.y -= zoom-1;
          225         return addpt(divpt(p, zoom), cpos);
          226 }
          227 
          228 Point
          229 c2s(Point p){
          230         return addpt(mulpt(subpt(p, cpos), zoom), spos);
          231 }
          232 
          233 Rectangle
          234 c2sr(Rectangle r){
          235         return Rpt(c2s(r.min), c2s(r.max));
          236 }
          237 
          238 void
          239 update(Rectangle *rp){
          240         if(canvas==nil)
          241                 draw(screen, screen->r, back, nil, ZP);
          242         else {
          243                 if(rp == nil)
          244                         rp = &canvas->r;
          245                 gendrawdiff(screen, screen->r, c2sr(canvas->r), back, ZP, nil, ZP, SoverD);
          246                 zoomdraw(screen, c2sr(*rp), ZR, back, canvas, rp->min, zoom);
          247         }
          248         flushimage(display, 1);
          249 }
          250 
          251 void
          252 expand(Rectangle r)
          253 {
          254         Rectangle nr;
          255         Image *tmp;
          256 
          257         if(canvas==nil){
          258                 if((canvas = allocimage(display, r, screen->chan, 0, DNofill)) == nil)
          259                         sysfatal("allocimage: %r");
          260                 draw(canvas, canvas->r, back, nil, ZP);
          261                 return;
          262         }
          263         nr = canvas->r;
          264         combinerect(&nr, r);
          265         if(eqrect(nr, canvas->r))
          266                 return;
          267         if((tmp = allocimage(display, nr, canvas->chan, 0, DNofill)) == nil)
          268                 return;
          269         draw(tmp, canvas->r, canvas, nil, canvas->r.min);
          270         gendrawdiff(tmp, tmp->r, canvas->r, back, ZP, nil, ZP, SoverD);
          271         freeimage(canvas);
          272         canvas = tmp;
          273 }
          274 
          275 void
          276 save(Rectangle r, int mark)
          277 {
          278         Image *tmp;
          279         int x;
          280 
          281         if(mark){
          282                 x = nundo++ % nelem(undo);
          283                 if(undo[x])
          284                         freeimage(undo[x]);
          285                 undo[x] = nil;
          286         }
          287         if(canvas==nil || nundo<0)
          288                 return;
          289         if(!rectclip(&r, canvas->r))
          290                 return;
          291         if((tmp = allocimage(display, r, canvas->chan, 0, DNofill)) == nil)
          292                 return;
          293         draw(tmp, r, canvas, nil, r.min);
          294         x = nundo++ % nelem(undo);
          295         if(undo[x])
          296                 freeimage(undo[x]);
          297         undo[x] = tmp;
          298 }
          299 
          300 void
          301 restore(int n)
          302 {
          303         Image *tmp;
          304         int x;
          305 
          306         while(nundo > 0){
          307                 if(n-- == 0)
          308                         return;
          309                 x = --nundo % nelem(undo);
          310                 if((tmp = undo[x]) == nil)
          311                         return;
          312                 undo[x] = nil;
          313                 if(canvas == nil || canvas->chan != tmp->chan){
          314                         freeimage(canvas);
          315                         canvas = tmp;
          316                         update(nil);
          317                 } else {
          318                         expand(tmp->r);
          319                         draw(canvas, tmp->r, tmp, nil, tmp->r.min);
          320                         update(&tmp->r);
          321                         freeimage(tmp);
          322                 }
          323         }
          324 }
          325 
          326 typedef struct {
          327         Rectangle        r;
          328         Rectangle        r0;
          329         Image*                dst;
          330 
          331         int                yscan;        /* current scanline */
          332         int                wscan;        /* bscan width in bytes */
          333         Image*                iscan;        /* scanline image */
          334         uchar*                bscan;        /* scanline buffer */
          335 
          336         int                nmask;        /* size of bmask in bytes */
          337         int                wmask;        /* width of bmask in bytes */
          338         Image*                imask;        /* mask image */
          339         uchar*                bmask;        /* mask buffer */
          340 
          341         int                ncmp;
          342         uchar                bcmp[4];
          343 } Filldata;
          344 
          345 void
          346 fillscan(Filldata *f, Point p0)
          347 {
          348         int x, y;
          349         uchar *b;
          350 
          351         x = p0.x;
          352         y = p0.y;
          353         b = f->bmask + y*f->wmask;
          354         if(b[x/8] & 0x80>>(x%8))
          355                 return;
          356 
          357         if(f->yscan != y){
          358                 draw(f->iscan, f->iscan->r, f->dst, nil, Pt(f->r.min.x, f->r.min.y+y));
          359                 if(unloadimage(f->iscan, f->iscan->r, f->bscan, f->wscan) < 0)
          360                         return;
          361                 f->yscan = y;
          362         }
          363 
          364         for(x = p0.x; x >= 0; x--){
          365                 if(memcmp(f->bscan + x*f->ncmp, f->bcmp, f->ncmp))
          366                         break;
          367                 b[x/8] |= 0x80>>(x%8);
          368         }
          369         for(x = p0.x+1; x < f->r0.max.x; x++){
          370                 if(memcmp(f->bscan + x*f->ncmp, f->bcmp, f->ncmp))
          371                         break;
          372                 b[x/8] |= 0x80>>(x%8);
          373         }
          374 
          375         y = p0.y-1;
          376         if(y >= 0){
          377                 for(x = p0.x; x >= 0; x--){
          378                         if((b[x/8] & 0x80>>(x%8)) == 0)
          379                                 break;
          380                         fillscan(f, Pt(x, y));
          381                 }
          382                 for(x = p0.x+1; x < f->r0.max.x; x++){
          383                         if((b[x/8] & 0x80>>(x%8)) == 0)
          384                                 break;
          385                         fillscan(f, Pt(x, y));
          386                 }
          387         }
          388 
          389         y = p0.y+1;
          390         if(y < f->r0.max.y){
          391                 for(x = p0.x; x >= 0; x--){
          392                         if((b[x/8] & 0x80>>(x%8)) == 0)
          393                                 break;
          394                         fillscan(f, Pt(x, y));
          395                 }
          396                 for(x = p0.x+1; x < f->r0.max.x; x++){
          397                         if((b[x/8] & 0x80>>(x%8)) == 0)
          398                                 break;
          399                         fillscan(f, Pt(x, y));
          400                 }
          401         }
          402 }
          403 
          404 void
          405 floodfill(Image *dst, Rectangle r, Point p, Image *src)
          406 {
          407         Filldata f;
          408 
          409         if(!rectclip(&r, dst->r))
          410                 return;
          411         if(!ptinrect(p, r))
          412                 return;
          413         memset(&f, 0, sizeof(f));
          414         f.dst = dst;
          415         f.r = r;
          416         f.r0 = rectsubpt(r, r.min);
          417         f.wmask = bytesperline(f.r0, 1);
          418         f.nmask = f.wmask*f.r0.max.y;
          419         if((f.bmask = mallocz(f.nmask, 1)) == nil)
          420                 goto out;
          421         if((f.imask = allocimage(display, f.r0, GREY1, 0, DNofill)) == nil)
          422                 goto out;
          423 
          424         r = f.r0;
          425         r.max.y = 1;
          426         if((f.iscan = allocimage(display, r, RGB24, 0, DNofill)) == nil)
          427                 goto out;
          428         f.yscan = -1;
          429         f.wscan = bytesperline(f.iscan->r, f.iscan->depth);
          430         if((f.bscan = mallocz(f.wscan, 0)) == nil)
          431                 goto out;
          432 
          433         r = Rect(0,0,1,1);
          434         f.ncmp = (f.iscan->depth+7) / 8;
          435         draw(f.iscan, r, dst, nil, p);
          436         if(unloadimage(f.iscan, r, f.bcmp, sizeof(f.bcmp)) < 0)
          437                 goto out;
          438 
          439         fillscan(&f, subpt(p, f.r.min));
          440 
          441         loadimage(f.imask, f.imask->r, f.bmask, f.nmask);
          442         draw(f.dst, f.r, src, f.imask, f.imask->r.min);
          443 out:
          444         free(f.bmask);
          445         free(f.bscan);
          446         if(f.iscan)
          447                 freeimage(f.iscan);
          448         if(f.imask)
          449                 freeimage(f.imask);
          450 }
          451 
          452 void
          453 translate(Point d)
          454 {
          455         Rectangle r, nr;
          456 
          457         if(canvas==nil || d.x==0 && d.y==0)
          458                 return;
          459         r = c2sr(canvas->r);
          460         nr = rectaddpt(r, d);
          461         rectclip(&r, screen->clipr);
          462         draw(screen, rectaddpt(r, d), screen, nil, r.min);
          463         zoomdraw(screen, nr, rectaddpt(r, d), back, canvas, canvas->r.min, zoom);
          464         gendrawdiff(screen, screen->r, nr, back, ZP, nil, ZP, SoverD);
          465         spos = addpt(spos, d);
          466         flushimage(display, 1);
          467 }
          468 
          469 void
          470 setzoom(Point o, int z)
          471 {
          472         if(z < 1)
          473                 return;
          474         cpos = s2c(o);
          475         spos = o;
          476         zoom = z;
          477         update(nil);
          478 }
          479 
          480 void
          481 center(void)
          482 {
          483         cpos = ZP;
          484         if(canvas)
          485                 cpos = addpt(canvas->r.min,
          486                         divpt(subpt(canvas->r.max, canvas->r.min), 2));
          487         spos = addpt(screen->r.min,
          488                 divpt(subpt(screen->r.max, screen->r.min), 2));
          489         update(nil);
          490 }
          491 
          492 void
          493 drawpal(void)
          494 {
          495         Rectangle r, rr;
          496         int i;
          497 
          498         r = screen->r;
          499         r.min.y = r.max.y - 20;
          500         replclipr(screen, 0, r);
          501 
          502         penr = r;
          503         penr.min.x = r.max.x - NBRUSH*Dy(r);
          504 
          505         palr = r;
          506         palr.max.x = penr.min.x;
          507 
          508         r = penr;
          509         draw(screen, r, back, nil, ZP);
          510         for(i=0; i<NBRUSH; i++){
          511                 r.max.x = penr.min.x + (i+1)*Dx(penr) / NBRUSH;
          512                 rr = r;
          513                 if(i == brush)
          514                         rr.min.y += Dy(r)/3;
          515                 if(i == NBRUSH-1){
          516                         /* last is special brush for fill draw */
          517                         draw(screen, rr, ink, nil, ZP);
          518                 } else {
          519                         rr.min = addpt(rr.min, divpt(subpt(rr.max, rr.min), 2));
          520                         rr.max = rr.min;
          521                         strokedraw(screen, rr, ink, i);
          522                 }
          523                 r.min.x = r.max.x;
          524         }
          525 
          526         r = palr;
          527         for(i=1; i<=nelem(pal); i++){
          528                 r.max.x = palr.min.x + i*Dx(palr) / nelem(pal);
          529                 rr = r;
          530                 if(ink == pal[i-1])
          531                         rr.min.y += Dy(r)/3;
          532                 draw(screen, rr, pal[i-1], nil, ZP);
          533                 gendrawdiff(screen, r, rr, back, ZP, nil, ZP, SoverD);
          534                 r.min.x = r.max.x;
          535         }
          536 
          537         r = screen->r;
          538         r.max.y -= Dy(palr);
          539         replclipr(screen, 0, r);
          540 }
          541 
          542 int
          543 hitpal(Mouse m)
          544 {
          545         if(ptinrect(m.xy, penr)){
          546                 if(m.buttons & 7){
          547                         brush = ((m.xy.x - penr.min.x) * NBRUSH) / Dx(penr);
          548                         drawpal();
          549                 }
          550                 return 1;
          551         }
          552         if(ptinrect(m.xy, palr)){
          553                 Image *col;
          554 
          555                 col = pal[(m.xy.x - palr.min.x) * nelem(pal) / Dx(palr)];
          556                 switch(m.buttons & 7){
          557                 case 1:
          558                         ink = col;
          559                         drawpal();
          560                         break;
          561                 case 2:
          562                         back = col;
          563                         drawpal();
          564                         update(nil);
          565                         break;
          566                 }
          567                 return 1;
          568         }
          569         return 0;
          570 }
          571 
          572 void
          573 catch(void * _, char *msg)
          574 {
          575         USED(_);
          576         if(strstr(msg, "closed pipe"))
          577                 noted(NCONT);
          578         noted(NDFLT);
          579 }
          580 
          581 int
          582 pipeline(char *fmt, ...)
          583 {
          584         char buf[1024];
          585         va_list a;
          586         int p[2];
          587 
          588         va_start(a, fmt);
          589         vsnprint(buf, sizeof(buf), fmt, a);
          590         va_end(a);
          591         if(pipe(p) < 0)
          592                 return -1;
          593         switch(rfork(RFPROC|RFMEM|RFFDG|RFNOTEG)){ // RFEND not available in libc port
          594         case -1:
          595                 close(p[0]);
          596                 close(p[1]);
          597                 return -1;
          598         case 0:
          599                 close(p[1]);
          600                 dup(p[0], 0);
          601                 dup(p[0], 1);
          602                 close(p[0]);
          603                 execl("/bin/rc", "rc", "-c", buf, nil);
          604                 exits("exec");
          605         }
          606         close(p[0]);
          607         return p[1];
          608 }
          609 
          610 void
          611 usage(void)
          612 {
          613         fprint(2, "usage: %s [ file ]\n", argv0);
          614         exits("usage");
          615 }
          616 
          617 void
          618 main(int argc, char *argv[])
          619 {
          620         char *s, buf[1024];
          621         Rectangle r;
          622         Image *img;
          623         int i, fd;
          624         Event e;
          625         Mouse m;
          626         Point p, d;
          627 
          628         ARGBEGIN {
          629         default:
          630                 usage();
          631         } ARGEND;
          632 
          633         if(argc == 1)
          634                 filename = strdup(argv[0]);
          635         else if(argc != 0)
          636                 usage();
          637 
          638         if(initdraw(0, 0, "paint") < 0)
          639                 sysfatal("initdraw: %r");
          640 
          641         if(filename){
          642                 if((fd = open(filename, OREAD)) < 0)
          643                         sysfatal("open: %r");
          644                 if((canvas = readimage(display, fd, 0)) == nil)
          645                         sysfatal("readimage: %r");
          646                 close(fd);
          647         }
          648 
          649         /* palette initialization */
          650         for(i=0; i<nelem(pal); i++){
          651                 pal[i] = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1,
          652                         c64[i % nelem(c64)]<<8 | 0xFF);
          653                 if(pal[i] == nil)
          654                         sysfatal("allocimage: %r");
          655         }
          656         ink = pal[0];
          657         back = pal[1];
          658         drawpal();
          659         center();
          660 
          661         einit(Emouse | Ekeyboard);
          662 
          663         notify(catch);
          664         for(;;) {
          665                 switch(event(&e)){
          666                 case Emouse:
          667                         if(hitpal(e.mouse))
          668                                 continue;
          669 
          670                         img = ink;
          671                         switch(e.mouse.buttons & 7){
          672                         case 2:
          673                                 img = back;
          674                                 /* no break */
          675                         case 1:
          676                                 p = s2c(e.mouse.xy);
          677                                 if(brush == NBRUSH-1){
          678                                         /* flood fill brush */
          679                                         if(canvas == nil || !ptinrect(p, canvas->r)){
          680                                                 back = img;
          681                                                 drawpal();
          682                                                 update(nil);
          683                                                 break;
          684                                         }
          685                                         r = canvas->r;
          686                                         save(r, 1);
          687                                         floodfill(canvas, r, p, img);
          688                                         update(&r);
          689 
          690                                         /* wait for mouse release */
          691                                         while(event(&e) == Emouse && (e.mouse.buttons & 7) != 0)
          692                                                 ;
          693                                         break;
          694                                 }
          695                                 r = strokerect(Rpt(p, p), brush);
          696                                 expand(r);
          697                                 save(r, 1);
          698                                 strokedraw(canvas, Rpt(p, p), img, brush);
          699                                 update(&r);
          700                                 for(;;){
          701                                         m = e.mouse;
          702                                         if(event(&e) != Emouse)
          703                                                 break;
          704                                         if((e.mouse.buttons ^ m.buttons) & 7)
          705                                                 break;
          706                                         d = s2c(e.mouse.xy);
          707                                         if(eqpt(d, p))
          708                                                 continue;
          709                                         r = strokerect(Rpt(p, d), brush);
          710                                         expand(r);
          711                                         save(r, 0);
          712                                         strokedraw(canvas, Rpt(p, d), img, brush);
          713                                         update(&r);
          714                                         p = d;
          715                                 }
          716                                 break;
          717                         case 4:
          718                                 for(;;){
          719                                         m = e.mouse;
          720                                         if(event(&e) != Emouse)
          721                                                 break;
          722                                         if((e.mouse.buttons & 7) != 4)
          723                                                 break;
          724                                         translate(subpt(e.mouse.xy, m.xy));
          725                                 }
          726                                 break;
          727                         }
          728                         break;
          729                 case Ekeyboard:
          730                         switch(e.kbdc){
          731                         case Kesc:
          732                                 zoom = 1;
          733                                 center();
          734                                 break;
          735                         case '+':
          736                                 if(zoom < 0x1000)
          737                                         setzoom(e.mouse.xy, zoom*2);
          738                                 break;
          739                         case '-':
          740                                 if(zoom > 1)
          741                                         setzoom(e.mouse.xy, zoom/2);
          742                                 break;
          743                         case 'c':
          744                                 if(canvas == nil)
          745                                         break;
          746                                 save(canvas->r, 1);
          747                                 freeimage(canvas);
          748                                 canvas = nil;
          749                                 update(nil);
          750                                 break;
          751                         case 'u':
          752                                 restore(16);
          753                                 break;
          754                         case 'f':
          755                                 brush = NBRUSH-1;
          756                                 drawpal();
          757                                 break;
          758                         case '0': case '1': case '2': case '3': case '4':
          759                         case '5': case '6': case '7': case '8': case '9':
          760                                 brush = e.kbdc - '0';
          761                                 drawpal();
          762                                 break;
          763                         default:
          764                                 if(e.kbdc == Kdel)
          765                                         e.kbdc = 'q';
          766                                 buf[0] = 0;
          767                                 if(filename && (e.kbdc == 'r' || e.kbdc == 'w'))
          768                                         snprint(buf, sizeof(buf), "%C %s", e.kbdc, filename);
          769                                 else if(e.kbdc > 0x20 && e.kbdc < 0x7f)
          770                                         snprint(buf, sizeof(buf), "%C", e.kbdc);
          771                                 if(eenter("Cmd", buf, sizeof(buf), &e.mouse) <= 0)
          772                                         break;
          773                                 if(strcmp(buf, "q") == 0)
          774                                         exits(nil);
          775                                 s = buf+1;
          776                                 while(*s == ' ' || *s == '\t')
          777                                         s++;
          778                                 if(*s == 0)
          779                                         break;
          780                                 switch(buf[0]){
          781                                 case 'r':
          782                                         if((fd = open(s, OREAD)) < 0){
          783                                         Error:
          784                                                 snprint(buf, sizeof(buf), "%r");
          785                                                 eenter(buf, nil, 0, &e.mouse);
          786                                                 break;
          787                                         }
          788                                         free(filename);
          789                                         filename = strdup(s);
          790                                 Readimage:
          791                                         unlockdisplay(display);
          792                                         img = readimage(display, fd, 1);
          793                                         close(fd);
          794                                         lockdisplay(display);
          795                                         if(img == nil){
          796                                                 werrstr("readimage: %r");
          797                                                 goto Error;
          798                                         }
          799                                         if(canvas){
          800                                                 save(canvas->r, 1);
          801                                                 freeimage(canvas);
          802                                         }
          803                                         canvas = img;
          804                                         center();
          805                                         break;
          806                                 case 'w':
          807                                         if((fd = create(s, OWRITE, 0660)) < 0)
          808                                                 goto Error;
          809                                         free(filename);
          810                                         filename = strdup(s);
          811                                 Writeimage:
          812                                         if(canvas)
          813                                         if(writeimage(fd, canvas, 0) < 0){
          814                                                 close(fd);
          815                                                 werrstr("writeimage: %r");
          816                                                 goto Error;
          817                                         }
          818                                         close(fd);
          819                                         break;
          820                                 case '<':
          821                                         if((fd = pipeline("%s", s)) < 0)
          822                                                 goto Error;
          823                                         goto Readimage;
          824                                 case '>':
          825                                         if((fd = pipeline("%s", s)) < 0)
          826                                                 goto Error;
          827                                         goto Writeimage;
          828                                 case '|':
          829                                         if(canvas == nil)
          830                                                 break;
          831                                         if((fd = pipeline("%s", s)) < 0)
          832                                                 goto Error;
          833                                         switch(rfork(RFMEM|RFPROC|RFFDG)){
          834                                         case -1:
          835                                                 close(fd);
          836                                                 werrstr("rfork: %r");
          837                                                 goto Error;
          838                                         case 0:
          839                                                 writeimage(fd, canvas, 1);
          840                                                 exits(nil);
          841                                         }
          842                                         goto Readimage;
          843                                 }
          844                                 break;
          845                         }
          846                         break;
          847                 }
          848         }
          849 }
          850 
          851 void
          852 eresized(int _)
          853 {
          854         USED(_);
          855         if(getwindow(display, Refnone) < 0)
          856                 sysfatal("resize failed");
          857         drawpal();
          858         update(nil);
          859 }