plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

wind.c (34335B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <draw.h>
      4 #include <thread.h>
      5 #include <cursor.h>
      6 #include <mouse.h>
      7 #include <keyboard.h>
      8 #include <frame.h>
      9 #include <fcall.h>
     10 #include <9pclient.h>
     11 #include <plumb.h>
     12 #include <complete.h>
     13 #include "dat.h"
     14 #include "fns.h"
     15 
     16 #define MOVEIT if(0)
     17 
     18 enum
     19 {
     20 	HiWater	= 64000000,	/* max size of history */
     21 	LoWater	= 400000,	/* min size of history after max'ed */
     22 	MinWater	= 20000	/* room to leave available when reallocating */
     23 };
     24 
     25 static	int		topped;
     26 static	int		id;
     27 
     28 static	Image	*cols[NCOL];
     29 static	Image	*grey;
     30 static	Image	*darkgrey;
     31 static	Cursor	*lastcursor;
     32 static	Image	*titlecol;
     33 static	Image	*lighttitlecol;
     34 static	Image	*holdcol;
     35 static	Image	*lightholdcol;
     36 static	Image	*paleholdcol;
     37 
     38 static int
     39 wscale(Window *w, int n)
     40 {
     41 	if(w == nil || w->i == nil)
     42 		return n;
     43 	return scalesize(w->i->display, n);
     44 }
     45 
     46 Window*
     47 wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl, int scrolling)
     48 {
     49 	Window *w;
     50 	Rectangle r;
     51 
     52 	if(cols[0] == nil){
     53 		/* greys are multiples of 0x11111100+0xFF, 14* being palest */
     54 		grey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
     55 		darkgrey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x666666FF);
     56 		cols[BACK] = display->white;
     57 		cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
     58 		cols[BORD] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x999999FF);
     59 		cols[TEXT] = display->black;
     60 		cols[HTEXT] = display->black;
     61 		titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreygreen);
     62 		lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreygreen);
     63 		holdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedblue);
     64 		lightholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreyblue);
     65 		paleholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreyblue);
     66 	}
     67 	w = emalloc(sizeof(Window));
     68 	w->screenr = i->r;
     69 	r = insetrect(i->r, wscale(w, Selborder)+wscale(w, 1));
     70 	w->i = i;
     71 	w->mc = *mc;
     72 	w->ck = ck;
     73 	w->cctl = cctl;
     74 	w->cursorp = nil;
     75 	w->conswrite = chancreate(sizeof(Conswritemesg), 0);
     76 	w->consread =  chancreate(sizeof(Consreadmesg), 0);
     77 	w->mouseread =  chancreate(sizeof(Mousereadmesg), 0);
     78 	w->wctlread =  chancreate(sizeof(Consreadmesg), 0);
     79 	w->scrollr = r;
     80 	w->scrollr.max.x = r.min.x+wscale(w, Scrollwid);
     81 	w->lastsr = ZR;
     82 	r.min.x += wscale(w, Scrollwid)+wscale(w, Scrollgap);
     83 	frinit(&w->f, r, font, i, cols);
     84 	w->f.maxtab = maxtab*stringwidth(font, "0");
     85 	w->topped = ++topped;
     86 	w->id = ++id;
     87 	w->notefd = -1;
     88 	w->scrolling = scrolling;
     89 	w->dir = estrdup(startdir);
     90 	w->label = estrdup("<unnamed>");
     91 	r = insetrect(w->i->r, wscale(w, Selborder));
     92 	draw(w->i, r, cols[BACK], nil, w->f.entire.min);
     93 	wborder(w, wscale(w, Selborder));
     94 	wscrdraw(w);
     95 	incref(&w->ref);	/* ref will be removed after mounting; avoids delete before ready to be deleted */
     96 	return w;
     97 }
     98 
     99 void
    100 wsetname(Window *w)
    101 {
    102 	int i, n;
    103 	char err[ERRMAX];
    104 
    105 	n = sprint(w->name, "window.%d.%d", w->id, w->namecount++);
    106 	for(i='A'; i<='Z'; i++){
    107 		if(nameimage(w->i, w->name, 1) > 0)
    108 			return;
    109 		errstr(err, sizeof err);
    110 		if(strcmp(err, "image name in use") != 0)
    111 			break;
    112 		w->name[n] = i;
    113 		w->name[n+1] = 0;
    114 	}
    115 	w->name[0] = 0;
    116 	fprint(2, "rio: setname failed: %s\n", err);
    117 }
    118 
    119 void
    120 wresize(Window *w, Image *i, int move)
    121 {
    122 	Rectangle r, or;
    123 
    124 	or = w->i->r;
    125 	if(move || (Dx(or)==Dx(i->r) && Dy(or)==Dy(i->r)))
    126 		draw(i, i->r, w->i, nil, w->i->r.min);
    127 	if(w->i != i){
    128 fprint(2, "res %p %p\n", w->i, i);
    129 		freeimage(w->i);
    130 		w->i = i;
    131 	}
    132 /*	wsetname(w); */
    133 /*XXX	w->mc.image = i; */
    134 	r = insetrect(i->r, wscale(w, Selborder)+wscale(w, 1));
    135 	w->scrollr = r;
    136 	w->scrollr.max.x = r.min.x+wscale(w, Scrollwid);
    137 	w->lastsr = ZR;
    138 	r.min.x += wscale(w, Scrollwid)+wscale(w, Scrollgap);
    139 	if(move)
    140 		frsetrects(&w->f, r, w->i);
    141 	else{
    142 		frclear(&w->f, FALSE);
    143 		frinit(&w->f, r, w->f.font, w->i, cols);
    144 		wsetcols(w);
    145 		w->f.maxtab = maxtab*stringwidth(w->f.font, "0");
    146 		r = insetrect(w->i->r, wscale(w, Selborder));
    147 		draw(w->i, r, cols[BACK], nil, w->f.entire.min);
    148 		wfill(w);
    149 		wsetselect(w, w->q0, w->q1);
    150 		wscrdraw(w);
    151 	}
    152 	wborder(w, wscale(w, Selborder));
    153 	w->topped = ++topped;
    154 	w->resized = TRUE;
    155 	w->mouse.counter++;
    156 }
    157 
    158 void
    159 wrefresh(Window *w, Rectangle r)
    160 {
    161 	/* USED(r); */
    162 
    163 	/* BUG: rectangle is ignored */
    164 	if(w == input)
    165 		wborder(w, wscale(w, Selborder));
    166 	else
    167 		wborder(w, wscale(w, Unselborder));
    168 	if(w->mouseopen)
    169 		return;
    170 	draw(w->i, insetrect(w->i->r, Borderwidth), w->f.cols[BACK], nil, w->i->r.min);
    171 	w->f.ticked = 0;
    172 	if(w->f.p0 > 0)
    173 		frdrawsel(&w->f, frptofchar(&w->f, 0), 0, w->f.p0, 0);
    174 	if(w->f.p1 < w->f.nchars)
    175 		frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, w->f.nchars, 0);
    176 	frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 1);
    177 	w->lastsr = ZR;
    178 	wscrdraw(w);
    179 }
    180 
    181 int
    182 wclose(Window *w)
    183 {
    184 	int i;
    185 
    186 	i = decref(&w->ref);
    187 	if(i > 0)
    188 		return 0;
    189 	if(i < 0)
    190 		error("negative ref count");
    191 	if(!w->deleted)
    192 		wclosewin(w);
    193 	wsendctlmesg(w, Exited, ZR, nil);
    194 	return 1;
    195 }
    196 
    197 
    198 void
    199 winctl(void *arg)
    200 {
    201 	Rune *rp, *bp, *up, *kbdr;
    202 	uint qh;
    203 	int nr, nb, c, wid, i, npart, initial, lastb, scrolling;
    204 	char *s, *t, part[UTFmax];
    205 	Window *w;
    206 	Mousestate *mp, m;
    207 	enum { WKey, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT };
    208 	Alt alts[NWALT+1];
    209 	Mousereadmesg mrm;
    210 	Conswritemesg cwm;
    211 	Consreadmesg crm;
    212 	Consreadmesg cwrm;
    213 	Stringpair pair;
    214 	Wctlmesg wcm;
    215 	char buf[4*12+1];
    216 
    217 	w = arg;
    218 	snprint(buf, sizeof buf, "winctl-id%d", w->id);
    219 	threadsetname(buf);
    220 
    221 	mrm.cm = chancreate(sizeof(Mouse), 0);
    222 	cwm.cw = chancreate(sizeof(Stringpair), 0);
    223 	crm.c1 = chancreate(sizeof(Stringpair), 0);
    224 	crm.c2 = chancreate(sizeof(Stringpair), 0);
    225 	cwrm.c1 = chancreate(sizeof(Stringpair), 0);
    226 	cwrm.c2 = chancreate(sizeof(Stringpair), 0);
    227 
    228 
    229 	alts[WKey].c = w->ck;
    230 	alts[WKey].v = &kbdr;
    231 	alts[WKey].op = CHANRCV;
    232 	alts[WMouse].c = w->mc.c;
    233 	alts[WMouse].v = &w->mc.m;
    234 	alts[WMouse].op = CHANRCV;
    235 	alts[WMouseread].c = w->mouseread;
    236 	alts[WMouseread].v = &mrm;
    237 	alts[WMouseread].op = CHANSND;
    238 	alts[WCtl].c = w->cctl;
    239 	alts[WCtl].v = &wcm;
    240 	alts[WCtl].op = CHANRCV;
    241 	alts[WCwrite].c = w->conswrite;
    242 	alts[WCwrite].v = &cwm;
    243 	alts[WCwrite].op = CHANSND;
    244 	alts[WCread].c = w->consread;
    245 	alts[WCread].v = &crm;
    246 	alts[WCread].op = CHANSND;
    247 	alts[WWread].c = w->wctlread;
    248 	alts[WWread].v = &cwrm;
    249 	alts[WWread].op = CHANSND;
    250 	alts[NWALT].op = CHANEND;
    251 
    252 	npart = 0;
    253 	lastb = -1;
    254 	for(;;){
    255 		if(w->mouseopen && w->mouse.counter != w->mouse.lastcounter)
    256 			alts[WMouseread].op = CHANSND;
    257 		else
    258 			alts[WMouseread].op = CHANNOP;
    259 		//	if(!w->scrolling && !w->mouseopen && w->qh>w->org+w->f.nchars)
    260 		//		alts[WCwrite].op = CHANNOP;
    261 		//	else
    262 		alts[WCwrite].op = CHANSND;
    263 		if(w->deleted || !w->wctlready)
    264 			alts[WWread].op = CHANNOP;
    265 		else
    266 			alts[WWread].op = CHANSND;
    267 		/* this code depends on NL and EOT fitting in a single byte */
    268 		/* kind of expensive for each loop; worth precomputing? */
    269 		if(w->holding)
    270 			alts[WCread].op = CHANNOP;
    271 		else if(npart || (w->rawing && w->nraw>0))
    272 			alts[WCread].op = CHANSND;
    273 		else{
    274 			alts[WCread].op = CHANNOP;
    275 			for(i=w->qh; i<w->nr; i++){
    276 				c = w->r[i];
    277 				if(c=='\n' || c=='\004'){
    278 					alts[WCread].op = CHANSND;
    279 					break;
    280 				}
    281 			}
    282 		}
    283 		switch(alt(alts)){
    284 		case WKey:
    285 			for(i=0; kbdr[i]!=L'\0'; i++)
    286 				wkeyctl(w, kbdr[i]);
    287 /*			wkeyctl(w, r); */
    288 /*			while(nbrecv(w->ck, &r)) */
    289 /*				wkeyctl(w, r); */
    290 			break;
    291 		case WMouse:
    292 			if(w->mouseopen) {
    293 				w->mouse.counter++;
    294 
    295 				/* queue click events */
    296 				if(!w->mouse.qfull && lastb != w->mc.m.buttons) {	/* add to ring */
    297 					mp = &w->mouse.queue[w->mouse.wi];
    298 					if(++w->mouse.wi == nelem(w->mouse.queue))
    299 						w->mouse.wi = 0;
    300 					if(w->mouse.wi == w->mouse.ri)
    301 						w->mouse.qfull = TRUE;
    302 					mp->m = w->mc.m;
    303 					mp->counter = w->mouse.counter;
    304 					lastb = w->mc.m.buttons;
    305 				}
    306 			} else
    307 				wmousectl(w);
    308 			break;
    309 		case WMouseread:
    310 			/* send a queued event or, if the queue is empty, the current state */
    311 			/* if the queue has filled, we discard all the events it contained. */
    312 			/* the intent is to discard frantic clicking by the user during long latencies. */
    313 			w->mouse.qfull = FALSE;
    314 			if(w->mouse.wi != w->mouse.ri) {
    315 				m = w->mouse.queue[w->mouse.ri];
    316 				if(++w->mouse.ri == nelem(w->mouse.queue))
    317 					w->mouse.ri = 0;
    318 			} else {
    319 				m.m = w->mc.m;
    320 				m.counter = w->mouse.counter;
    321 			}
    322 			w->mouse.lastcounter = m.counter;
    323 			send(mrm.cm, &m.m);
    324 			continue;
    325 		case WCtl:
    326 			if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Exited){
    327 				chanfree(crm.c1);
    328 				chanfree(crm.c2);
    329 				chanfree(mrm.cm);
    330 				chanfree(cwm.cw);
    331 				chanfree(cwrm.c1);
    332 				chanfree(cwrm.c2);
    333 				threadexits(nil);
    334 			}
    335 			continue;
    336 		case WCwrite:
    337 			recv(cwm.cw, &pair);
    338 			rp = pair.s;
    339 			nr = pair.ns;
    340 			bp = rp;
    341 			up = rp;
    342 			initial = 0;
    343 			for(i=0; i<nr; i++){
    344 				switch(*bp){
    345 				case 0:
    346 					break;
    347 				case '\b':
    348 					if(up == rp)
    349 						initial++;
    350 					else
    351 						--up;
    352 					break;
    353 				case '\r':
    354 					while(i<nr-1 && *(bp+1) == '\r'){
    355 						bp++;
    356 						i++;
    357 					}
    358 					if(i<nr-1 && *(bp+1) != '\n'){
    359 						while(up > rp && *(up-1) != '\n')
    360 							up--;
    361 						if(up == rp)
    362 							initial = wbswidth(w, '\r');
    363 					}else if(i == nr-1)
    364 						*up++ = '\n';
    365 					break;
    366 				default:
    367 					*up++ = *bp;
    368 					break;
    369 				}
    370 				bp++;
    371 			}
    372 			if(initial){
    373 				if(initial > w->qh)
    374 					initial = w->qh;
    375 				qh = w->qh - initial;
    376 				wdelete(w, qh, qh+initial);
    377 				w->qh = qh;
    378 			}
    379 			nr = up - rp;
    380 			scrolling = w->scrolling && w->org <= w->qh && w->qh <= w->org + w->f.nchars;
    381 			w->qh = winsert(w, rp, nr, w->qh)+nr;
    382 			if(scrolling)
    383 				wshow(w, w->qh);
    384 			wsetselect(w, w->q0, w->q1);
    385 			wscrdraw(w);
    386 			free(rp);
    387 			break;
    388 		case WCread:
    389 			recv(crm.c1, &pair);
    390 			t = pair.s;
    391 			nb = pair.ns;
    392 			i = npart;
    393 			npart = 0;
    394 			if(i)
    395 				memmove(t, part, i);
    396 			while(i<nb && (w->qh<w->nr || w->nraw>0)){
    397 				if(w->qh == w->nr){
    398 					wid = runetochar(t+i, &w->raw[0]);
    399 					w->nraw--;
    400 					runemove(w->raw, w->raw+1, w->nraw);
    401 				}else
    402 					wid = runetochar(t+i, &w->r[w->qh++]);
    403 				c = t[i];	/* knows break characters fit in a byte */
    404 				i += wid;
    405 				if(!w->rawing && (c == '\n' || c=='\004')){
    406 				/*	if(c == '\004') */
    407 				/*		i--; */
    408 					break;
    409 				}
    410 			}
    411 		/*	if(i==nb && w->qh<w->nr && w->r[w->qh]=='\004') */
    412 		/*		w->qh++; */
    413 			if(i > nb){
    414 				npart = i-nb;
    415 				memmove(part, t+nb, npart);
    416 				i = nb;
    417 			}
    418 			pair.s = t;
    419 			pair.ns = i;
    420 			send(crm.c2, &pair);
    421 			continue;
    422 		case WWread:
    423 			w->wctlready = 0;
    424 			recv(cwrm.c1, &pair);
    425 			if(w->deleted || w->i==nil)
    426 				pair.ns = sprint(pair.s, "");
    427 			else{
    428 				s = "visible";
    429 				for(i=0; i<nhidden; i++)
    430 					if(hidden[i] == w){
    431 						s = "hidden";
    432 						break;
    433 					}
    434 				t = "notcurrent";
    435 				if(w == input)
    436 					t = "current";
    437 				pair.ns = snprint(pair.s, pair.ns, "%11d %11d %11d %11d %s %s ",
    438 					w->i->r.min.x, w->i->r.min.y, w->i->r.max.x, w->i->r.max.y, t, s);
    439 			}
    440 			send(cwrm.c2, &pair);
    441 			continue;
    442 		}
    443 		if(!w->deleted)
    444 			flushimage(display, 1);
    445 	}
    446 }
    447 
    448 void
    449 waddraw(Window *w, Rune *r, int nr)
    450 {
    451 	w->raw = runerealloc(w->raw, w->nraw+nr);
    452 	runemove(w->raw+w->nraw, r, nr);
    453 	w->nraw += nr;
    454 }
    455 
    456 /*
    457  * Need to do this in a separate proc because if process we're interrupting
    458  * is dying and trying to print tombstone, kernel is blocked holding p->debug lock.
    459  */
    460 void
    461 interruptproc(void *v)
    462 {
    463 	int *notefd;
    464 
    465 	notefd = v;
    466 	write(*notefd, "interrupt", 9);
    467 	free(notefd);
    468 }
    469 
    470 int
    471 windfilewidth(Window *w, uint q0, int oneelement)
    472 {
    473 	uint q;
    474 	Rune r;
    475 
    476 	q = q0;
    477 	while(q > 0){
    478 		r = w->r[q-1];
    479 		if(r<=' ')
    480 			break;
    481 		if(oneelement && r=='/')
    482 			break;
    483 		--q;
    484 	}
    485 	return q0-q;
    486 }
    487 
    488 void
    489 showcandidates(Window *w, Completion *c)
    490 {
    491 	int i;
    492 	Fmt f;
    493 	Rune *rp;
    494 	uint nr, qline, q0;
    495 	char *s;
    496 
    497 	runefmtstrinit(&f);
    498 	if (c->nmatch == 0)
    499 		s = "[no matches in ";
    500 	else
    501 		s = "[";
    502 	if(c->nfile > 32)
    503 		fmtprint(&f, "%s%d files]\n", s, c->nfile);
    504 	else{
    505 		fmtprint(&f, "%s", s);
    506 		for(i=0; i<c->nfile; i++){
    507 			if(i > 0)
    508 				fmtprint(&f, " ");
    509 			fmtprint(&f, "%s", c->filename[i]);
    510 		}
    511 		fmtprint(&f, "]\n");
    512 	}
    513 	/* place text at beginning of line before host point */
    514 	qline = w->qh;
    515 	while(qline>0 && w->r[qline-1] != '\n')
    516 		qline--;
    517 
    518 	rp = runefmtstrflush(&f);
    519 	nr = runestrlen(rp);
    520 
    521 	q0 = w->q0;
    522 	q0 += winsert(w, rp, runestrlen(rp), qline) - qline;
    523 	free(rp);
    524 	wsetselect(w, q0+nr, q0+nr);
    525 }
    526 
    527 Rune*
    528 namecomplete(Window *w)
    529 {
    530 	int nstr, npath;
    531 	Rune *rp, *path, *str;
    532 	Completion *c;
    533 	char *s, *dir, *root;
    534 
    535 	/* control-f: filename completion; works back to white space or / */
    536 	if(w->q0<w->nr && w->r[w->q0]>' ')	/* must be at end of word */
    537 		return nil;
    538 	nstr = windfilewidth(w, w->q0, TRUE);
    539 	str = runemalloc(nstr);
    540 	runemove(str, w->r+(w->q0-nstr), nstr);
    541 	npath = windfilewidth(w, w->q0-nstr, FALSE);
    542 	path = runemalloc(npath);
    543 	runemove(path, w->r+(w->q0-nstr-npath), npath);
    544 	rp = nil;
    545 
    546 	/* is path rooted? if not, we need to make it relative to window path */
    547 	if(npath>0 && path[0]=='/'){
    548 		dir = malloc(UTFmax*npath+1);
    549 		sprint(dir, "%.*S", npath, path);
    550 	}else{
    551 		if(strcmp(w->dir, "") == 0)
    552 			root = ".";
    553 		else
    554 			root = w->dir;
    555 		dir = malloc(strlen(root)+1+UTFmax*npath+1);
    556 		sprint(dir, "%s/%.*S", root, npath, path);
    557 	}
    558 	dir = cleanname(dir);
    559 
    560 	s = smprint("%.*S", nstr, str);
    561 	c = complete(dir, s);
    562 	free(s);
    563 	if(c == nil)
    564 		goto Return;
    565 
    566 	if(!c->advance)
    567 		showcandidates(w, c);
    568 
    569 	if(c->advance)
    570 		rp = runesmprint("%s", c->string);
    571 
    572   Return:
    573 	freecompletion(c);
    574 	free(dir);
    575 	free(path);
    576 	free(str);
    577 	return rp;
    578 }
    579 
    580 void
    581 wkeyctl(Window *w, Rune r)
    582 {
    583 	uint q0 ,q1;
    584 	int n, nb, nr;
    585 	Rune *rp;
    586 
    587 	if(r == 0)
    588 		return;
    589 	if(w->deleted)
    590 		return;
    591 	w->rawing = rawon();
    592 	/* navigation keys work only when mouse is not open */
    593 	if(!w->mouseopen)
    594 		switch(r){
    595 		case Kdown:
    596 			n = w->f.maxlines/3;
    597 			goto case_Down;
    598 		case Kscrollonedown:
    599 			n = mousescrollsize(w->f.maxlines);
    600 			if(n <= 0)
    601 				n = 1;
    602 			goto case_Down;
    603 		case Kpgdown:
    604 			n = 2*w->f.maxlines/3;
    605 		case_Down:
    606 			q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.min.y+n*w->f.font->height));
    607 			wsetorigin(w, q0, TRUE);
    608 			return;
    609 		case Kup:
    610 			n = w->f.maxlines/3;
    611 			goto case_Up;
    612 		case Kscrolloneup:
    613 			n = mousescrollsize(w->f.maxlines);
    614 			if(n <= 0)
    615 				n = 1;
    616 			goto case_Up;
    617 		case Kpgup:
    618 			n = 2*w->f.maxlines/3;
    619 		case_Up:
    620 			q0 = wbacknl(w, w->org, n);
    621 			wsetorigin(w, q0, TRUE);
    622 			return;
    623 		case Kleft:
    624 			if(w->q0 > 0){
    625 				q0 = w->q0-1;
    626 				wsetselect(w, q0, q0);
    627 				wshow(w, q0);
    628 			}
    629 			return;
    630 		case Kright:
    631 			if(w->q1 < w->nr){
    632 				q1 = w->q1+1;
    633 				wsetselect(w, q1, q1);
    634 				wshow(w, q1);
    635 			}
    636 			return;
    637 		case Khome:
    638 			if(w->org > w->iq1) {
    639 				q0 = wbacknl(w, w->iq1, 1);
    640 				wsetorigin(w, q0, TRUE);
    641 			} else
    642 				wshow(w, 0);
    643 			return;
    644 		case Kend:
    645 			if(w->iq1 > w->org+w->f.nchars) {
    646 				q0 = wbacknl(w, w->iq1, 1);
    647 				wsetorigin(w, q0, TRUE);
    648 			} else
    649 				wshow(w, w->nr);
    650 			return;
    651 		case 0x01:	/* ^A: beginning of line */
    652 			if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\n')
    653 				return;
    654 			nb = wbswidth(w, 0x15 /* ^U */);
    655 			wsetselect(w, w->q0-nb, w->q0-nb);
    656 			wshow(w, w->q0);
    657 			return;
    658 		case 0x05:	/* ^E: end of line */
    659 			q0 = w->q0;
    660 			while(q0 < w->nr && w->r[q0]!='\n')
    661 				q0++;
    662 			wsetselect(w, q0, q0);
    663 			wshow(w, w->q0);
    664 			return;
    665 		}
    666 	/*
    667 	 * This if used to be below the if(w->rawing ...),
    668 	 * but let's try putting it here.  This will allow ESC-processing
    669 	 * to toggle hold mode even in remote SSH connections.
    670 	 * The drawback is that vi-style processing gets harder.
    671 	 * If you find yourself in some weird readline mode, good
    672 	 * luck getting out without ESC.  Let's see who complains.
    673 	 */
    674 	if(r==0x1B || (w->holding && r==0x7F)){	/* toggle hold */
    675 		if(w->holding)
    676 			--w->holding;
    677 		else
    678 			w->holding++;
    679 		wrepaint(w);
    680 		if(r == 0x1B)
    681 			return;
    682 	}
    683 	if(!w->holding && w->rawing && (w->q0==w->nr || w->mouseopen)){
    684 		waddraw(w, &r, 1);
    685 		return;
    686 	}
    687 	if(r == Kcmd+'x'){
    688 		wsnarf(w);
    689 		wcut(w);
    690 		wscrdraw(w);
    691 		return;
    692 	}
    693 	if(r == Kcmd+'c'){
    694 		wsnarf(w);
    695 		return;
    696 	}
    697 	if(r == Kcmd+'v'){
    698 		riogetsnarf();
    699 		wpaste(w);
    700 		wscrdraw(w);
    701 		return;
    702 	}
    703 	if(r != 0x7F){
    704 		wsnarf(w);
    705 		wcut(w);
    706 	}
    707 	switch(r){
    708 	case 0x03:		/* maybe send interrupt */
    709 		/* since ^C is so commonly used as interrupt, special case it */
    710 		if (intrc() != 0x03)
    711 			break;
    712 		/* fall through */
    713 	case 0x7F:		/* send interrupt */
    714 		w->qh = w->nr;
    715 		wshow(w, w->qh);
    716 		winterrupt(w);
    717 		w->iq1 = w->q0;
    718 		return;
    719 	case 0x06:	/* ^F: file name completion */
    720 	case Kins:		/* Insert: file name completion */
    721 		rp = namecomplete(w);
    722 		if(rp == nil)
    723 			return;
    724 		nr = runestrlen(rp);
    725 		q0 = w->q0;
    726 		q0 = winsert(w, rp, nr, q0);
    727 		wshow(w, q0+nr);
    728 		w->iq1 = w->q0;
    729 		free(rp);
    730 		return;
    731 	case 0x08:	/* ^H: erase character */
    732 	case 0x15:	/* ^U: erase line */
    733 	case 0x17:	/* ^W: erase word */
    734 		if(w->q0==0 || w->q0==w->qh)
    735 			return;
    736 		nb = wbswidth(w, r);
    737 		q1 = w->q0;
    738 		q0 = q1-nb;
    739 		if(q0 < w->org){
    740 			q0 = w->org;
    741 			nb = q1-q0;
    742 		}
    743 		if(nb > 0){
    744 			wdelete(w, q0, q0+nb);
    745 			wsetselect(w, q0, q0);
    746 		}
    747 		w->iq1 = w->q0;
    748 		return;
    749 	}
    750 	/* otherwise ordinary character; just insert */
    751 	q0 = w->q0;
    752 	q0 = winsert(w, &r, 1, q0);
    753 	wshow(w, q0+1);
    754 	w->iq1 = w->q0;
    755 }
    756 
    757 void
    758 wsetcols(Window *w)
    759 {
    760 	if(w->holding)
    761 		if(w == input)
    762 			w->f.cols[TEXT] = w->f.cols[HTEXT] = holdcol;
    763 		else
    764 			w->f.cols[TEXT] = w->f.cols[HTEXT] = lightholdcol;
    765 	else
    766 		if(w == input)
    767 			w->f.cols[TEXT] = w->f.cols[HTEXT] = display->black;
    768 		else
    769 			w->f.cols[TEXT] = w->f.cols[HTEXT] = darkgrey;
    770 }
    771 
    772 void
    773 wrepaint(Window *w)
    774 {
    775 	wsetcols(w);
    776 	if(!w->mouseopen){
    777 		frredraw(&w->f);
    778 	}
    779 	if(w == input){
    780 		wborder(w, wscale(w, Selborder));
    781 		wsetcursor(w, 0);
    782 	}else
    783 		wborder(w, wscale(w, Unselborder));
    784 }
    785 
    786 int
    787 wbswidth(Window *w, Rune c)
    788 {
    789 	uint q, eq, stop;
    790 	Rune r;
    791 	int skipping;
    792 
    793 	/* there is known to be at least one character to erase */
    794 	if(c == 0x08)	/* ^H: erase character */
    795 		return 1;
    796 	q = w->q0;
    797 	stop = 0;
    798 	if(q > w->qh)
    799 		stop = w->qh;
    800 	skipping = TRUE;
    801 	while(q > stop){
    802 		r = w->r[q-1];
    803 		if(r == '\n'){		/* eat at most one more character */
    804 			if(q == w->q0 && c != '\r')	/* eat the newline */
    805 				--q;
    806 			break;
    807 		}
    808 		if(c == 0x17){
    809 			eq = isalnum(r);
    810 			if(eq && skipping)	/* found one; stop skipping */
    811 				skipping = FALSE;
    812 			else if(!eq && !skipping)
    813 				break;
    814 		}
    815 		--q;
    816 	}
    817 	return w->q0-q;
    818 }
    819 
    820 void
    821 wsnarf(Window *w)
    822 {
    823 	if(w->q1 == w->q0)
    824 		return;
    825 	nsnarf = w->q1-w->q0;
    826 	snarf = runerealloc(snarf, nsnarf);
    827 	snarfversion++;	/* maybe modified by parent */
    828 	runemove(snarf, w->r+w->q0, nsnarf);
    829 	rioputsnarf();
    830 }
    831 
    832 void
    833 wcut(Window *w)
    834 {
    835 	if(w->q1 == w->q0)
    836 		return;
    837 	wdelete(w, w->q0, w->q1);
    838 	wsetselect(w, w->q0, w->q0);
    839 }
    840 
    841 void
    842 wpaste(Window *w)
    843 {
    844 	uint q0;
    845 
    846 	if(nsnarf == 0)
    847 		return;
    848 	wcut(w);
    849 	q0 = w->q0;
    850 	if(w->rawing && !w->holding && q0==w->nr){
    851 		waddraw(w, snarf, nsnarf);
    852 		wsetselect(w, q0, q0);
    853 	}else{
    854 		q0 = winsert(w, snarf, nsnarf, w->q0);
    855 		wsetselect(w, q0, q0+nsnarf);
    856 	}
    857 }
    858 
    859 void
    860 wplumb(Window *w)
    861 {
    862 	Plumbmsg *m;
    863 	static CFid *fd;
    864 	char buf[32];
    865 	uint p0, p1;
    866 	Cursor *c;
    867 
    868 	if(fd == nil)
    869 		fd = plumbopenfid("send", OWRITE);
    870 	if(fd == nil)
    871 		return;
    872 	m = emalloc(sizeof(Plumbmsg));
    873 	m->src = estrdup("rio");
    874 	m->dst = nil;
    875 	m->wdir = estrdup(w->dir);
    876 	m->type = estrdup("text");
    877 	p0 = w->q0;
    878 	p1 = w->q1;
    879 	if(w->q1 > w->q0)
    880 		m->attr = nil;
    881 	else{
    882 		while(p0>0 && w->r[p0-1]!=' ' && w->r[p0-1]!='\t' && w->r[p0-1]!='\n')
    883 			p0--;
    884 		while(p1<w->nr && w->r[p1]!=' ' && w->r[p1]!='\t' && w->r[p1]!='\n')
    885 			p1++;
    886 		sprint(buf, "click=%d", w->q0-p0);
    887 		m->attr = plumbunpackattr(buf);
    888 	}
    889 	if(p1-p0 > messagesize-1024){
    890 		plumbfree(m);
    891 		return;	/* too large for 9P */
    892 	}
    893 	m->data = runetobyte(w->r+p0, p1-p0, &m->ndata);
    894 	if(plumbsendtofid(fd, m) < 0){
    895 		c = lastcursor;
    896 		riosetcursor(&query, 1);
    897 		sleep(300);
    898 		riosetcursor(c, 1);
    899 	}
    900 	plumbfree(m);
    901 }
    902 
    903 int
    904 winborder(Window *w, Point xy)
    905 {
    906 	return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->screenr, wscale(w, Selborder)));
    907 }
    908 
    909 void
    910 wlook(Window *w)
    911 {
    912 	int i, n, e;
    913 
    914 	i = w->q1;
    915 	n = i - w->q0;
    916 	e = w->nr - n;
    917 	if(n <= 0 || e < n)
    918 		return;
    919 
    920 	if(i > e)
    921 		i = 0;
    922 
    923 	while(runestrncmp(w->r+w->q0, w->r+i, n) != 0){
    924 		if(i < e)
    925 			i++;
    926 		else
    927 			i = 0;
    928 	}
    929 
    930 	wsetselect(w, i, i+n);
    931 	wshow(w, i);
    932 }
    933 
    934 void
    935 wmousectl(Window *w)
    936 {
    937 	int but;
    938 
    939 	if(w->mc.m.buttons == 1)
    940 		but = 1;
    941 	else if(w->mc.m.buttons == 2)
    942 		but = 2;
    943 	else if(w->mc.m.buttons == 4)
    944 		but = 3;
    945 	else{
    946 		if(w->mc.m.buttons == 8)
    947 			wkeyctl(w, Kscrolloneup);
    948 		if(w->mc.m.buttons == 16)
    949 			wkeyctl(w, Kscrollonedown);
    950 		return;
    951 	}
    952 
    953 	incref(&w->ref);		/* hold up window while we track */
    954 	if(w->deleted)
    955 		goto Return;
    956 	if(ptinrect(w->mc.m.xy, w->scrollr)){
    957 		if(but)
    958 			wscroll(w, but);
    959 		goto Return;
    960 	}
    961 	if(but == 1)
    962 		wselect(w);
    963 	/* else all is handled by main process */
    964    Return:
    965 	wclose(w);
    966 }
    967 
    968 void
    969 wdelete(Window *w, uint q0, uint q1)
    970 {
    971 	uint n, p0, p1;
    972 
    973 	n = q1-q0;
    974 	if(n == 0)
    975 		return;
    976 	runemove(w->r+q0, w->r+q1, w->nr-q1);
    977 	w->nr -= n;
    978 	if(q0 < w->iq1)
    979 		w->iq1 -= min(n, w->iq1-q0);
    980 	if(q0 < w->q0)
    981 		w->q0 -= min(n, w->q0-q0);
    982 	if(q0 < w->q1)
    983 		w->q1 -= min(n, w->q1-q0);
    984 	if(q1 < w->qh)
    985 		w->qh -= n;
    986 	else if(q0 < w->qh)
    987 		w->qh = q0;
    988 	if(q1 <= w->org)
    989 		w->org -= n;
    990 	else if(q0 < w->org+w->f.nchars){
    991 		p1 = q1 - w->org;
    992 		if(p1 > w->f.nchars)
    993 			p1 = w->f.nchars;
    994 		if(q0 < w->org){
    995 			w->org = q0;
    996 			p0 = 0;
    997 		}else
    998 			p0 = q0 - w->org;
    999 		frdelete(&w->f, p0, p1);
   1000 		wfill(w);
   1001 	}
   1002 }
   1003 
   1004 
   1005 static Window	*clickwin;
   1006 static uint	clickmsec;
   1007 static Window	*selectwin;
   1008 static uint	selectq;
   1009 
   1010 /*
   1011  * called from frame library
   1012  */
   1013 void
   1014 framescroll(Frame *f, int dl)
   1015 {
   1016 	if(f != &selectwin->f)
   1017 		error("frameselect not right frame");
   1018 	wframescroll(selectwin, dl);
   1019 }
   1020 
   1021 void
   1022 wframescroll(Window *w, int dl)
   1023 {
   1024 	uint q0;
   1025 
   1026 	if(dl == 0){
   1027 		wscrsleep(w, 100);
   1028 		return;
   1029 	}
   1030 	if(dl < 0){
   1031 		q0 = wbacknl(w, w->org, -dl);
   1032 		if(selectq > w->org+w->f.p0)
   1033 			wsetselect(w, w->org+w->f.p0, selectq);
   1034 		else
   1035 			wsetselect(w, selectq, w->org+w->f.p0);
   1036 	}else{
   1037 		if(w->org+w->f.nchars == w->nr)
   1038 			return;
   1039 		q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.min.y+dl*w->f.font->height));
   1040 		if(selectq >= w->org+w->f.p1)
   1041 			wsetselect(w, w->org+w->f.p1, selectq);
   1042 		else
   1043 			wsetselect(w, selectq, w->org+w->f.p1);
   1044 	}
   1045 	wsetorigin(w, q0, TRUE);
   1046 }
   1047 
   1048 void
   1049 wselect(Window *w)
   1050 {
   1051 	uint q0, q1;
   1052 	int b, x, y, first;
   1053 
   1054 	first = 1;
   1055 	selectwin = w;
   1056 	/*
   1057 	 * Double-click immediately if it might make sense.
   1058 	 */
   1059 	b = w->mc.m.buttons;
   1060 	q0 = w->q0;
   1061 	q1 = w->q1;
   1062 	selectq = w->org+frcharofpt(&w->f, w->mc.m.xy);
   1063 	if(clickwin==w && w->mc.m.msec-clickmsec<500)
   1064 	if(q0==q1 && selectq==w->q0){
   1065 		wdoubleclick(w, &q0, &q1);
   1066 		wsetselect(w, q0, q1);
   1067 		flushimage(display, 1);
   1068 		x = w->mc.m.xy.x;
   1069 		y = w->mc.m.xy.y;
   1070 		/* stay here until something interesting happens */
   1071 		do
   1072 			readmouse(&w->mc);
   1073 		while(w->mc.m.buttons==b && abs(w->mc.m.xy.x-x)<3 && abs(w->mc.m.xy.y-y)<3);
   1074 		w->mc.m.xy.x = x;	/* in case we're calling frselect */
   1075 		w->mc.m.xy.y = y;
   1076 		q0 = w->q0;	/* may have changed */
   1077 		q1 = w->q1;
   1078 		selectq = q0;
   1079 	}
   1080 	if(w->mc.m.buttons == b){
   1081 		w->f.scroll = framescroll;
   1082 		frselect(&w->f, &w->mc);
   1083 		/* horrible botch: while asleep, may have lost selection altogether */
   1084 		if(selectq > w->nr)
   1085 			selectq = w->org + w->f.p0;
   1086 		w->f.scroll = nil;
   1087 		if(selectq < w->org)
   1088 			q0 = selectq;
   1089 		else
   1090 			q0 = w->org + w->f.p0;
   1091 		if(selectq > w->org+w->f.nchars)
   1092 			q1 = selectq;
   1093 		else
   1094 			q1 = w->org+w->f.p1;
   1095 	}
   1096 	if(q0 == q1){
   1097 		if(q0==w->q0 && clickwin==w && w->mc.m.msec-clickmsec<500){
   1098 			wdoubleclick(w, &q0, &q1);
   1099 			clickwin = nil;
   1100 		}else{
   1101 			clickwin = w;
   1102 			clickmsec = w->mc.m.msec;
   1103 		}
   1104 	}else
   1105 		clickwin = nil;
   1106 	wsetselect(w, q0, q1);
   1107 	flushimage(display, 1);
   1108 	while(w->mc.m.buttons){
   1109 		w->mc.m.msec = 0;
   1110 		b = w->mc.m.buttons;
   1111 		if(b & 6){
   1112 			if(b & 2){
   1113 				wsnarf(w);
   1114 				wcut(w);
   1115 			}else{
   1116 				if(first){
   1117 					first = 0;
   1118 					riogetsnarf();
   1119 				}
   1120 				wpaste(w);
   1121 			}
   1122 		}
   1123 		wscrdraw(w);
   1124 		flushimage(display, 1);
   1125 		while(w->mc.m.buttons == b)
   1126 			readmouse(&w->mc);
   1127 		clickwin = nil;
   1128 	}
   1129 }
   1130 
   1131 void
   1132 wsendctlmesg(Window *w, int type, Rectangle r, Image *image)
   1133 {
   1134 	Wctlmesg wcm;
   1135 
   1136 	wcm.type = type;
   1137 	wcm.r = r;
   1138 	wcm.image = image;
   1139 	send(w->cctl, &wcm);
   1140 }
   1141 
   1142 int
   1143 wctlmesg(Window *w, int m, Rectangle r, Image *i)
   1144 {
   1145 	char buf[64];
   1146 
   1147 	switch(m){
   1148 	default:
   1149 		error("unknown control message");
   1150 		break;
   1151 	case Wakeup:
   1152 		break;
   1153 	case Moved:
   1154 	case Reshaped:
   1155 		if(w->deleted){
   1156 			freeimage(i);
   1157 			break;
   1158 		}
   1159 		w->screenr = r;
   1160 		strcpy(buf, w->name);
   1161 		wresize(w, i, m==Moved);
   1162 		w->wctlready = 1;
   1163 		if(Dx(r) > 0){
   1164 			if(w != input)
   1165 				wcurrent(w);
   1166 		}else if(w == input)
   1167 			wcurrent(nil);
   1168 		flushimage(display, 1);
   1169 		break;
   1170 	case Refresh:
   1171 		if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r))
   1172 			break;
   1173 		if(!w->mouseopen)
   1174 			wrefresh(w, r);
   1175 		flushimage(display, 1);
   1176 		break;
   1177 	case Movemouse:
   1178 		if(sweeping || !ptinrect(r.min, w->i->r))
   1179 			break;
   1180 		wmovemouse(w, r.min);
   1181 	case Rawon:
   1182 		break;
   1183 	case Rawoff:
   1184 		if(w->deleted)
   1185 			break;
   1186 		while(w->nraw > 0){
   1187 			wkeyctl(w, w->raw[0]);
   1188 			--w->nraw;
   1189 			runemove(w->raw, w->raw+1, w->nraw);
   1190 		}
   1191 		break;
   1192 	case Holdon:
   1193 	case Holdoff:
   1194 		if(w->deleted)
   1195 			break;
   1196 		wrepaint(w);
   1197 		flushimage(display, 1);
   1198 		break;
   1199 	case Deleted:
   1200 		if(w->deleted)
   1201 			break;
   1202 		write(w->notefd, "hangup", 6);
   1203 		wclosewin(w);
   1204 		break;
   1205 	case Exited:
   1206 		frclear(&w->f, TRUE);
   1207 		close(w->notefd);
   1208 		chanfree(w->mc.c);
   1209 		chanfree(w->ck);
   1210 		chanfree(w->cctl);
   1211 		chanfree(w->conswrite);
   1212 		chanfree(w->consread);
   1213 		chanfree(w->mouseread);
   1214 		chanfree(w->wctlread);
   1215 		free(w->raw);
   1216 		free(w->r);
   1217 		free(w->dir);
   1218 		free(w->label);
   1219 		free(w);
   1220 		break;
   1221 	}
   1222 	return m;
   1223 }
   1224 
   1225 /*
   1226  * Convert back to physical coordinates
   1227  */
   1228 void
   1229 wmovemouse(Window *w, Point p)
   1230 {
   1231 	p.x += w->screenr.min.x-w->i->r.min.x;
   1232 	p.y += w->screenr.min.y-w->i->r.min.y;
   1233 	moveto(mousectl, p);
   1234 }
   1235 
   1236 void
   1237 wcurrent(Window *w)
   1238 {
   1239 	Window *oi;
   1240 
   1241 	if(wkeyboard!=nil && w==wkeyboard)
   1242 		return;
   1243 	oi = input;
   1244 	input = w;
   1245 	if(oi!=w && oi!=nil)
   1246 		wrepaint(oi);
   1247 	if(w !=nil){
   1248 		wrepaint(w);
   1249 		wsetcursor(w, 0);
   1250 	}
   1251 	if(w != oi){
   1252 		if(oi){
   1253 			oi->wctlready = 1;
   1254 			wsendctlmesg(oi, Wakeup, ZR, nil);
   1255 		}
   1256 		if(w){
   1257 			w->wctlready = 1;
   1258 			wsendctlmesg(w, Wakeup, ZR, nil);
   1259 		}
   1260 	}
   1261 }
   1262 
   1263 void
   1264 wsetcursor(Window *w, int force)
   1265 {
   1266 	Cursor *p;
   1267 
   1268 	if(w==nil || /*w!=input || */ w->i==nil || Dx(w->screenr)<=0)
   1269 		p = nil;
   1270 	else if(wpointto(mouse->xy) == w){
   1271 		p = w->cursorp;
   1272 		if(p==nil && w->holding)
   1273 			p = &whitearrow;
   1274 	}else
   1275 		p = nil;
   1276 	if(!menuing)
   1277 		riosetcursor(p, force && !menuing);
   1278 }
   1279 
   1280 void
   1281 riosetcursor(Cursor *p, int force)
   1282 {
   1283 	if(!force && p==lastcursor)
   1284 		return;
   1285 	setcursor(mousectl, p);
   1286 	lastcursor = p;
   1287 }
   1288 
   1289 Window*
   1290 wtop(Point pt)
   1291 {
   1292 	Window *w;
   1293 
   1294 	w = wpointto(pt);
   1295 	if(w){
   1296 		if(w->topped == topped)
   1297 			return nil;
   1298 		topwindow(w->i);
   1299 		wcurrent(w);
   1300 		flushimage(display, 1);
   1301 		w->topped = ++topped;
   1302 	}
   1303 	return w;
   1304 }
   1305 
   1306 void
   1307 wtopme(Window *w)
   1308 {
   1309 	if(w!=nil && w->i!=nil && !w->deleted && w->topped!=topped){
   1310 		topwindow(w->i);
   1311 		flushimage(display, 1);
   1312 		w->topped = ++ topped;
   1313 	}
   1314 }
   1315 
   1316 void
   1317 wbottomme(Window *w)
   1318 {
   1319 	if(w!=nil && w->i!=nil && !w->deleted){
   1320 		bottomwindow(w->i);
   1321 		flushimage(display, 1);
   1322 		w->topped = 0;
   1323 	}
   1324 }
   1325 
   1326 Window*
   1327 wlookid(int id)
   1328 {
   1329 	int i;
   1330 
   1331 	for(i=0; i<nwindow; i++)
   1332 		if(window[i]->id == id)
   1333 			return window[i];
   1334 	return nil;
   1335 }
   1336 
   1337 void
   1338 wclosewin(Window *w)
   1339 {
   1340 	Rectangle r;
   1341 	int i;
   1342 
   1343 	w->deleted = TRUE;
   1344 	if(w == input){
   1345 		input = nil;
   1346 		wsetcursor(w, 0);
   1347 	}
   1348 	if(w == wkeyboard)
   1349 		wkeyboard = nil;
   1350 	for(i=0; i<nhidden; i++)
   1351 		if(hidden[i] == w){
   1352 			--nhidden;
   1353 			memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof(hidden[0]));
   1354 			break;
   1355 		}
   1356 	for(i=0; i<nwindow; i++)
   1357 		if(window[i] == w){
   1358 			--nwindow;
   1359 			memmove(window+i, window+i+1, (nwindow-i)*sizeof(Window*));
   1360 			w->deleted = TRUE;
   1361 			r = w->i->r;
   1362 			/* move it off-screen to hide it, in case client is slow in letting it go */
   1363 			MOVEIT originwindow(w->i, r.min, view->r.max);
   1364 			freeimage(w->i);
   1365 			w->i = nil;
   1366 			return;
   1367 		}
   1368 	error("unknown window in closewin");
   1369 }
   1370 
   1371 void
   1372 wsetpid(Window *w, int pid, int dolabel)
   1373 {
   1374 	char buf[128];
   1375 
   1376 	w->pid = pid;
   1377 	if(dolabel){
   1378 		sprint(buf, "rc %d", pid);
   1379 		free(w->label);
   1380 		w->label = estrdup(buf);
   1381 		drawsetlabel(w->label);
   1382 	}
   1383 }
   1384 
   1385 static Rune left1[] =  {
   1386 	'{', '[', '(', '<', 0xAB,
   1387 	0x207d, 0x2329, 0x27e6, 0x27e8, 0x27ea,
   1388 	0xfe59, 0xfe5b, 0xfe5d, 0xff08, 0xff3b, 0xff5b,
   1389 	0
   1390 };
   1391 static Rune right1[] = {
   1392 	'}', ']', ')', '>', 0xBB,
   1393 	0x207e, 0x232a, 0x27e7, 0x27e9, 0x27eb,
   1394 	0xfe5a, 0xfe5c, 0xfe5e, 0xff09, 0xff3d, 0xff5d,
   1395 	0
   1396 };
   1397 static Rune left2[] =  { '\n', 0 };
   1398 static Rune left3[] =  { '\'', '"', '`', 0 };
   1399 
   1400 Rune *left[] = {
   1401 	left1,
   1402 	left2,
   1403 	left3,
   1404 	nil
   1405 };
   1406 Rune *right[] = {
   1407 	right1,
   1408 	left2,
   1409 	left3,
   1410 	nil
   1411 };
   1412 
   1413 void
   1414 wdoubleclick(Window *w, uint *q0, uint *q1)
   1415 {
   1416 	int c, i;
   1417 	Rune *r, *l, *p;
   1418 	uint q;
   1419 
   1420 	for(i=0; left[i]!=nil; i++){
   1421 		q = *q0;
   1422 		l = left[i];
   1423 		r = right[i];
   1424 		/* try matching character to left, looking right */
   1425 		if(q == 0)
   1426 			c = '\n';
   1427 		else
   1428 			c = w->r[q-1];
   1429 		p = strrune(l, c);
   1430 		if(p != nil){
   1431 			if(wclickmatch(w, c, r[p-l], 1, &q))
   1432 				*q1 = q-(c!='\n');
   1433 			return;
   1434 		}
   1435 		/* try matching character to right, looking left */
   1436 		if(q == w->nr)
   1437 			c = '\n';
   1438 		else
   1439 			c = w->r[q];
   1440 		p = strrune(r, c);
   1441 		if(p != nil){
   1442 			if(wclickmatch(w, c, l[p-r], -1, &q)){
   1443 				*q1 = *q0+(*q0<w->nr && c=='\n');
   1444 				*q0 = q;
   1445 				if(c!='\n' || q!=0 || w->r[0]=='\n')
   1446 					(*q0)++;
   1447 			}
   1448 			return;
   1449 		}
   1450 	}
   1451 	/* try filling out word to right */
   1452 	while(*q1<w->nr && isalnum(w->r[*q1]))
   1453 		(*q1)++;
   1454 	/* try filling out word to left */
   1455 	while(*q0>0 && isalnum(w->r[*q0-1]))
   1456 		(*q0)--;
   1457 }
   1458 
   1459 int
   1460 wclickmatch(Window *w, int cl, int cr, int dir, uint *q)
   1461 {
   1462 	Rune c;
   1463 	int nest;
   1464 
   1465 	nest = 1;
   1466 	for(;;){
   1467 		if(dir > 0){
   1468 			if(*q == w->nr)
   1469 				break;
   1470 			c = w->r[*q];
   1471 			(*q)++;
   1472 		}else{
   1473 			if(*q == 0)
   1474 				break;
   1475 			(*q)--;
   1476 			c = w->r[*q];
   1477 		}
   1478 		if(c == cr){
   1479 			if(--nest==0)
   1480 				return 1;
   1481 		}else if(c == cl)
   1482 			nest++;
   1483 	}
   1484 	return cl=='\n' && nest==1;
   1485 }
   1486 
   1487 
   1488 uint
   1489 wbacknl(Window *w, uint p, uint n)
   1490 {
   1491 	int i, j;
   1492 
   1493 	/* look for start of this line if n==0 */
   1494 	if(n==0 && p>0 && w->r[p-1]!='\n')
   1495 		n = 1;
   1496 	i = n;
   1497 	while(i-->0 && p>0){
   1498 		--p;	/* it's at a newline now; back over it */
   1499 		if(p == 0)
   1500 			break;
   1501 		/* at 128 chars, call it a line anyway */
   1502 		for(j=128; --j>0 && p>0; p--)
   1503 			if(w->r[p-1]=='\n')
   1504 				break;
   1505 	}
   1506 	return p;
   1507 }
   1508 
   1509 void
   1510 wshow(Window *w, uint q0)
   1511 {
   1512 	int qe;
   1513 	int nl;
   1514 	uint q;
   1515 
   1516 	qe = w->org+w->f.nchars;
   1517 	if(w->org<=q0 && (q0<qe || (q0==qe && qe==w->nr)))
   1518 		wscrdraw(w);
   1519 	else{
   1520 		nl = 4*w->f.maxlines/5;
   1521 		q = wbacknl(w, q0, nl);
   1522 		/* avoid going backwards if trying to go forwards - long lines! */
   1523 		if(!(q0>w->org && q<w->org))
   1524 			wsetorigin(w, q, TRUE);
   1525 		while(q0 > w->org+w->f.nchars)
   1526 			wsetorigin(w, w->org+1, FALSE);
   1527 	}
   1528 }
   1529 
   1530 void
   1531 wsetorigin(Window *w, uint org, int exact)
   1532 {
   1533 	int i, a, fixup;
   1534 	Rune *r;
   1535 	uint n;
   1536 
   1537 	if(org>0 && !exact){
   1538 		/* org is an estimate of the char posn; find a newline */
   1539 		/* don't try harder than 256 chars */
   1540 		for(i=0; i<256 && org<w->nr; i++){
   1541 			if(w->r[org] == '\n'){
   1542 				org++;
   1543 				break;
   1544 			}
   1545 			org++;
   1546 		}
   1547 	}
   1548 	a = org-w->org;
   1549 	fixup = 0;
   1550 	if(a>=0 && a<w->f.nchars){
   1551 		frdelete(&w->f, 0, a);
   1552 		fixup = 1;	/* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
   1553 	}else if(a<0 && -a<w->f.nchars){
   1554 		n = w->org - org;
   1555 		r = runemalloc(n);
   1556 		runemove(r, w->r+org, n);
   1557 		frinsert(&w->f, r, r+n, 0);
   1558 		free(r);
   1559 	}else
   1560 		frdelete(&w->f, 0, w->f.nchars);
   1561 	w->org = org;
   1562 	wfill(w);
   1563 	wscrdraw(w);
   1564 	wsetselect(w, w->q0, w->q1);
   1565 	if(fixup && w->f.p1 > w->f.p0)
   1566 		frdrawsel(&w->f, frptofchar(&w->f, w->f.p1-1), w->f.p1-1, w->f.p1, 1);
   1567 }
   1568 
   1569 void
   1570 wsetselect(Window *w, uint q0, uint q1)
   1571 {
   1572 	int p0, p1;
   1573 
   1574 	/* w->f.p0 and w->f.p1 are always right; w->q0 and w->q1 may be off */
   1575 	w->q0 = q0;
   1576 	w->q1 = q1;
   1577 	/* compute desired p0,p1 from q0,q1 */
   1578 	p0 = q0-w->org;
   1579 	p1 = q1-w->org;
   1580 	if(p0 < 0)
   1581 		p0 = 0;
   1582 	if(p1 < 0)
   1583 		p1 = 0;
   1584 	if(p0 > w->f.nchars)
   1585 		p0 = w->f.nchars;
   1586 	if(p1 > w->f.nchars)
   1587 		p1 = w->f.nchars;
   1588 	if(p0==w->f.p0 && p1==w->f.p1)
   1589 		return;
   1590 	/* screen disagrees with desired selection */
   1591 	if(w->f.p1<=p0 || p1<=w->f.p0 || p0==p1 || w->f.p1==w->f.p0){
   1592 		/* no overlap or too easy to bother trying */
   1593 		frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 0);
   1594 		frdrawsel(&w->f, frptofchar(&w->f, p0), p0, p1, 1);
   1595 		goto Return;
   1596 	}
   1597 	/* overlap; avoid unnecessary painting */
   1598 	if(p0 < w->f.p0){
   1599 		/* extend selection backwards */
   1600 		frdrawsel(&w->f, frptofchar(&w->f, p0), p0, w->f.p0, 1);
   1601 	}else if(p0 > w->f.p0){
   1602 		/* trim first part of selection */
   1603 		frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, p0, 0);
   1604 	}
   1605 	if(p1 > w->f.p1){
   1606 		/* extend selection forwards */
   1607 		frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, p1, 1);
   1608 	}else if(p1 < w->f.p1){
   1609 		/* trim last part of selection */
   1610 		frdrawsel(&w->f, frptofchar(&w->f, p1), p1, w->f.p1, 0);
   1611 	}
   1612 
   1613     Return:
   1614 	w->f.p0 = p0;
   1615 	w->f.p1 = p1;
   1616 }
   1617 
   1618 uint
   1619 winsert(Window *w, Rune *r, int n, uint q0)
   1620 {
   1621 	uint m;
   1622 
   1623 	if(n == 0)
   1624 		return q0;
   1625 	if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){
   1626 		m = min(HiWater-LoWater, min(w->org, w->qh));
   1627 		w->org -= m;
   1628 		w->qh -= m;
   1629 		if(w->q0 > m)
   1630 			w->q0 -= m;
   1631 		else
   1632 			w->q0 = 0;
   1633 		if(w->q1 > m)
   1634 			w->q1 -= m;
   1635 		else
   1636 			w->q1 = 0;
   1637 		w->nr -= m;
   1638 		runemove(w->r, w->r+m, w->nr);
   1639 		q0 -= m;
   1640 	}
   1641 	if(w->nr+n > w->maxr){
   1642 		/*
   1643 		 * Minimize realloc breakage:
   1644 		 *	Allocate at least MinWater
   1645 		 * 	Double allocation size each time
   1646 		 *	But don't go much above HiWater
   1647 		 */
   1648 		m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater;
   1649 		if(m > HiWater)
   1650 			m = max(HiWater+MinWater, w->nr+n);
   1651 		if(m > w->maxr){
   1652 			w->r = runerealloc(w->r, m);
   1653 			w->maxr = m;
   1654 		}
   1655 	}
   1656 	runemove(w->r+q0+n, w->r+q0, w->nr-q0);
   1657 	runemove(w->r+q0, r, n);
   1658 	w->nr += n;
   1659 	/* if output touches, advance selection, not qh; works best for keyboard and output */
   1660 	if(q0 <= w->q1)
   1661 		w->q1 += n;
   1662 	if(q0 <= w->q0)
   1663 		w->q0 += n;
   1664 	if(q0 < w->qh)
   1665 		w->qh += n;
   1666 	if(q0 < w->iq1)
   1667 		w->iq1 += n;
   1668 	if(q0 < w->org)
   1669 		w->org += n;
   1670 	else if(q0 <= w->org+w->f.nchars)
   1671 		frinsert(&w->f, r, r+n, q0-w->org);
   1672 	return q0;
   1673 }
   1674 
   1675 void
   1676 wfill(Window *w)
   1677 {
   1678 	Rune *rp;
   1679 	int i, n, m, nl;
   1680 
   1681 	if(w->f.lastlinefull)
   1682 		return;
   1683 	rp = malloc(messagesize);
   1684 	do{
   1685 		n = w->nr-(w->org+w->f.nchars);
   1686 		if(n == 0)
   1687 			break;
   1688 		if(n > 2000)	/* educated guess at reasonable amount */
   1689 			n = 2000;
   1690 		runemove(rp, w->r+(w->org+w->f.nchars), n);
   1691 		/*
   1692 		 * it's expensive to frinsert more than we need, so
   1693 		 * count newlines.
   1694 		 */
   1695 		nl = w->f.maxlines-w->f.nlines;
   1696 		m = 0;
   1697 		for(i=0; i<n; ){
   1698 			if(rp[i++] == '\n'){
   1699 				m++;
   1700 				if(m >= nl)
   1701 					break;
   1702 			}
   1703 		}
   1704 		frinsert(&w->f, rp, rp+i, w->f.nchars);
   1705 	}while(w->f.lastlinefull == FALSE);
   1706 	free(rp);
   1707 }
   1708 
   1709 char*
   1710 wcontents(Window *w, int *ip)
   1711 {
   1712 	return runetobyte(w->r, w->nr, ip);
   1713 }