22 void servedevtext(void);
23 void listenproc(void*);
24 void textthread(void*);
26 typedef struct Text Text;
27 typedef struct Readbuf Readbuf;
31 HiWater = 640000, /* max size of history */
32 LoWater = 400000, /* min size of history after max'ed */
36 /* various geometric paramters */
39 Scrollwid = 12, /* width of scroll bar */
40 Scrollgap = 4, /* gap right of scroll bar */
56 #define CUT 0x18 /* ctrl-x */
57 #define COPY 0x03 /* crtl-c */
58 #define PASTE 0x16 /* crtl-v */
60 #define READBUFSIZE 8192
67 Frame *f; /* frame ofr terminal */
69 uint nr; /* num of runes in term */
70 uint maxr; /* max num of runes in r */
71 Rune *r; /* runes for term */
72 uint nraw; /* num of runes in raw buffer */
73 Rune *raw; /* raw buffer */
74 uint org; /* first rune on the screen */
75 uint q0; /* start of selection region */
76 uint q1; /* end of selection region */
77 uint qh; /* unix point */
78 int npart; /* partial runes read from console */
80 int nsnarf; /* snarf buffer */
86 short n; /* # bytes in buf */
87 uchar data[READBUFSIZE]; /* data bytes */
98 void runewrite(Rune*, int);
100 void conswrite(char*, int);
101 int bswidth(Rune c, uint start, int eatnl);
103 void paste(Rune*, int, int);
104 void snarfupdate(void);
108 void setorigin(uint org, int exact);
110 uint backnl(uint, uint);
112 uint backnl(uint, uint);
113 void addraw(Rune*, int);
115 void doubleclick(uint *q0, uint *q1);
116 int clickmatch(int cl, int cr, int dir, uint *q);
117 Rune *strrune(Rune *s, Rune c);
119 Rectangle scrpos(Rectangle r, ulong p0, ulong p1, ulong tot);
122 void hostproc(void *arg);
123 void hoststart(void);
124 void plumbstart(void);
125 void plumb(uint, uint);
126 void plumbclick(uint*, uint*);
127 uint insert(Rune*, int, uint, int);
128 void scrolldown(int);
131 #define runemalloc(n) malloc((n)*sizeof(Rune))
132 #define runerealloc(a, n) realloc(a, (n)*sizeof(Rune))
133 #define runemove(a, b, n) memmove(a, b, (n)*sizeof(Rune))
134 Rectangle scrollr; /* scroll bar rectangle */
135 Rectangle lastsr; /* used for scroll bar */
136 int holdon; /* hold mode */
137 int rawon(void); /* raw mode */
138 int cooked; /* force cooked */
139 int scrolling; /* window scrolls */
140 int clickmsec; /* time of last click */
141 uint clickq0; /* point of last click */
143 int sfd; /* slave fd, to get/set terminal mode */
155 int label(Rune*, int);
157 char childwdir[1024];
158 void hangupnote(void*, char*);
187 Cursor whitearrow = {
189 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
190 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC,
191 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
192 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, },
193 {0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C,
194 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C,
195 0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C,
196 0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }
201 {0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe,
202 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8,
203 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0,
204 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, },
205 {0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c,
206 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0,
207 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80,
208 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
214 fprint(2, "usage: 9term [-ars] [-W winsize] [cmd ...]\n");
215 threadexitsall("usage");
219 threadmain(int argc, char *argv[])
226 _wantfocuschanges = 1;
231 case 'a': /* acme mode */
235 font = EARGF(usage());
240 case 'w': /* started from "rio" window manager */
244 winsize = EARGF(usage());
249 putenv("font", font);
251 p = getenv("tabstop");
253 p = getenv("TABSTOP");
254 if(p != 0 && maxtab <= 0)
255 maxtab = strtoul(p, 0, 0);
257 maxtab = 4; /* be like rio */
259 snprint(buf, sizeof buf, "%d", maxtab);
260 putenv("tabstop", buf);
262 initdraw(0, nil, "9term");
264 noteenable("sys: child");
267 mc = initmouse(nil, screen);
268 kc = initkeyboard(nil);
269 rcpid = rcstart(argc, argv, &rcfd, &sfd);
273 t.f = mallocz(sizeof(Frame), 1);
276 cols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
277 cols[HIGH] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DDarkyellow);
278 cols[BORD] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DYellowgreen);
280 cols[BACK] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DWhite);
281 cols[HIGH] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
282 cols[BORD] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x999999FF);
284 cols[TEXT] = display->black;
285 cols[HTEXT] = display->black;
286 palegrey = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x666666FF);
288 hcols[BACK] = cols[BACK];
289 hcols[HIGH] = cols[HIGH];
290 blue = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DMedblue);
291 paleblue = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DGreyblue);
294 hcols[TEXT] = hcols[BORD];
295 hcols[HTEXT] = hcols[TEXT];
297 plumbcolor = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x006600FF);
298 execcolor = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xAA0000FF);
300 if(!blue || !palegrey || !paleblue || !plumbcolor || !execcolor)
301 sysfatal("alloc colors: %r");
302 draw(screen, screen->r, cols[BACK], nil, ZP);
310 return r=='_' || ('0' <= r && r <= '9')
311 || ('a' <= r && r <= 'z')
312 || ('A' <= r && r <= 'Z');
316 hangupnote(void *a, char *msg)
318 if(getpid() != mainpid)
320 if(strcmp(msg, "hangup") == 0 && rcpid != 0){
321 postnote(PNGROUP, rcpid, "hangup");
324 if(strstr(msg, "child")){
328 n = awaitnohang(buf, sizeof buf-1);
331 if(atoi(buf) == rcpid)
349 /* Let typing have a go -- maybe there's a rubout waiting. */
352 i = 1-i; /* toggle */
353 n = read(rcfd, rcbuf[i].data, sizeof rcbuf[i].data);
356 fprint(2, "9term: host read error: %r\n");
357 threadexitsall("host");
368 hostc = chancreate(sizeof(int), 0);
369 proccreate(hostproc, hostc, 32*1024);
391 a[3].c = mc->resizec;
403 flushimage(display, 1);
405 if(!scrolling && t.qh > t.org+t.f->nchars)
409 sysfatal("impossible");
418 conswrite((char*)rcbuf[i].data, rcbuf[i].n);
430 if(getwindow(display, Refnone) < 0)
431 sysfatal("can't reattach to window");
432 draw(screen, screen->r, cols[BACK], nil, ZP);
445 cols[TEXT] = cols[HTEXT] = display->black;
446 hcols[TEXT] = hcols[HTEXT] = blue;
448 cols[TEXT] = cols[HTEXT] = palegrey;
449 hcols[TEXT] = hcols[HTEXT] = paleblue;
458 scrollr.max.x = r.min.x+Scrollwid;
459 lastsr = Rect(0,0,0,0);
461 r.min.x += Scrollwid+Scrollgap;
464 frinit(t.f, r, font, screen, holdon ? hcols : cols);
465 t.f->maxtab = maxtab*stringwidth(font, "0");
469 p = stringsize(font, "0");
470 if(p.x == 0 || p.y == 0)
473 updatewinsize(Dy(r)/p.y, Dx(r)/p.x, Dx(r), Dy(r));
480 setcursor(mc, &whitearrow);
484 draw(screen, screen->r, cols[BACK], nil, ZP);
490 wordclick(uint *q0, uint *q1)
492 while(*q1<t.nr && !isspace(t.r[*q1]))
494 while(*q0>0 && !isspace(t.r[*q0-1]))
499 aselect(uint *q0, uint *q1, Image *color)
502 uint oldq0, oldq1, newq0, newq1;
504 /* save old selection */
508 /* sweep out area and record it */
509 t.f->cols[HIGH] = color;
510 t.f->cols[HTEXT] = display->white;
516 if(t.m.buttons != 0){
524 /* restore old selection */
525 t.f->cols[HIGH] = cols[HIGH];
526 t.f->cols[HTEXT] = cols[HTEXT];
534 /* selected a region */
541 /* clicked inside previous selection */
542 /* the "<=" in newq0 <= oldq1 allows us to click the right edge */
543 if(oldq0 <= newq0 && newq0 <= oldq1){
555 static Rune Lnl[1] = { '\n' };
565 if(but != 1 && but != 2 && but != 4 && but != 8 && but != 16)
568 if (ptinrect(t.m.xy, scrollr)) {
570 if(t.qh<=t.org+t.f->nchars)
581 if(aselect(&q0, &q1, execcolor) >= 0){
588 paste(t.r+q0, q1-q0, 1);
589 if(t.r[q1-1] != '\n')
600 if(aselect(&q0, &q1, plumbcolor) >= 0)
605 scrollup(mousescrollsize(t.f->maxlines));
608 scrolldown(mousescrollsize(t.f->maxlines));
620 q0 = frcharofpt(t.f, t.m.xy) + t.org;
621 if(t.m.msec-clickmsec<500 && clickq0==q0 && t.q0==t.q1 && b==1){
622 doubleclick(&t.q0, &t.q1);
624 /* t.t.i->flush(); */
627 /* stay here until something interesting happens */
631 } while(t.m.buttons==b && abs(t.m.xy.x-x)<4 && abs(t.m.xy.y-y)<4);
632 t.m.xy.x = x; /* in case we're calling frselect */
637 if(t.m.buttons == b) {
640 t.q0 = t.f->p0 + t.org;
641 t.q1 = t.f->p1 + t.org;
642 clickmsec = t.m.msec;
645 if((t.m.buttons != b) &&(b&1)){
646 enum{Cancut = 1, Canpaste = 2} state = Cancut | Canpaste;
654 }else if(t.m.buttons&4){
658 paste(t.snarf, t.nsnarf, 0);
669 Rune newline[] = { '\n', 0 };
675 menu2str[Scroll] = "+ scroll";
677 menu2str[Scroll] = "- scroll";
679 menu2str[Cooked] = "+ mustecho";
681 menu2str[Cooked] = "- mustecho";
683 switch(menuhit(but, mc, &menu2, nil)){
694 paste(t.snarf, t.nsnarf, 0);
710 paste(t.snarf, t.nsnarf, 1);
711 if(t.nsnarf == 0 || t.snarf[t.nsnarf-1] != '\n')
712 paste(newline, 1, 1);
717 scrolling = !scrolling;
730 sysfatal("bad menu item");
735 windfilewidth(uint q0, int oneelement)
745 if(oneelement && r=='/')
753 showcandidates(Completion *c)
763 s = "[no matches in ";
767 fmtprint(&f, "%s%d files]\n", s, c->nfile);
769 fmtprint(&f, "%s", s);
770 for(i=0; i<c->nfile; i++){
773 fmtprint(&f, "%s", c->filename[i]);
777 /* place text at beginning of line before host point */
779 while(qline>0 && t.r[qline-1] != '\n')
782 rp = runefmtstrflush(&f);
786 q0 += insert(rp, nr, qline, 0) - qline;
797 Rune *rp, *path, *str;
799 char *s, *dir, *root;
801 /* control-f: filename completion; works back to white space or / */
802 if(t.q0<t.nr && t.r[t.q0]>' ') /* must be at end of word */
804 nstr = windfilewidth(t.q0, TRUE);
805 str = runemalloc(nstr);
806 runemove(str, t.r+(t.q0-nstr), nstr);
807 npath = windfilewidth(t.q0-nstr, FALSE);
808 path = runemalloc(npath);
809 runemove(path, t.r+(t.q0-nstr-npath), npath);
812 /* is path rooted? if not, we need to make it relative to window path */
813 if(npath>0 && path[0]=='/'){
814 dir = malloc(UTFmax*npath+1);
815 sprint(dir, "%.*S", npath, path);
817 if(strcmp(wdir, "") == 0)
821 dir = malloc(strlen(root)+1+UTFmax*npath+1);
822 sprint(dir, "%s/%.*S", root, npath, path);
824 dir = cleanname(dir);
826 s = smprint("%.*S", nstr, str);
827 c = complete(dir, s);
836 rp = runesmprint("%s", c->string);
849 setorigin(backnl(t.org, n), 1);
855 setorigin(line2q(n), 1);
856 if(t.qh<=t.org+t.f->nchars)
870 scrollup(t.f->maxlines*2/3);
873 scrolldown(t.f->maxlines*2/3);
876 scrollup(t.f->maxlines/3);
879 scrolldown(t.f->maxlines/3);
906 * Non-standard extensions.
921 paste(t.snarf, t.nsnarf, 0);
927 if(rawon() && t.q0 == t.nr){
933 if(r == 0x7F){ /* DEL: send interrupt; what a mess */
938 t.qh = t.q0 = t.q1 = t.nr;
940 write(rcfd, "\x7F", 1);
944 if(r==ESC){ /* toggle hold */
947 /* replaceintegerproperty("_9WM_HOLD_MODE", 1, 32, holdon); */
956 case 0x06: /* ^F: file name completion */
957 case Kins: /* Insert: file name completion */
965 case 0x08: /* ^H: erase character */
966 case 0x15: /* ^U: erase line */
967 case 0x17: /* ^W: erase word */
968 if (t.q0 != 0 && t.q0 != t.qh)
969 t.q0 -= bswidth(r, t.q0, 1);
981 bswidth(Rune c, uint start, int eatnl)
987 /* there is known to be at least one character to erase */
988 if(c == 0x08) /* ^H: erase character */
997 if(r == '\n'){ /* eat at most one more character */
998 if(q == start && eatnl) /* eat the newline */
1004 if(eq && skipping) /* found one; stop skipping */
1006 else if(!eq && !skipping)
1025 /* look to see if there is a complete line */
1026 for(i=t.qh; i<t.nr; i++){
1028 if(c=='\n' || c=='\004' || c=='\x7F')
1049 while(n >= UTFmax && (t.qh<t.nr || t.nraw > 0)) {
1051 width = runetochar(p, &t.raw[0]);
1053 runemove(t.raw, t.raw+1, t.nraw);
1055 width = runetochar(p, &t.r[t.qh++]);
1059 if(!raw && (c == '\n' || c == '\004' || c == '\x7F'))
1065 * If we've been echoing, make sure the terminal isn't
1066 * while we do the write. This screws up if someone
1067 * else tries to turn off echo at the same time we do
1068 * (we'll turn it on again after the write), but that's not
1071 s = setecho(sfd, 0);
1072 if(write(rcfd, buf, n) < 0)
1080 conswrite(char *p, int n)
1083 Rune buf2[1000], *q;
1085 /* convert to runes */
1088 /* handle partial runes */
1089 while(i < UTFmax && n>0) {
1094 if(fullrune(t.part, i)) {
1096 chartorune(buf2, t.part);
1101 /* there is a little extra room in a message buf */
1104 while(n >= UTFmax || fullrune(p, n)) {
1109 if(n < UTFmax && !fullrune(p, n))
1111 i = chartorune(q, p);
1117 runewrite(buf2, q-buf2);
1121 assert(n+t.npart < UTFmax);
1122 memcpy(t.part+t.npart, p, n);
1131 runewrite(Rune *r, int n)
1143 /* get rid of backspaces */
1146 for(i=0; i<n; i++) {
1152 } else if(*p == '\r') { /* treat like ^U */
1153 /* convert CR without NL into erased line */
1154 /* i feel really sleazy about this but it helps */
1155 while(i<n-1 && *(p+1) == '\r'){
1159 if(i<n-1 && *(p+1) != '\n'){
1160 while(q > r && *(q-1) != '\n')
1163 initial = bswidth(0x15, t.qh, 0);
1172 /* write turned into a delete */
1179 runemove(t.r+q0, t.r+q1, t.nr-q1);
1192 else if(q0 < t.org+t.f->nchars){
1200 if(p1 > t.f->nchars)
1202 frdelete(t.f, p0, p1);
1208 insert(r, n, t.qh, 1);
1221 if (q0 < t.org && q1 >= t.org)
1227 runemove(t.r+q0, t.r+q1, t.nr-q1);
1236 else if(q0 < t.org+t.f->nchars){
1237 assert(q0 >= t.org);
1240 if(p1 > t.f->nchars)
1242 frdelete(t.f, p0, p1);
1263 t.snarf = runerealloc(t.snarf, n);
1264 for(i=0,p=t.snarf; i<n; p++)
1265 i += chartorune(p, pp+i);
1266 t.nsnarf = p-t.snarf;
1270 char sbuf[SnarfSize];
1281 t.snarf = runerealloc(t.snarf, n);
1282 for(i=0,p=sbuf,rp=t.snarf; i<n && p < sbuf+SnarfSize-UTFmax; i++){
1283 *rp++ = *(t.r+t.q0+i);
1284 p += runetochar(p, t.r+t.q0+i);
1286 t.nsnarf = rp-t.snarf;
1308 insert(Rune *r, int n, uint q0, int hostwrite)
1314 if(t.nr+n>HiWater && q0>=t.org && q0>=t.qh){
1315 m = min(HiWater-LoWater, min(t.org, t.qh));
1327 runemove(t.r, t.r+m, t.nr);
1330 if(t.nr+n > t.maxr){
1332 * Minimize realloc breakage:
1333 * Allocate at least MinWater
1334 * Double allocation size each time
1335 * But don't go much above HiWater
1337 m = max(min(2*(t.nr+n), HiWater), t.nr+n)+MinWater;
1339 m = max(HiWater+MinWater, t.nr+n);
1341 t.r = runerealloc(t.r, m);
1345 runemove(t.r+q0+n, t.r+q0, t.nr-q0);
1346 runemove(t.r+q0, r, n);
1348 /* if output touches, advance selection, not qh; works best for keyboard and output */
1353 if(q0 < t.qh || (q0==t.qh && hostwrite))
1359 else if(q0 <= t.org+t.f->nchars)
1360 frinsert(t.f, r, r+n, q0-t.org);
1365 paste(Rune *r, int n, int advance)
1369 if(rawon() && t.q0==t.nr){
1380 * if this is a button2 execute then we might have been passed
1381 * runes inside the buffer. must save them before realloc.
1384 if(t.r <= r && r < t.r+n){
1385 rbuf = runemalloc(n);
1386 runemove(rbuf, r, n);
1390 insert(r, n, t.q0, 0);
1398 if (t.f->nlines >= t.f->maxlines)
1400 frinsert(t.f, t.r + t.org + t.f->nchars, t.r + t.nr, t.f->nchars);
1410 if(t.org+f->p0 == t.q0 && t.org+f->p1 == t.q1)
1415 frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0);
1428 frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1);
1431 if(t.qh<=t.org+t.f.nchars && t.cwqueue != 0)
1432 t.cwqueue->wakeup <-= 0;
1448 nl = t.f->maxlines/5;
1450 nl = 4*t.f->maxlines/5;
1452 /* avoid going in the wrong direction */
1453 if (q0>t.org && q<t.org)
1456 /* keep trying until q0 is on the screen */
1457 while(!cansee(q0)) {
1458 assert(q0 >= t.org);
1460 q = line2q(t.f->maxlines-nl);
1471 qe = t.org+t.f->nchars;
1473 if(q0>=t.org && q0 < qe)
1477 if (t.f->nlines < t.f->maxlines)
1479 if (q0 > 0 && t.r[t.nr-1] == '\n')
1486 setorigin(uint org, int exact)
1491 if(org>0 && !exact){
1492 /* try and start after a newline */
1493 /* don't try harder than 256 chars */
1494 for(i=0; i<256 && org<t.nr; i++){
1495 if(t.r[org-1] == '\n')
1502 if(a>=0 && a<t.f->nchars)
1503 frdelete(t.f, 0, a);
1504 else if(a<0 && -a<100*t.f->maxlines){
1506 frinsert(t.f, t.r+org, t.r+org+n, 0);
1508 frdelete(t.f, 0, t.f->nchars);
1521 return frcharofpt(f, Pt(f->r.min.x, f->r.min.y + n*font->height))+t.org;
1525 backnl(uint p, uint n)
1530 /* at 256 chars, call it a line anyway */
1531 for(j=256; --j>0 && p>0; p--)
1534 if (p == 0 || i == 0)
1541 addraw(Rune *r, int nr)
1543 t.raw = runerealloc(t.raw, t.nraw+nr);
1544 runemove(t.raw+t.nraw, r, nr);
1547 if(t.crqueue != nil)
1548 t.crqueue->wakeup <-= 0;
1553 Rune left1[] = { '{', '[', '(', '<', 0xab, 0 };
1554 Rune right1[] = { '}', ']', ')', '>', 0xbb, 0 };
1555 Rune left2[] = { '\n', 0 };
1556 Rune left3[] = { '\'', '"', '`', 0 };
1573 doubleclick(uint *q0, uint *q1)
1579 for(i=0; left[i]!=0; i++){
1583 /* try matching character to left, looking right */
1590 if(clickmatch(c, r[p-l], 1, &q))
1594 /* try matching character to right, looking left */
1601 if(clickmatch(c, l[p-r], -1, &q)){
1602 *q1 = *q0+(*q0<t.nr && c=='\n');
1604 if(c!='\n' || q!=0 || t.r[0]=='\n')
1610 /* try filling out word to right */
1611 while(*q1<t.nr && isexpand(t.r[*q1]))
1613 /* try filling out word to left */
1614 while(*q0>0 && isexpand(t.r[*q0-1]))
1619 clickmatch(int cl, int cr, int dir, uint *q)
1643 return cl=='\n' && nest==1;
1653 assert(t.q0 <= t.q1 && t.q1 <= t.nr);
1654 assert(t.org <= t.nr && t.qh <= t.nr);
1655 assert(f->p0 <= f->p1 && f->p1 <= f->nchars);
1656 assert(t.org + f->nchars <= t.nr);
1657 assert(t.org+f->nchars==t.nr || (f->nlines >= f->maxlines));
1661 strrune(Rune *s, Rune c)
1680 Rectangle r, r1, r2;
1684 r.min.x += 1; /* border between margin and bar */
1686 if(scrx==0 || scrx->r.max.y < r.max.y){
1689 scrx = allocimage(display, Rect(0, 0, 32, r.max.y), screen->chan, 1, DPaleyellow);
1691 sysfatal("scroll balloc");
1695 r2 = scrpos(r1, t.org, t.org+t.f->nchars, t.nr);
1696 if(!eqrect(r2, lastsr)){
1698 draw(scrx, r1, cols[BORD], nil, ZP);
1699 draw(scrx, r2, cols[BACK], nil, r2.min);
1701 // r2.min.x = r2.max.x-1;
1702 // draw(scrx, r2, cols[BORD], nil, ZP);
1703 draw(screen, r, scrx, nil, r1.min);
1708 scrpos(Rectangle r, ulong p0, ulong p1, ulong tot)
1713 q = insetrect(r, 1);
1714 h = q.max.y-q.min.y;
1717 if(tot > 1024L*1024L)
1718 tot >>= 10, p0 >>= 10, p1 >>= 10;
1720 q.min.y += h*p0/tot;
1722 q.max.y -= h*(tot-p1)/tot;
1723 if(q.max.y < q.min.y+2){
1724 if(q.min.y+2 <= r.max.y)
1725 q.max.y = q.min.y+2;
1727 q.min.y = q.max.y-2;
1737 int x, y, my, h, first, exact;
1739 s = insetrect(scrollr, 1);
1740 h = s.max.y-s.min.y;
1741 x = (s.min.x+s.max.x)/2;
1745 if(t.m.xy.x<s.min.x || s.max.x<=t.m.xy.x){
1754 // if(!eqpt(t.m.xy, Pt(x, my)))
1755 // cursorset(Pt(x, my));
1761 if(t.nr > 1024*1024)
1762 p0 = ((t.nr>>10)*(y-s.min.y)/h)<<10;
1764 p0 = t.nr*(y-s.min.y)/h;
1767 p0 = backnl(t.org, (my-s.min.y)/font->height);
1769 p0 = t.org+frcharofpt(t.f, Pt(s.max.x, my));
1772 setorigin(p0, exact);
1778 }while(t.m.buttons & (1<<(but-1)));
1784 if((plumbfd = plumbopen("send", OWRITE)) < 0)
1785 fprint(2, "9term: plumbopen: %r\n");
1789 plumb(uint q0, uint q1)
1796 pm = malloc(sizeof(Plumbmsg));
1797 pm->src = strdup("9term");
1799 pm->wdir = strdup(wdir);
1800 pm->type = strdup("text");
1806 wordclick(&q0, &q1);
1807 sprint(cbuf, "click=%d", p0-q0);
1808 pm->attr = plumbunpackattr(cbuf);
1814 pm->data = malloc(SnarfSize);
1816 for(i=0,p=pm->data; i<n && p < pm->data + SnarfSize-UTFmax; i++)
1817 p += runetochar(p, t.r+q0+i);
1819 pm->ndata = strlen(pm->data);
1820 if(plumbsend(plumbfd, pm) < 0){
1821 setcursor(mc, &query);
1824 setcursor(mc, &whitearrow);
1832 * Process in-band messages about window title changes.
1833 * The messages are of the form:
1837 * where xxx is the new directory. This format was chosen
1838 * because it changes the label on xterm windows.
1841 label(Rune *sr, int n)
1843 Rune *sl, *el, *er, *r;
1847 for(r=er-1; r>=sr; r--)
1854 if(el-sr > sizeof wdir)
1855 sr = el - sizeof wdir;
1856 for(sl=el-3; sl>=sr; sl--)
1857 if(sl[0]=='\033' && sl[1]==']' && sl[2]==';')
1862 snprint(wdir, sizeof wdir, "%.*S", (el-1)-(sl+3), sl+3);
1865 /* remove trailing /-sysname if present */
1866 p = strrchr(wdir, '/');
1867 if(p && *(p+1) == '-'){
1873 runemove(sl, el, er-el);
1881 return !cooked && !isecho(sfd);
1885 * Clumsy hack to make " and "" work.
1886 * Then again, what's not a clumsy hack here in Unix land?
1893 removethesocket(void)
1896 if(remove(thesocket) < 0)
1897 fprint(2, "remove %s: %r\n", thesocket);
1905 snprint(buf, sizeof buf, "unix!/tmp/9term-text.%d", getpid());
1907 if((afd = announce(buf, adir)) < 0){
1908 putenv("text9term", "");
1912 putenv("text9term", buf);
1913 proccreate(listenproc, nil, STACK);
1914 strcpy(thesocket, buf+5);
1915 atexit(removethesocket);
1919 listenproc(void *arg)
1926 fd = listen(adir, dir);
1931 proccreate(textthread, (void*)fd, STACK);
1936 textthread(void *arg)
1938 int fd, i, x, n, end;
1940 char buf[4096], *p, *ep;
1944 ep = buf+sizeof buf;
1945 end = t.org+t.nr; /* avoid possible output loop */
1947 if(i >= end || ep-p < UTFmax){
1948 for(x=0; x<p-buf; x+=n)
1949 if((n = write(fd, buf+x, (p-x)-buf)) <= 0)
1962 p += runetochar(p, &r);