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