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