/* * the actual viewer that handles screen stuff */ #include #include #include #include #include #include #include #include #include #include "page.h" Document *doc; Image *im; int page; int upside = 0; int showbottom = 0; /* on the next showpage, move the image so the bottom is visible. */ Rectangle ulrange; /* the upper left corner of the image must be in this rectangle */ Point ul; /* the upper left corner of the image is at this point on the screen */ Point pclip(Point, Rectangle); Rectangle mkrange(Rectangle screenr, Rectangle imr); void redraw(Image*); Cursor reading={ {-1, -1}, {0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00, 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0, 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, }, {0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0, 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, } }; Cursor query = { {-7,-7}, {0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8, 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, }, {0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c, 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0, 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, } }; enum { Left = 1, Middle = 2, Right = 4, RMenu = 3, }; void unhide(void) { static int wctl = -1; if(wctl < 0) wctl = open("/dev/wctl", OWRITE); if(wctl < 0) return; write(wctl, "unhide", 6); } int max(int a, int b) { return a > b ? a : b; } int min(int a, int b) { return a < b ? a : b; } char* menugen(int n) { static char menustr[32]; char *p; int len; if(n == doc->npage) return "exit"; if(n > doc->npage) return nil; if(reverse) n = doc->npage-1-n; p = doc->pagename(doc, n); len = (sizeof menustr)-2; if(strlen(p) > len && strrchr(p, '/')) p = strrchr(p, '/')+1; if(strlen(p) > len) p = p+strlen(p)-len; strcpy(menustr+1, p); if(page == n) menustr[0] = '>'; else menustr[0] = ' '; return menustr; } void showpage(int page, Menu *m) { Image *tmp; if(doc->fwdonly) m->lasthit = 0; /* this page */ else m->lasthit = reverse ? doc->npage-1-page : page; esetcursor(&reading); freeimage(im); if((page < 0 || page >= doc->npage) && !doc->fwdonly){ im = nil; return; } im = doc->drawpage(doc, page); if(im == nil) { if(doc->fwdonly) /* this is how we know we're out of pages */ wexits(0); im = xallocimage(display, Rect(0,0,50,50), GREY1, 1, DBlack); if(im == nil) { fprint(2, "out of memory: %r\n"); wexits("memory"); } string(im, ZP, display->white, ZP, display->defaultfont, "?"); }else if(resizing){ resize(Dx(im->r), Dy(im->r)); } if(im->r.min.x > 0 || im->r.min.y > 0) { tmp = xallocimage(display, Rect(0, 0, Dx(im->r), Dy(im->r)), im->chan, 0, DNofill); if(tmp == nil) { fprint(2, "out of memory during showpage: %r\n"); wexits("memory"); } drawop(tmp, tmp->r, im, nil, im->r.min, S); freeimage(im); im = tmp; } if(upside) rot180(im); esetcursor(nil); if(showbottom){ ul.y = screen->r.max.y - Dy(im->r); showbottom = 0; } redraw(screen); flushimage(display, 1); } char* writebitmap(void) { char basename[64]; char name[64+30]; static char result[200]; char *p, *q; int fd; if(im == nil) return "no image"; memset(basename, 0, sizeof basename); if(doc->docname) strncpy(basename, doc->docname, sizeof(basename)-1); else if((p = menugen(page)) && p[0] != '\0') strncpy(basename, p+1, sizeof(basename)-1); if(basename[0]) { if(q = strrchr(basename, '/')) q++; else q = basename; if(p = strchr(q, '.')) *p = 0; memset(name, 0, sizeof name); snprint(name, sizeof(name)-1, "%s.%d.bit", q, page+1); if(access(name, 0) >= 0) { strcat(name, "XXXX"); mktemp(name); } if(access(name, 0) >= 0) return "couldn't think of a name for bitmap"; } else { strcpy(name, "bitXXXX"); mktemp(name); if(access(name, 0) >= 0) return "couldn't think of a name for bitmap"; } if((fd = create(name, OWRITE, 0666)) < 0) { snprint(result, sizeof result, "cannot create %s: %r", name); return result; } if(writeimage(fd, im, 0) < 0) { snprint(result, sizeof result, "cannot writeimage: %r"); close(fd); return result; } close(fd); snprint(result, sizeof result, "wrote %s", name); return result; } static void translate(Point); static int showdata(Plumbmsg *msg) { char *s; s = plumblookup(msg->attr, "action"); return s && strcmp(s, "showdata")==0; } /* correspond to entries in miditems[] below, * changing one means you need to change */ enum{ Restore = 0, Zin, Fit, Rot, Upside, Empty1, Next, Prev, Zerox, Empty2, Reverse, Del, Write, Empty3, Exit, }; void viewer(Document *dd) { int i, fd, n, oldpage; int nxt; Menu menu, midmenu; Mouse m; Event e; Point dxy, oxy, xy0; Rectangle r; Image *tmp; static char *fwditems[] = { "this page", "next page", "exit", 0 }; static char *miditems[] = { "orig size", "zoom in", "fit window", "rotate 90", "upside down", "", "next", "prev", "zerox", "", "reverse", "discard", "write", "", "quit", 0 }; char *s; enum { Eplumb = 4 }; Plumbmsg *pm; doc = dd; /* save global for menuhit */ ul = screen->r.min; einit(Emouse|Ekeyboard); if(doc->addpage != nil) eplumb(Eplumb, "image"); esetcursor(&reading); r.min = ZP; /* * im is a global pointer to the current image. * eventually, i think we will have a layer between * the display routines and the ps/pdf/whatever routines * to perhaps cache and handle images of different * sizes, etc. */ im = 0; page = reverse ? doc->npage-1 : 0; if(doc->fwdonly) { menu.item = fwditems; menu.gen = 0; menu.lasthit = 0; } else { menu.item = 0; menu.gen = menugen; menu.lasthit = 0; } midmenu.item = miditems; midmenu.gen = 0; midmenu.lasthit = Next; showpage(page, &menu); esetcursor(nil); nxt = 0; for(;;) { /* * throughout, if doc->fwdonly is set, we restrict the functionality * a fair amount. we don't care about doc->npage anymore, and * all that can be done is select the next page. */ switch(eread(Emouse|Ekeyboard|Eplumb, &e)){ case Ekeyboard: if(e.kbdc <= 0xFF && isdigit(e.kbdc)) { nxt = nxt*10+e.kbdc-'0'; break; } else if(e.kbdc != '\n') nxt = 0; switch(e.kbdc) { case 'r': /* reverse page order */ if(doc->fwdonly) break; reverse = !reverse; menu.lasthit = doc->npage-1-menu.lasthit; /* * the theory is that if we are reversing the * document order and are on the first or last * page then we're just starting and really want * to view the other end. maybe the if * should be dropped and this should happen always. */ if(page == 0 || page == doc->npage-1) { page = doc->npage-1-page; showpage(page, &menu); } break; case 'w': /* write bitmap of current screen */ esetcursor(&reading); s = writebitmap(); if(s) string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP, display->defaultfont, s); esetcursor(nil); flushimage(display, 1); break; case 'd': /* remove image from working set */ if(doc->rmpage && page < doc->npage) { if(doc->rmpage(doc, page) >= 0) { if(doc->npage < 0) wexits(0); if(page >= doc->npage) page = doc->npage-1; showpage(page, &menu); } } break; case 'q': case 0x04: /* ctrl-d */ wexits(0); case 'u': if(im==nil) break; esetcursor(&reading); rot180(im); esetcursor(nil); upside = !upside; redraw(screen); flushimage(display, 1); break; case '-': case '\b': case Kleft: if(page > 0 && !doc->fwdonly) { --page; showpage(page, &menu); } break; case '\n': if(nxt) { nxt--; if(nxt >= 0 && nxt < doc->npage && !doc->fwdonly) showpage(page=nxt, &menu); nxt = 0; break; } goto Gotonext; case Kright: case ' ': Gotonext: if(doc->npage && ++page >= doc->npage && !doc->fwdonly) wexits(0); showpage(page, &menu); break; /* * The upper y coordinate of the image is at ul.y in screen->r. * Panning up means moving the upper left corner down. If the * upper left corner is currently visible, we need to go back a page. */ case Kup: if(screen->r.min.y <= ul.y && ul.y < screen->r.max.y){ if(page > 0 && !doc->fwdonly){ --page; showbottom = 1; showpage(page, &menu); } } else { i = Dy(screen->r)/2; if(i > 10) i -= 10; if(i+ul.y > screen->r.min.y) i = screen->r.min.y - ul.y; translate(Pt(0, i)); } break; /* * If the lower y coordinate is on the screen, we go to the next page. * The lower y coordinate is at ul.y + Dy(im->r). */ case Kdown: i = ul.y + Dy(im->r); if(screen->r.min.y <= i && i <= screen->r.max.y){ ul.y = screen->r.min.y; goto Gotonext; } else { i = -Dy(screen->r)/2; if(i < -10) i += 10; if(i+ul.y+Dy(im->r) <= screen->r.max.y) i = screen->r.max.y - Dy(im->r) - ul.y - 1; translate(Pt(0, i)); } break; default: esetcursor(&query); sleep(1000); esetcursor(nil); break; } break; case Emouse: m = e.mouse; switch(m.buttons){ case Left: oxy = m.xy; xy0 = oxy; do { dxy = subpt(m.xy, oxy); oxy = m.xy; translate(dxy); m = emouse(); } while(m.buttons == Left); if(m.buttons) { dxy = subpt(xy0, oxy); translate(dxy); } break; case Middle: if(doc->npage == 0) break; n = emenuhit(Middle, &m, &midmenu); if(n == -1) break; switch(n){ case Next: /* next */ if(reverse) page--; else page++; if(page < 0) { if(reverse) return; else page = 0; } if((page >= doc->npage) && !doc->fwdonly) return; showpage(page, &menu); nxt = 0; break; case Prev: /* prev */ if(reverse) page++; else page--; if(page < 0) { if(reverse) return; else page = 0; } if((page >= doc->npage) && !doc->fwdonly && !reverse) return; showpage(page, &menu); nxt = 0; break; case Zerox: /* prev */ zerox(); break; case Zin: /* zoom in */ { double delta; Rectangle r; r = egetrect(Middle, &m); if((rectclip(&r, rectaddpt(im->r, ul)) == 0) || Dx(r) == 0 || Dy(r) == 0) break; /* use the smaller side to expand */ if(Dx(r) < Dy(r)) delta = (double)Dx(im->r)/(double)Dx(r); else delta = (double)Dy(im->r)/(double)Dy(r); esetcursor(&reading); tmp = xallocimage(display, Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)), im->chan, 0, DBlack); if(tmp == nil) { fprint(2, "out of memory during zoom: %r\n"); wexits("memory"); } resample(im, tmp); freeimage(im); im = tmp; esetcursor(nil); ul = screen->r.min; redraw(screen); flushimage(display, 1); break; } case Fit: /* fit */ { double delta; Rectangle r; delta = (double)Dx(screen->r)/(double)Dx(im->r); if((double)Dy(im->r)*delta > Dy(screen->r)) delta = (double)Dy(screen->r)/(double)Dy(im->r); r = Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)); esetcursor(&reading); tmp = xallocimage(display, r, im->chan, 0, DBlack); if(tmp == nil) { fprint(2, "out of memory during fit: %r\n"); wexits("memory"); } resample(im, tmp); freeimage(im); im = tmp; esetcursor(nil); ul = screen->r.min; redraw(screen); flushimage(display, 1); break; } case Rot: /* rotate 90 */ esetcursor(&reading); im = rot90(im); esetcursor(nil); redraw(screen); flushimage(display, 1); break; case Upside: /* upside-down */ if(im==nil) break; esetcursor(&reading); rot180(im); esetcursor(nil); upside = !upside; redraw(screen); flushimage(display, 1); break; case Restore: /* restore */ showpage(page, &menu); break; case Reverse: /* reverse */ if(doc->fwdonly) break; reverse = !reverse; menu.lasthit = doc->npage-1-menu.lasthit; if(page == 0 || page == doc->npage-1) { page = doc->npage-1-page; showpage(page, &menu); } break; case Write: /* write */ esetcursor(&reading); s = writebitmap(); if(s) string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP, display->defaultfont, s); esetcursor(nil); flushimage(display, 1); break; case Del: /* delete */ if(doc->rmpage && page < doc->npage) { if(doc->rmpage(doc, page) >= 0) { if(doc->npage < 0) wexits(0); if(page >= doc->npage) page = doc->npage-1; showpage(page, &menu); } } break; case Exit: /* exit */ return; case Empty1: case Empty2: case Empty3: break; }; case Right: if(doc->npage == 0) break; oldpage = page; n = emenuhit(RMenu, &m, &menu); if(n == -1) break; if(doc->fwdonly) { switch(n){ case 0: /* this page */ break; case 1: /* next page */ showpage(++page, &menu); break; case 2: /* exit */ return; } break; } if(n == doc->npage) return; else page = reverse ? doc->npage-1-n : n; if(oldpage != page) showpage(page, &menu); nxt = 0; break; } break; case Eplumb: pm = e.v; if(pm->ndata <= 0){ plumbfree(pm); break; } if(showdata(pm)) { s = estrdup("/tmp/pageplumbXXXXXXX"); fd = opentemp(s); write(fd, pm->data, pm->ndata); /* lose fd reference on purpose; the file is open ORCLOSE */ } else if(pm->data[0] == '/') { s = estrdup(pm->data); } else { s = emalloc(strlen(pm->wdir)+1+pm->ndata+1); sprint(s, "%s/%s", pm->wdir, pm->data); cleanname(s); } if((i = doc->addpage(doc, s)) >= 0) { page = i; unhide(); showpage(page, &menu); } free(s); plumbfree(pm); break; } } } Image *gray; /* * A draw operation that touches only the area contained in bot but not in top. * mp and sp get aligned with bot.min. */ static void gendrawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Point sp, Image *mask, Point mp, int op) { Rectangle r; Point origin; Point delta; USED(op); if(Dx(bot)*Dy(bot) == 0) return; /* no points in bot - top */ if(rectinrect(bot, top)) return; /* bot - top ≡ bot */ if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){ gendrawop(dst, bot, src, sp, mask, mp, op); return; } origin = bot.min; /* split bot into rectangles that don't intersect top */ /* left side */ if(bot.min.x < top.min.x){ r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y); delta = subpt(r.min, origin); gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); bot.min.x = top.min.x; } /* right side */ if(bot.max.x > top.max.x){ r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y); delta = subpt(r.min, origin); gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); bot.max.x = top.max.x; } /* top */ if(bot.min.y < top.min.y){ r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y); delta = subpt(r.min, origin); gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); bot.min.y = top.min.y; } /* bottom */ if(bot.max.y > top.max.y){ r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y); delta = subpt(r.min, origin); gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); bot.max.y = top.max.y; } } static void drawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Image *mask, Point p, int op) { gendrawdiff(dst, bot, top, src, p, mask, p, op); } /* * Translate the image in the window by delta. */ static void translate(Point delta) { Point u; Rectangle r, or; if(im == nil) return; u = pclip(addpt(ul, delta), ulrange); delta = subpt(u, ul); if(delta.x == 0 && delta.y == 0) return; /* * The upper left corner of the image is currently at ul. * We want to move it to u. */ or = rectaddpt(Rpt(ZP, Pt(Dx(im->r), Dy(im->r))), ul); r = rectaddpt(or, delta); drawop(screen, r, screen, nil, ul, S); ul = u; /* fill in gray where image used to be but isn't. */ drawdiff(screen, insetrect(or, -2), insetrect(r, -2), gray, nil, ZP, S); /* fill in black border */ drawdiff(screen, insetrect(r, -2), r, display->black, nil, ZP, S); /* fill in image where it used to be off the screen. */ if(rectclip(&or, screen->r)) drawdiff(screen, r, rectaddpt(or, delta), im, nil, im->r.min, S); else drawop(screen, r, im, nil, im->r.min, S); flushimage(display, 1); } void redraw(Image *screen) { Rectangle r; if(im == nil) return; ulrange.max = screen->r.max; ulrange.min = subpt(screen->r.min, Pt(Dx(im->r), Dy(im->r))); ul = pclip(ul, ulrange); drawop(screen, screen->r, im, nil, subpt(im->r.min, subpt(ul, screen->r.min)), S); if(im->repl) return; /* fill in any outer edges */ /* black border */ r = rectaddpt(im->r, subpt(ul, im->r.min)); border(screen, r, -2, display->black, ZP); r.min = subpt(r.min, Pt(2,2)); r.max = addpt(r.max, Pt(2,2)); /* gray for the rest */ if(gray == nil) { gray = xallocimage(display, Rect(0,0,1,1), RGB24, 1, 0x888888FF); if(gray == nil) { fprint(2, "g out of memory: %r\n"); wexits("mem"); } } border(screen, r, -4000, gray, ZP); // flushimage(display, 0); } void eresized(int new) { Rectangle r; r = screen->r; if(new && getwindow(display, Refnone) < 0) fprint(2,"can't reattach to window"); ul = addpt(ul, subpt(screen->r.min, r.min)); redraw(screen); } /* clip p to be in r */ Point pclip(Point p, Rectangle r) { if(p.x < r.min.x) p.x = r.min.x; else if(p.x >= r.max.x) p.x = r.max.x-1; if(p.y < r.min.y) p.y = r.min.y; else if(p.y >= r.max.y) p.y = r.max.y-1; return p; } /* * resize is perhaps a misnomer. * this really just grows the window to be at least dx across * and dy high. if the window hits the bottom or right edge, * it is backed up until it hits the top or left edge. */ void resize(int dx, int dy) { static Rectangle sr; Rectangle r, or; dx += 2*Borderwidth; dy += 2*Borderwidth; if(wctlfd < 0){ wctlfd = open("/dev/wctl", OWRITE); if(wctlfd < 0) return; } r = insetrect(screen->r, -Borderwidth); if(Dx(r) >= dx && Dy(r) >= dy) return; if(Dx(sr)*Dy(sr) == 0) sr = screenrect(); or = r; r.max.x = max(r.min.x+dx, r.max.x); r.max.y = max(r.min.y+dy, r.max.y); if(r.max.x > sr.max.x){ if(Dx(r) > Dx(sr)){ r.min.x = 0; r.max.x = sr.max.x; }else r = rectaddpt(r, Pt(sr.max.x-r.max.x, 0)); } if(r.max.y > sr.max.y){ if(Dy(r) > Dy(sr)){ r.min.y = 0; r.max.y = sr.max.y; }else r = rectaddpt(r, Pt(0, sr.max.y-r.max.y)); } /* * Sometimes we can't actually grow the window big enough, * and resizing it to the same shape makes it flash. */ if(Dx(r) == Dx(or) && Dy(r) == Dy(or)) return; fprint(wctlfd, "resize -minx %d -miny %d -maxx %d -maxy %d\n", r.min.x, r.min.y, r.max.x, r.max.y); } /* * If we allocimage after a resize but before flushing the draw buffer, * we won't have seen the reshape event, and we won't have called * getwindow, and allocimage will fail. So we flushimage before every alloc. */ Image* xallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val) { flushimage(display, 0); return allocimage(d, r, chan, repl, val); } /* all code below this line should be in the library, but is stolen from colors instead */ static char* rdenv(char *name) { char *v; int fd, size; fd = open(name, OREAD); if(fd < 0) return 0; size = seek(fd, 0, 2); v = malloc(size+1); if(v == 0){ fprint(2, "page: can't malloc: %r\n"); wexits("no mem"); } seek(fd, 0, 0); read(fd, v, size); v[size] = 0; close(fd); return v; } void newwin(void) { char *srv, *mntsrv; char spec[100]; int srvfd, cons, pid; switch(rfork(RFFDG|RFPROC|RFNAMEG|RFENVG|RFNOTEG|RFNOWAIT)){ case -1: fprint(2, "page: can't fork: %r\n"); wexits("no fork"); case 0: break; default: wexits(0); } srv = rdenv("/env/wsys"); if(srv == 0){ mntsrv = rdenv("/mnt/term/env/wsys"); if(mntsrv == 0){ fprint(2, "page: can't find $wsys\n"); wexits("srv"); } srv = malloc(strlen(mntsrv)+10); sprint(srv, "/mnt/term%s", mntsrv); free(mntsrv); pid = 0; /* can't send notes to remote processes! */ }else pid = getpid(); srvfd = open(srv, ORDWR); free(srv); if(srvfd == -1){ fprint(2, "page: can't open %s: %r\n", srv); wexits("no srv"); } sprint(spec, "new -pid %d", pid); if(mount(srvfd, -1, "/mnt/wsys", 0, spec) == -1){ fprint(2, "page: can't mount /mnt/wsys: %r (spec=%s)\n", spec); wexits("no mount"); } close(srvfd); unmount("/mnt/acme", "/dev"); bind("/mnt/wsys", "/dev", MBEFORE); cons = open("/dev/cons", OREAD); if(cons==-1){ NoCons: fprint(2, "page: can't open /dev/cons: %r"); wexits("no cons"); } dup(cons, 0); close(cons); cons = open("/dev/cons", OWRITE); if(cons==-1) goto NoCons; dup(cons, 1); dup(cons, 2); close(cons); // wctlfd = open("/dev/wctl", OWRITE); } Rectangle screenrect(void) { int fd; char buf[12*5]; fd = open("/dev/screen", OREAD); if(fd == -1) fd=open("/mnt/term/dev/screen", OREAD); if(fd == -1){ fprint(2, "page: can't open /dev/screen: %r\n"); wexits("window read"); } if(read(fd, buf, sizeof buf) != sizeof buf){ fprint(2, "page: can't read /dev/screen: %r\n"); wexits("screen read"); } close(fd); return Rect(atoi(buf+12), atoi(buf+24), atoi(buf+36), atoi(buf+48)); } void zerox(void) { int pfd[2]; pipe(pfd); switch(rfork(RFFDG|RFREND|RFPROC)) { case -1: wexits("cannot fork in zerox: %r"); case 0: dup(pfd[1], 0); close(pfd[0]); execl("/bin/page", "page", "-w", 0); wexits("cannot exec in zerox: %r\n"); default: close(pfd[1]); writeimage(pfd[0], im, 0); close(pfd[0]); break; } }