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