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