aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2005-11-06 22:16:48 +0000
committerrsc <devnull@localhost>2005-11-06 22:16:48 +0000
commite830a908498c8f0270948fd08c50f6d773315880 (patch)
treea16fe62419a4e3a95ab2d8d3a70f1a870d0bb55c
parenta6c0ff35ee294c0a808e1792eb4f43820fed8f16 (diff)
downloadplan9port-e830a908498c8f0270948fd08c50f6d773315880.tar.gz
plan9port-e830a908498c8f0270948fd08c50f6d773315880.tar.bz2
plan9port-e830a908498c8f0270948fd08c50f6d773315880.zip
New 9term using rio sources more directly.
-rw-r--r--src/cmd/9term/9term.c2072
-rw-r--r--src/cmd/9term/dat.h243
-rw-r--r--src/cmd/9term/data.c180
-rw-r--r--src/cmd/9term/fns.h36
-rw-r--r--src/cmd/9term/malloc.c63
-rw-r--r--src/cmd/9term/mkfile4
-rw-r--r--src/cmd/9term/scrl.c183
-rw-r--r--src/cmd/9term/time.c125
-rw-r--r--src/cmd/9term/util.c149
-rw-r--r--src/cmd/9term/wind.c1628
10 files changed, 2902 insertions, 1781 deletions
diff --git a/src/cmd/9term/9term.c b/src/cmd/9term/9term.c
index 33104df1..285878e8 100644
--- a/src/cmd/9term/9term.c
+++ b/src/cmd/9term/9term.c
@@ -10,208 +10,37 @@
#include <frame.h>
#include <plumb.h>
#include <complete.h>
+#define Extern
+#include "dat.h"
+#include "fns.h"
#include "term.h"
-enum
-{
- STACK = 32768
-};
-
-int noecho = 0;
-
-void servedevtext(void);
-void listenproc(void*);
-void textthread(void*);
-
-typedef struct Text Text;
-typedef struct Readbuf Readbuf;
-
-enum
-{
- HiWater = 640000, /* max size of history */
- LoWater = 400000, /* min size of history after max'ed */
- MinWater = 20000,
-};
-
-/* various geometric paramters */
-enum
-{
- Scrollwid = 12, /* width of scroll bar */
- Scrollgap = 4, /* gap right of scroll bar */
- Maxtab = 4,
-};
-
-enum
-{
- Cut,
- Paste,
- Snarf,
- Send,
- Plumb,
- Scroll,
- Cooked,
-};
-
-#define ESC 0x1B
-#define CUT 0x18 /* ctrl-x */
-#define COPY 0x03 /* crtl-c */
-#define PASTE 0x16 /* crtl-v */
-
-#define READBUFSIZE 8192
-#define TRUE 1
-#define FALSE 0
-
-
-struct Text
-{
- Frame *f; /* frame ofr terminal */
- Mouse m;
- uint nr; /* num of runes in term */
- uint maxr; /* max num of runes in r */
- Rune *r; /* runes for term */
- uint nraw; /* num of runes in raw buffer */
- Rune *raw; /* raw buffer */
- uint org; /* first rune on the screen */
- uint q0; /* start of selection region */
- uint q1; /* end of selection region */
- uint qh; /* unix point */
- int npart; /* partial runes read from console */
- char part[UTFmax];
- int nsnarf; /* snarf buffer */
- Rune *snarf;
-};
-
-struct Readbuf
-{
- short n; /* # bytes in buf */
- uchar data[READBUFSIZE]; /* data bytes */
-};
-
-void mouse(void);
-void domenu2(int);
-void loop(void);
-void geom(void);
-void fill(void);
-void tcheck(void);
-void updatesel(void);
-void doreshape(void);
-void runewrite(Rune*, int);
-void consread(void);
-void conswrite(char*, int);
-int bswidth(Rune c, uint start, int eatnl);
-void cut(void);
-void paste(Rune*, int, int);
-void snarfupdate(void);
-void snarf(void);
-void show(uint);
-void key(Rune);
-void setorigin(uint org, int exact);
-uint line2q(uint);
-uint backnl(uint, uint);
-int cansee(uint);
-uint backnl(uint, uint);
-void addraw(Rune*, int);
-void mselect(void);
-void doubleclick(uint *q0, uint *q1);
-int clickmatch(int cl, int cr, int dir, uint *q);
-Rune *strrune(Rune *s, Rune c);
-int consready(void);
-Rectangle scrpos(Rectangle r, ulong p0, ulong p1, ulong tot);
-void scrdraw(void);
-void scroll(int);
-void hostproc(void *arg);
-void hoststart(void);
-void plumbstart(void);
-void plumb(uint, uint);
-void plumbclick(uint*, uint*);
-uint insert(Rune*, int, uint, int);
-void scrolldown(int);
-void scrollup(int);
-
-#define runemalloc(n) malloc((n)*sizeof(Rune))
-#define runerealloc(a, n) realloc(a, (n)*sizeof(Rune))
-#define runemove(a, b, n) memmove(a, b, (n)*sizeof(Rune))
-Rectangle scrollr; /* scroll bar rectangle */
-Rectangle lastsr; /* used for scroll bar */
-int holdon; /* hold mode */
-int rawon(void); /* raw mode */
-int cooked; /* force cooked */
-int scrolling; /* window scrolls */
-int clickmsec; /* time of last click */
-uint clickq0; /* point of last click */
-int rcfd;
-int sfd; /* slave fd, to get/set terminal mode */
-int rcpid;
-int maxtab;
-int use9wm;
-Mousectl* mc;
-Keyboardctl* kc;
-Channel* hostc;
-Readbuf rcbuf[2];
-int mainpid;
-int acmecolors;
-int plumbfd;
-int button2exec;
-int label(Rune*, int);
-char wdir[1024];
-char childwdir[1024];
-void hangupnote(void*, char*);
-char thesocket[100];
-
-char *menu2str[] = {
- "cut",
- "paste",
- "snarf",
- "send",
- "plumb",
- "scroll",
- "cooked",
- 0
-};
-
-Image* cols[NCOL];
-Image* hcols[NCOL];
-Image* palegrey;
-Image* paleblue;
-Image* blue;
-Image *plumbcolor;
-Image *execcolor;
-
-Menu menu2 =
-{
- menu2str
-};
-
-Text t;
-
-Cursor whitearrow = {
- {0, 0},
- {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
- 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC,
- 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
- 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, },
- {0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C,
- 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C,
- 0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C,
- 0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }
-};
-
-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, }
-};
+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 [-ars] [-W winsize] [cmd ...]\n");
+ fprint(2, "usage: 9term [-s] [-f font] [-W winsize] [cmd ...]\n");
threadexitsall("usage");
}
@@ -219,95 +48,79 @@ void
threadmain(int argc, char *argv[])
{
char *p, *font;
- char buf[32];
-
+
rfork(RFNOTEG);
font = nil;
_wantfocuschanges = 1;
mainpid = getpid();
+ messagesize = 8192;
+
ARGBEGIN{
default:
usage();
- case 'a': /* acme mode */
- button2exec++;
- break;
case 'f':
font = EARGF(usage());
break;
case 's':
- scrolling++;
+ scrolling = TRUE;
break;
- case 'w': /* started from "rio" window manager */
- use9wm = 1;
+ 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 != 0 && maxtab <= 0)
+ if(p && maxtab <= 0)
maxtab = strtoul(p, 0, 0);
if(maxtab <= 0)
- maxtab = 4; /* be like rio */
-
- snprint(buf, sizeof buf, "%d", maxtab);
- putenv("tabstop", buf);
-
- initdraw(0, nil, "9term");
+ maxtab = 4;
+ free(p);
+
+ startdir = ".";
+
+ initdraw(derror, nil, "9term");
notify(hangupnote);
noteenable("sys: child");
- servedevtext();
-
- mc = initmouse(nil, screen);
- kc = initkeyboard(nil);
- rcpid = rcstart(argc, argv, &rcfd, &sfd);
- hoststart();
- plumbstart();
-
- t.f = mallocz(sizeof(Frame), 1);
-
- if(acmecolors){
- cols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
- cols[HIGH] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DDarkyellow);
- cols[BORD] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DYellowgreen);
- }else{
- cols[BACK] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DWhite);
- cols[HIGH] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
- cols[BORD] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x999999FF);
- }
- cols[TEXT] = display->black;
- cols[HTEXT] = display->black;
- palegrey = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x666666FF);
-
- hcols[BACK] = cols[BACK];
- hcols[HIGH] = cols[HIGH];
- blue = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DMedblue);
- paleblue = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DGreyblue);
+// 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;
- hcols[BORD] = blue;
- hcols[TEXT] = hcols[BORD];
- hcols[HTEXT] = hcols[TEXT];
+ winclosechan = chancreate(sizeof(Window*), 0);
+ deletechan = chancreate(sizeof(char*), 0);
- plumbcolor = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x006600FF);
- execcolor = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xAA0000FF);
+ timerinit();
+ rcpid = rcstart(argc, argv, &rcfd, &sfd);
+ w = new(screen, FALSE, scrolling, rcpid, ".", nil, nil);
- if(!blue || !palegrey || !paleblue || !plumbcolor || !execcolor)
- sysfatal("alloc colors: %r");
- draw(screen, screen->r, cols[BACK], nil, ZP);
- geom();
- loop();
+ threadcreate(keyboardthread, nil, STACK);
+ threadcreate(mousethread, nil, STACK);
+ threadcreate(resizethread, nil, STACK);
+
+ proccreate(rcoutputproc, nil, STACK);
+ proccreate(rcinputproc, nil, STACK);
}
-int
-isexpand(Rune r)
+void
+derror(Display *d, char *errorstr)
{
- return r=='_' || ('0' <= r && r <= '9') || isalpharune(r);
+ USED(d);
+ error(errorstr);
}
void
@@ -335,1518 +148,275 @@ hangupnote(void *a, char *msg)
}
void
-hostproc(void *arg)
+keyboardthread(void *v)
{
- Channel *c;
- int i, n, which;
-
- c = arg;
+ Rune buf[2][20], *rp;
+ int i, n;
- i = 0;
+ USED(v);
+ threadsetname("keyboardthread");
+ n = 0;
for(;;){
- /* Let typing have a go -- maybe there's a rubout waiting. */
- yield();
-
- i = 1-i; /* toggle */
- n = read(rcfd, rcbuf[i].data, sizeof rcbuf[i].data);
- if(n <= 0){
- if(n < 0)
- fprint(2, "9term: host read error: %r\n");
- threadexitsall("host");
- }
- rcbuf[i].n = n;
- which = i;
- send(c, &which);
+ rp = buf[n];
+ n = 1-n;
+ recv(keyboardctl->c, rp);
+ for(i=1; i<nelem(buf[0])-1; i++)
+ if(nbrecv(keyboardctl->c, rp+i) <= 0)
+ break;
+ rp[i] = L'\0';
+ sendp(w->ck, rp);
}
}
void
-hoststart(void)
+resizethread(void *v)
{
- hostc = chancreate(sizeof(int), 0);
- proccreate(hostproc, hostc, 32*1024);
-}
-
-void
-loop(void)
-{
- Rune r;
- int i;
- Alt a[5];
-
- a[0].c = mc->c;
- a[0].v = &mc->m;
- a[0].op = CHANRCV;
-
- a[1].c = kc->c;
- a[1].v = &r;
- a[1].op = CHANRCV;
-
- a[2].c = hostc;
- a[2].v = &i;
- a[2].op = CHANRCV;
-
- a[3].c = mc->resizec;
- a[3].v = nil;
- a[3].op = CHANRCV;
-
- a[4].c = nil;
- a[4].v = nil;
- a[4].op = CHANEND;
-
- for(;;) {
- tcheck();
-
- scrdraw();
- flushimage(display, 1);
- a[2].op = CHANRCV;
- if(!scrolling && t.qh > t.org+t.f->nchars)
- a[2].op = CHANNOP;;
- switch(alt(a)) {
- default:
- sysfatal("impossible");
- case 0:
- t.m = mc->m;
- mouse();
- break;
- case 1:
- key(r);
- break;
- case 2:
- conswrite((char*)rcbuf[i].data, rcbuf[i].n);
- break;
- case 3:
- doreshape();
- break;
- }
+ USED(v);
+
+ while(recv(mousectl->resizec, nil) == 1){
+ if(getwindow(display, Refnone) < 0)
+ sysfatal("can't reattach to window");
+ wresize(w, screen, 0);
}
}
-
+
void
-doreshape(void)
+mousethread(void *v)
{
- if(getwindow(display, Refnone) < 0)
- sysfatal("can't reattach to window");
- draw(screen, screen->r, cols[BACK], nil, ZP);
- geom();
- scrdraw();
-}
+ int sending;
+ Mouse tmp;
-void
-geom(void)
-{
- Point p;
- Rectangle r;
+ USED(v);
- if(!acmecolors){
- if(_windowhasfocus){
- cols[TEXT] = cols[HTEXT] = display->black;
- hcols[TEXT] = hcols[HTEXT] = blue;
- }else{
- cols[TEXT] = cols[HTEXT] = palegrey;
- hcols[TEXT] = hcols[HTEXT] = paleblue;
+ 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){
+ sending = TRUE;
+ goto Send;
+ }else if(mouse->buttons&2)
+ button2menu(w);
+ else
+ /* send to rio */;
}
-
- r = screen->r;
- r.min.y++;
- r.max.y--;
-
- scrollr = r;
- scrollr.max.x = r.min.x+Scrollwid;
- lastsr = Rect(0,0,0,0);
-
- r.min.x += Scrollwid+Scrollgap;
-
- frclear(t.f, 0);
- frinit(t.f, r, font, screen, holdon ? hcols : cols);
- t.f->maxtab = maxtab*stringwidth(font, "0");
- fill();
- updatesel();
-
- p = stringsize(font, "0");
- if(p.x == 0 || p.y == 0)
- return;
-
- updatewinsize(Dy(r)/p.y, Dx(r)/p.x, Dx(r), Dy(r));
}
-
+
void
-drawhold(int holdon)
+wborder(Window *w, int type)
{
- if(holdon)
- setcursor(mc, &whitearrow);
- else
- setcursor(mc, nil);
-
- draw(screen, screen->r, cols[BACK], nil, ZP);
- geom();
- scrdraw();
}
-void
-wordclick(uint *q0, uint *q1)
+Window*
+wpointto(Point pt)
{
- while(*q1<t.nr && !isspace(t.r[*q1]))
- (*q1)++;
- while(*q0>0 && !isspace(t.r[*q0-1]))
- (*q0)--;
+ return w;
}
-int
-aselect(uint *q0, uint *q1, Image *color)
+Window*
+new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **argv)
{
- int cancel;
- uint oldq0, oldq1, newq0, newq1;
-
- /* save old selection */
- oldq0 = t.q0;
- oldq1 = t.q1;
-
- /* sweep out area and record it */
- t.f->cols[HIGH] = color;
- t.f->cols[HTEXT] = display->white;
- mselect();
- newq0 = t.q0;
- newq1 = t.q1;
-
- cancel = 0;
- if(t.m.buttons != 0){
- while(t.m.buttons){
- readmouse(mc);
- t.m = mc->m;
- }
- cancel = 1;
- }
-
- /* restore old selection */
- t.f->cols[HIGH] = cols[HIGH];
- t.f->cols[HTEXT] = cols[HTEXT];
- t.q0 = oldq0;
- t.q1 = oldq1;
- updatesel();
+ Window *w;
+ Mousectl *mc;
+ Channel *cm, *ck, *cctl;
- if(cancel)
- return -1;
-
- /* selected a region */
- if(newq0 < newq1){
- *q0 = newq0;
- *q1 = newq1;
- return 0;
- }
-
- /* clicked inside previous selection */
- /* the "<=" in newq0 <= oldq1 allows us to click the right edge */
- if(oldq0 <= newq0 && newq0 <= oldq1){
- *q0 = oldq0;
- *q1 = oldq1;
- return 0;
- }
-
- /* just a click */
- *q0 = newq0;
- *q1 = newq1;
- return 0;
+ 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;
}
-static Rune Lnl[1] = { '\n' };
+/*
+ * Button 2 menu. Extra entry for always cook
+ */
+int cooked;
-void
-mouse(void)
+enum
{
- int but;
- uint q0, q1;
+ Cut,
+ Paste,
+ Snarf,
+ Plumb,
+ Send,
+ Scroll,
+ Cook,
+};
- but = t.m.buttons;
+char *menu2str[] = {
+ "cut",
+ "paste",
+ "snarf",
+ "plumb",
+ "send",
+ "scroll",
+ "cook",
+ nil
+};
- if(but != 1 && but != 2 && but != 4 && but != 8 && but != 16)
- return;
- if (ptinrect(t.m.xy, scrollr)) {
- scroll(but);
- if(t.qh<=t.org+t.f->nchars)
- consread();
- return;
- }
-
- switch(but) {
- case 1:
- mselect();
- break;
- case 2:
- if(button2exec){
- if(aselect(&q0, &q1, execcolor) >= 0){
- if(q0 == q1)
- wordclick(&q0, &q1);
- if(q0 == q1)
- break;
- t.q0 = t.q1 = t.nr;
- updatesel();
- paste(t.r+q0, q1-q0, 1);
- if(t.r[q1-1] != '\n')
- paste(Lnl, 1, 1);
- }
- break;
- }
- domenu2(2);
- break;
- case 4:
- bouncemouse(&t.m);
- break;
- /*
- if(aselect(&q0, &q1, plumbcolor) >= 0)
- plumb(q0, q1);
- break;
- */
- case 8:
- scrollup(mousescrollsize(t.f->maxlines));
- break;
- case 16:
- scrolldown(mousescrollsize(t.f->maxlines));
- break;
- }
-}
-
-void
-mselect(void)
+Menu menu2 =
{
- int b, x, y;
- uint q0;
-
- b = t.m.buttons;
- q0 = frcharofpt(t.f, t.m.xy) + t.org;
- if(t.m.msec-clickmsec<500 && clickq0==q0 && t.q0==t.q1 && b==1){
- doubleclick(&t.q0, &t.q1);
- updatesel();
-/* t.t.i->flush(); */
- x = t.m.xy.x;
- y = t.m.xy.y;
- /* stay here until something interesting happens */
- do {
- readmouse(mc);
- t.m = mc->m;
- } while(t.m.buttons==b && abs(t.m.xy.x-x)<4 && abs(t.m.xy.y-y)<4);
- t.m.xy.x = x; /* in case we're calling frselect */
- t.m.xy.y = y;
- clickmsec = 0;
- }
-
- if(t.m.buttons == b) {
- frselect(t.f, mc);
- t.m = mc->m;
- t.q0 = t.f->p0 + t.org;
- t.q1 = t.f->p1 + t.org;
- clickmsec = t.m.msec;
- clickq0 = t.q0;
- }
- if((t.m.buttons != b) &&(b&1)){
- enum{Cancut = 1, Canpaste = 2} state = Cancut | Canpaste;
- while(t.m.buttons){
- if(t.m.buttons&2){
- if(state&Cancut){
- snarf();
- cut();
- state = Canpaste;
- }
- }else if(t.m.buttons&4){
- if(state&Canpaste){
- snarfupdate();
- if(t.nsnarf){
- paste(t.snarf, t.nsnarf, 0);
- }
- state = Cancut;
- }
- }
- readmouse(mc);
- t.m = mc->m;
- }
- }
-}
+ menu2str
+};
-Rune newline[] = { '\n', 0 };
+Rune newline[] = { '\n' };
void
-domenu2(int but)
+button2menu(Window *w)
{
- if(scrolling)
- menu2str[Scroll] = "+ scroll";
+ if(w->deleted)
+ return;
+ incref(&w->ref);
+ if(w->scrolling)
+ menu2str[Scroll] = "noscroll";
else
- menu2str[Scroll] = "- scroll";
+ menu2str[Scroll] = "scroll";
if(cooked)
- menu2str[Cooked] = "+ mustecho";
+ menu2str[Cook] = "nocook";
else
- menu2str[Cooked] = "- mustecho";
+ menu2str[Cook] = "cook";
- switch(menuhit(but, mc, &menu2, nil)){
- case -1:
- break;
+ switch(menuhit(2, mousectl, &menu2, wscreen)){
case Cut:
- snarf();
- cut();
- if(scrolling)
- show(t.q0);
- break;
- case Paste:
- snarfupdate();
- paste(t.snarf, t.nsnarf, 0);
- if(scrolling)
- show(t.q0);
+ wsnarf(w);
+ wcut(w);
+ wscrdraw(w);
break;
+
case Snarf:
- snarf();
- if(scrolling)
- show(t.q0);
- break;
- case Send:
- if(t.q0 != t.q1)
- snarf();
- else
- snarfupdate();
- t.q0 = t.q1 = t.nr;
- updatesel();
- paste(t.snarf, t.nsnarf, 1);
- if(t.nsnarf == 0 || t.snarf[t.nsnarf-1] != '\n')
- paste(newline, 1, 1);
- show(t.nr);
- consread();
+ wsnarf(w);
break;
- case Scroll:
- scrolling = !scrolling;
- if (scrolling) {
- show(t.nr);
- consread();
- }
+
+ case Paste:
+ //XXX getsnarf();
+ wpaste(w);
+ wscrdraw(w);
break;
+
case Plumb:
- plumb(t.q0, t.q1);
+ wplumb(w);
break;
- case Cooked:
- cooked = !cooked;
- break;
- default:
- sysfatal("bad menu item");
- }
-}
-int
-windfilewidth(uint q0, int oneelement)
-{
- uint q;
- Rune r;
-
- q = q0;
- while(q > 0){
- r = t.r[q-1];
- if(r<=' ')
- break;
- if(oneelement && r=='/')
+ case Send:
+ //XXX getsnarf();
+ wsnarf(w);
+ if(nsnarf == 0)
break;
- --q;
- }
- return q0-q;
-}
-
-void
-showcandidates(Completion *c)
-{
- int i;
- Fmt f;
- Rune *rp;
- uint nr, qline, q0;
- char *s;
-
- runefmtstrinit(&f);
- if (c->nmatch == 0)
- s = "[no matches in ";
- else
- s = "[";
- if(c->nfile > 32)
- fmtprint(&f, "%s%d files]\n", s, c->nfile);
- else{
- fmtprint(&f, "%s", s);
- for(i=0; i<c->nfile; i++){
- if(i > 0)
- fmtprint(&f, " ");
- fmtprint(&f, "%s", c->filename[i]);
- }
- fmtprint(&f, "]\n");
- }
- /* place text at beginning of line before host point */
- qline = t.qh;
- while(qline>0 && t.r[qline-1] != '\n')
- qline--;
-
- rp = runefmtstrflush(&f);
- nr = runestrlen(rp);
-
- q0 = t.q0;
- q0 += insert(rp, nr, qline, 0) - qline;
- free(rp);
- t.q0 = q0+nr;
- t.q1 = q0+nr;
- updatesel();
-}
-
-Rune*
-namecomplete(void)
-{
- int nstr, npath;
- Rune *rp, *path, *str;
- Completion *c;
- char *s, *dir, *root;
-
- /* control-f: filename completion; works back to white space or / */
- if(t.q0<t.nr && t.r[t.q0]>' ') /* must be at end of word */
- return nil;
- nstr = windfilewidth(t.q0, TRUE);
- str = runemalloc(nstr);
- runemove(str, t.r+(t.q0-nstr), nstr);
- npath = windfilewidth(t.q0-nstr, FALSE);
- path = runemalloc(npath);
- runemove(path, t.r+(t.q0-nstr-npath), npath);
- rp = nil;
-
- /* is path rooted? if not, we need to make it relative to window path */
- if(npath>0 && path[0]=='/'){
- dir = malloc(UTFmax*npath+1);
- sprint(dir, "%.*S", npath, path);
- }else{
- if(strcmp(wdir, "") == 0)
- root = ".";
- else
- root = wdir;
- dir = malloc(strlen(root)+1+UTFmax*npath+1);
- sprint(dir, "%s/%.*S", root, npath, path);
- }
- dir = cleanname(dir);
-
- s = smprint("%.*S", nstr, str);
- c = complete(dir, s);
- free(s);
- if(c == nil)
- goto Return;
-
- if(!c->advance)
- showcandidates(c);
-
- if(c->advance)
- rp = runesmprint("%s", c->string);
-
- Return:
- freecompletion(c);
- free(dir);
- free(path);
- free(str);
- return rp;
-}
-
-void
-scrollup(int n)
-{
- setorigin(backnl(t.org, n), 1);
-}
-
-void
-scrolldown(int n)
-{
- setorigin(line2q(n), 1);
- if(t.qh<=t.org+t.f->nchars)
- consread();
-}
-
-void
-key(Rune r)
-{
- Rune *rp;
- int nr;
-
- if(r == 0)
- return;
- switch(r){
- case Kpgup:
- scrollup(t.f->maxlines*2/3);
- return;
- case Kpgdown:
- scrolldown(t.f->maxlines*2/3);
- return;
- case Kup:
- scrollup(t.f->maxlines/3);
- return;
- case Kdown:
- scrolldown(t.f->maxlines/3);
- return;
- case Kleft:
- if(t.q0 > 0){
- t.q0--;
- t.q1 = t.q0;
- updatesel();
- show(t.q0);
- }
- return;
- case Kright:
- if(t.q1 < t.nr){
- t.q1++;
- t.q0 = t.q1;
- updatesel();
- show(t.q1);
- }
- return;
- case Khome:
- show(0);
- return;
- case Kend:
- case 0x05:
- show(t.nr);
- return;
-
- /*
- * Non-standard extensions.
- */
- case CUT:
- snarf();
- cut();
- if(scrolling)
- show(t.q0);
- return;
- case COPY:
- snarf();
- if(scrolling)
- show(t.q0);
- return;
- case PASTE:
- snarfupdate();
- paste(t.snarf, t.nsnarf, 0);
- if(scrolling)
- show(t.q0);
- return;
- }
-
- /*
- * This if used to be below the if(rawon() && t.q0==t.nr),
- * but let's try putting it here. This will allow ESC-processing
- * to toggle hold mode even in remote SSH connections.
- * The drawback is that vi-style processing gets harder.
- * If you find yourself in some weird readline mode, good
- * luck getting out without ESC. Let's see who complains.
- */
- if(r==ESC){ /* toggle hold */
- holdon = !holdon;
- drawhold(holdon);
- /* replaceintegerproperty("_9WM_HOLD_MODE", 1, 32, holdon); */
- if(!holdon)
- consread();
- return;
- }
-
- if(!holdon && rawon() && t.q0 == t.nr){
- addraw(&r, 1);
- consread();
- return;
- }
-
- if(r == 0x7F){ /* DEL: send interrupt; what a mess */
- char rubout[1];
-
- if(holdon){
- holdon = 0;
- drawhold(holdon);
+ 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);
}
- t.qh = t.q0 = t.q1 = t.nr;
- show(t.q0);
- rubout[0] = getintr(sfd);
- write(rcfd, rubout, 1);
- return;
- }
-
- snarf();
+ wsetselect(w, w->nr, w->nr);
+ wshow(w, w->nr);
+ break;
- switch(r) {
- case 0x06: /* ^F: file name completion */
- case Kins: /* Insert: file name completion */
- rp = namecomplete();
- if(rp == nil)
- return;
- nr = runestrlen(rp);
- paste(rp, nr, 1);
- free(rp);
- return;
- case 0x08: /* ^H: erase character */
- case 0x15: /* ^U: erase line */
- case 0x17: /* ^W: erase word */
- if (t.q0 != 0 && t.q0 != t.qh)
- t.q0 -= bswidth(r, t.q0, 1);
- cut();
+ case Scroll:
+ if(w->scrolling ^= 1)
+ wshow(w, w->nr);
break;
- default:
- paste(&r, 1, 1);
+
+ case Cook:
+ cooked ^= 1;
break;
}
- if(scrolling)
- show(t.q0);
-}
-
-int
-bswidth(Rune c, uint start, int eatnl)
-{
- uint q, eq, stop;
- Rune r;
- int skipping;
-
- /* there is known to be at least one character to erase */
- if(c == 0x08) /* ^H: erase character */
- return 1;
- q = start;
- stop = 0;
- if(q > t.qh)
- stop = t.qh;
- skipping = 1;
- while(q > stop){
- r = t.r[q-1];
- if(r == '\n'){ /* eat at most one more character */
- if(q == start && eatnl) /* eat the newline */
- --q;
- break;
- }
- if(c == 0x17){
- eq = isexpand(r);
- if(eq && skipping) /* found one; stop skipping */
- skipping = 0;
- else if(!eq && !skipping)
- break;
- }
- --q;
- }
- return start-q;
+ wclose(w);
+ wsendctlmesg(w, Wakeup, ZR, nil);
+ flushimage(display, 1);
}
int
-consready(void)
-{
- int i, c;
-
- if(holdon)
- return 0;
-
- if(rawon())
- return t.nraw != 0 || t.qh < t.nr;
-
- /* look to see if there is a complete line */
- for(i=t.qh; i<t.nr; i++){
- c = t.r[i];
- if(c=='\n' || c=='\004' || c==0x7F)
- return 1;
- }
- return 0;
-}
-
-
-void
-consread(void)
-{
- char buf[8000], *p;
- int c, width, n;
- int s, raw;
-
- raw = rawon();
- for(;;) {
- if(!consready())
- return;
- n = sizeof(buf);
- p = buf;
- c = 0;
- while(n >= UTFmax && (t.qh<t.nr || t.nraw > 0)) {
- if(t.qh == t.nr){
- width = runetochar(p, &t.raw[0]);
- t.nraw--;
- runemove(t.raw, t.raw+1, t.nraw);
- }else
- width = runetochar(p, &t.r[t.qh++]);
- c = *p;
- p += width;
- n -= width;
- if(c == 0x7F){
- *(p-1) = getintr(sfd);
- if(!raw)
- break;
- }
- if(!raw && (c == '\n' || c == '\004'))
- break;
- }
- n = p-buf;
-
- /*
- * If we've been echoing, make sure the terminal isn't
- * while we do the write. This screws up if someone
- * else tries to turn off echo at the same time we do
- * (we'll turn it on again after the write), but that's not
- * too likely.
- */
- s = setecho(sfd, 0);
- if(write(rcfd, buf, n) < 0)
- threadexitsall(0);
- if(s)
- setecho(sfd, s);
- }
-}
-
-void
-conswrite(char *p, int n)
-{
- int n2, i;
- Rune buf2[1000], *q;
-
- /* convert to runes */
- i = t.npart;
- if(i > 0){
- /* handle partial runes */
- while(i < UTFmax && n>0) {
- t.part[i] = *p;
- i++;
- p++;
- n--;
- if(fullrune(t.part, i)) {
- t.npart = 0;
- chartorune(buf2, t.part);
- runewrite(buf2, 1);
- break;
- }
- }
- /* there is a little extra room in a message buf */
- }
-
- while(n >= UTFmax || fullrune(p, n)) {
- n2 = nelem(buf2);
- q = buf2;
-
- while(n2) {
- if(n < UTFmax && !fullrune(p, n))
- break;
- i = chartorune(q, p);
- p += i;
- n -= i;
- n2--;
- q++;
- }
- runewrite(buf2, q-buf2);
- }
-
- if(n != 0) {
- assert(n+t.npart < UTFmax);
- memcpy(t.part+t.npart, p, n);
- t.npart += n;
- }
-
- if(scrolling)
- show(t.qh);
-}
-
-void
-runewrite(Rune *r, int n)
-{
- static int havecr;
- int i;
- uint initial;
- uint q0, q1;
- uint p0, p1;
- Rune *p, *q;
-
- n = label(r, n);
- if(n == 0)
- return;
-
- /* process trailing \r from previous write */
- initial = 0;
- if(havecr && *r != '\r' && *r != '\n')
- initial = bswidth(0x15, t.qh, 0);
- havecr = 0;
-
- /* get rid of backspaces */
- p = q = r;
- for(i=0; i<n; i++) {
- if(*p == '\b') {
- if(q == r)
- initial++;
- else
- --q;
- } else if(*p == '\r') { /* treat like ^U */
- /* convert CR without NL into erased line */
- /* i feel really sleazy about this but it helps */
- while(i<n-1 && *(p+1) == '\r'){
- i++;
- p++;
- }
- if(i<n-1 && *(p+1) != '\n'){
- while(q > r && *(q-1) != '\n')
- q--;
- if(q==r)
- initial = bswidth(0x15, t.qh, 0);
- }else if(i == n-1)
- havecr = 1;
- } else if(*p)
- *q++ = *p;
- p++;
- }
- n = q-r;
-
- if(initial){
- /* write turned into a delete */
-
- if(initial > t.qh)
- initial = t.qh;
- q0 = t.qh-initial;
- q1 = t.qh;
-
- runemove(t.r+q0, t.r+q1, t.nr-q1);
- t.nr -= initial;
- t.qh -= initial;
- if(t.q0 > q1)
- t.q0 -= initial;
- else if(t.q0 > q0)
- t.q0 = q0;
- if(t.q1 > q1)
- t.q1 -= initial;
- else if(t.q1 > q0)
- t.q1 = q0;
- if(t.org > q1)
- t.org -= initial;
- else if(q0 < t.org+t.f->nchars){
- if(t.org < q0)
- p0 = q0 - t.org;
- else {
- t.org = q0;
- p0 = 0;
- }
- p1 = q1 - t.org;
- if(p1 > t.f->nchars)
- p1 = t.f->nchars;
- frdelete(t.f, p0, p1);
- fill();
- }
- updatesel();
- }
-
- insert(r, n, t.qh, 1);
-}
-
-
-void
-cut(void)
-{
- uint n, p0, p1;
- uint q0, q1;
-
- q0 = t.q0;
- q1 = t.q1;
-
- if (q0 < t.org && q1 >= t.org)
- show(q0);
-
- n = q1-q0;
- if(n == 0)
- return;
- runemove(t.r+q0, t.r+q1, t.nr-q1);
- t.nr -= n;
- t.q0 = t.q1 = q0;
- if(q1 < t.qh)
- t.qh -= n;
- else if(q0 < t.qh)
- t.qh = q0;
- if(q1 < t.org)
- t.org -= n;
- else if(q0 < t.org+t.f->nchars){
- assert(q0 >= t.org);
- p0 = q0 - t.org;
- p1 = q1 - t.org;
- if(p1 > t.f->nchars)
- p1 = t.f->nchars;
- frdelete(t.f, p0, p1);
- fill();
- }
- updatesel();
-}
-
-void
-snarfupdate(void)
-{
- char *pp;
- int n, i;
- Rune *p;
-
- pp = getsnarf();
- if(pp == nil)
- return;
- n = strlen(pp);
- if(n <= 0) {
- /*t.nsnarf = 0;*/
- return;
- }
- t.snarf = runerealloc(t.snarf, n);
- for(i=0,p=t.snarf; i<n; p++)
- i += chartorune(p, pp+i);
- t.nsnarf = p-t.snarf;
-
-}
-
-char sbuf[SnarfSize];
-void
-snarf(void)
-{
- char *p;
- int i, n;
- Rune *rp;
-
- if(t.q1 == t.q0)
- return;
- n = t.q1-t.q0;
- t.snarf = runerealloc(t.snarf, n);
- for(i=0,p=sbuf,rp=t.snarf; i<n && p < sbuf+SnarfSize-UTFmax; i++){
- *rp++ = *(t.r+t.q0+i);
- p += runetochar(p, t.r+t.q0+i);
- }
- t.nsnarf = rp-t.snarf;
- *p = '\0';
- putsnarf(sbuf);
-}
-
-uint
-min(uint x, uint y)
-{
- if(x < y)
- return x;
- return y;
-}
-
-uint
-max(uint x, uint y)
-{
- if(x > y)
- return x;
- return y;
-}
-
-uint
-insert(Rune *r, int n, uint q0, int hostwrite)
-{
- uint m;
-
- if(n == 0)
- return q0;
- if(t.nr+n>HiWater && q0>=t.org && q0>=t.qh){
- m = min(HiWater-LoWater, min(t.org, t.qh));
- t.org -= m;
- t.qh -= m;
- if(t.q0 > m)
- t.q0 -= m;
- else
- t.q0 = 0;
- if(t.q1 > m)
- t.q1 -= m;
- else
- t.q1 = 0;
- t.nr -= m;
- runemove(t.r, t.r+m, t.nr);
- q0 -= m;
- }
- if(t.nr+n > t.maxr){
- /*
- * Minimize realloc breakage:
- * Allocate at least MinWater
- * Double allocation size each time
- * But don't go much above HiWater
- */
- m = max(min(2*(t.nr+n), HiWater), t.nr+n)+MinWater;
- if(m > HiWater)
- m = max(HiWater+MinWater, t.nr+n);
- if(m > t.maxr){
- t.r = runerealloc(t.r, m);
- t.maxr = m;
- }
- }
- runemove(t.r+q0+n, t.r+q0, t.nr-q0);
- runemove(t.r+q0, r, n);
- t.nr += n;
- /* if output touches, advance selection, not qh; works best for keyboard and output */
- if(q0 <= t.q1)
- t.q1 += n;
- if(q0 <= t.q0)
- t.q0 += n;
- if(q0 < t.qh || (q0==t.qh && hostwrite))
- t.qh += n;
- else
- consread();
- if(q0 < t.org)
- t.org += n;
- else if(q0 <= t.org+t.f->nchars)
- frinsert(t.f, r, r+n, q0-t.org);
- return q0;
-}
-
-void
-paste(Rune *r, int n, int advance)
-{
- Rune *rbuf;
-
- if(!holdon && rawon() && t.q0==t.nr){
- addraw(r, n);
- consread();
- return;
- }
-
- cut();
- if(n == 0)
- return;
-
- /*
- * if this is a button2 execute then we might have been passed
- * runes inside the buffer. must save them before realloc.
- */
- rbuf = nil;
- if(t.r <= r && r < t.r+n){
- rbuf = runemalloc(n);
- runemove(rbuf, r, n);
- r = rbuf;
- }
-
- insert(r, n, t.q0, 0);
- updatesel();
- free(rbuf);
-}
-
-void
-fill(void)
+rawon(void)
{
- if (t.f->nlines >= t.f->maxlines)
- return;
- frinsert(t.f, t.r + t.org + t.f->nchars, t.r + t.nr, t.f->nchars);
+ return !cooked && !isecho(sfd);
}
-void
-updatesel(void)
-{
- Frame *f;
- uint n;
-
- f = t.f;
- if(t.org+f->p0 == t.q0 && t.org+f->p1 == t.q1)
- return;
-
- n = t.f->nchars;
-
- frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0);
- if (t.q0 >= t.org)
- f->p0 = t.q0-t.org;
- else
- f->p0 = 0;
- if(f->p0 > n)
- f->p0 = n;
- if (t.q1 >= t.org)
- f->p1 = t.q1-t.org;
- else
- f->p1 = 0;
- if(f->p1 > n)
- f->p1 = n;
- frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1);
-
/*
- if(t.qh<=t.org+t.f.nchars && t.cwqueue != 0)
- t.cwqueue->wakeup <-= 0;
-*/
-
- tcheck();
-}
-
-void
-show(uint q0)
-{
- int nl;
- uint q, oq;
-
- if(cansee(q0))
- return;
-
- if (q0<t.org)
- nl = t.f->maxlines/5;
- else
- nl = 4*t.f->maxlines/5;
- q = backnl(q0, nl);
- /* avoid going in the wrong direction */
- if (q0>t.org && q<t.org)
- q = t.org;
- setorigin(q, 0);
- /* keep trying until q0 is on the screen */
- while(!cansee(q0)) {
- assert(q0 >= t.org);
- oq = q;
- q = line2q(t.f->maxlines-nl);
- assert(q > oq);
- setorigin(q, 1);
- }
-}
-
-int
-cansee(uint q0)
-{
- uint qe;
-
- qe = t.org+t.f->nchars;
-
- if(q0>=t.org && q0 < qe)
- return 1;
- if (q0 != qe)
- return 0;
- if (t.f->nlines < t.f->maxlines)
- return 1;
- if (q0 > 0 && t.r[t.nr-1] == '\n')
- return 0;
- return 1;
-}
+ * I/O with child rc.
+ */
+int label(Rune*, int);
void
-setorigin(uint org, int exact)
+rcoutputproc(void *arg)
{
- int i, a;
- uint n;
+ int i, cnt, n, nb, nr;
+ static char data[9000];
+ Conswritemesg cwm;
+ Rune *r;
+ Stringpair pair;
- if(org>0 && !exact){
- /* try and start after a newline */
- /* don't try harder than 256 chars */
- for(i=0; i<256 && org<t.nr; i++){
- if(t.r[org-1] == '\n')
- break;
- org++;
- }
- }
- a = org-t.org;
-
- if(a>=0 && a<t.f->nchars)
- frdelete(t.f, 0, a);
- else if(a<0 && -a<100*t.f->maxlines){
- n = t.org - org;
- frinsert(t.f, t.r+org, t.r+org+n, 0);
- }else
- frdelete(t.f, 0, t.f->nchars);
- t.org = org;
- fill();
- updatesel();
-}
-
-
-uint
-line2q(uint n)
-{
- Frame *f;
-
- f = t.f;
- return frcharofpt(f, Pt(f->r.min.x, f->r.min.y + n*font->height))+t.org;
-}
-
-uint
-backnl(uint p, uint n)
-{
- int i, j;
-
- for (i = n;; i--) {
- /* at 256 chars, call it a line anyway */
- for(j=256; --j>0 && p>0; p--)
- if(t.r[p-1]=='\n')
- break;
- if (p == 0 || i == 0)
- return p;
- p--;
- }
-}
-
-void
-addraw(Rune *r, int nr)
-{
- t.raw = runerealloc(t.raw, t.nraw+nr);
- runemove(t.raw+t.nraw, r, nr);
- t.nraw += nr;
-/*
- if(t.crqueue != nil)
- t.crqueue->wakeup <-= 0;
-*/
-}
-
-
-Rune left1[] = { '{', '[', '(', '<', 0xab, 0 };
-Rune right1[] = { '}', ']', ')', '>', 0xbb, 0 };
-Rune left2[] = { '\n', 0 };
-Rune left3[] = { '\'', '"', '`', 0 };
-
-Rune *left[] = {
- left1,
- left2,
- left3,
- 0
-};
-
-Rune *right[] = {
- right1,
- left2,
- left3,
- 0
-};
-
-void
-doubleclick(uint *q0, uint *q1)
-{
- int c, i;
- Rune *r, *l, *p;
- uint q;
-
- for(i=0; left[i]!=0; i++){
- q = *q0;
- l = left[i];
- r = right[i];
- /* try matching character to left, looking right */
- if(q == 0)
- c = '\n';
- else
- c = t.r[q-1];
- p = strrune(l, c);
- if(p != 0){
- if(clickmatch(c, r[p-l], 1, &q))
- *q1 = q-(c!='\n');
- return;
- }
- /* try matching character to right, looking left */
- if(q == t.nr)
- c = '\n';
- else
- c = t.r[q];
- p = strrune(r, c);
- if(p != 0){
- if(clickmatch(c, l[p-r], -1, &q)){
- *q1 = *q0+(*q0<t.nr && c=='\n');
- *q0 = q;
- if(c!='\n' || q!=0 || t.r[0]=='\n')
- (*q0)++;
- }
- return;
- }
- }
- /* try filling out word to right */
- while(*q1<t.nr && isexpand(t.r[*q1]))
- (*q1)++;
- /* try filling out word to left */
- while(*q0>0 && isexpand(t.r[*q0-1]))
- (*q0)--;
-}
-
-int
-clickmatch(int cl, int cr, int dir, uint *q)
-{
- Rune c;
- int nest;
-
- nest = 1;
+ i = 0;
+ cnt = 0;
for(;;){
- if(dir > 0){
- if(*q == t.nr)
- break;
- c = t.r[*q];
- (*q)++;
- }else{
- if(*q == 0)
- break;
- (*q)--;
- c = t.r[*q];
+ /* 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");
}
- if(c == cr){
- if(--nest==0)
- return 1;
- }else if(c == cl)
- nest++;
- }
- return cl=='\n' && nest==1;
-}
-
-void
-tcheck(void)
-{
- Frame *f;
-
- f = t.f;
-
- assert(t.q0 <= t.q1 && t.q1 <= t.nr);
- assert(t.org <= t.nr && t.qh <= t.nr);
- assert(f->p0 <= f->p1 && f->p1 <= f->nchars);
- assert(t.org + f->nchars <= t.nr);
- assert(t.org+f->nchars==t.nr || (f->nlines >= f->maxlines));
-}
-
-Rune*
-strrune(Rune *s, Rune c)
-{
- Rune c1;
-
- if(c == 0) {
- while(*s++)
- ;
- return s-1;
- }
-
- while(c1 = *s++)
- if(c1 == c)
- return s-1;
- return 0;
-}
-
-void
-scrdraw(void)
-{
- Rectangle r, r1, r2;
- static Image *scrx;
-
- r = scrollr;
- r.min.x += 1; /* border between margin and bar */
- r1 = r;
- if(scrx==0 || scrx->r.max.y < r.max.y){
- if(scrx)
- freeimage(scrx);
- scrx = allocimage(display, Rect(0, 0, 32, r.max.y), screen->chan, 1, DPaleyellow);
- if(scrx == 0)
- sysfatal("scroll balloc");
- }
- r1.min.x = 0;
- r1.max.x = Dx(r);
- r2 = scrpos(r1, t.org, t.org+t.f->nchars, t.nr);
- if(!eqrect(r2, lastsr)){
- lastsr = r2;
- draw(scrx, r1, cols[BORD], nil, ZP);
- draw(scrx, r2, cols[BACK], nil, r2.min);
-// r2 = r1;
-// r2.min.x = r2.max.x-1;
-// draw(scrx, r2, cols[BORD], nil, ZP);
- draw(screen, r, scrx, nil, r1.min);
- }
-}
-
-Rectangle
-scrpos(Rectangle r, ulong p0, ulong p1, ulong tot)
-{
- long h;
- Rectangle q;
-
- q = insetrect(r, 1);
- h = q.max.y-q.min.y;
- if(tot == 0)
- return q;
- if(tot > 1024L*1024L)
- tot >>= 10, p0 >>= 10, p1 >>= 10;
- if(p0 > 0)
- q.min.y += h*p0/tot;
- if(p1 < tot)
- q.max.y -= h*(tot-p1)/tot;
- if(q.max.y < q.min.y+2){
- if(q.min.y+2 <= r.max.y)
- q.max.y = q.min.y+2;
- else
- q.min.y = q.max.y-2;
- }
- return q;
-}
-
-void
-scroll(int but)
-{
- uint p0, oldp0;
- Rectangle s;
- int x, y, my, h, first, exact;
-
- s = insetrect(scrollr, 1);
- h = s.max.y-s.min.y;
- x = (s.min.x+s.max.x)/2;
- oldp0 = ~0;
- first = 1;
- do{
- if(t.m.xy.x<s.min.x || s.max.x<=t.m.xy.x){
- readmouse(mc);
- t.m = mc->m;
- }else{
- my = t.m.xy.y;
- if(my < s.min.y)
- my = s.min.y;
- if(my >= s.max.y)
- my = s.max.y;
-// if(!eqpt(t.m.xy, Pt(x, my)))
-// cursorset(Pt(x, my));
- exact = 1;
- if(but == 2){
- y = my;
- if(y > s.max.y-2)
- y = s.max.y-2;
- if(t.nr > 1024*1024)
- p0 = ((t.nr>>10)*(y-s.min.y)/h)<<10;
- else
- p0 = t.nr*(y-s.min.y)/h;
- exact = 0;
- } else if(but == 1)
- p0 = backnl(t.org, (my-s.min.y)/font->height);
- else
- p0 = t.org+frcharofpt(t.f, Pt(s.max.x, my));
-
- if(oldp0 != p0)
- setorigin(p0, exact);
- oldp0 = p0;
- scrdraw();
- readmouse(mc);
- t.m = mc->m;
+ 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++;
}
- }while(t.m.buttons & (1<<(but-1)));
-}
-
-void
-plumbstart(void)
-{
- if((plumbfd = plumbopen("send", OWRITE)) < 0)
- fprint(2, "9term: plumbopen: %r\n");
-}
-
-void
-plumb(uint q0, uint q1)
-{
- Plumbmsg *pm;
- char *p;
- int i, p0, n;
- char cbuf[100];
+ if(nb < cnt)
+ memmove(data, data+nb, cnt-nb);
+ cnt -= nb;
+
+ nr = label(r, nr);
+ if(nr == 0)
+ continue;
- pm = malloc(sizeof(Plumbmsg));
- pm->src = strdup("9term");
- pm->dst = 0;
- pm->wdir = strdup(wdir);
- pm->type = strdup("text");
- pm->data = nil;
- if(q1 > q0)
- pm->attr = nil;
- else{
- p0 = q0;
- wordclick(&q0, &q1);
- sprint(cbuf, "click=%d", p0-q0);
- pm->attr = plumbunpackattr(cbuf);
+ recv(w->conswrite, &cwm);
+ pair.s = r;
+ pair.ns = nr;
+ send(cwm.cw, &pair);
}
- if(q0==q1){
- plumbfree(pm);
- return;
- }
- pm->data = malloc(SnarfSize);
- n = q1 - q0;
- for(i=0,p=pm->data; i<n && p < pm->data + SnarfSize-UTFmax; i++)
- p += runetochar(p, t.r+q0+i);
- *p = '\0';
- pm->ndata = strlen(pm->data);
- if(plumbsend(plumbfd, pm) < 0){
- setcursor(mc, &query);
- sleep(500);
- if(holdon)
- setcursor(mc, &whitearrow);
- else
- setcursor(mc, nil);
- }
- plumbfree(pm);
}
/*
@@ -1862,7 +432,7 @@ int
label(Rune *sr, int n)
{
Rune *sl, *el, *er, *r;
- char *p;
+ char *p, *dir;
er = sr+n;
for(r=er-1; r>=sr; r--)
@@ -1872,21 +442,23 @@ label(Rune *sr, int n)
return n;
el = r+1;
- if(el-sr > sizeof wdir)
- sr = el - sizeof wdir;
for(sl=el-3; sl>=sr; sl--)
if(sl[0]=='\033' && sl[1]==']' && sl[2]==';')
break;
if(sl < sr)
return n;
- snprint(wdir, sizeof wdir, "%.*S", (el-1)-(sl+3), sl+3);
- drawsetlabel(wdir);
+ 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(wdir, '/');
+ p = strrchr(dir, '/');
if(p && *(p+1) == '-'){
- if(p == wdir)
+ if(p == dir)
p++;
*p = 0;
}
@@ -1896,92 +468,30 @@ label(Rune *sr, int n)
return n;
}
-int
-rawon(void)
-{
- return !cooked && !isecho(sfd);
-}
-
-/*
- * Clumsy hack to make " and "" work.
- * Then again, what's not a clumsy hack here in Unix land?
- */
-
-char adir[100];
-int afd;
-
-void
-removethesocket(void)
-{
- if(thesocket[0])
- if(remove(thesocket) < 0)
- fprint(2, "remove %s: %r\n", thesocket);
-}
-
-void
-servedevtext(void)
-{
- char buf[100];
-
- snprint(buf, sizeof buf, "unix!/tmp/9term-text.%d", getpid());
-
- if((afd = announce(buf, adir)) < 0){
- putenv("text9term", "");
- return;
- }
-
- putenv("text9term", buf);
- proccreate(listenproc, nil, STACK);
- strcpy(thesocket, buf+5);
- atexit(removethesocket);
-}
-
void
-listenproc(void *arg)
+rcinputproc(void *arg)
{
- int fd;
- char dir[100];
+ static char data[9000];
+ int s;
+ Consreadmesg crm;
+ Channel *c1, *c2;
+ Stringpair pair;
- USED(arg);
for(;;){
- fd = listen(adir, dir);
- if(fd < 0){
- close(afd);
- return;
- }
- proccreate(textthread, (void*)fd, STACK);
+ 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
-textthread(void *arg)
-{
- int fd, i, x, n, end;
- Rune r;
- char buf[4096], *p, *ep;
-
- fd = (int)arg;
- p = buf;
- ep = buf+sizeof buf;
- end = t.org+t.nr; /* avoid possible output loop */
- for(i=t.org;; i++){
- if(i >= end || ep-p < UTFmax){
- for(x=0; x<p-buf; x+=n)
- if((n = write(fd, buf+x, (p-x)-buf)) <= 0)
- goto break2;
-
- if(i >= end)
- break;
- p = buf;
- }
- if(i < t.org)
- i = t.org;
- r = t.r[i-t.org];
- if(r < Runeself)
- *p++ = r;
- else
- p += runetochar(p, &r);
- }
-break2:
- close(fd);
-}
diff --git a/src/cmd/9term/dat.h b/src/cmd/9term/dat.h
new file mode 100644
index 00000000..cf7aa9f9
--- /dev/null
+++ b/src/cmd/9term/dat.h
@@ -0,0 +1,243 @@
+#define STACK 32768
+#undef Borderwidth
+#define Borderwidth 0
+
+typedef struct Consreadmesg Consreadmesg;
+typedef struct Conswritemesg Conswritemesg;
+typedef struct Stringpair Stringpair;
+typedef struct Dirtab Dirtab;
+typedef struct Mouseinfo Mouseinfo;
+typedef struct Mousereadmesg Mousereadmesg;
+typedef struct Mousestate Mousestate;
+typedef struct Timer Timer;
+typedef struct Wctlmesg Wctlmesg;
+typedef struct Window Window;
+
+enum
+{
+ Selborder = 0, /* border of selected window */
+ Unselborder = 0, /* border of unselected window */
+ Scrollwid = 12, /* width of scroll bar */
+ Scrollgap = 4, /* gap right of scroll bar */
+ BIG = 3, /* factor by which window dimension can exceed screen */
+ TRUE = 1,
+ FALSE = 0,
+};
+
+enum
+{
+ Kscrolloneup = KF|0x20,
+ Kscrollonedown = KF|0x21,
+};
+
+enum /* control messages */
+{
+ Wakeup,
+ Reshaped,
+ Moved,
+ Refresh,
+ Movemouse,
+ Rawon,
+ Rawoff,
+ Holdon,
+ Holdoff,
+ Deleted,
+ Exited,
+};
+
+struct Wctlmesg
+{
+ int type;
+ Rectangle r;
+ Image *image;
+};
+
+struct Conswritemesg
+{
+ Channel *cw; /* chan(Stringpair) */
+};
+
+struct Consreadmesg
+{
+ Channel *c1; /* chan(tuple(char*, int) == Stringpair) */
+ Channel *c2; /* chan(tuple(char*, int) == Stringpair) */
+};
+
+struct Mousereadmesg
+{
+ Channel *cm; /* chan(Mouse) */
+};
+
+struct Stringpair /* rune and nrune or byte and nbyte */
+{
+ void *s;
+ int ns;
+};
+
+struct Mousestate
+{
+ Mouse m;
+ ulong counter; /* serial no. of mouse event */
+};
+
+struct Mouseinfo
+{
+ Mousestate queue[16];
+ int ri; /* read index into queue */
+ int wi; /* write index */
+ ulong counter; /* serial no. of last mouse event we received */
+ ulong lastcounter; /* serial no. of last mouse event sent to client */
+ int lastb; /* last button state we received */
+ uchar qfull; /* filled the queue; no more recording until client comes back */
+};
+
+struct Window
+{
+ Ref ref;
+ QLock lk;
+ Frame f;
+ Image *i;
+ Mousectl mc;
+ Mouseinfo mouse;
+ Channel *ck; /* chan(Rune[10]) */
+ Channel *cctl; /* chan(Wctlmesg)[20] */
+ Channel *conswrite; /* chan(Conswritemesg) */
+ Channel *consread; /* chan(Consreadmesg) */
+ Channel *mouseread; /* chan(Mousereadmesg) */
+ Channel *wctlread; /* chan(Consreadmesg) */
+ uint nr; /* number of runes in window */
+ uint maxr; /* number of runes allocated in r */
+ Rune *r;
+ uint nraw;
+ Rune *raw;
+ uint org;
+ uint q0;
+ uint q1;
+ uint qh;
+ int id;
+ char name[32];
+ uint namecount;
+ Rectangle scrollr;
+ /*
+ * Rio once used originwindow, so screenr could be different from i->r.
+ * Now they're always the same but the code doesn't assume so.
+ */
+ Rectangle screenr; /* screen coordinates of window */
+ int resized;
+ int wctlready;
+ Rectangle lastsr;
+ int topped;
+ int notefd;
+ uchar scrolling;
+ Cursor cursor;
+ Cursor *cursorp;
+ uchar holding;
+ uchar rawing;
+ uchar ctlopen;
+ uchar wctlopen;
+ uchar deleted;
+ uchar mouseopen;
+ char *label;
+ int pid;
+ char *dir;
+};
+
+int winborder(Window*, Point);
+void winctl(void*);
+void winshell(void*);
+Window* wlookid(int);
+Window* wmk(Image*, Mousectl*, Channel*, Channel*, int);
+Window* wpointto(Point);
+Window* wtop(Point);
+void wtopme(Window*);
+void wbottomme(Window*);
+char* wcontents(Window*, int*);
+int wbswidth(Window*, Rune);
+int wclickmatch(Window*, int, int, int, uint*);
+int wclose(Window*);
+int wctlmesg(Window*, int, Rectangle, Image*);
+int wctlmesg(Window*, int, Rectangle, Image*);
+uint wbacknl(Window*, uint, uint);
+uint winsert(Window*, Rune*, int, uint);
+void waddraw(Window*, Rune*, int);
+void wborder(Window*, int);
+void wclosewin(Window*);
+void wcurrent(Window*);
+void wcut(Window*);
+void wdelete(Window*, uint, uint);
+void wdoubleclick(Window*, uint*, uint*);
+void wfill(Window*);
+void wframescroll(Window*, int);
+void wkeyctl(Window*, Rune);
+void wmousectl(Window*);
+void wmovemouse(Window*, Point);
+void wpaste(Window*);
+void wplumb(Window*);
+void wrefresh(Window*, Rectangle);
+void wrepaint(Window*);
+void wresize(Window*, Image*, int);
+void wscrdraw(Window*);
+void wscroll(Window*, int);
+void wselect(Window*);
+void wsendctlmesg(Window*, int, Rectangle, Image*);
+void wsetcursor(Window*, int);
+void wsetname(Window*);
+void wsetorigin(Window*, uint, int);
+void wsetpid(Window*, int, int);
+void wsetselect(Window*, uint, uint);
+void wshow(Window*, uint);
+void wsnarf(Window*);
+void wscrsleep(Window*, uint);
+void wsetcols(Window*);
+
+void deletetimeoutproc(void*);
+
+struct Timer
+{
+ int dt;
+ int cancel;
+ Channel *c; /* chan(int) */
+ Timer *next;
+};
+
+#ifndef Extern
+#define Extern extern
+#endif
+
+Extern Font *font;
+Extern Mousectl *mousectl;
+Extern Mouse *mouse;
+Extern Keyboardctl *keyboardctl;
+Extern Display *display;
+Extern Image *view;
+Extern Screen *wscreen;
+Extern Cursor boxcursor;
+Extern Cursor crosscursor;
+Extern Cursor sightcursor;
+Extern Cursor whitearrow;
+Extern Cursor query;
+Extern Cursor *corners[9];
+Extern Image *background;
+Extern Image *lightgrey;
+Extern Image *red;
+Extern Window **window;
+Extern Window *wkeyboard; /* window of simulated keyboard */
+Extern int nwindow;
+Extern int snarffd;
+Extern Window *input;
+Extern QLock all; /* BUG */
+Extern Window *hidden[100];
+Extern int nhidden;
+Extern int nsnarf;
+Extern Rune* snarf;
+Extern int scrolling;
+Extern int maxtab;
+Extern Channel* winclosechan;
+Extern Channel* deletechan;
+Extern char *startdir;
+Extern int sweeping;
+Extern int wctlfd;
+Extern int errorshouldabort;
+Extern int menuing; /* menu action is pending; waiting for window to be indicated */
+Extern int snarfversion; /* updated each time it is written */
+Extern int messagesize; /* negotiated in 9P version setup */
diff --git a/src/cmd/9term/data.c b/src/cmd/9term/data.c
new file mode 100644
index 00000000..86e589b3
--- /dev/null
+++ b/src/cmd/9term/data.c
@@ -0,0 +1,180 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <cursor.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <frame.h>
+#include <fcall.h>
+#include "dat.h"
+#include "fns.h"
+
+Cursor crosscursor = {
+ {-7, -7},
+ {0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0,
+ 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0,
+ 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, },
+ {0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
+ 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x7F, 0xFE,
+ 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
+ 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, }
+};
+
+Cursor boxcursor = {
+ {-7, -7},
+ {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
+ 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, },
+ {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
+ 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
+ 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
+ 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00, }
+};
+
+Cursor sightcursor = {
+ {-7, -7},
+ {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
+ 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
+ 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8, },
+ {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
+ 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
+ 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
+ 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00, }
+};
+
+Cursor whitearrow = {
+ {0, 0},
+ {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
+ 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC,
+ 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
+ 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, },
+ {0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C,
+ 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C,
+ 0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C,
+ 0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }
+};
+
+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, }
+};
+
+Cursor tl = {
+ {-4, -4},
+ {0xfe, 0x00, 0x82, 0x00, 0x8c, 0x00, 0x87, 0xff,
+ 0xa0, 0x01, 0xb0, 0x01, 0xd0, 0x01, 0x11, 0xff,
+ 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00,
+ 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x1f, 0x00, },
+ {0x00, 0x00, 0x7c, 0x00, 0x70, 0x00, 0x78, 0x00,
+ 0x5f, 0xfe, 0x4f, 0xfe, 0x0f, 0xfe, 0x0e, 0x00,
+ 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00,
+ 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, 0x00, }
+};
+
+Cursor t = {
+ {-7, -8},
+ {0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x06, 0xc0,
+ 0x1c, 0x70, 0x10, 0x10, 0x0c, 0x60, 0xfc, 0x7f,
+ 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80, 0x03, 0x80,
+ 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }
+};
+
+Cursor tr = {
+ {-11, -4},
+ {0x00, 0x7f, 0x00, 0x41, 0x00, 0x31, 0xff, 0xe1,
+ 0x80, 0x05, 0x80, 0x0d, 0x80, 0x0b, 0xff, 0x88,
+ 0x00, 0x88, 0x0, 0x88, 0x00, 0x88, 0x00, 0x88,
+ 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xf8, },
+ {0x00, 0x00, 0x00, 0x3e, 0x00, 0x0e, 0x00, 0x1e,
+ 0x7f, 0xfa, 0x7f, 0xf2, 0x7f, 0xf0, 0x00, 0x70,
+ 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
+ 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, }
+};
+
+Cursor r = {
+ {-8, -7},
+ {0x07, 0xc0, 0x04, 0x40, 0x04, 0x40, 0x04, 0x58,
+ 0x04, 0x68, 0x04, 0x6c, 0x04, 0x06, 0x04, 0x02,
+ 0x04, 0x06, 0x04, 0x6c, 0x04, 0x68, 0x04, 0x58,
+ 0x04, 0x40, 0x04, 0x40, 0x04, 0x40, 0x07, 0xc0, },
+ {0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80,
+ 0x03, 0x90, 0x03, 0x90, 0x03, 0xf8, 0x03, 0xfc,
+ 0x03, 0xf8, 0x03, 0x90, 0x03, 0x90, 0x03, 0x80,
+ 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
+};
+
+Cursor br = {
+ {-11, -11},
+ {0x00, 0xf8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88,
+ 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88,
+ 0xff, 0x88, 0x80, 0x0b, 0x80, 0x0d, 0x80, 0x05,
+ 0xff, 0xe1, 0x00, 0x31, 0x00, 0x41, 0x00, 0x7f, },
+ {0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
+ 0x0, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
+ 0x00, 0x70, 0x7f, 0xf0, 0x7f, 0xf2, 0x7f, 0xfa,
+ 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x3e, 0x00, 0x00, }
+};
+
+Cursor b = {
+ {-7, -7},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
+ 0xfc, 0x7f, 0x0c, 0x60, 0x10, 0x10, 0x1c, 0x70,
+ 0x06, 0xc0, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, },
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe,
+ 0x03, 0x80, 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }
+};
+
+Cursor bl = {
+ {-4, -11},
+ {0x1f, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00,
+ 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00,
+ 0x11, 0xff, 0xd0, 0x01, 0xb0, 0x01, 0xa0, 0x01,
+ 0x87, 0xff, 0x8c, 0x00, 0x82, 0x00, 0xfe, 0x00, },
+ {0x00, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00,
+ 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00,
+ 0x0e, 0x00, 0x0f, 0xfe, 0x4f, 0xfe, 0x5f, 0xfe,
+ 0x78, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x00, 0x0, }
+};
+
+Cursor l = {
+ {-7, -7},
+ {0x03, 0xe0, 0x02, 0x20, 0x02, 0x20, 0x1a, 0x20,
+ 0x16, 0x20, 0x36, 0x20, 0x60, 0x20, 0x40, 0x20,
+ 0x60, 0x20, 0x36, 0x20, 0x16, 0x20, 0x1a, 0x20,
+ 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x03, 0xe0, },
+ {0x00, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0,
+ 0x09, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x3f, 0xc0,
+ 0x1f, 0xc0, 0x09, 0xc0, 0x09, 0xc0, 0x01, 0xc0,
+ 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x00, 0x00, }
+};
+
+Cursor *corners[9] = {
+ &tl, &t, &tr,
+ &l, nil, &r,
+ &bl, &b, &br,
+};
+
+void
+iconinit(void)
+{
+ background = allocimage(display, Rect(0,0,1,1), RGB24, 1, 0x777777FF);
+ red = allocimage(display, Rect(0,0,1,1), RGB24, 1, 0xDD0000FF);
+}
diff --git a/src/cmd/9term/fns.h b/src/cmd/9term/fns.h
new file mode 100644
index 00000000..9fc30933
--- /dev/null
+++ b/src/cmd/9term/fns.h
@@ -0,0 +1,36 @@
+#undef isalnum
+#define isalnum runeisalnum
+
+void keyboardsend(char*, int);
+int whide(Window*);
+int wunhide(int);
+void freescrtemps(void);
+int parsewctl(char**, Rectangle, Rectangle*, int*, int*, int*, int*, char**, char*, char*);
+Window *new(Image*, int, int, int, char*, char*, char**);
+void riosetcursor(Cursor*, int);
+int min(int, int);
+int max(int, int);
+Rune* strrune(Rune*, Rune);
+int isalnum(Rune);
+void timerstop(Timer*);
+void timercancel(Timer*);
+Timer* timerstart(int);
+void error(char*);
+void killprocs(void);
+int shutdown(void*, char*);
+void iconinit(void);
+void *erealloc(void*, uint);
+void *emalloc(uint);
+char *estrdup(char*);
+void button3menu(void);
+void button2menu(Window*);
+void cvttorunes(char*, int, Rune*, int*, int*, int*);
+/* was (byte*,int) runetobyte(Rune*, int); */
+char* runetobyte(Rune*, int, int*);
+void timerinit(void);
+int goodrect(Rectangle);
+int rawon(void);
+
+#define runemalloc(n) malloc((n)*sizeof(Rune))
+#define runerealloc(a, n) realloc(a, (n)*sizeof(Rune))
+#define runemove(a, b, n) memmove(a, b, (n)*sizeof(Rune))
diff --git a/src/cmd/9term/malloc.c b/src/cmd/9term/malloc.c
new file mode 100644
index 00000000..130942bd
--- /dev/null
+++ b/src/cmd/9term/malloc.c
@@ -0,0 +1,63 @@
+/*
+ * These are here mainly so that I can link against
+ * debugmalloc.c instead and not recompile the world.
+ */
+
+#include <u.h>
+#define NOPLAN9DEFINES
+#include <libc.h>
+
+static Lock malloclock;
+
+void*
+p9malloc(ulong n)
+{
+ void *v;
+
+ if(n == 0)
+ n++;
+ lock(&malloclock);
+ v = malloc(n);
+ unlock(&malloclock);
+ print("p9malloc %lud => %p; pc %lux\n", n, v, getcallerpc(&n));
+ return v;
+}
+
+void
+p9free(void *v)
+{
+ if(v == nil)
+ return;
+ lock(&malloclock);
+ print("p9free %p; pc %lux\n", v, getcallerpc(&v));
+ free(v);
+ unlock(&malloclock);
+}
+
+void*
+p9calloc(ulong a, ulong b)
+{
+ void *v;
+
+ if(a*b == 0)
+ a = b = 1;
+
+ lock(&malloclock);
+ v = calloc(a*b, 1);
+ unlock(&malloclock);
+ print("p9calloc %lud %lud => %p; pc %lux\n", a, b, v, getcallerpc(&a));
+ return v;
+}
+
+void*
+p9realloc(void *v, ulong n)
+{
+ void *vv;
+
+ lock(&malloclock);
+ vv = realloc(v, n);
+ unlock(&malloclock);
+ print("p9realloc %p %lud => %p; pc %lux\n", v, n, vv, getcallerpc(&v));
+ return vv;
+}
+
diff --git a/src/cmd/9term/mkfile b/src/cmd/9term/mkfile
index ae2618f4..e1ef4d68 100644
--- a/src/cmd/9term/mkfile
+++ b/src/cmd/9term/mkfile
@@ -6,7 +6,11 @@ OFILES=\
rcstart.$O\
$SYSNAME.$O\
+HFILES=dat.h fns.h term.h
+
<$PLAN9/src/mkmany
Darwin.$O Linux.$O FreeBSD.$O: bsdpty.c
+$O.9term: data.$O scrl.$O time.$O util.$O wind.$O
+
diff --git a/src/cmd/9term/scrl.c b/src/cmd/9term/scrl.c
new file mode 100644
index 00000000..8a614ad6
--- /dev/null
+++ b/src/cmd/9term/scrl.c
@@ -0,0 +1,183 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <cursor.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <frame.h>
+#include <fcall.h>
+#include "dat.h"
+#include "fns.h"
+
+static Image *scrtmp;
+
+static
+void
+scrtemps(void)
+{
+ int h;
+
+ if(scrtmp)
+ return;
+ h = BIG*Dy(screen->r);
+ scrtmp = allocimage(display, Rect(0, 0, 32, h), screen->chan, 0, DWhite);
+ if(scrtmp == nil)
+ error("scrtemps");
+}
+
+void
+freescrtemps(void)
+{
+ freeimage(scrtmp);
+ scrtmp = nil;
+}
+
+static
+Rectangle
+scrpos(Rectangle r, uint p0, uint p1, uint tot)
+{
+ Rectangle q;
+ int h;
+
+ q = r;
+ h = q.max.y-q.min.y;
+ if(tot == 0)
+ return q;
+ if(tot > 1024*1024){
+ tot>>=10;
+ p0>>=10;
+ p1>>=10;
+ }
+ if(p0 > 0)
+ q.min.y += h*p0/tot;
+ if(p1 < tot)
+ q.max.y -= h*(tot-p1)/tot;
+ if(q.max.y < q.min.y+2){
+ if(q.min.y+2 <= r.max.y)
+ q.max.y = q.min.y+2;
+ else
+ q.min.y = q.max.y-2;
+ }
+ return q;
+}
+
+void
+wscrdraw(Window *w)
+{
+ Rectangle r, r1, r2;
+ Image *b;
+
+ scrtemps();
+ if(w->i == nil)
+ error("scrdraw");
+ r = w->scrollr;
+ b = scrtmp;
+ r1 = r;
+ r1.min.x = 0;
+ r1.max.x = Dx(r);
+ r2 = scrpos(r1, w->org, w->org+w->f.nchars, w->nr);
+ if(!eqrect(r2, w->lastsr)){
+ w->lastsr = r2;
+ /* move r1, r2 to (0,0) to avoid clipping */
+ r2 = rectsubpt(r2, r1.min);
+ r1 = rectsubpt(r1, r1.min);
+ draw(b, r1, w->f.cols[BORD], nil, ZP);
+ draw(b, r2, w->f.cols[BACK], nil, ZP);
+ r2.min.x = r2.max.x-1;
+ draw(b, r2, w->f.cols[BORD], nil, ZP);
+ draw(w->i, r, b, nil, Pt(0, r1.min.y));
+ }
+}
+
+void
+wscrsleep(Window *w, uint dt)
+{
+ Timer *timer;
+ int y, b;
+ static Alt alts[3];
+
+ timer = timerstart(dt);
+ y = w->mc.m.xy.y;
+ b = w->mc.m.buttons;
+ alts[0].c = timer->c;
+ alts[0].v = nil;
+ alts[0].op = CHANRCV;
+ alts[1].c = w->mc.c;
+ alts[1].v = &w->mc.m;
+ alts[1].op = CHANRCV;
+ alts[2].op = CHANEND;
+ for(;;)
+ switch(alt(alts)){
+ case 0:
+ timerstop(timer);
+ return;
+ case 1:
+ if(abs(w->mc.m.xy.y-y)>2 || w->mc.m.buttons!=b){
+ timercancel(timer);
+ return;
+ }
+ break;
+ }
+}
+
+void
+wscroll(Window *w, int but)
+{
+ uint p0, oldp0;
+ Rectangle s;
+ int x, y, my, h, first;
+
+ s = insetrect(w->scrollr, 1);
+ h = s.max.y-s.min.y;
+ x = (s.min.x+s.max.x)/2;
+ oldp0 = ~0;
+ first = TRUE;
+ do{
+ flushimage(display, 1);
+ if(w->mc.m.xy.x<s.min.x || s.max.x<=w->mc.m.xy.x){
+ readmouse(&w->mc);
+ }else{
+ my = w->mc.m.xy.y;
+ if(my < s.min.y)
+ my = s.min.y;
+ if(my >= s.max.y)
+ my = s.max.y;
+ if(!eqpt(w->mc.m.xy, Pt(x, my))){
+ wmovemouse(w, Pt(x, my));
+ readmouse(&w->mc); /* absorb event generated by moveto() */
+ }
+ if(but == 2){
+ y = my;
+ if(y > s.max.y-2)
+ y = s.max.y-2;
+ if(w->nr > 1024*1024)
+ p0 = ((w->nr>>10)*(y-s.min.y)/h)<<10;
+ else
+ p0 = w->nr*(y-s.min.y)/h;
+ if(oldp0 != p0)
+ wsetorigin(w, p0, FALSE);
+ oldp0 = p0;
+ readmouse(&w->mc);
+ continue;
+ }
+ if(but == 1)
+ p0 = wbacknl(w, w->org, (my-s.min.y)/w->f.font->height);
+ else
+ p0 = w->org+frcharofpt(&w->f, Pt(s.max.x, my));
+ if(oldp0 != p0)
+ wsetorigin(w, p0, TRUE);
+ oldp0 = p0;
+ /* debounce */
+ if(first){
+ flushimage(display, 1);
+ sleep(200);
+ nbrecv(w->mc.c, &w->mc.m);
+ first = FALSE;
+ }
+ wscrsleep(w, 100);
+ }
+ }while(w->mc.m.buttons & (1<<(but-1)));
+ while(w->mc.m.buttons)
+ readmouse(&w->mc);
+}
diff --git a/src/cmd/9term/time.c b/src/cmd/9term/time.c
new file mode 100644
index 00000000..5dedf144
--- /dev/null
+++ b/src/cmd/9term/time.c
@@ -0,0 +1,125 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <cursor.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <frame.h>
+#include <fcall.h>
+#include "dat.h"
+#include "fns.h"
+
+static Channel* ctimer; /* chan(Timer*)[100] */
+static Timer *timer;
+
+static
+uint
+msec(void)
+{
+ return nsec()/1000000;
+}
+
+void
+timerstop(Timer *t)
+{
+ t->next = timer;
+ timer = t;
+}
+
+void
+timercancel(Timer *t)
+{
+ t->cancel = TRUE;
+}
+
+static
+void
+timerproc(void *a)
+{
+ int i, nt, na, dt, del;
+ Timer **t, *x;
+ uint old, new;
+
+ USED(a);
+ rfork(RFFDG);
+ threadsetname("TIMERPROC");
+ t = nil;
+ na = 0;
+ nt = 0;
+ old = msec();
+ for(;;){
+ sleep(1); /* will sleep minimum incr */
+ new = msec();
+ dt = new-old;
+ old = new;
+ if(dt < 0) /* timer wrapped; go around, losing a tick */
+ continue;
+ for(i=0; i<nt; i++){
+ x = t[i];
+ x->dt -= dt;
+ del = 0;
+ if(x->cancel){
+ timerstop(x);
+ del = 1;
+ }else if(x->dt <= 0){
+ /*
+ * avoid possible deadlock if client is
+ * now sending on ctimer
+ */
+ if(nbsendul(x->c, 0) > 0)
+ del = 1;
+ }
+ if(del){
+ memmove(&t[i], &t[i+1], (nt-i-1)*sizeof t[0]);
+ --nt;
+ --i;
+ }
+ }
+ if(nt == 0){
+ x = recvp(ctimer);
+ gotit:
+ if(nt == na){
+ na += 10;
+ t = realloc(t, na*sizeof(Timer*));
+ if(t == nil)
+ abort();
+ }
+ t[nt++] = x;
+ old = msec();
+ }
+ if(nbrecv(ctimer, &x) > 0)
+ goto gotit;
+ }
+}
+
+void
+timerinit(void)
+{
+ ctimer = chancreate(sizeof(Timer*), 100);
+ proccreate(timerproc, nil, STACK);
+}
+
+/*
+ * timeralloc() and timerfree() don't lock, so can only be
+ * called from the main proc.
+ */
+
+Timer*
+timerstart(int dt)
+{
+ Timer *t;
+
+ t = timer;
+ if(t)
+ timer = timer->next;
+ else{
+ t = emalloc(sizeof(Timer));
+ t->c = chancreate(sizeof(int), 0);
+ }
+ t->next = nil;
+ t->dt = dt;
+ t->cancel = FALSE;
+ sendp(ctimer, t);
+ return t;
+}
diff --git a/src/cmd/9term/util.c b/src/cmd/9term/util.c
new file mode 100644
index 00000000..8a35d1d7
--- /dev/null
+++ b/src/cmd/9term/util.c
@@ -0,0 +1,149 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <cursor.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <frame.h>
+#include <fcall.h>
+#include "dat.h"
+#include "fns.h"
+
+void
+cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls)
+{
+ uchar *q;
+ Rune *s;
+ int j, w;
+
+ /*
+ * Always guaranteed that n bytes may be interpreted
+ * without worrying about partial runes. This may mean
+ * reading up to UTFmax-1 more bytes than n; the caller
+ * knows this. If n is a firm limit, the caller should
+ * set p[n] = 0.
+ */
+ q = (uchar*)p;
+ s = r;
+ for(j=0; j<n; j+=w){
+ if(*q < Runeself){
+ w = 1;
+ *s = *q++;
+ }else{
+ w = chartorune(s, (char*)q);
+ q += w;
+ }
+ if(*s)
+ s++;
+ else if(nulls)
+ *nulls = TRUE;
+ }
+ *nb = (char*)q-p;
+ *nr = s-r;
+}
+
+void
+error(char *s)
+{
+ fprint(2, "rio: %s: %r\n", s);
+ if(errorshouldabort)
+ abort();
+ threadexitsall("error");
+}
+
+void*
+erealloc(void *p, uint n)
+{
+ p = realloc(p, n);
+ if(p == nil)
+ error("realloc failed");
+ return p;
+}
+
+void*
+emalloc(uint n)
+{
+ void *p;
+
+ p = malloc(n);
+ if(p == nil)
+ error("malloc failed");
+ memset(p, 0, n);
+ return p;
+}
+
+char*
+estrdup(char *s)
+{
+ char *p;
+
+ p = malloc(strlen(s)+1);
+ if(p == nil)
+ error("strdup failed");
+ strcpy(p, s);
+ return p;
+}
+
+int
+isalnum(Rune c)
+{
+ /*
+ * Hard to get absolutely right. Use what we know about ASCII
+ * and assume anything above the Latin control characters is
+ * potentially an alphanumeric.
+ */
+ if(c <= ' ')
+ return FALSE;
+ if(0x7F<=c && c<=0xA0)
+ return FALSE;
+ if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
+ return FALSE;
+ return TRUE;
+}
+
+Rune*
+strrune(Rune *s, Rune c)
+{
+ Rune c1;
+
+ if(c == 0) {
+ while(*s++)
+ ;
+ return s-1;
+ }
+
+ while(c1 = *s++)
+ if(c1 == c)
+ return s-1;
+ return nil;
+}
+
+int
+min(int a, int b)
+{
+ if(a < b)
+ return a;
+ return b;
+}
+
+int
+max(int a, int b)
+{
+ if(a > b)
+ return a;
+ return b;
+}
+
+char*
+runetobyte(Rune *r, int n, int *ip)
+{
+ char *s;
+ int m;
+
+ s = emalloc(n*UTFmax+1);
+ m = snprint(s, n*UTFmax+1, "%.*S", n, r);
+ *ip = m;
+ return s;
+}
+
diff --git a/src/cmd/9term/wind.c b/src/cmd/9term/wind.c
new file mode 100644
index 00000000..600a4e42
--- /dev/null
+++ b/src/cmd/9term/wind.c
@@ -0,0 +1,1628 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <cursor.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <frame.h>
+#include <fcall.h>
+#include <plumb.h>
+#include <complete.h>
+#include "dat.h"
+#include "fns.h"
+
+#define MOVEIT if(0)
+
+enum
+{
+ HiWater = 640000, /* max size of history */
+ LoWater = 400000, /* min size of history after max'ed */
+ MinWater = 20000, /* room to leave available when reallocating */
+};
+
+static int topped;
+static int id;
+
+static Image *cols[NCOL];
+static Image *grey;
+static Image *darkgrey;
+static Cursor *lastcursor;
+static Image *titlecol;
+static Image *lighttitlecol;
+static Image *holdcol;
+static Image *lightholdcol;
+static Image *paleholdcol;
+
+Window*
+wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl, int scrolling)
+{
+ Window *w;
+ Rectangle r;
+
+ if(cols[0] == nil){
+ /* greys are multiples of 0x11111100+0xFF, 14* being palest */
+ grey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
+ darkgrey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x666666FF);
+ cols[BACK] = display->white;
+ cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
+ cols[BORD] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x999999FF);
+ cols[TEXT] = display->black;
+ cols[HTEXT] = display->black;
+ titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreygreen);
+ lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreygreen);
+ holdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedblue);
+ lightholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreyblue);
+ paleholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreyblue);
+ }
+ w = emalloc(sizeof(Window));
+ w->screenr = i->r;
+ r = insetrect(i->r, Selborder+1);
+ w->i = i;
+ w->mc = *mc;
+ w->ck = ck;
+ w->cctl = cctl;
+ w->cursorp = nil;
+ w->conswrite = chancreate(sizeof(Conswritemesg), 0);
+ w->consread = chancreate(sizeof(Consreadmesg), 0);
+ w->mouseread = chancreate(sizeof(Mousereadmesg), 0);
+ w->wctlread = chancreate(sizeof(Consreadmesg), 0);
+ w->scrollr = r;
+ w->scrollr.max.x = r.min.x+Scrollwid;
+ w->lastsr = ZR;
+ r.min.x += Scrollwid+Scrollgap;
+ frinit(&w->f, r, font, i, cols);
+ w->f.maxtab = maxtab*stringwidth(font, "0");
+ w->topped = ++topped;
+ w->id = ++id;
+ w->notefd = -1;
+ w->scrolling = scrolling;
+ w->dir = estrdup(startdir);
+ w->label = estrdup("<unnamed>");
+ r = insetrect(w->i->r, Selborder);
+ draw(w->i, r, cols[BACK], nil, w->f.entire.min);
+ wborder(w, Selborder);
+ wscrdraw(w);
+ incref(&w->ref); /* ref will be removed after mounting; avoids delete before ready to be deleted */
+ return w;
+}
+
+void
+wsetname(Window *w)
+{
+ int i, n;
+ char err[ERRMAX];
+
+ n = sprint(w->name, "window.%d.%d", w->id, w->namecount++);
+ for(i='A'; i<='Z'; i++){
+ if(nameimage(w->i, w->name, 1) > 0)
+ return;
+ errstr(err, sizeof err);
+ if(strcmp(err, "image name in use") != 0)
+ break;
+ w->name[n] = i;
+ w->name[n+1] = 0;
+ }
+ w->name[0] = 0;
+ fprint(2, "rio: setname failed: %s\n", err);
+}
+
+void
+wresize(Window *w, Image *i, int move)
+{
+ Rectangle r, or;
+
+ or = w->i->r;
+ if(move || (Dx(or)==Dx(i->r) && Dy(or)==Dy(i->r)))
+ draw(i, i->r, w->i, nil, w->i->r.min);
+ if(w->i != i){
+fprint(2, "res %p %p\n", w->i, i);
+ freeimage(w->i);
+ w->i = i;
+ }
+// wsetname(w);
+//XXX w->mc.image = i;
+ r = insetrect(i->r, Selborder+1);
+ w->scrollr = r;
+ w->scrollr.max.x = r.min.x+Scrollwid;
+ w->lastsr = ZR;
+ r.min.x += Scrollwid+Scrollgap;
+ if(move)
+ frsetrects(&w->f, r, w->i);
+ else{
+ frclear(&w->f, FALSE);
+ frinit(&w->f, r, w->f.font, w->i, cols);
+ wsetcols(w);
+ w->f.maxtab = maxtab*stringwidth(w->f.font, "0");
+ r = insetrect(w->i->r, Selborder);
+ draw(w->i, r, cols[BACK], nil, w->f.entire.min);
+ wfill(w);
+ wsetselect(w, w->q0, w->q1);
+ wscrdraw(w);
+ }
+ wborder(w, Selborder);
+ w->topped = ++topped;
+ w->resized = TRUE;
+ w->mouse.counter++;
+}
+
+void
+wrefresh(Window *w, Rectangle r)
+{
+ /* USED(r); */
+
+ /* BUG: rectangle is ignored */
+ if(w == input)
+ wborder(w, Selborder);
+ else
+ wborder(w, Unselborder);
+ if(w->mouseopen)
+ return;
+ draw(w->i, insetrect(w->i->r, Borderwidth), w->f.cols[BACK], nil, w->i->r.min);
+ w->f.ticked = 0;
+ if(w->f.p0 > 0)
+ frdrawsel(&w->f, frptofchar(&w->f, 0), 0, w->f.p0, 0);
+ if(w->f.p1 < w->f.nchars)
+ frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, w->f.nchars, 0);
+ frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 1);
+ w->lastsr = ZR;
+ wscrdraw(w);
+}
+
+int
+wclose(Window *w)
+{
+ int i;
+
+ i = decref(&w->ref);
+ if(i > 0)
+ return 0;
+ if(i < 0)
+ error("negative ref count");
+ if(!w->deleted)
+ wclosewin(w);
+ wsendctlmesg(w, Exited, ZR, nil);
+ return 1;
+}
+
+
+void
+winctl(void *arg)
+{
+ Rune *rp, *bp, *tp, *up, *kbdr;
+ uint qh;
+ int nr, nb, c, wid, i, npart, initial, lastb;
+ char *s, *t, part[3];
+ Window *w;
+ Mousestate *mp, m;
+ enum { WKey, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT };
+ Alt alts[NWALT+1];
+ Mousereadmesg mrm;
+ Conswritemesg cwm;
+ Consreadmesg crm;
+ Consreadmesg cwrm;
+ Stringpair pair;
+ Wctlmesg wcm;
+ char buf[4*12+1];
+
+ w = arg;
+ snprint(buf, sizeof buf, "winctl-id%d", w->id);
+ threadsetname(buf);
+
+ mrm.cm = chancreate(sizeof(Mouse), 0);
+ cwm.cw = chancreate(sizeof(Stringpair), 0);
+ crm.c1 = chancreate(sizeof(Stringpair), 0);
+ crm.c2 = chancreate(sizeof(Stringpair), 0);
+ cwrm.c1 = chancreate(sizeof(Stringpair), 0);
+ cwrm.c2 = chancreate(sizeof(Stringpair), 0);
+
+
+ alts[WKey].c = w->ck;
+ alts[WKey].v = &kbdr;
+ alts[WKey].op = CHANRCV;
+ alts[WMouse].c = w->mc.c;
+ alts[WMouse].v = &w->mc.m;
+ alts[WMouse].op = CHANRCV;
+ alts[WMouseread].c = w->mouseread;
+ alts[WMouseread].v = &mrm;
+ alts[WMouseread].op = CHANSND;
+ alts[WCtl].c = w->cctl;
+ alts[WCtl].v = &wcm;
+ alts[WCtl].op = CHANRCV;
+ alts[WCwrite].c = w->conswrite;
+ alts[WCwrite].v = &cwm;
+ alts[WCwrite].op = CHANSND;
+ alts[WCread].c = w->consread;
+ alts[WCread].v = &crm;
+ alts[WCread].op = CHANSND;
+ alts[WWread].c = w->wctlread;
+ alts[WWread].v = &cwrm;
+ alts[WWread].op = CHANSND;
+ alts[NWALT].op = CHANEND;
+
+ npart = 0;
+ lastb = -1;
+ for(;;){
+ if(w->mouseopen && w->mouse.counter != w->mouse.lastcounter)
+ alts[WMouseread].op = CHANSND;
+ else
+ alts[WMouseread].op = CHANNOP;
+ if(!w->scrolling && !w->mouseopen && w->qh>w->org+w->f.nchars)
+ alts[WCwrite].op = CHANNOP;
+ else
+ alts[WCwrite].op = CHANSND;
+ if(w->deleted || !w->wctlready)
+ alts[WWread].op = CHANNOP;
+ else
+ alts[WWread].op = CHANSND;
+ /* this code depends on NL and EOT fitting in a single byte */
+ /* kind of expensive for each loop; worth precomputing? */
+ if(w->holding)
+ alts[WCread].op = CHANNOP;
+ else if(npart || (w->rawing && w->nraw>0))
+ alts[WCread].op = CHANSND;
+ else{
+ alts[WCread].op = CHANNOP;
+ for(i=w->qh; i<w->nr; i++){
+ c = w->r[i];
+ if(c=='\n' || c=='\004'){
+ alts[WCread].op = CHANSND;
+ break;
+ }
+ }
+ }
+ switch(alt(alts)){
+ case WKey:
+ for(i=0; kbdr[i]!=L'\0'; i++)
+ wkeyctl(w, kbdr[i]);
+// wkeyctl(w, r);
+// while(nbrecv(w->ck, &r))
+// wkeyctl(w, r);
+ break;
+ case WMouse:
+ if(w->mouseopen) {
+ w->mouse.counter++;
+
+ /* queue click events */
+ if(!w->mouse.qfull && lastb != w->mc.m.buttons) { /* add to ring */
+ mp = &w->mouse.queue[w->mouse.wi];
+ if(++w->mouse.wi == nelem(w->mouse.queue))
+ w->mouse.wi = 0;
+ if(w->mouse.wi == w->mouse.ri)
+ w->mouse.qfull = TRUE;
+ mp->m = w->mc.m;
+ mp->counter = w->mouse.counter;
+ lastb = w->mc.m.buttons;
+ }
+ } else
+ wmousectl(w);
+ break;
+ case WMouseread:
+ /* send a queued event or, if the queue is empty, the current state */
+ /* if the queue has filled, we discard all the events it contained. */
+ /* the intent is to discard frantic clicking by the user during long latencies. */
+ w->mouse.qfull = FALSE;
+ if(w->mouse.wi != w->mouse.ri) {
+ m = w->mouse.queue[w->mouse.ri];
+ if(++w->mouse.ri == nelem(w->mouse.queue))
+ w->mouse.ri = 0;
+ } else
+ m = (Mousestate){w->mc.m, w->mouse.counter};
+
+ w->mouse.lastcounter = m.counter;
+ send(mrm.cm, &m.m);
+ continue;
+ case WCtl:
+ if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Exited){
+ chanfree(crm.c1);
+ chanfree(crm.c2);
+ chanfree(mrm.cm);
+ chanfree(cwm.cw);
+ chanfree(cwrm.c1);
+ chanfree(cwrm.c2);
+ threadexits(nil);
+ }
+ continue;
+ case WCwrite:
+ recv(cwm.cw, &pair);
+ rp = pair.s;
+ nr = pair.ns;
+ bp = rp;
+ for(i=0; i<nr; i++)
+ if(*bp++ == '\b'){
+ --bp;
+ initial = 0;
+ tp = runemalloc(nr);
+ runemove(tp, rp, i);
+ up = tp+i;
+ for(; i<nr; i++){
+ *up = *bp++;
+ if(*up == '\b')
+ if(up == tp)
+ initial++;
+ else
+ --up;
+ else
+ up++;
+ }
+ if(initial){
+ if(initial > w->qh)
+ initial = w->qh;
+ qh = w->qh-initial;
+ wdelete(w, qh, qh+initial);
+ w->qh = qh;
+ }
+ free(rp);
+ rp = tp;
+ nr = up-tp;
+ rp[nr] = 0;
+ break;
+ }
+ w->qh = winsert(w, rp, nr, w->qh)+nr;
+ if(w->scrolling || w->mouseopen)
+ wshow(w, w->qh);
+ wsetselect(w, w->q0, w->q1);
+ wscrdraw(w);
+ free(rp);
+ break;
+ case WCread:
+ recv(crm.c1, &pair);
+ t = pair.s;
+ nb = pair.ns;
+ i = npart;
+ npart = 0;
+ if(i)
+ memmove(t, part, i);
+ while(i<nb && (w->qh<w->nr || w->nraw>0)){
+ if(w->qh == w->nr){
+ wid = runetochar(t+i, &w->raw[0]);
+ w->nraw--;
+ runemove(w->raw, w->raw+1, w->nraw);
+ }else
+ wid = runetochar(t+i, &w->r[w->qh++]);
+ c = t[i]; /* knows break characters fit in a byte */
+ i += wid;
+ if(!w->rawing && (c == '\n' || c=='\004')){
+ // if(c == '\004')
+ // i--;
+ break;
+ }
+ }
+ // if(i==nb && w->qh<w->nr && w->r[w->qh]=='\004')
+ // w->qh++;
+ if(i > nb){
+ npart = i-nb;
+ memmove(part, t+nb, npart);
+ i = nb;
+ }
+ pair.s = t;
+ pair.ns = i;
+ send(crm.c2, &pair);
+ continue;
+ case WWread:
+ w->wctlready = 0;
+ recv(cwrm.c1, &pair);
+ if(w->deleted || w->i==nil)
+ pair.ns = sprint(pair.s, "");
+ else{
+ s = "visible";
+ for(i=0; i<nhidden; i++)
+ if(hidden[i] == w){
+ s = "hidden";
+ break;
+ }
+ t = "notcurrent";
+ if(w == input)
+ t = "current";
+ pair.ns = snprint(pair.s, pair.ns, "%11d %11d %11d %11d %s %s ",
+ w->i->r.min.x, w->i->r.min.y, w->i->r.max.x, w->i->r.max.y, t, s);
+ }
+ send(cwrm.c2, &pair);
+ continue;
+ }
+ if(!w->deleted)
+ flushimage(display, 1);
+ }
+}
+
+void
+waddraw(Window *w, Rune *r, int nr)
+{
+ w->raw = runerealloc(w->raw, w->nraw+nr);
+ runemove(w->raw+w->nraw, r, nr);
+ w->nraw += nr;
+}
+
+/*
+ * Need to do this in a separate proc because if process we're interrupting
+ * is dying and trying to print tombstone, kernel is blocked holding p->debug lock.
+ */
+void
+interruptproc(void *v)
+{
+ int *notefd;
+
+ notefd = v;
+ write(*notefd, "interrupt", 9);
+ free(notefd);
+}
+
+int
+windfilewidth(Window *w, uint q0, int oneelement)
+{
+ uint q;
+ Rune r;
+
+ q = q0;
+ while(q > 0){
+ r = w->r[q-1];
+ if(r<=' ')
+ break;
+ if(oneelement && r=='/')
+ break;
+ --q;
+ }
+ return q0-q;
+}
+
+void
+showcandidates(Window *w, Completion *c)
+{
+ int i;
+ Fmt f;
+ Rune *rp;
+ uint nr, qline, q0;
+ char *s;
+
+ runefmtstrinit(&f);
+ if (c->nmatch == 0)
+ s = "[no matches in ";
+ else
+ s = "[";
+ if(c->nfile > 32)
+ fmtprint(&f, "%s%d files]\n", s, c->nfile);
+ else{
+ fmtprint(&f, "%s", s);
+ for(i=0; i<c->nfile; i++){
+ if(i > 0)
+ fmtprint(&f, " ");
+ fmtprint(&f, "%s", c->filename[i]);
+ }
+ fmtprint(&f, "]\n");
+ }
+ /* place text at beginning of line before host point */
+ qline = w->qh;
+ while(qline>0 && w->r[qline-1] != '\n')
+ qline--;
+
+ rp = runefmtstrflush(&f);
+ nr = runestrlen(rp);
+
+ q0 = w->q0;
+ q0 += winsert(w, rp, runestrlen(rp), qline) - qline;
+ free(rp);
+ wsetselect(w, q0+nr, q0+nr);
+}
+
+Rune*
+namecomplete(Window *w)
+{
+ int nstr, npath;
+ Rune *rp, *path, *str;
+ Completion *c;
+ char *s, *dir, *root;
+
+ /* control-f: filename completion; works back to white space or / */
+ if(w->q0<w->nr && w->r[w->q0]>' ') /* must be at end of word */
+ return nil;
+ nstr = windfilewidth(w, w->q0, TRUE);
+ str = runemalloc(nstr);
+ runemove(str, w->r+(w->q0-nstr), nstr);
+ npath = windfilewidth(w, w->q0-nstr, FALSE);
+ path = runemalloc(npath);
+ runemove(path, w->r+(w->q0-nstr-npath), npath);
+ rp = nil;
+
+ /* is path rooted? if not, we need to make it relative to window path */
+ if(npath>0 && path[0]=='/'){
+ dir = malloc(UTFmax*npath+1);
+ sprint(dir, "%.*S", npath, path);
+ }else{
+ if(strcmp(w->dir, "") == 0)
+ root = ".";
+ else
+ root = w->dir;
+ dir = malloc(strlen(root)+1+UTFmax*npath+1);
+ sprint(dir, "%s/%.*S", root, npath, path);
+ }
+ dir = cleanname(dir);
+
+ s = smprint("%.*S", nstr, str);
+ c = complete(dir, s);
+ free(s);
+ if(c == nil)
+ goto Return;
+
+ if(!c->advance)
+ showcandidates(w, c);
+
+ if(c->advance)
+ rp = runesmprint("%s", c->string);
+
+ Return:
+ freecompletion(c);
+ free(dir);
+ free(path);
+ free(str);
+ return rp;
+}
+
+void
+wkeyctl(Window *w, Rune r)
+{
+ uint q0 ,q1;
+ int n, nb, nr;
+ Rune *rp;
+ int *notefd;
+
+ if(r == 0)
+ return;
+ if(w->deleted)
+ return;
+ w->rawing = rawon();
+ /* navigation keys work only when mouse is not open */
+ if(!w->mouseopen)
+ switch(r){
+ case Kdown:
+ n = w->f.maxlines/3;
+ goto case_Down;
+ case Kscrollonedown:
+ n = mousescrollsize(w->f.maxlines);
+ if(n <= 0)
+ n = 1;
+ goto case_Down;
+ case Kpgdown:
+ n = 2*w->f.maxlines/3;
+ case_Down:
+ q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.min.y+n*w->f.font->height));
+ wsetorigin(w, q0, TRUE);
+ return;
+ case Kup:
+ n = w->f.maxlines/3;
+ goto case_Up;
+ case Kscrolloneup:
+ n = mousescrollsize(w->f.maxlines);
+ if(n <= 0)
+ n = 1;
+ goto case_Up;
+ case Kpgup:
+ n = 2*w->f.maxlines/3;
+ case_Up:
+ q0 = wbacknl(w, w->org, n);
+ wsetorigin(w, q0, TRUE);
+ return;
+ case Kleft:
+ if(w->q0 > 0){
+ q0 = w->q0-1;
+ wsetselect(w, q0, q0);
+ wshow(w, q0);
+ }
+ return;
+ case Kright:
+ if(w->q1 < w->nr){
+ q1 = w->q1+1;
+ wsetselect(w, q1, q1);
+ wshow(w, q1);
+ }
+ return;
+ case Khome:
+ wshow(w, 0);
+ return;
+ case Kend:
+ wshow(w, w->nr);
+ return;
+ case 0x01: /* ^A: beginning of line */
+ if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\n')
+ return;
+ nb = wbswidth(w, 0x15 /* ^U */);
+ wsetselect(w, w->q0-nb, w->q0-nb);
+ wshow(w, w->q0);
+ return;
+ case 0x05: /* ^E: end of line */
+ q0 = w->q0;
+ while(q0 < w->nr && w->r[q0]!='\n')
+ q0++;
+ wsetselect(w, q0, q0);
+ wshow(w, w->q0);
+ return;
+ }
+ /*
+ * This if used to be below the if(w->rawing ...),
+ * but let's try putting it here. This will allow ESC-processing
+ * to toggle hold mode even in remote SSH connections.
+ * The drawback is that vi-style processing gets harder.
+ * If you find yourself in some weird readline mode, good
+ * luck getting out without ESC. Let's see who complains.
+ */
+ if(r==0x1B || (w->holding && r==0x7F)){ /* toggle hold */
+ if(w->holding)
+ --w->holding;
+ else
+ w->holding++;
+ wrepaint(w);
+ if(r == 0x1B)
+ return;
+ }
+ if(!w->holding && w->rawing && (w->q0==w->nr || w->mouseopen)){
+ waddraw(w, &r, 1);
+ return;
+ }
+ if(r != 0x7F){
+ wsnarf(w);
+ wcut(w);
+ }
+ switch(r){
+ case 0x7F: /* send interrupt */
+ w->qh = w->nr;
+ wshow(w, w->qh);
+ notefd = emalloc(sizeof(int));
+ *notefd = w->notefd;
+ proccreate(interruptproc, notefd, 4096);
+ return;
+ case 0x06: /* ^F: file name completion */
+ case Kins: /* Insert: file name completion */
+ rp = namecomplete(w);
+ if(rp == nil)
+ return;
+ nr = runestrlen(rp);
+ q0 = w->q0;
+ q0 = winsert(w, rp, nr, q0);
+ wshow(w, q0+nr);
+ free(rp);
+ return;
+ case 0x08: /* ^H: erase character */
+ case 0x15: /* ^U: erase line */
+ case 0x17: /* ^W: erase word */
+ if(w->q0==0 || w->q0==w->qh)
+ return;
+ nb = wbswidth(w, r);
+ q1 = w->q0;
+ q0 = q1-nb;
+ if(q0 < w->org){
+ q0 = w->org;
+ nb = q1-q0;
+ }
+ if(nb > 0){
+ wdelete(w, q0, q0+nb);
+ wsetselect(w, q0, q0);
+ }
+ return;
+ }
+ /* otherwise ordinary character; just insert */
+ q0 = w->q0;
+ q0 = winsert(w, &r, 1, q0);
+ wshow(w, q0+1);
+}
+
+void
+wsetcols(Window *w)
+{
+ if(w->holding)
+ if(w == input)
+ w->f.cols[TEXT] = w->f.cols[HTEXT] = holdcol;
+ else
+ w->f.cols[TEXT] = w->f.cols[HTEXT] = lightholdcol;
+ else
+ if(w == input)
+ w->f.cols[TEXT] = w->f.cols[HTEXT] = display->black;
+ else
+ w->f.cols[TEXT] = w->f.cols[HTEXT] = darkgrey;
+}
+
+void
+wrepaint(Window *w)
+{
+ wsetcols(w);
+ if(!w->mouseopen)
+ _frredraw(&w->f, w->f.r.min);
+ if(w == input){
+ wborder(w, Selborder);
+ wsetcursor(w, 0);
+ }else
+ wborder(w, Unselborder);
+}
+
+int
+wbswidth(Window *w, Rune c)
+{
+ uint q, eq, stop;
+ Rune r;
+ int skipping;
+
+ /* there is known to be at least one character to erase */
+ if(c == 0x08) /* ^H: erase character */
+ return 1;
+ q = w->q0;
+ stop = 0;
+ if(q > w->qh)
+ stop = w->qh;
+ skipping = TRUE;
+ while(q > stop){
+ r = w->r[q-1];
+ if(r == '\n'){ /* eat at most one more character */
+ if(q == w->q0) /* eat the newline */
+ --q;
+ break;
+ }
+ if(c == 0x17){
+ eq = isalnum(r);
+ if(eq && skipping) /* found one; stop skipping */
+ skipping = FALSE;
+ else if(!eq && !skipping)
+ break;
+ }
+ --q;
+ }
+ return w->q0-q;
+}
+
+void
+wsnarf(Window *w)
+{
+ if(w->q1 == w->q0)
+ return;
+ nsnarf = w->q1-w->q0;
+ snarf = runerealloc(snarf, nsnarf);
+ snarfversion++; /* maybe modified by parent */
+ runemove(snarf, w->r+w->q0, nsnarf);
+//XXX putsnarf();
+}
+
+void
+wcut(Window *w)
+{
+ if(w->q1 == w->q0)
+ return;
+ wdelete(w, w->q0, w->q1);
+ wsetselect(w, w->q0, w->q0);
+}
+
+void
+wpaste(Window *w)
+{
+ uint q0;
+
+ if(nsnarf == 0)
+ return;
+ wcut(w);
+ q0 = w->q0;
+ if(w->rawing && q0==w->nr){
+ waddraw(w, snarf, nsnarf);
+ wsetselect(w, q0, q0);
+ }else{
+ q0 = winsert(w, snarf, nsnarf, w->q0);
+ wsetselect(w, q0, q0+nsnarf);
+ }
+}
+
+void
+wplumb(Window *w)
+{
+ Plumbmsg *m;
+ static int fd = -2;
+ char buf[32];
+ uint p0, p1;
+ Cursor *c;
+
+ if(fd == -2)
+ fd = plumbopen("send", OWRITE|OCEXEC);
+ if(fd < 0)
+ return;
+ m = emalloc(sizeof(Plumbmsg));
+ m->src = estrdup("rio");
+ m->dst = nil;
+ m->wdir = estrdup(w->dir);
+ m->type = estrdup("text");
+ p0 = w->q0;
+ p1 = w->q1;
+ if(w->q1 > w->q0)
+ m->attr = nil;
+ else{
+ while(p0>0 && w->r[p0-1]!=' ' && w->r[p0-1]!='\t' && w->r[p0-1]!='\n')
+ p0--;
+ while(p1<w->nr && w->r[p1]!=' ' && w->r[p1]!='\t' && w->r[p1]!='\n')
+ p1++;
+ sprint(buf, "click=%d", w->q0-p0);
+ m->attr = plumbunpackattr(buf);
+ }
+ if(p1-p0 > messagesize-1024){
+ plumbfree(m);
+ return; /* too large for 9P */
+ }
+ m->data = runetobyte(w->r+p0, p1-p0, &m->ndata);
+ if(plumbsend(fd, m) < 0){
+ c = lastcursor;
+ riosetcursor(&query, 1);
+ sleep(300);
+ riosetcursor(c, 1);
+ }
+ plumbfree(m);
+}
+
+int
+winborder(Window *w, Point xy)
+{
+ return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->screenr, Selborder));
+}
+
+void
+wmousectl(Window *w)
+{
+ int but;
+
+ if(w->mc.m.buttons == 1)
+ but = 1;
+ else if(w->mc.m.buttons == 2)
+ but = 2;
+ else if(w->mc.m.buttons == 4)
+ but = 3;
+ else{
+ if(w->mc.m.buttons == 8)
+ wkeyctl(w, Kscrolloneup);
+ if(w->mc.m.buttons == 16)
+ wkeyctl(w, Kscrollonedown);
+ return;
+ }
+
+ incref(&w->ref); /* hold up window while we track */
+ if(w->deleted)
+ goto Return;
+ if(ptinrect(w->mc.m.xy, w->scrollr)){
+ if(but)
+ wscroll(w, but);
+ goto Return;
+ }
+ if(but == 1)
+ wselect(w);
+ /* else all is handled by main process */
+ Return:
+ wclose(w);
+}
+
+void
+wdelete(Window *w, uint q0, uint q1)
+{
+ uint n, p0, p1;
+
+ n = q1-q0;
+ if(n == 0)
+ return;
+ runemove(w->r+q0, w->r+q1, w->nr-q1);
+ w->nr -= n;
+ if(q0 < w->q0)
+ w->q0 -= min(n, w->q0-q0);
+ if(q0 < w->q1)
+ w->q1 -= min(n, w->q1-q0);
+ if(q1 < w->qh)
+ w->qh -= n;
+ else if(q0 < w->qh)
+ w->qh = q0;
+ if(q1 <= w->org)
+ w->org -= n;
+ else if(q0 < w->org+w->f.nchars){
+ p1 = q1 - w->org;
+ if(p1 > w->f.nchars)
+ p1 = w->f.nchars;
+ if(q0 < w->org){
+ w->org = q0;
+ p0 = 0;
+ }else
+ p0 = q0 - w->org;
+ frdelete(&w->f, p0, p1);
+ wfill(w);
+ }
+}
+
+
+static Window *clickwin;
+static uint clickmsec;
+static Window *selectwin;
+static uint selectq;
+
+/*
+ * called from frame library
+ */
+void
+framescroll(Frame *f, int dl)
+{
+ if(f != &selectwin->f)
+ error("frameselect not right frame");
+ wframescroll(selectwin, dl);
+}
+
+void
+wframescroll(Window *w, int dl)
+{
+ uint q0;
+
+ if(dl == 0){
+ wscrsleep(w, 100);
+ return;
+ }
+ if(dl < 0){
+ q0 = wbacknl(w, w->org, -dl);
+ if(selectq > w->org+w->f.p0)
+ wsetselect(w, w->org+w->f.p0, selectq);
+ else
+ wsetselect(w, selectq, w->org+w->f.p0);
+ }else{
+ if(w->org+w->f.nchars == w->nr)
+ return;
+ q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.min.y+dl*w->f.font->height));
+ if(selectq >= w->org+w->f.p1)
+ wsetselect(w, w->org+w->f.p1, selectq);
+ else
+ wsetselect(w, selectq, w->org+w->f.p1);
+ }
+ wsetorigin(w, q0, TRUE);
+}
+
+void
+wselect(Window *w)
+{
+ uint q0, q1;
+ int b, x, y, first;
+
+ first = 1;
+ selectwin = w;
+ /*
+ * Double-click immediately if it might make sense.
+ */
+ b = w->mc.m.buttons;
+ q0 = w->q0;
+ q1 = w->q1;
+ selectq = w->org+frcharofpt(&w->f, w->mc.m.xy);
+ if(clickwin==w && w->mc.m.msec-clickmsec<500)
+ if(q0==q1 && selectq==w->q0){
+ wdoubleclick(w, &q0, &q1);
+ wsetselect(w, q0, q1);
+ flushimage(display, 1);
+ x = w->mc.m.xy.x;
+ y = w->mc.m.xy.y;
+ /* stay here until something interesting happens */
+ do
+ readmouse(&w->mc);
+ while(w->mc.m.buttons==b && abs(w->mc.m.xy.x-x)<3 && abs(w->mc.m.xy.y-y)<3);
+ w->mc.m.xy.x = x; /* in case we're calling frselect */
+ w->mc.m.xy.y = y;
+ q0 = w->q0; /* may have changed */
+ q1 = w->q1;
+ selectq = q0;
+ }
+ if(w->mc.m.buttons == b){
+ w->f.scroll = framescroll;
+ frselect(&w->f, &w->mc);
+ /* horrible botch: while asleep, may have lost selection altogether */
+ if(selectq > w->nr)
+ selectq = w->org + w->f.p0;
+ w->f.scroll = nil;
+ if(selectq < w->org)
+ q0 = selectq;
+ else
+ q0 = w->org + w->f.p0;
+ if(selectq > w->org+w->f.nchars)
+ q1 = selectq;
+ else
+ q1 = w->org+w->f.p1;
+ }
+ if(q0 == q1){
+ if(q0==w->q0 && clickwin==w && w->mc.m.msec-clickmsec<500){
+ wdoubleclick(w, &q0, &q1);
+ clickwin = nil;
+ }else{
+ clickwin = w;
+ clickmsec = w->mc.m.msec;
+ }
+ }else
+ clickwin = nil;
+ wsetselect(w, q0, q1);
+ flushimage(display, 1);
+ while(w->mc.m.buttons){
+ w->mc.m.msec = 0;
+ b = w->mc.m.buttons;
+ if(b & 6){
+ if(b & 2){
+ wsnarf(w);
+ wcut(w);
+ }else{
+ if(first){
+ first = 0;
+ getsnarf();
+ }
+ wpaste(w);
+ }
+ }
+ wscrdraw(w);
+ flushimage(display, 1);
+ while(w->mc.m.buttons == b)
+ readmouse(&w->mc);
+ clickwin = nil;
+ }
+}
+
+void
+wsendctlmesg(Window *w, int type, Rectangle r, Image *image)
+{
+ Wctlmesg wcm;
+
+ wcm.type = type;
+ wcm.r = r;
+ wcm.image = image;
+ send(w->cctl, &wcm);
+}
+
+int
+wctlmesg(Window *w, int m, Rectangle r, Image *i)
+{
+ char buf[64];
+
+ switch(m){
+ default:
+ error("unknown control message");
+ break;
+ case Wakeup:
+ break;
+ case Moved:
+ case Reshaped:
+ if(w->deleted){
+ freeimage(i);
+ break;
+ }
+ w->screenr = r;
+ strcpy(buf, w->name);
+ wresize(w, i, m==Moved);
+ w->wctlready = 1;
+ if(Dx(r) > 0){
+ if(w != input)
+ wcurrent(w);
+ }else if(w == input)
+ wcurrent(nil);
+ flushimage(display, 1);
+ break;
+ case Refresh:
+ if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r))
+ break;
+ if(!w->mouseopen)
+ wrefresh(w, r);
+ flushimage(display, 1);
+ break;
+ case Movemouse:
+ if(sweeping || !ptinrect(r.min, w->i->r))
+ break;
+ wmovemouse(w, r.min);
+ case Rawon:
+ break;
+ case Rawoff:
+ if(w->deleted)
+ break;
+ while(w->nraw > 0){
+ wkeyctl(w, w->raw[0]);
+ --w->nraw;
+ runemove(w->raw, w->raw+1, w->nraw);
+ }
+ break;
+ case Holdon:
+ case Holdoff:
+ if(w->deleted)
+ break;
+ wrepaint(w);
+ flushimage(display, 1);
+ break;
+ case Deleted:
+ if(w->deleted)
+ break;
+ write(w->notefd, "hangup", 6);
+ wclosewin(w);
+ break;
+ case Exited:
+ frclear(&w->f, TRUE);
+ close(w->notefd);
+ chanfree(w->mc.c);
+ chanfree(w->ck);
+ chanfree(w->cctl);
+ chanfree(w->conswrite);
+ chanfree(w->consread);
+ chanfree(w->mouseread);
+ chanfree(w->wctlread);
+ free(w->raw);
+ free(w->r);
+ free(w->dir);
+ free(w->label);
+ free(w);
+ break;
+ }
+ return m;
+}
+
+/*
+ * Convert back to physical coordinates
+ */
+void
+wmovemouse(Window *w, Point p)
+{
+ p.x += w->screenr.min.x-w->i->r.min.x;
+ p.y += w->screenr.min.y-w->i->r.min.y;
+ moveto(mousectl, p);
+}
+
+void
+wcurrent(Window *w)
+{
+ Window *oi;
+
+ if(wkeyboard!=nil && w==wkeyboard)
+ return;
+ oi = input;
+ input = w;
+ if(oi!=w && oi!=nil)
+ wrepaint(oi);
+ if(w !=nil){
+ wrepaint(w);
+ wsetcursor(w, 0);
+ }
+ if(w != oi){
+ if(oi){
+ oi->wctlready = 1;
+ wsendctlmesg(oi, Wakeup, ZR, nil);
+ }
+ if(w){
+ w->wctlready = 1;
+ wsendctlmesg(w, Wakeup, ZR, nil);
+ }
+ }
+}
+
+void
+wsetcursor(Window *w, int force)
+{
+ Cursor *p;
+
+ if(w==nil || /*w!=input || */ w->i==nil || Dx(w->screenr)<=0)
+ p = nil;
+ else if(wpointto(mouse->xy) == w){
+ p = w->cursorp;
+ if(p==nil && w->holding)
+ p = &whitearrow;
+ }else
+ p = nil;
+ if(!menuing)
+ riosetcursor(p, force && !menuing);
+}
+
+void
+riosetcursor(Cursor *p, int force)
+{
+ if(!force && p==lastcursor)
+ return;
+ setcursor(mousectl, p);
+ lastcursor = p;
+}
+
+Window*
+wtop(Point pt)
+{
+ Window *w;
+
+ w = wpointto(pt);
+ if(w){
+ if(w->topped == topped)
+ return nil;
+ topwindow(w->i);
+ wcurrent(w);
+ flushimage(display, 1);
+ w->topped = ++topped;
+ }
+ return w;
+}
+
+void
+wtopme(Window *w)
+{
+ if(w!=nil && w->i!=nil && !w->deleted && w->topped!=topped){
+ topwindow(w->i);
+ flushimage(display, 1);
+ w->topped = ++ topped;
+ }
+}
+
+void
+wbottomme(Window *w)
+{
+ if(w!=nil && w->i!=nil && !w->deleted){
+ bottomwindow(w->i);
+ flushimage(display, 1);
+ w->topped = 0;
+ }
+}
+
+Window*
+wlookid(int id)
+{
+ int i;
+
+ for(i=0; i<nwindow; i++)
+ if(window[i]->id == id)
+ return window[i];
+ return nil;
+}
+
+void
+wclosewin(Window *w)
+{
+ Rectangle r;
+ int i;
+
+ w->deleted = TRUE;
+ if(w == input){
+ input = nil;
+ wsetcursor(w, 0);
+ }
+ if(w == wkeyboard)
+ wkeyboard = nil;
+ for(i=0; i<nhidden; i++)
+ if(hidden[i] == w){
+ --nhidden;
+ memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof(hidden[0]));
+ break;
+ }
+ for(i=0; i<nwindow; i++)
+ if(window[i] == w){
+ --nwindow;
+ memmove(window+i, window+i+1, (nwindow-i)*sizeof(Window*));
+ w->deleted = TRUE;
+ r = w->i->r;
+ /* move it off-screen to hide it, in case client is slow in letting it go */
+ MOVEIT originwindow(w->i, r.min, view->r.max);
+ freeimage(w->i);
+ w->i = nil;
+ return;
+ }
+ error("unknown window in closewin");
+}
+
+void
+wsetpid(Window *w, int pid, int dolabel)
+{
+ char buf[128];
+ int fd;
+
+ w->pid = pid;
+ if(dolabel){
+ sprint(buf, "rc %d", pid);
+ free(w->label);
+ w->label = estrdup(buf);
+ }
+ sprint(buf, "/proc/%d/notepg", pid);
+ fd = open(buf, OWRITE|OCEXEC);
+ if(w->notefd > 0)
+ close(w->notefd);
+ w->notefd = fd;
+}
+
+static Rune left1[] = { '{', '[', '(', '<', 0xAB, 0 };
+static Rune right1[] = { '}', ']', ')', '>', 0xBB, 0 };
+static Rune left2[] = { '\n', 0 };
+static Rune left3[] = { '\'', '"', '`', 0 };
+
+Rune *left[] = {
+ left1,
+ left2,
+ left3,
+ nil
+};
+Rune *right[] = {
+ right1,
+ left2,
+ left3,
+ nil
+};
+
+void
+wdoubleclick(Window *w, uint *q0, uint *q1)
+{
+ int c, i;
+ Rune *r, *l, *p;
+ uint q;
+
+ for(i=0; left[i]!=nil; i++){
+ q = *q0;
+ l = left[i];
+ r = right[i];
+ /* try matching character to left, looking right */
+ if(q == 0)
+ c = '\n';
+ else
+ c = w->r[q-1];
+ p = strrune(l, c);
+ if(p != nil){
+ if(wclickmatch(w, c, r[p-l], 1, &q))
+ *q1 = q-(c!='\n');
+ return;
+ }
+ /* try matching character to right, looking left */
+ if(q == w->nr)
+ c = '\n';
+ else
+ c = w->r[q];
+ p = strrune(r, c);
+ if(p != nil){
+ if(wclickmatch(w, c, l[p-r], -1, &q)){
+ *q1 = *q0+(*q0<w->nr && c=='\n');
+ *q0 = q;
+ if(c!='\n' || q!=0 || w->r[0]=='\n')
+ (*q0)++;
+ }
+ return;
+ }
+ }
+ /* try filling out word to right */
+ while(*q1<w->nr && isalnum(w->r[*q1]))
+ (*q1)++;
+ /* try filling out word to left */
+ while(*q0>0 && isalnum(w->r[*q0-1]))
+ (*q0)--;
+}
+
+int
+wclickmatch(Window *w, int cl, int cr, int dir, uint *q)
+{
+ Rune c;
+ int nest;
+
+ nest = 1;
+ for(;;){
+ if(dir > 0){
+ if(*q == w->nr)
+ break;
+ c = w->r[*q];
+ (*q)++;
+ }else{
+ if(*q == 0)
+ break;
+ (*q)--;
+ c = w->r[*q];
+ }
+ if(c == cr){
+ if(--nest==0)
+ return 1;
+ }else if(c == cl)
+ nest++;
+ }
+ return cl=='\n' && nest==1;
+}
+
+
+uint
+wbacknl(Window *w, uint p, uint n)
+{
+ int i, j;
+
+ /* look for start of this line if n==0 */
+ if(n==0 && p>0 && w->r[p-1]!='\n')
+ n = 1;
+ i = n;
+ while(i-->0 && p>0){
+ --p; /* it's at a newline now; back over it */
+ if(p == 0)
+ break;
+ /* at 128 chars, call it a line anyway */
+ for(j=128; --j>0 && p>0; p--)
+ if(w->r[p-1]=='\n')
+ break;
+ }
+ return p;
+}
+
+void
+wshow(Window *w, uint q0)
+{
+ int qe;
+ int nl;
+ uint q;
+
+ qe = w->org+w->f.nchars;
+ if(w->org<=q0 && (q0<qe || (q0==qe && qe==w->nr)))
+ wscrdraw(w);
+ else{
+ nl = 4*w->f.maxlines/5;
+ q = wbacknl(w, q0, nl);
+ /* avoid going backwards if trying to go forwards - long lines! */
+ if(!(q0>w->org && q<w->org))
+ wsetorigin(w, q, TRUE);
+ while(q0 > w->org+w->f.nchars)
+ wsetorigin(w, w->org+1, FALSE);
+ }
+}
+
+void
+wsetorigin(Window *w, uint org, int exact)
+{
+ int i, a, fixup;
+ Rune *r;
+ uint n;
+
+ if(org>0 && !exact){
+ /* org is an estimate of the char posn; find a newline */
+ /* don't try harder than 256 chars */
+ for(i=0; i<256 && org<w->nr; i++){
+ if(w->r[org] == '\n'){
+ org++;
+ break;
+ }
+ org++;
+ }
+ }
+ a = org-w->org;
+ fixup = 0;
+ if(a>=0 && a<w->f.nchars){
+ frdelete(&w->f, 0, a);
+ fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
+ }else if(a<0 && -a<w->f.nchars){
+ n = w->org - org;
+ r = runemalloc(n);
+ runemove(r, w->r+org, n);
+ frinsert(&w->f, r, r+n, 0);
+ free(r);
+ }else
+ frdelete(&w->f, 0, w->f.nchars);
+ w->org = org;
+ wfill(w);
+ wscrdraw(w);
+ wsetselect(w, w->q0, w->q1);
+ if(fixup && w->f.p1 > w->f.p0)
+ frdrawsel(&w->f, frptofchar(&w->f, w->f.p1-1), w->f.p1-1, w->f.p1, 1);
+}
+
+void
+wsetselect(Window *w, uint q0, uint q1)
+{
+ int p0, p1;
+
+ /* w->f.p0 and w->f.p1 are always right; w->q0 and w->q1 may be off */
+ w->q0 = q0;
+ w->q1 = q1;
+ /* compute desired p0,p1 from q0,q1 */
+ p0 = q0-w->org;
+ p1 = q1-w->org;
+ if(p0 < 0)
+ p0 = 0;
+ if(p1 < 0)
+ p1 = 0;
+ if(p0 > w->f.nchars)
+ p0 = w->f.nchars;
+ if(p1 > w->f.nchars)
+ p1 = w->f.nchars;
+ if(p0==w->f.p0 && p1==w->f.p1)
+ return;
+ /* screen disagrees with desired selection */
+ if(w->f.p1<=p0 || p1<=w->f.p0 || p0==p1 || w->f.p1==w->f.p0){
+ /* no overlap or too easy to bother trying */
+ frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 0);
+ frdrawsel(&w->f, frptofchar(&w->f, p0), p0, p1, 1);
+ goto Return;
+ }
+ /* overlap; avoid unnecessary painting */
+ if(p0 < w->f.p0){
+ /* extend selection backwards */
+ frdrawsel(&w->f, frptofchar(&w->f, p0), p0, w->f.p0, 1);
+ }else if(p0 > w->f.p0){
+ /* trim first part of selection */
+ frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, p0, 0);
+ }
+ if(p1 > w->f.p1){
+ /* extend selection forwards */
+ frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, p1, 1);
+ }else if(p1 < w->f.p1){
+ /* trim last part of selection */
+ frdrawsel(&w->f, frptofchar(&w->f, p1), p1, w->f.p1, 0);
+ }
+
+ Return:
+ w->f.p0 = p0;
+ w->f.p1 = p1;
+}
+
+uint
+winsert(Window *w, Rune *r, int n, uint q0)
+{
+ uint m;
+
+ if(n == 0)
+ return q0;
+ if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){
+ m = min(HiWater-LoWater, min(w->org, w->qh));
+ w->org -= m;
+ w->qh -= m;
+ if(w->q0 > m)
+ w->q0 -= m;
+ else
+ w->q0 = 0;
+ if(w->q1 > m)
+ w->q1 -= m;
+ else
+ w->q1 = 0;
+ w->nr -= m;
+ runemove(w->r, w->r+m, w->nr);
+ q0 -= m;
+ }
+ if(w->nr+n > w->maxr){
+ /*
+ * Minimize realloc breakage:
+ * Allocate at least MinWater
+ * Double allocation size each time
+ * But don't go much above HiWater
+ */
+ m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater;
+ if(m > HiWater)
+ m = max(HiWater+MinWater, w->nr+n);
+ if(m > w->maxr){
+ w->r = runerealloc(w->r, m);
+ w->maxr = m;
+ }
+ }
+ runemove(w->r+q0+n, w->r+q0, w->nr-q0);
+ runemove(w->r+q0, r, n);
+ w->nr += n;
+ /* if output touches, advance selection, not qh; works best for keyboard and output */
+ if(q0 <= w->q1)
+ w->q1 += n;
+ if(q0 <= w->q0)
+ w->q0 += n;
+ if(q0 < w->qh)
+ w->qh += n;
+ if(q0 < w->org)
+ w->org += n;
+ else if(q0 <= w->org+w->f.nchars)
+ frinsert(&w->f, r, r+n, q0-w->org);
+ return q0;
+}
+
+void
+wfill(Window *w)
+{
+ Rune *rp;
+ int i, n, m, nl;
+
+ if(w->f.lastlinefull)
+ return;
+ rp = malloc(messagesize);
+ do{
+ n = w->nr-(w->org+w->f.nchars);
+ if(n == 0)
+ break;
+ if(n > 2000) /* educated guess at reasonable amount */
+ n = 2000;
+ runemove(rp, w->r+(w->org+w->f.nchars), n);
+ /*
+ * it's expensive to frinsert more than we need, so
+ * count newlines.
+ */
+ nl = w->f.maxlines-w->f.nlines;
+ m = 0;
+ for(i=0; i<n; ){
+ if(rp[i++] == '\n'){
+ m++;
+ if(m >= nl)
+ break;
+ }
+ }
+ frinsert(&w->f, rp, rp+i, w->f.nchars);
+ }while(w->f.lastlinefull == FALSE);
+ free(rp);
+}
+
+char*
+wcontents(Window *w, int *ip)
+{
+ return runetobyte(w->r, w->nr, ip);
+}