#include #include #include #include #include #include #include #include #include #include #include #include #define Extern #include "dat.h" #include "fns.h" #include "term.h" int use9wm; int mainpid; int plumbfd; int rcpid; int rcfd; int sfd; int noecho; Window *w; char *fontname; void derror(Display*, char*); void mousethread(void*); void keyboardthread(void*); void winclosethread(void*); void deletethread(void*); void rcoutputproc(void*); void rcinputproc(void*); void hangupnote(void*, char*); void resizethread(void*); int errorshouldabort = 0; void usage(void) { fprint(2, "usage: 9term [-s] [-f font] [-W winsize] [cmd ...]\n"); threadexitsall("usage"); } void threadmain(int argc, char *argv[]) { char *p, *font; rfork(RFNOTEG); font = nil; _wantfocuschanges = 1; mainpid = getpid(); messagesize = 8192; ARGBEGIN{ default: usage(); case 'f': font = EARGF(usage()); break; case 's': scrolling = TRUE; break; case 'w': /* started from rio or 9wm */ use9wm = TRUE; break; case 'W': winsize = EARGF(usage()); break; }ARGEND if(font) putenv("font", font); p = getenv("tabstop"); if(p == 0) p = getenv("TABSTOP"); if(p && maxtab <= 0) maxtab = strtoul(p, 0, 0); if(maxtab <= 0) maxtab = 4; free(p); startdir = "."; initdraw(derror, nil, "9term"); notify(hangupnote); noteenable("sys: child"); // servedevtext(); mousectl = initmouse(nil, screen); if(mousectl == nil) error("cannot find mouse"); keyboardctl = initkeyboard(nil); if(keyboardctl == nil) error("cannot find keyboard"); if((plumbfd = plumbopen("send", OWRITE)) < 0) fprint(2, "9term: plumbopen: %r\n"); mouse = &mousectl->m; winclosechan = chancreate(sizeof(Window*), 0); deletechan = chancreate(sizeof(char*), 0); timerinit(); rcpid = rcstart(argc, argv, &rcfd, &sfd); w = new(screen, FALSE, scrolling, rcpid, ".", nil, nil); threadcreate(keyboardthread, nil, STACK); threadcreate(mousethread, nil, STACK); threadcreate(resizethread, nil, STACK); proccreate(rcoutputproc, nil, STACK); proccreate(rcinputproc, nil, STACK); } void derror(Display *d, char *errorstr) { USED(d); error(errorstr); } void hangupnote(void *a, char *msg) { if(getpid() != mainpid) noted(NDFLT); if(strcmp(msg, "hangup") == 0 && rcpid != 0){ postnote(PNGROUP, rcpid, "hangup"); noted(NDFLT); } if(strstr(msg, "child")){ char buf[128]; int n; n = awaitnohang(buf, sizeof buf-1); if(n > 0){ buf[n] = 0; if(atoi(buf) == rcpid) threadexitsall(0); } noted(NCONT); } noted(NDFLT); } void keyboardthread(void *v) { Rune buf[2][20], *rp; int i, n; USED(v); threadsetname("keyboardthread"); n = 0; for(;;){ rp = buf[n]; n = 1-n; recv(keyboardctl->c, rp); for(i=1; ic, rp+i) <= 0) break; rp[i] = L'\0'; sendp(w->ck, rp); } } void resizethread(void *v) { USED(v); while(recv(mousectl->resizec, nil) == 1){ if(getwindow(display, Refnone) < 0) sysfatal("can't reattach to window"); wresize(w, screen, 0); } } void mousethread(void *v) { int sending; Mouse tmp; USED(v); sending = FALSE; threadsetname("mousethread"); while(readmouse(mousectl) >= 0){ if(sending){ Send: /* send to window */ if(mouse->buttons == 0) sending = FALSE; else wsetcursor(w, 0); tmp = mousectl->m; send(w->mc.c, &tmp); continue; } if((mouse->buttons&1) || ptinrect(mouse->xy, w->scrollr)){ sending = TRUE; goto Send; }else if(mouse->buttons&2) button2menu(w); else /* send to rio */; } } void wborder(Window *w, int type) { } Window* wpointto(Point pt) { return w; } Window* new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **argv) { Window *w; Mousectl *mc; Channel *cm, *ck, *cctl; if(i == nil) return nil; cm = chancreate(sizeof(Mouse), 0); ck = chancreate(sizeof(Rune*), 0); cctl = chancreate(sizeof(Wctlmesg), 4); if(cm==nil || ck==nil || cctl==nil) error("new: channel alloc failed"); mc = emalloc(sizeof(Mousectl)); *mc = *mousectl; // mc->image = i; mc->c = cm; w = wmk(i, mc, ck, cctl, scrollit); free(mc); /* wmk copies *mc */ window = erealloc(window, ++nwindow*sizeof(Window*)); window[nwindow-1] = w; if(hideit){ hidden[nhidden++] = w; w->screenr = ZR; } threadcreate(winctl, w, 8192); if(!hideit) wcurrent(w); flushimage(display, 1); wsetpid(w, pid, 1); wsetname(w); if(dir) w->dir = estrdup(dir); return w; } /* * Button 2 menu. Extra entry for always cook */ int cooked; enum { Cut, Paste, Snarf, Plumb, Send, Scroll, Cook, }; char *menu2str[] = { "cut", "paste", "snarf", "plumb", "send", "scroll", "cook", nil }; Menu menu2 = { menu2str }; Rune newline[] = { '\n' }; void button2menu(Window *w) { if(w->deleted) return; incref(&w->ref); if(w->scrolling) menu2str[Scroll] = "noscroll"; else menu2str[Scroll] = "scroll"; if(cooked) menu2str[Cook] = "nocook"; else menu2str[Cook] = "cook"; switch(menuhit(2, mousectl, &menu2, wscreen)){ case Cut: wsnarf(w); wcut(w); wscrdraw(w); break; case Snarf: wsnarf(w); break; case Paste: riogetsnarf(); wpaste(w); wscrdraw(w); break; case Plumb: wplumb(w); break; case Send: riogetsnarf(); wsnarf(w); if(nsnarf == 0) break; if(w->rawing){ waddraw(w, snarf, nsnarf); if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004') waddraw(w, newline, 1); }else{ winsert(w, snarf, nsnarf, w->nr); if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004') winsert(w, newline, 1, w->nr); } wsetselect(w, w->nr, w->nr); wshow(w, w->nr); break; case Scroll: if(w->scrolling ^= 1) wshow(w, w->nr); break; case Cook: cooked ^= 1; break; } wclose(w); wsendctlmesg(w, Wakeup, ZR, nil); flushimage(display, 1); } int rawon(void) { return !cooked && !isecho(sfd); } /* * I/O with child rc. */ int label(Rune*, int); void rcoutputproc(void *arg) { int i, cnt, n, nb, nr; static char data[9000]; Conswritemesg cwm; Rune *r; Stringpair pair; i = 0; cnt = 0; for(;;){ /* XXX Let typing have a go -- maybe there's a rubout waiting. */ i = 1-i; n = read(rcfd, data+cnt, sizeof data-cnt); if(n <= 0){ if(n < 0) fprint(2, "9term: rc read error: %r\n"); threadexitsall("eof on rc output"); } cnt += n; r = runemalloc(cnt); cvttorunes(data, cnt-UTFmax, r, &nb, &nr, nil); /* approach end of buffer */ while(fullrune(data+nb, cnt-nb)){ nb += chartorune(&r[nr], data+nb); if(r[nr]) nr++; } if(nb < cnt) memmove(data, data+nb, cnt-nb); cnt -= nb; nr = label(r, nr); if(nr == 0) continue; recv(w->conswrite, &cwm); pair.s = r; pair.ns = nr; send(cwm.cw, &pair); } } void winterrupt(Window *w) { char rubout[1]; USED(w); rubout[0] = getintr(sfd); write(rcfd, rubout, 1); } /* * Process in-band messages about window title changes. * The messages are of the form: * * \033];xxx\007 * * where xxx is the new directory. This format was chosen * because it changes the label on xterm windows. */ int label(Rune *sr, int n) { Rune *sl, *el, *er, *r; char *p, *dir; er = sr+n; for(r=er-1; r>=sr; r--) if(*r == '\007') break; if(r < sr) return n; el = r+1; for(sl=el-3; sl>=sr; sl--) if(sl[0]=='\033' && sl[1]==']' && sl[2]==';') break; if(sl < sr) return n; dir = smprint("%.*S", (el-1)-(sl+3), sl+3); if(dir){ drawsetlabel(dir); free(w->dir); w->dir = dir; } /* remove trailing /-sysname if present */ p = strrchr(dir, '/'); if(p && *(p+1) == '-'){ if(p == dir) p++; *p = 0; } runemove(sl, el, er-el); n -= (el-sl); return n; } void rcinputproc(void *arg) { static char data[9000]; int s; Consreadmesg crm; Channel *c1, *c2; Stringpair pair; for(;;){ recv(w->consread, &crm); c1 = crm.c1; c2 = crm.c2; pair.s = data; pair.ns = sizeof data; send(c1, &pair); recv(c2, &pair); s = setecho(sfd, 0); if(write(rcfd, pair.s, pair.ns) < 0) threadexitsall(nil); if(s) setecho(sfd, s); } } void rioputsnarf(void) { char *s; s = smprint("%.*S", nsnarf, snarf); if(s){ putsnarf(s); free(s); } } void riogetsnarf(void) { char *s; int n, nb, nulls; fprint(2, "getsnarf\n"); s = getsnarf(); if(s == nil) return; n = strlen(s)+1; free(snarf); snarf = runemalloc(n); cvttorunes(s, n, snarf, &nb, &nsnarf, &nulls); free(s); }