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