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