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