]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/rio/wind.c
rio: /dev/kbd cleanup
[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, 0);
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, 1);
875                 sleep(300);
876                 riosetcursor(c, 1);
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 int
907 winborder(Window *w, Point xy)
908 {
909         return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->screenr, Selborder));
910 }
911
912 void
913 wmousectl(Window *w)
914 {
915         int but;
916
917         for(but=1;; but++){
918                 if(but > 5)
919                         return;
920                 if(w->mc.buttons == 1<<(but-1))
921                         break;
922         }
923
924         incref(w);              /* hold up window while we track */
925         if(w->i != nil){
926                 if(shiftdown && but > 3)
927                         wkeyctl(w, but == 4 ? Kscrolloneup : Kscrollonedown);
928                 else if(ptinrect(w->mc.xy, w->scrollr) || (but > 3))
929                         wscroll(w, but);
930                 else if(but == 1)
931                         wselect(w);
932         }
933         wclose(w);
934 }
935
936 void
937 wdelete(Window *w, uint q0, uint q1)
938 {
939         uint n, p0, p1;
940
941         n = q1-q0;
942         if(n == 0)
943                 return;
944         runemove(w->r+q0, w->r+q1, w->nr-q1);
945         w->nr -= n;
946         if(q0 < w->q0)
947                 w->q0 -= min(n, w->q0-q0);
948         if(q0 < w->q1)
949                 w->q1 -= min(n, w->q1-q0);
950         if(q1 < w->qh)
951                 w->qh -= n;
952         else if(q0 < w->qh)
953                 w->qh = q0;
954         if(q1 <= w->org)
955                 w->org -= n;
956         else if(q0 < w->org+w->nchars){
957                 p1 = q1 - w->org;
958                 if(p1 > w->nchars)
959                         p1 = w->nchars;
960                 if(q0 < w->org){
961                         w->org = q0;
962                         p0 = 0;
963                 }else
964                         p0 = q0 - w->org;
965                 frdelete(w, p0, p1);
966                 wfill(w);
967         }
968 }
969
970
971 static Window   *clickwin;
972 static uint     clickmsec;
973 static Window   *selectwin;
974 static uint     selectq;
975
976 /*
977  * called from frame library
978  */
979 void
980 framescroll(Frame *f, int dl)
981 {
982         if(f != &selectwin->Frame)
983                 error("frameselect not right frame");
984         wframescroll(selectwin, dl);
985 }
986
987 void
988 wframescroll(Window *w, int dl)
989 {
990         uint q0;
991
992         if(dl == 0){
993                 wscrsleep(w, 100);
994                 return;
995         }
996         if(dl < 0){
997                 q0 = wbacknl(w, w->org, -dl);
998                 if(selectq > w->org+w->p0)
999                         wsetselect(w, w->org+w->p0, selectq);
1000                 else
1001                         wsetselect(w, selectq, w->org+w->p0);
1002         }else{
1003                 if(w->org+w->nchars == w->nr)
1004                         return;
1005                 q0 = w->org+frcharofpt(w, Pt(w->Frame.r.min.x, w->Frame.r.min.y+dl*w->font->height));
1006                 if(selectq >= w->org+w->p1)
1007                         wsetselect(w, w->org+w->p1, selectq);
1008                 else
1009                         wsetselect(w, selectq, w->org+w->p1);
1010         }
1011         wsetorigin(w, q0, TRUE);
1012 }
1013
1014 void
1015 wselect(Window *w)
1016 {
1017         uint q0, q1;
1018         int b, x, y, first;
1019
1020         first = 1;
1021         selectwin = w;
1022         /*
1023          * Double-click immediately if it might make sense.
1024          */
1025         b = w->mc.buttons;
1026         q0 = w->q0;
1027         q1 = w->q1;
1028         selectq = w->org+frcharofpt(w, w->mc.xy);
1029         if(clickwin==w && w->mc.msec-clickmsec<500)
1030         if(q0==q1 && selectq==w->q0){
1031                 wdoubleclick(w, &q0, &q1);
1032                 wsetselect(w, q0, q1);
1033                 x = w->mc.xy.x;
1034                 y = w->mc.xy.y;
1035                 /* stay here until something interesting happens */
1036                 do
1037                         readmouse(&w->mc);
1038                 while(w->mc.buttons==b && abs(w->mc.xy.x-x)<3 && abs(w->mc.xy.y-y)<3);
1039                 w->mc.xy.x = x; /* in case we're calling frselect */
1040                 w->mc.xy.y = y;
1041                 q0 = w->q0;     /* may have changed */
1042                 q1 = w->q1;
1043                 selectq = q0;
1044         }
1045         if(w->mc.buttons == b){
1046                 w->scroll = framescroll;
1047                 frselect(w, &w->mc);
1048                 /* horrible botch: while asleep, may have lost selection altogether */
1049                 if(selectq > w->nr)
1050                         selectq = w->org + w->p0;
1051                 w->Frame.scroll = nil;
1052                 if(selectq < w->org)
1053                         q0 = selectq;
1054                 else
1055                         q0 = w->org + w->p0;
1056                 if(selectq > w->org+w->nchars)
1057                         q1 = selectq;
1058                 else
1059                         q1 = w->org+w->p1;
1060         }
1061         if(q0 == q1){
1062                 if(q0==w->q0 && clickwin==w && w->mc.msec-clickmsec<500){
1063                         wdoubleclick(w, &q0, &q1);
1064                         clickwin = nil;
1065                 }else{
1066                         clickwin = w;
1067                         clickmsec = w->mc.msec;
1068                 }
1069         }else
1070                 clickwin = nil;
1071         wsetselect(w, q0, q1);
1072         while(w->mc.buttons){
1073                 w->mc.msec = 0;
1074                 b = w->mc.buttons;
1075                 if(b & 6){
1076                         if(b & 2){
1077                                 wsnarf(w);
1078                                 wcut(w);
1079                         }else{
1080                                 if(first){
1081                                         first = 0;
1082                                         getsnarf();
1083                                 }
1084                                 wpaste(w);
1085                         }
1086                 }
1087                 wscrdraw(w);
1088                 while(w->mc.buttons == b)
1089                         readmouse(&w->mc);
1090                 clickwin = nil;
1091         }
1092 }
1093
1094 void
1095 wsendctlmesg(Window *w, int type, Rectangle r, void *p)
1096 {
1097         Wctlmesg wcm;
1098
1099         wcm.type = type;
1100         wcm.r = r;
1101         wcm.p = p;
1102         send(w->cctl, &wcm);
1103 }
1104
1105 int
1106 wctlmesg(Window *w, int m, Rectangle r, void *p)
1107 {
1108         char buf[64];
1109         Image *i = p;
1110
1111         switch(m){
1112         default:
1113                 error("unknown control message");
1114                 break;
1115         case Wakeup:
1116                 if(p!=nil)
1117                         sendp((Channel*)p, w);
1118                 break;
1119         case Reshaped:
1120                 if(w->deleted){
1121                         freeimage(i);
1122                         break;
1123                 }
1124                 w->screenr = r;
1125                 strcpy(buf, w->name);
1126                 wresize(w, i);
1127                 proccreate(deletetimeoutproc, estrdup(buf), 4096);
1128                 if(Dx(r)<=0){   /* window got hidden, if we had the input, drop it */
1129                         if(w==input)
1130                                 input = nil;
1131                         break;
1132                 }
1133                 /* fall through to get input if needed */
1134         case Topped:
1135                 if(w->deleted || w==input)
1136                         break;
1137                 if(input!=nil){
1138                         Window *oi;
1139                         Channel *c;
1140         
1141                         oi = input;
1142                         incref(oi);
1143
1144                         /*
1145                          * have to wait until old input responds before
1146                          * changing input to us because the window might
1147                          * currently be mouse tracking and it is not
1148                          * prepared for getting its input revoked.
1149                          */
1150                         c = chancreate(sizeof(void*), 0);
1151                         wsendctlmesg(oi, Wakeup, ZR, c);
1152                         recv(c, nil);
1153                         chanfree(c);
1154
1155                         /*
1156                          * if we are still top window and nobody else has taken
1157                          * input from original window, take the input.
1158                          */
1159                         if(!w->deleted && w->topped==topped && oi==input){
1160                                 input = w;
1161
1162                                 oi->wctlready = 1;
1163                                 wsendctlmesg(oi, Repaint, ZR, nil);
1164                         }
1165                         wclose(oi);
1166                 } else
1167                         input = w;
1168                 w->wctlready = 1;
1169                 if(m!=Topped && w==input)
1170                         break;
1171                 /* fall thrugh for redraw after input change */
1172         case Repaint:
1173                 if(w->i==nil || Dx(w->screenr)<=0)
1174                         break;
1175                 wrepaint(w);
1176                 flushimage(display, 1);
1177                 break;
1178         case Refresh:
1179                 if(w->i==nil || Dx(w->screenr)<=0 || w->mouseopen)
1180                         break;
1181                 wrefresh(w);
1182                 flushimage(display, 1);
1183                 break;
1184         case Movemouse:
1185                 if(w->i==nil || Dx(w->screenr)<=0 || !ptinrect(r.min, w->i->r))
1186                         break;
1187                 wmovemouse(w, r.min);
1188         case Rawon:
1189                 break;
1190         case Rawoff:
1191                 while(w->nraw > 0){
1192                         wkeyctl(w, w->raw[0]);
1193                         --w->nraw;
1194                         runemove(w->raw, w->raw+1, w->nraw);
1195                 }
1196                 break;
1197         case Holdon:
1198         case Holdoff:
1199                 if(w->i==nil)
1200                         break;
1201                 if(w==input)
1202                         wsetcursor(w, 0);
1203                 wrepaint(w);
1204                 flushimage(display, 1);
1205                 break;
1206         case Deleted:
1207                 wclunk(w);
1208                 if(w->notefd >= 0)
1209                         write(w->notefd, "hangup", 6);
1210                 if(w->i!=nil){
1211                         proccreate(deletetimeoutproc, estrdup(w->name), 4096);
1212                         wclosewin(w);
1213                 }
1214                 break;
1215         case Exited:
1216                 wclosewin(w);
1217                 frclear(w, TRUE);
1218                 if(w->notefd >= 0)
1219                         close(w->notefd);
1220                 chanfree(w->mc.c);
1221                 chanfree(w->ck);
1222                 chanfree(w->cctl);
1223                 chanfree(w->conswrite);
1224                 chanfree(w->consread);
1225                 chanfree(w->mouseread);
1226                 chanfree(w->wctlread);
1227                 chanfree(w->kbdread);
1228                 chanfree(w->complete);
1229                 chanfree(w->gone);
1230                 free(w->raw);
1231                 free(w->r);
1232                 free(w->dir);
1233                 free(w->label);
1234                 free(w);
1235                 break;
1236         }
1237         return m;
1238 }
1239
1240 /*
1241  * Convert back to physical coordinates
1242  */
1243 void
1244 wmovemouse(Window *w, Point p)
1245 {
1246         if(w != input || menuing || sweeping)
1247                 return;
1248         p.x += w->screenr.min.x-w->i->r.min.x;
1249         p.y += w->screenr.min.y-w->i->r.min.y;
1250         moveto(mousectl, p);
1251 }
1252
1253 void
1254 wborder(Window *w, int type)
1255 {
1256         Image *col;
1257
1258         if(w->i == nil)
1259                 return;
1260         if(w->holding){
1261                 if(type == Selborder)
1262                         col = holdcol;
1263                 else
1264                         col = paleholdcol;
1265         }else{
1266                 if(type == Selborder)
1267                         col = titlecol;
1268                 else
1269                         col = lighttitlecol;
1270         }
1271         border(w->i, w->i->r, Selborder, col, ZP);
1272 }
1273
1274 Window*
1275 wpointto(Point pt)
1276 {
1277         int i;
1278         Window *v, *w;
1279
1280         w = nil;
1281         for(i=0; i<nwindow; i++){
1282                 v = window[i];
1283                 if(ptinrect(pt, v->screenr))
1284                 if(w==nil || v->topped>w->topped)
1285                         w = v;
1286         }
1287         return w;
1288 }
1289
1290 void
1291 wcurrent(Window *w)
1292 {
1293         if(w!=nil && w!=input)
1294                 wsendctlmesg(w, Topped, ZR, nil);
1295 }
1296
1297 void
1298 wsetcursor(Window *w, int force)
1299 {
1300         Cursor *p;
1301
1302         if(w==nil || w->i==nil || Dx(w->screenr)<=0)
1303                 p = nil;
1304         else if(wpointto(mouse->xy) == w){
1305                 p = w->cursorp;
1306                 if(p==nil && w->holding)
1307                         p = &whitearrow;
1308         }else
1309                 p = nil;
1310         if(!menuing)
1311                 riosetcursor(p, force && !menuing);
1312 }
1313
1314 void
1315 riosetcursor(Cursor *p, int force)
1316 {
1317         if(!force && p==lastcursor)
1318                 return;
1319         setcursor(mousectl, p);
1320         lastcursor = p;
1321 }
1322
1323
1324 void
1325 wtopme(Window *w)
1326 {
1327         if(w!=nil && w->i!=nil && w->topped!=topped){
1328                 w->topped = ++topped;
1329                 topwindow(w->i);
1330                 flushimage(display, 1);
1331         }
1332 }
1333
1334 void
1335 wbottomme(Window *w)
1336 {
1337         if(w!=nil && w->i!=nil){
1338                 w->topped = - ++topped;
1339                 bottomwindow(w->i);
1340                 flushimage(display, 1);
1341         }
1342 }
1343
1344 Window*
1345 wtop(Point pt)
1346 {
1347         Window *w;
1348
1349         w = wpointto(pt);
1350         if(w){
1351                 incref(w);
1352                 wtopme(w);
1353                 wcurrent(w);
1354                 wclose(w);
1355         }
1356         return w;
1357 }
1358
1359 Window*
1360 wlookid(int id)
1361 {
1362         int i;
1363
1364         for(i=0; i<nwindow; i++)
1365                 if(window[i]->id == id)
1366                         return window[i];
1367         return nil;
1368 }
1369
1370 void
1371 wclunk(Window *w)
1372 {
1373         int i;
1374
1375         if(w->deleted)
1376                 return;
1377         w->deleted = TRUE;
1378         if(w == input){
1379                 input = nil;
1380                 wsetcursor(w, 0);
1381         }
1382         if(w == wkeyboard)
1383                 wkeyboard = nil;
1384         for(i=0; i<nhidden; i++)
1385                 if(hidden[i] == w){
1386                         --nhidden;
1387                         memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof(hidden[0]));
1388                         break;
1389                 }
1390         for(i=0; i<nwindow; i++)
1391                 if(window[i] == w){
1392                         --nwindow;
1393                         memmove(window+i, window+i+1, (nwindow-i)*sizeof(window[0]));
1394                         break;
1395                 }
1396 }
1397
1398 void
1399 wclosewin(Window *w)
1400 {
1401         Image *i;
1402
1403         assert(w->deleted==TRUE);
1404
1405         i = w->i;
1406         if(i){
1407                 w->i = nil;
1408                 /* move it off-screen to hide it, in case client is slow in letting it go */
1409                 MOVEIT originwindow(i, i->r.min, view->r.max);
1410                 freeimage(i);
1411                 flushimage(display, 1);
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 = w->r+org;
1641                 frinsert(w, r, r+n, 0);
1642         }else
1643                 frdelete(w, 0, w->nchars);
1644         w->org = org;
1645         wfill(w);
1646         wscrdraw(w);
1647         wsetselect(w, w->q0, w->q1);
1648         if(fixup && w->p1 > w->p0)
1649                 frdrawsel(w, frptofchar(w, w->p1-1), w->p1-1, w->p1, 1);
1650 }
1651
1652 void
1653 wsetselect(Window *w, uint q0, uint q1)
1654 {
1655         int p0, p1;
1656
1657         /* w->p0 and w->p1 are always right; w->q0 and w->q1 may be off */
1658         w->q0 = q0;
1659         w->q1 = q1;
1660         /* compute desired p0,p1 from q0,q1 */
1661         p0 = q0-w->org;
1662         p1 = q1-w->org;
1663         if(p0 < 0)
1664                 p0 = 0;
1665         if(p1 < 0)
1666                 p1 = 0;
1667         if(p0 > w->nchars)
1668                 p0 = w->nchars;
1669         if(p1 > w->nchars)
1670                 p1 = w->nchars;
1671         if(p0==w->p0 && p1==w->p1)
1672                 return;
1673         /* screen disagrees with desired selection */
1674         if(w->p1<=p0 || p1<=w->p0 || p0==p1 || w->p1==w->p0){
1675                 /* no overlap or too easy to bother trying */
1676                 frdrawsel(w, frptofchar(w, w->p0), w->p0, w->p1, 0);
1677                 frdrawsel(w, frptofchar(w, p0), p0, p1, 1);
1678                 goto Return;
1679         }
1680         /* overlap; avoid unnecessary painting */
1681         if(p0 < w->p0){
1682                 /* extend selection backwards */
1683                 frdrawsel(w, frptofchar(w, p0), p0, w->p0, 1);
1684         }else if(p0 > w->p0){
1685                 /* trim first part of selection */
1686                 frdrawsel(w, frptofchar(w, w->p0), w->p0, p0, 0);
1687         }
1688         if(p1 > w->p1){
1689                 /* extend selection forwards */
1690                 frdrawsel(w, frptofchar(w, w->p1), w->p1, p1, 1);
1691         }else if(p1 < w->p1){
1692                 /* trim last part of selection */
1693                 frdrawsel(w, frptofchar(w, p1), p1, w->p1, 0);
1694         }
1695
1696     Return:
1697         w->p0 = p0;
1698         w->p1 = p1;
1699 }
1700
1701 uint
1702 winsert(Window *w, Rune *r, int n, uint q0)
1703 {
1704         uint m;
1705
1706         if(n == 0)
1707                 return q0;
1708         if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){
1709                 m = min(HiWater-LoWater, min(w->org, w->qh));
1710                 w->org -= m;
1711                 w->qh -= m;
1712                 if(w->q0 > m)
1713                         w->q0 -= m;
1714                 else
1715                         w->q0 = 0;
1716                 if(w->q1 > m)
1717                         w->q1 -= m;
1718                 else
1719                         w->q1 = 0;
1720                 w->nr -= m;
1721                 runemove(w->r, w->r+m, w->nr);
1722                 q0 -= m;
1723         }
1724         if(w->nr+n > w->maxr){
1725                 /*
1726                  * Minimize realloc breakage:
1727                  *      Allocate at least MinWater
1728                  *      Double allocation size each time
1729                  *      But don't go much above HiWater
1730                  */
1731                 m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater;
1732                 if(m > HiWater)
1733                         m = max(HiWater+MinWater, w->nr+n);
1734                 if(m > w->maxr){
1735                         w->r = runerealloc(w->r, m);
1736                         w->maxr = m;
1737                 }
1738         }
1739         runemove(w->r+q0+n, w->r+q0, w->nr-q0);
1740         runemove(w->r+q0, r, n);
1741         w->nr += n;
1742         /* if output touches, advance selection, not qh; works best for keyboard and output */
1743         if(q0 <= w->q1)
1744                 w->q1 += n;
1745         if(q0 <= w->q0)
1746                 w->q0 += n;
1747         if(q0 < w->qh)
1748                 w->qh += n;
1749         if(q0 < w->org)
1750                 w->org += n;
1751         else if(q0 <= w->org+w->nchars)
1752                 frinsert(w, r, r+n, q0-w->org);
1753         return q0;
1754 }
1755
1756 void
1757 wfill(Window *w)
1758 {
1759         Rune *rp;
1760         int i, n, m, nl;
1761
1762         while(w->lastlinefull == FALSE){
1763                 n = w->nr-(w->org+w->nchars);
1764                 if(n == 0)
1765                         break;
1766                 if(n > 2000)    /* educated guess at reasonable amount */
1767                         n = 2000;
1768                 rp = w->r+(w->org+w->nchars);
1769
1770                 /*
1771                  * it's expensive to frinsert more than we need, so
1772                  * count newlines.
1773                  */
1774                 nl = w->maxlines-w->nlines;
1775                 m = 0;
1776                 for(i=0; i<n; ){
1777                         if(rp[i++] == '\n'){
1778                                 m++;
1779                                 if(m >= nl)
1780                                         break;
1781                         }
1782                 }
1783                 frinsert(w, rp, rp+i, w->nchars);
1784         }
1785 }
1786
1787 char*
1788 wcontents(Window *w, int *ip)
1789 {
1790         return runetobyte(w->r, w->nr, ip);
1791 }