Blob


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 <9pclient.h>
11 #include <plumb.h>
12 #include <complete.h>
13 #include "dat.h"
14 #include "fns.h"
16 #define MOVEIT if(0)
18 enum
19 {
20 HiWater = 640000, /* max size of history */
21 LoWater = 400000, /* min size of history after max'ed */
22 MinWater = 20000 /* room to leave available when reallocating */
23 };
25 static int topped;
26 static int id;
28 static Image *cols[NCOL];
29 static Image *grey;
30 static Image *darkgrey;
31 static Cursor *lastcursor;
32 static Image *titlecol;
33 static Image *lighttitlecol;
34 static Image *holdcol;
35 static Image *lightholdcol;
36 static Image *paleholdcol;
38 Window*
39 wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl, int scrolling)
40 {
41 Window *w;
42 Rectangle r;
44 if(cols[0] == nil){
45 /* greys are multiples of 0x11111100+0xFF, 14* being palest */
46 grey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
47 darkgrey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x666666FF);
48 cols[BACK] = display->white;
49 cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
50 cols[BORD] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x999999FF);
51 cols[TEXT] = display->black;
52 cols[HTEXT] = display->black;
53 titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreygreen);
54 lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreygreen);
55 holdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedblue);
56 lightholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreyblue);
57 paleholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreyblue);
58 }
59 w = emalloc(sizeof(Window));
60 w->screenr = i->r;
61 r = insetrect(i->r, Selborder+1);
62 w->i = i;
63 w->mc = *mc;
64 w->ck = ck;
65 w->cctl = cctl;
66 w->cursorp = nil;
67 w->conswrite = chancreate(sizeof(Conswritemesg), 0);
68 w->consread = chancreate(sizeof(Consreadmesg), 0);
69 w->mouseread = chancreate(sizeof(Mousereadmesg), 0);
70 w->wctlread = chancreate(sizeof(Consreadmesg), 0);
71 w->scrollr = r;
72 w->scrollr.max.x = r.min.x+Scrollwid;
73 w->lastsr = ZR;
74 r.min.x += Scrollwid+Scrollgap;
75 frinit(&w->f, r, font, i, cols);
76 w->f.maxtab = maxtab*stringwidth(font, "0");
77 w->topped = ++topped;
78 w->id = ++id;
79 w->notefd = -1;
80 w->scrolling = scrolling;
81 w->dir = estrdup(startdir);
82 w->label = estrdup("<unnamed>");
83 r = insetrect(w->i->r, Selborder);
84 draw(w->i, r, cols[BACK], nil, w->f.entire.min);
85 wborder(w, Selborder);
86 wscrdraw(w);
87 incref(&w->ref); /* ref will be removed after mounting; avoids delete before ready to be deleted */
88 return w;
89 }
91 void
92 wsetname(Window *w)
93 {
94 int i, n;
95 char err[ERRMAX];
97 n = sprint(w->name, "window.%d.%d", w->id, w->namecount++);
98 for(i='A'; i<='Z'; i++){
99 if(nameimage(w->i, w->name, 1) > 0)
100 return;
101 errstr(err, sizeof err);
102 if(strcmp(err, "image name in use") != 0)
103 break;
104 w->name[n] = i;
105 w->name[n+1] = 0;
107 w->name[0] = 0;
108 fprint(2, "rio: setname failed: %s\n", err);
111 void
112 wresize(Window *w, Image *i, int move)
114 Rectangle r, or;
116 or = w->i->r;
117 if(move || (Dx(or)==Dx(i->r) && Dy(or)==Dy(i->r)))
118 draw(i, i->r, w->i, nil, w->i->r.min);
119 if(w->i != i){
120 fprint(2, "res %p %p\n", w->i, i);
121 freeimage(w->i);
122 w->i = i;
124 /* wsetname(w); */
125 /*XXX w->mc.image = i; */
126 r = insetrect(i->r, Selborder+1);
127 w->scrollr = r;
128 w->scrollr.max.x = r.min.x+Scrollwid;
129 w->lastsr = ZR;
130 r.min.x += Scrollwid+Scrollgap;
131 if(move)
132 frsetrects(&w->f, r, w->i);
133 else{
134 frclear(&w->f, FALSE);
135 frinit(&w->f, r, w->f.font, w->i, cols);
136 wsetcols(w);
137 w->f.maxtab = maxtab*stringwidth(w->f.font, "0");
138 r = insetrect(w->i->r, Selborder);
139 draw(w->i, r, cols[BACK], nil, w->f.entire.min);
140 wfill(w);
141 wsetselect(w, w->q0, w->q1);
142 wscrdraw(w);
144 wborder(w, Selborder);
145 w->topped = ++topped;
146 w->resized = TRUE;
147 w->mouse.counter++;
150 void
151 wrefresh(Window *w, Rectangle r)
153 /* USED(r); */
155 /* BUG: rectangle is ignored */
156 if(w == input)
157 wborder(w, Selborder);
158 else
159 wborder(w, Unselborder);
160 if(w->mouseopen)
161 return;
162 draw(w->i, insetrect(w->i->r, Borderwidth), w->f.cols[BACK], nil, w->i->r.min);
163 w->f.ticked = 0;
164 if(w->f.p0 > 0)
165 frdrawsel(&w->f, frptofchar(&w->f, 0), 0, w->f.p0, 0);
166 if(w->f.p1 < w->f.nchars)
167 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, w->f.nchars, 0);
168 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 1);
169 w->lastsr = ZR;
170 wscrdraw(w);
173 int
174 wclose(Window *w)
176 int i;
178 i = decref(&w->ref);
179 if(i > 0)
180 return 0;
181 if(i < 0)
182 error("negative ref count");
183 if(!w->deleted)
184 wclosewin(w);
185 wsendctlmesg(w, Exited, ZR, nil);
186 return 1;
190 void
191 winctl(void *arg)
193 Rune *rp, *bp, *up, *kbdr;
194 uint qh;
195 int nr, nb, c, wid, i, npart, initial, lastb;
196 char *s, *t, part[3];
197 Window *w;
198 Mousestate *mp, m;
199 enum { WKey, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT };
200 Alt alts[NWALT+1];
201 Mousereadmesg mrm;
202 Conswritemesg cwm;
203 Consreadmesg crm;
204 Consreadmesg cwrm;
205 Stringpair pair;
206 Wctlmesg wcm;
207 char buf[4*12+1];
209 w = arg;
210 snprint(buf, sizeof buf, "winctl-id%d", w->id);
211 threadsetname(buf);
213 mrm.cm = chancreate(sizeof(Mouse), 0);
214 cwm.cw = chancreate(sizeof(Stringpair), 0);
215 crm.c1 = chancreate(sizeof(Stringpair), 0);
216 crm.c2 = chancreate(sizeof(Stringpair), 0);
217 cwrm.c1 = chancreate(sizeof(Stringpair), 0);
218 cwrm.c2 = chancreate(sizeof(Stringpair), 0);
221 alts[WKey].c = w->ck;
222 alts[WKey].v = &kbdr;
223 alts[WKey].op = CHANRCV;
224 alts[WMouse].c = w->mc.c;
225 alts[WMouse].v = &w->mc.m;
226 alts[WMouse].op = CHANRCV;
227 alts[WMouseread].c = w->mouseread;
228 alts[WMouseread].v = &mrm;
229 alts[WMouseread].op = CHANSND;
230 alts[WCtl].c = w->cctl;
231 alts[WCtl].v = &wcm;
232 alts[WCtl].op = CHANRCV;
233 alts[WCwrite].c = w->conswrite;
234 alts[WCwrite].v = &cwm;
235 alts[WCwrite].op = CHANSND;
236 alts[WCread].c = w->consread;
237 alts[WCread].v = &crm;
238 alts[WCread].op = CHANSND;
239 alts[WWread].c = w->wctlread;
240 alts[WWread].v = &cwrm;
241 alts[WWread].op = CHANSND;
242 alts[NWALT].op = CHANEND;
244 npart = 0;
245 lastb = -1;
246 for(;;){
247 if(w->mouseopen && w->mouse.counter != w->mouse.lastcounter)
248 alts[WMouseread].op = CHANSND;
249 else
250 alts[WMouseread].op = CHANNOP;
251 if(!w->scrolling && !w->mouseopen && w->qh>w->org+w->f.nchars)
252 alts[WCwrite].op = CHANNOP;
253 else
254 alts[WCwrite].op = CHANSND;
255 if(w->deleted || !w->wctlready)
256 alts[WWread].op = CHANNOP;
257 else
258 alts[WWread].op = CHANSND;
259 /* this code depends on NL and EOT fitting in a single byte */
260 /* kind of expensive for each loop; worth precomputing? */
261 if(w->holding)
262 alts[WCread].op = CHANNOP;
263 else if(npart || (w->rawing && w->nraw>0))
264 alts[WCread].op = CHANSND;
265 else{
266 alts[WCread].op = CHANNOP;
267 for(i=w->qh; i<w->nr; i++){
268 c = w->r[i];
269 if(c=='\n' || c=='\004'){
270 alts[WCread].op = CHANSND;
271 break;
275 switch(alt(alts)){
276 case WKey:
277 for(i=0; kbdr[i]!=L'\0'; i++)
278 wkeyctl(w, kbdr[i]);
279 /* wkeyctl(w, r); */
280 /* while(nbrecv(w->ck, &r)) */
281 /* wkeyctl(w, r); */
282 break;
283 case WMouse:
284 if(w->mouseopen) {
285 w->mouse.counter++;
287 /* queue click events */
288 if(!w->mouse.qfull && lastb != w->mc.m.buttons) { /* add to ring */
289 mp = &w->mouse.queue[w->mouse.wi];
290 if(++w->mouse.wi == nelem(w->mouse.queue))
291 w->mouse.wi = 0;
292 if(w->mouse.wi == w->mouse.ri)
293 w->mouse.qfull = TRUE;
294 mp->m = w->mc.m;
295 mp->counter = w->mouse.counter;
296 lastb = w->mc.m.buttons;
298 } else
299 wmousectl(w);
300 break;
301 case WMouseread:
302 /* send a queued event or, if the queue is empty, the current state */
303 /* if the queue has filled, we discard all the events it contained. */
304 /* the intent is to discard frantic clicking by the user during long latencies. */
305 w->mouse.qfull = FALSE;
306 if(w->mouse.wi != w->mouse.ri) {
307 m = w->mouse.queue[w->mouse.ri];
308 if(++w->mouse.ri == nelem(w->mouse.queue))
309 w->mouse.ri = 0;
310 } else {
311 m.m = w->mc.m;
312 m.counter = w->mouse.counter;
314 w->mouse.lastcounter = m.counter;
315 send(mrm.cm, &m.m);
316 continue;
317 case WCtl:
318 if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Exited){
319 chanfree(crm.c1);
320 chanfree(crm.c2);
321 chanfree(mrm.cm);
322 chanfree(cwm.cw);
323 chanfree(cwrm.c1);
324 chanfree(cwrm.c2);
325 threadexits(nil);
327 continue;
328 case WCwrite:
329 recv(cwm.cw, &pair);
330 rp = pair.s;
331 nr = pair.ns;
332 bp = rp;
333 up = rp;
334 initial = 0;
335 for(i=0; i<nr; i++){
336 switch(*bp){
337 case 0:
338 break;
339 case '\b':
340 if(up == rp)
341 initial++;
342 else
343 --up;
344 break;
345 case '\r':
346 while(i<nr-1 && *(bp+1) == '\r'){
347 bp++;
348 i++;
350 if(i<nr-1 && *(bp+1) != '\n'){
351 while(up > rp && *(up-1) != '\n')
352 up--;
353 if(up == rp)
354 initial = wbswidth(w, '\r');
355 }else if(i == nr-1)
356 *up = '\n';
357 break;
358 default:
359 *up++ = *bp;
360 break;
362 bp++;
364 if(initial){
365 if(initial > w->qh)
366 initial = w->qh;
367 qh = w->qh - initial;
368 wdelete(w, qh, qh+initial);
369 w->qh = qh;
371 nr = up - rp;
372 w->qh = winsert(w, rp, nr, w->qh)+nr;
373 if(w->scrolling || w->mouseopen)
374 wshow(w, w->qh);
375 wsetselect(w, w->q0, w->q1);
376 wscrdraw(w);
377 free(rp);
378 break;
379 case WCread:
380 recv(crm.c1, &pair);
381 t = pair.s;
382 nb = pair.ns;
383 i = npart;
384 npart = 0;
385 if(i)
386 memmove(t, part, i);
387 while(i<nb && (w->qh<w->nr || w->nraw>0)){
388 if(w->qh == w->nr){
389 wid = runetochar(t+i, &w->raw[0]);
390 w->nraw--;
391 runemove(w->raw, w->raw+1, w->nraw);
392 }else
393 wid = runetochar(t+i, &w->r[w->qh++]);
394 c = t[i]; /* knows break characters fit in a byte */
395 i += wid;
396 if(!w->rawing && (c == '\n' || c=='\004')){
397 /* if(c == '\004') */
398 /* i--; */
399 break;
402 /* if(i==nb && w->qh<w->nr && w->r[w->qh]=='\004') */
403 /* w->qh++; */
404 if(i > nb){
405 npart = i-nb;
406 memmove(part, t+nb, npart);
407 i = nb;
409 pair.s = t;
410 pair.ns = i;
411 send(crm.c2, &pair);
412 continue;
413 case WWread:
414 w->wctlready = 0;
415 recv(cwrm.c1, &pair);
416 if(w->deleted || w->i==nil)
417 pair.ns = sprint(pair.s, "");
418 else{
419 s = "visible";
420 for(i=0; i<nhidden; i++)
421 if(hidden[i] == w){
422 s = "hidden";
423 break;
425 t = "notcurrent";
426 if(w == input)
427 t = "current";
428 pair.ns = snprint(pair.s, pair.ns, "%11d %11d %11d %11d %s %s ",
429 w->i->r.min.x, w->i->r.min.y, w->i->r.max.x, w->i->r.max.y, t, s);
431 send(cwrm.c2, &pair);
432 continue;
434 if(!w->deleted)
435 flushimage(display, 1);
439 void
440 waddraw(Window *w, Rune *r, int nr)
442 w->raw = runerealloc(w->raw, w->nraw+nr);
443 runemove(w->raw+w->nraw, r, nr);
444 w->nraw += nr;
447 /*
448 * Need to do this in a separate proc because if process we're interrupting
449 * is dying and trying to print tombstone, kernel is blocked holding p->debug lock.
450 */
451 void
452 interruptproc(void *v)
454 int *notefd;
456 notefd = v;
457 write(*notefd, "interrupt", 9);
458 free(notefd);
461 int
462 windfilewidth(Window *w, uint q0, int oneelement)
464 uint q;
465 Rune r;
467 q = q0;
468 while(q > 0){
469 r = w->r[q-1];
470 if(r<=' ')
471 break;
472 if(oneelement && r=='/')
473 break;
474 --q;
476 return q0-q;
479 void
480 showcandidates(Window *w, Completion *c)
482 int i;
483 Fmt f;
484 Rune *rp;
485 uint nr, qline, q0;
486 char *s;
488 runefmtstrinit(&f);
489 if (c->nmatch == 0)
490 s = "[no matches in ";
491 else
492 s = "[";
493 if(c->nfile > 32)
494 fmtprint(&f, "%s%d files]\n", s, c->nfile);
495 else{
496 fmtprint(&f, "%s", s);
497 for(i=0; i<c->nfile; i++){
498 if(i > 0)
499 fmtprint(&f, " ");
500 fmtprint(&f, "%s", c->filename[i]);
502 fmtprint(&f, "]\n");
504 /* place text at beginning of line before host point */
505 qline = w->qh;
506 while(qline>0 && w->r[qline-1] != '\n')
507 qline--;
509 rp = runefmtstrflush(&f);
510 nr = runestrlen(rp);
512 q0 = w->q0;
513 q0 += winsert(w, rp, runestrlen(rp), qline) - qline;
514 free(rp);
515 wsetselect(w, q0+nr, q0+nr);
518 Rune*
519 namecomplete(Window *w)
521 int nstr, npath;
522 Rune *rp, *path, *str;
523 Completion *c;
524 char *s, *dir, *root;
526 /* control-f: filename completion; works back to white space or / */
527 if(w->q0<w->nr && w->r[w->q0]>' ') /* must be at end of word */
528 return nil;
529 nstr = windfilewidth(w, w->q0, TRUE);
530 str = runemalloc(nstr);
531 runemove(str, w->r+(w->q0-nstr), nstr);
532 npath = windfilewidth(w, w->q0-nstr, FALSE);
533 path = runemalloc(npath);
534 runemove(path, w->r+(w->q0-nstr-npath), npath);
535 rp = nil;
537 /* is path rooted? if not, we need to make it relative to window path */
538 if(npath>0 && path[0]=='/'){
539 dir = malloc(UTFmax*npath+1);
540 sprint(dir, "%.*S", npath, path);
541 }else{
542 if(strcmp(w->dir, "") == 0)
543 root = ".";
544 else
545 root = w->dir;
546 dir = malloc(strlen(root)+1+UTFmax*npath+1);
547 sprint(dir, "%s/%.*S", root, npath, path);
549 dir = cleanname(dir);
551 s = smprint("%.*S", nstr, str);
552 c = complete(dir, s);
553 free(s);
554 if(c == nil)
555 goto Return;
557 if(!c->advance)
558 showcandidates(w, c);
560 if(c->advance)
561 rp = runesmprint("%s", c->string);
563 Return:
564 freecompletion(c);
565 free(dir);
566 free(path);
567 free(str);
568 return rp;
571 void
572 wkeyctl(Window *w, Rune r)
574 uint q0 ,q1;
575 int n, nb, nr;
576 Rune *rp;
578 if(r == 0)
579 return;
580 if(w->deleted)
581 return;
582 w->rawing = rawon();
583 /* navigation keys work only when mouse is not open */
584 if(!w->mouseopen)
585 switch(r){
586 case Kdown:
587 n = w->f.maxlines/3;
588 goto case_Down;
589 case Kscrollonedown:
590 n = mousescrollsize(w->f.maxlines);
591 if(n <= 0)
592 n = 1;
593 goto case_Down;
594 case Kpgdown:
595 n = 2*w->f.maxlines/3;
596 case_Down:
597 q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.min.y+n*w->f.font->height));
598 wsetorigin(w, q0, TRUE);
599 return;
600 case Kup:
601 n = w->f.maxlines/3;
602 goto case_Up;
603 case Kscrolloneup:
604 n = mousescrollsize(w->f.maxlines);
605 if(n <= 0)
606 n = 1;
607 goto case_Up;
608 case Kpgup:
609 n = 2*w->f.maxlines/3;
610 case_Up:
611 q0 = wbacknl(w, w->org, n);
612 wsetorigin(w, q0, TRUE);
613 return;
614 case Kleft:
615 if(w->q0 > 0){
616 q0 = w->q0-1;
617 wsetselect(w, q0, q0);
618 wshow(w, q0);
620 return;
621 case Kright:
622 if(w->q1 < w->nr){
623 q1 = w->q1+1;
624 wsetselect(w, q1, q1);
625 wshow(w, q1);
627 return;
628 case Khome:
629 wshow(w, 0);
630 return;
631 case Kend:
632 wshow(w, w->nr);
633 return;
634 case 0x01: /* ^A: beginning of line */
635 if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\n')
636 return;
637 nb = wbswidth(w, 0x15 /* ^U */);
638 wsetselect(w, w->q0-nb, w->q0-nb);
639 wshow(w, w->q0);
640 return;
641 case 0x05: /* ^E: end of line */
642 q0 = w->q0;
643 while(q0 < w->nr && w->r[q0]!='\n')
644 q0++;
645 wsetselect(w, q0, q0);
646 wshow(w, w->q0);
647 return;
649 /*
650 * This if used to be below the if(w->rawing ...),
651 * but let's try putting it here. This will allow ESC-processing
652 * to toggle hold mode even in remote SSH connections.
653 * The drawback is that vi-style processing gets harder.
654 * If you find yourself in some weird readline mode, good
655 * luck getting out without ESC. Let's see who complains.
656 */
657 if(r==0x1B || (w->holding && r==0x7F)){ /* toggle hold */
658 if(w->holding)
659 --w->holding;
660 else
661 w->holding++;
662 wrepaint(w);
663 if(r == 0x1B)
664 return;
666 if(!w->holding && w->rawing && (w->q0==w->nr || w->mouseopen)){
667 waddraw(w, &r, 1);
668 return;
670 if(r != 0x7F){
671 wsnarf(w);
672 wcut(w);
674 switch(r){
675 case 0x7F: /* send interrupt */
676 w->qh = w->nr;
677 wshow(w, w->qh);
678 winterrupt(w);
679 return;
680 case 0x06: /* ^F: file name completion */
681 case Kins: /* Insert: file name completion */
682 rp = namecomplete(w);
683 if(rp == nil)
684 return;
685 nr = runestrlen(rp);
686 q0 = w->q0;
687 q0 = winsert(w, rp, nr, q0);
688 wshow(w, q0+nr);
689 free(rp);
690 return;
691 case 0x08: /* ^H: erase character */
692 case 0x15: /* ^U: erase line */
693 case 0x17: /* ^W: erase word */
694 if(w->q0==0 || w->q0==w->qh)
695 return;
696 nb = wbswidth(w, r);
697 q1 = w->q0;
698 q0 = q1-nb;
699 if(q0 < w->org){
700 q0 = w->org;
701 nb = q1-q0;
703 if(nb > 0){
704 wdelete(w, q0, q0+nb);
705 wsetselect(w, q0, q0);
707 return;
709 /* otherwise ordinary character; just insert */
710 q0 = w->q0;
711 q0 = winsert(w, &r, 1, q0);
712 wshow(w, q0+1);
715 void
716 wsetcols(Window *w)
718 if(w->holding)
719 if(w == input)
720 w->f.cols[TEXT] = w->f.cols[HTEXT] = holdcol;
721 else
722 w->f.cols[TEXT] = w->f.cols[HTEXT] = lightholdcol;
723 else
724 if(w == input)
725 w->f.cols[TEXT] = w->f.cols[HTEXT] = display->black;
726 else
727 w->f.cols[TEXT] = w->f.cols[HTEXT] = darkgrey;
730 void
731 wrepaint(Window *w)
733 wsetcols(w);
734 if(!w->mouseopen){
735 if(font->maxdepth > 1)
736 draw(w->f.b, w->f.r, cols[BACK], nil, ZP);
737 _frredraw(&w->f, w->f.r.min);
739 if(w == input){
740 wborder(w, Selborder);
741 wsetcursor(w, 0);
742 }else
743 wborder(w, Unselborder);
746 int
747 wbswidth(Window *w, Rune c)
749 uint q, eq, stop;
750 Rune r;
751 int skipping;
753 /* there is known to be at least one character to erase */
754 if(c == 0x08) /* ^H: erase character */
755 return 1;
756 q = w->q0;
757 stop = 0;
758 if(q > w->qh)
759 stop = w->qh;
760 skipping = TRUE;
761 while(q > stop){
762 r = w->r[q-1];
763 if(r == '\n'){ /* eat at most one more character */
764 if(q == w->q0 && c != '\r') /* eat the newline */
765 --q;
766 break;
768 if(c == 0x17){
769 eq = isalnum(r);
770 if(eq && skipping) /* found one; stop skipping */
771 skipping = FALSE;
772 else if(!eq && !skipping)
773 break;
775 --q;
777 return w->q0-q;
780 void
781 wsnarf(Window *w)
783 if(w->q1 == w->q0)
784 return;
785 nsnarf = w->q1-w->q0;
786 snarf = runerealloc(snarf, nsnarf);
787 snarfversion++; /* maybe modified by parent */
788 runemove(snarf, w->r+w->q0, nsnarf);
789 rioputsnarf();
792 void
793 wcut(Window *w)
795 if(w->q1 == w->q0)
796 return;
797 wdelete(w, w->q0, w->q1);
798 wsetselect(w, w->q0, w->q0);
801 void
802 wpaste(Window *w)
804 uint q0;
806 if(nsnarf == 0)
807 return;
808 wcut(w);
809 q0 = w->q0;
810 if(w->rawing && !w->holding && q0==w->nr){
811 waddraw(w, snarf, nsnarf);
812 wsetselect(w, q0, q0);
813 }else{
814 q0 = winsert(w, snarf, nsnarf, w->q0);
815 wsetselect(w, q0, q0+nsnarf);
819 void
820 wplumb(Window *w)
822 Plumbmsg *m;
823 static CFid *fd;
824 char buf[32];
825 uint p0, p1;
826 Cursor *c;
828 if(fd == nil)
829 fd = plumbopenfid("send", OWRITE);
830 if(fd == nil)
831 return;
832 m = emalloc(sizeof(Plumbmsg));
833 m->src = estrdup("rio");
834 m->dst = nil;
835 m->wdir = estrdup(w->dir);
836 m->type = estrdup("text");
837 p0 = w->q0;
838 p1 = w->q1;
839 if(w->q1 > w->q0)
840 m->attr = nil;
841 else{
842 while(p0>0 && w->r[p0-1]!=' ' && w->r[p0-1]!='\t' && w->r[p0-1]!='\n')
843 p0--;
844 while(p1<w->nr && w->r[p1]!=' ' && w->r[p1]!='\t' && w->r[p1]!='\n')
845 p1++;
846 sprint(buf, "click=%d", w->q0-p0);
847 m->attr = plumbunpackattr(buf);
849 if(p1-p0 > messagesize-1024){
850 plumbfree(m);
851 return; /* too large for 9P */
853 m->data = runetobyte(w->r+p0, p1-p0, &m->ndata);
854 if(plumbsendtofid(fd, m) < 0){
855 c = lastcursor;
856 riosetcursor(&query, 1);
857 sleep(300);
858 riosetcursor(c, 1);
860 plumbfree(m);
863 int
864 winborder(Window *w, Point xy)
866 return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->screenr, Selborder));
869 void
870 wmousectl(Window *w)
872 int but;
874 if(w->mc.m.buttons == 1)
875 but = 1;
876 else if(w->mc.m.buttons == 2)
877 but = 2;
878 else if(w->mc.m.buttons == 4)
879 but = 3;
880 else{
881 if(w->mc.m.buttons == 8)
882 wkeyctl(w, Kscrolloneup);
883 if(w->mc.m.buttons == 16)
884 wkeyctl(w, Kscrollonedown);
885 return;
888 incref(&w->ref); /* hold up window while we track */
889 if(w->deleted)
890 goto Return;
891 if(ptinrect(w->mc.m.xy, w->scrollr)){
892 if(but)
893 wscroll(w, but);
894 goto Return;
896 if(but == 1)
897 wselect(w);
898 /* else all is handled by main process */
899 Return:
900 wclose(w);
903 void
904 wdelete(Window *w, uint q0, uint q1)
906 uint n, p0, p1;
908 n = q1-q0;
909 if(n == 0)
910 return;
911 runemove(w->r+q0, w->r+q1, w->nr-q1);
912 w->nr -= n;
913 if(q0 < w->q0)
914 w->q0 -= min(n, w->q0-q0);
915 if(q0 < w->q1)
916 w->q1 -= min(n, w->q1-q0);
917 if(q1 < w->qh)
918 w->qh -= n;
919 else if(q0 < w->qh)
920 w->qh = q0;
921 if(q1 <= w->org)
922 w->org -= n;
923 else if(q0 < w->org+w->f.nchars){
924 p1 = q1 - w->org;
925 if(p1 > w->f.nchars)
926 p1 = w->f.nchars;
927 if(q0 < w->org){
928 w->org = q0;
929 p0 = 0;
930 }else
931 p0 = q0 - w->org;
932 frdelete(&w->f, p0, p1);
933 wfill(w);
938 static Window *clickwin;
939 static uint clickmsec;
940 static Window *selectwin;
941 static uint selectq;
943 /*
944 * called from frame library
945 */
946 void
947 framescroll(Frame *f, int dl)
949 if(f != &selectwin->f)
950 error("frameselect not right frame");
951 wframescroll(selectwin, dl);
954 void
955 wframescroll(Window *w, int dl)
957 uint q0;
959 if(dl == 0){
960 wscrsleep(w, 100);
961 return;
963 if(dl < 0){
964 q0 = wbacknl(w, w->org, -dl);
965 if(selectq > w->org+w->f.p0)
966 wsetselect(w, w->org+w->f.p0, selectq);
967 else
968 wsetselect(w, selectq, w->org+w->f.p0);
969 }else{
970 if(w->org+w->f.nchars == w->nr)
971 return;
972 q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.min.y+dl*w->f.font->height));
973 if(selectq >= w->org+w->f.p1)
974 wsetselect(w, w->org+w->f.p1, selectq);
975 else
976 wsetselect(w, selectq, w->org+w->f.p1);
978 wsetorigin(w, q0, TRUE);
981 void
982 wselect(Window *w)
984 uint q0, q1;
985 int b, x, y, first;
987 first = 1;
988 selectwin = w;
989 /*
990 * Double-click immediately if it might make sense.
991 */
992 b = w->mc.m.buttons;
993 q0 = w->q0;
994 q1 = w->q1;
995 selectq = w->org+frcharofpt(&w->f, w->mc.m.xy);
996 if(clickwin==w && w->mc.m.msec-clickmsec<500)
997 if(q0==q1 && selectq==w->q0){
998 wdoubleclick(w, &q0, &q1);
999 wsetselect(w, q0, q1);
1000 flushimage(display, 1);
1001 x = w->mc.m.xy.x;
1002 y = w->mc.m.xy.y;
1003 /* stay here until something interesting happens */
1005 readmouse(&w->mc);
1006 while(w->mc.m.buttons==b && abs(w->mc.m.xy.x-x)<3 && abs(w->mc.m.xy.y-y)<3);
1007 w->mc.m.xy.x = x; /* in case we're calling frselect */
1008 w->mc.m.xy.y = y;
1009 q0 = w->q0; /* may have changed */
1010 q1 = w->q1;
1011 selectq = q0;
1013 if(w->mc.m.buttons == b){
1014 w->f.scroll = framescroll;
1015 frselect(&w->f, &w->mc);
1016 /* horrible botch: while asleep, may have lost selection altogether */
1017 if(selectq > w->nr)
1018 selectq = w->org + w->f.p0;
1019 w->f.scroll = nil;
1020 if(selectq < w->org)
1021 q0 = selectq;
1022 else
1023 q0 = w->org + w->f.p0;
1024 if(selectq > w->org+w->f.nchars)
1025 q1 = selectq;
1026 else
1027 q1 = w->org+w->f.p1;
1029 if(q0 == q1){
1030 if(q0==w->q0 && clickwin==w && w->mc.m.msec-clickmsec<500){
1031 wdoubleclick(w, &q0, &q1);
1032 clickwin = nil;
1033 }else{
1034 clickwin = w;
1035 clickmsec = w->mc.m.msec;
1037 }else
1038 clickwin = nil;
1039 wsetselect(w, q0, q1);
1040 flushimage(display, 1);
1041 while(w->mc.m.buttons){
1042 w->mc.m.msec = 0;
1043 b = w->mc.m.buttons;
1044 if(b & 6){
1045 if(b & 2){
1046 wsnarf(w);
1047 wcut(w);
1048 }else{
1049 if(first){
1050 first = 0;
1051 riogetsnarf();
1053 wpaste(w);
1056 wscrdraw(w);
1057 flushimage(display, 1);
1058 while(w->mc.m.buttons == b)
1059 readmouse(&w->mc);
1060 clickwin = nil;
1064 void
1065 wsendctlmesg(Window *w, int type, Rectangle r, Image *image)
1067 Wctlmesg wcm;
1069 wcm.type = type;
1070 wcm.r = r;
1071 wcm.image = image;
1072 send(w->cctl, &wcm);
1075 int
1076 wctlmesg(Window *w, int m, Rectangle r, Image *i)
1078 char buf[64];
1080 switch(m){
1081 default:
1082 error("unknown control message");
1083 break;
1084 case Wakeup:
1085 break;
1086 case Moved:
1087 case Reshaped:
1088 if(w->deleted){
1089 freeimage(i);
1090 break;
1092 w->screenr = r;
1093 strcpy(buf, w->name);
1094 wresize(w, i, m==Moved);
1095 w->wctlready = 1;
1096 if(Dx(r) > 0){
1097 if(w != input)
1098 wcurrent(w);
1099 }else if(w == input)
1100 wcurrent(nil);
1101 flushimage(display, 1);
1102 break;
1103 case Refresh:
1104 if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r))
1105 break;
1106 if(!w->mouseopen)
1107 wrefresh(w, r);
1108 flushimage(display, 1);
1109 break;
1110 case Movemouse:
1111 if(sweeping || !ptinrect(r.min, w->i->r))
1112 break;
1113 wmovemouse(w, r.min);
1114 case Rawon:
1115 break;
1116 case Rawoff:
1117 if(w->deleted)
1118 break;
1119 while(w->nraw > 0){
1120 wkeyctl(w, w->raw[0]);
1121 --w->nraw;
1122 runemove(w->raw, w->raw+1, w->nraw);
1124 break;
1125 case Holdon:
1126 case Holdoff:
1127 if(w->deleted)
1128 break;
1129 wrepaint(w);
1130 flushimage(display, 1);
1131 break;
1132 case Deleted:
1133 if(w->deleted)
1134 break;
1135 write(w->notefd, "hangup", 6);
1136 wclosewin(w);
1137 break;
1138 case Exited:
1139 frclear(&w->f, TRUE);
1140 close(w->notefd);
1141 chanfree(w->mc.c);
1142 chanfree(w->ck);
1143 chanfree(w->cctl);
1144 chanfree(w->conswrite);
1145 chanfree(w->consread);
1146 chanfree(w->mouseread);
1147 chanfree(w->wctlread);
1148 free(w->raw);
1149 free(w->r);
1150 free(w->dir);
1151 free(w->label);
1152 free(w);
1153 break;
1155 return m;
1159 * Convert back to physical coordinates
1161 void
1162 wmovemouse(Window *w, Point p)
1164 p.x += w->screenr.min.x-w->i->r.min.x;
1165 p.y += w->screenr.min.y-w->i->r.min.y;
1166 moveto(mousectl, p);
1169 void
1170 wcurrent(Window *w)
1172 Window *oi;
1174 if(wkeyboard!=nil && w==wkeyboard)
1175 return;
1176 oi = input;
1177 input = w;
1178 if(oi!=w && oi!=nil)
1179 wrepaint(oi);
1180 if(w !=nil){
1181 wrepaint(w);
1182 wsetcursor(w, 0);
1184 if(w != oi){
1185 if(oi){
1186 oi->wctlready = 1;
1187 wsendctlmesg(oi, Wakeup, ZR, nil);
1189 if(w){
1190 w->wctlready = 1;
1191 wsendctlmesg(w, Wakeup, ZR, nil);
1196 void
1197 wsetcursor(Window *w, int force)
1199 Cursor *p;
1201 if(w==nil || /*w!=input || */ w->i==nil || Dx(w->screenr)<=0)
1202 p = nil;
1203 else if(wpointto(mouse->xy) == w){
1204 p = w->cursorp;
1205 if(p==nil && w->holding)
1206 p = &whitearrow;
1207 }else
1208 p = nil;
1209 if(!menuing)
1210 riosetcursor(p, force && !menuing);
1213 void
1214 riosetcursor(Cursor *p, int force)
1216 if(!force && p==lastcursor)
1217 return;
1218 setcursor(mousectl, p);
1219 lastcursor = p;
1222 Window*
1223 wtop(Point pt)
1225 Window *w;
1227 w = wpointto(pt);
1228 if(w){
1229 if(w->topped == topped)
1230 return nil;
1231 topwindow(w->i);
1232 wcurrent(w);
1233 flushimage(display, 1);
1234 w->topped = ++topped;
1236 return w;
1239 void
1240 wtopme(Window *w)
1242 if(w!=nil && w->i!=nil && !w->deleted && w->topped!=topped){
1243 topwindow(w->i);
1244 flushimage(display, 1);
1245 w->topped = ++ topped;
1249 void
1250 wbottomme(Window *w)
1252 if(w!=nil && w->i!=nil && !w->deleted){
1253 bottomwindow(w->i);
1254 flushimage(display, 1);
1255 w->topped = 0;
1259 Window*
1260 wlookid(int id)
1262 int i;
1264 for(i=0; i<nwindow; i++)
1265 if(window[i]->id == id)
1266 return window[i];
1267 return nil;
1270 void
1271 wclosewin(Window *w)
1273 Rectangle r;
1274 int i;
1276 w->deleted = TRUE;
1277 if(w == input){
1278 input = nil;
1279 wsetcursor(w, 0);
1281 if(w == wkeyboard)
1282 wkeyboard = nil;
1283 for(i=0; i<nhidden; i++)
1284 if(hidden[i] == w){
1285 --nhidden;
1286 memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof(hidden[0]));
1287 break;
1289 for(i=0; i<nwindow; i++)
1290 if(window[i] == w){
1291 --nwindow;
1292 memmove(window+i, window+i+1, (nwindow-i)*sizeof(Window*));
1293 w->deleted = TRUE;
1294 r = w->i->r;
1295 /* move it off-screen to hide it, in case client is slow in letting it go */
1296 MOVEIT originwindow(w->i, r.min, view->r.max);
1297 freeimage(w->i);
1298 w->i = nil;
1299 return;
1301 error("unknown window in closewin");
1304 void
1305 wsetpid(Window *w, int pid, int dolabel)
1307 char buf[128];
1309 w->pid = pid;
1310 if(dolabel){
1311 sprint(buf, "rc %d", pid);
1312 free(w->label);
1313 w->label = estrdup(buf);
1314 drawsetlabel(w->label);
1318 static Rune left1[] = {
1319 '{', '[', '(', '<', 0xAB,
1320 0x207d, 0x2329, 0x27e6, 0x27e8, 0x27ea,
1321 0xfe59, 0xfe5b, 0xfe5d, 0xff08, 0xff3b, 0xff5b,
1324 static Rune right1[] = {
1325 '}', ']', ')', '>', 0xBB,
1326 0x207e, 0x232a, 0x27e7, 0x27e9, 0x27eb,
1327 0xfe5a, 0xfe5c, 0xfe5e, 0xff09, 0xff3d, 0xff5d,
1330 static Rune left2[] = { '\n', 0 };
1331 static Rune left3[] = { '\'', '"', '`', 0 };
1333 Rune *left[] = {
1334 left1,
1335 left2,
1336 left3,
1337 nil
1339 Rune *right[] = {
1340 right1,
1341 left2,
1342 left3,
1343 nil
1346 void
1347 wdoubleclick(Window *w, uint *q0, uint *q1)
1349 int c, i;
1350 Rune *r, *l, *p;
1351 uint q;
1353 for(i=0; left[i]!=nil; i++){
1354 q = *q0;
1355 l = left[i];
1356 r = right[i];
1357 /* try matching character to left, looking right */
1358 if(q == 0)
1359 c = '\n';
1360 else
1361 c = w->r[q-1];
1362 p = strrune(l, c);
1363 if(p != nil){
1364 if(wclickmatch(w, c, r[p-l], 1, &q))
1365 *q1 = q-(c!='\n');
1366 return;
1368 /* try matching character to right, looking left */
1369 if(q == w->nr)
1370 c = '\n';
1371 else
1372 c = w->r[q];
1373 p = strrune(r, c);
1374 if(p != nil){
1375 if(wclickmatch(w, c, l[p-r], -1, &q)){
1376 *q1 = *q0+(*q0<w->nr && c=='\n');
1377 *q0 = q;
1378 if(c!='\n' || q!=0 || w->r[0]=='\n')
1379 (*q0)++;
1381 return;
1384 /* try filling out word to right */
1385 while(*q1<w->nr && isalnum(w->r[*q1]))
1386 (*q1)++;
1387 /* try filling out word to left */
1388 while(*q0>0 && isalnum(w->r[*q0-1]))
1389 (*q0)--;
1392 int
1393 wclickmatch(Window *w, int cl, int cr, int dir, uint *q)
1395 Rune c;
1396 int nest;
1398 nest = 1;
1399 for(;;){
1400 if(dir > 0){
1401 if(*q == w->nr)
1402 break;
1403 c = w->r[*q];
1404 (*q)++;
1405 }else{
1406 if(*q == 0)
1407 break;
1408 (*q)--;
1409 c = w->r[*q];
1411 if(c == cr){
1412 if(--nest==0)
1413 return 1;
1414 }else if(c == cl)
1415 nest++;
1417 return cl=='\n' && nest==1;
1421 uint
1422 wbacknl(Window *w, uint p, uint n)
1424 int i, j;
1426 /* look for start of this line if n==0 */
1427 if(n==0 && p>0 && w->r[p-1]!='\n')
1428 n = 1;
1429 i = n;
1430 while(i-->0 && p>0){
1431 --p; /* it's at a newline now; back over it */
1432 if(p == 0)
1433 break;
1434 /* at 128 chars, call it a line anyway */
1435 for(j=128; --j>0 && p>0; p--)
1436 if(w->r[p-1]=='\n')
1437 break;
1439 return p;
1442 void
1443 wshow(Window *w, uint q0)
1445 int qe;
1446 int nl;
1447 uint q;
1449 qe = w->org+w->f.nchars;
1450 if(w->org<=q0 && (q0<qe || (q0==qe && qe==w->nr)))
1451 wscrdraw(w);
1452 else{
1453 nl = 4*w->f.maxlines/5;
1454 q = wbacknl(w, q0, nl);
1455 /* avoid going backwards if trying to go forwards - long lines! */
1456 if(!(q0>w->org && q<w->org))
1457 wsetorigin(w, q, TRUE);
1458 while(q0 > w->org+w->f.nchars)
1459 wsetorigin(w, w->org+1, FALSE);
1463 void
1464 wsetorigin(Window *w, uint org, int exact)
1466 int i, a, fixup;
1467 Rune *r;
1468 uint n;
1470 if(org>0 && !exact){
1471 /* org is an estimate of the char posn; find a newline */
1472 /* don't try harder than 256 chars */
1473 for(i=0; i<256 && org<w->nr; i++){
1474 if(w->r[org] == '\n'){
1475 org++;
1476 break;
1478 org++;
1481 a = org-w->org;
1482 fixup = 0;
1483 if(a>=0 && a<w->f.nchars){
1484 frdelete(&w->f, 0, a);
1485 fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
1486 }else if(a<0 && -a<w->f.nchars){
1487 n = w->org - org;
1488 r = runemalloc(n);
1489 runemove(r, w->r+org, n);
1490 frinsert(&w->f, r, r+n, 0);
1491 free(r);
1492 }else
1493 frdelete(&w->f, 0, w->f.nchars);
1494 w->org = org;
1495 wfill(w);
1496 wscrdraw(w);
1497 wsetselect(w, w->q0, w->q1);
1498 if(fixup && w->f.p1 > w->f.p0)
1499 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1-1), w->f.p1-1, w->f.p1, 1);
1502 void
1503 wsetselect(Window *w, uint q0, uint q1)
1505 int p0, p1;
1507 /* w->f.p0 and w->f.p1 are always right; w->q0 and w->q1 may be off */
1508 w->q0 = q0;
1509 w->q1 = q1;
1510 /* compute desired p0,p1 from q0,q1 */
1511 p0 = q0-w->org;
1512 p1 = q1-w->org;
1513 if(p0 < 0)
1514 p0 = 0;
1515 if(p1 < 0)
1516 p1 = 0;
1517 if(p0 > w->f.nchars)
1518 p0 = w->f.nchars;
1519 if(p1 > w->f.nchars)
1520 p1 = w->f.nchars;
1521 if(p0==w->f.p0 && p1==w->f.p1)
1522 return;
1523 /* screen disagrees with desired selection */
1524 if(w->f.p1<=p0 || p1<=w->f.p0 || p0==p1 || w->f.p1==w->f.p0){
1525 /* no overlap or too easy to bother trying */
1526 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 0);
1527 frdrawsel(&w->f, frptofchar(&w->f, p0), p0, p1, 1);
1528 goto Return;
1530 /* overlap; avoid unnecessary painting */
1531 if(p0 < w->f.p0){
1532 /* extend selection backwards */
1533 frdrawsel(&w->f, frptofchar(&w->f, p0), p0, w->f.p0, 1);
1534 }else if(p0 > w->f.p0){
1535 /* trim first part of selection */
1536 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, p0, 0);
1538 if(p1 > w->f.p1){
1539 /* extend selection forwards */
1540 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, p1, 1);
1541 }else if(p1 < w->f.p1){
1542 /* trim last part of selection */
1543 frdrawsel(&w->f, frptofchar(&w->f, p1), p1, w->f.p1, 0);
1546 Return:
1547 w->f.p0 = p0;
1548 w->f.p1 = p1;
1551 uint
1552 winsert(Window *w, Rune *r, int n, uint q0)
1554 uint m;
1556 if(n == 0)
1557 return q0;
1558 if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){
1559 m = min(HiWater-LoWater, min(w->org, w->qh));
1560 w->org -= m;
1561 w->qh -= m;
1562 if(w->q0 > m)
1563 w->q0 -= m;
1564 else
1565 w->q0 = 0;
1566 if(w->q1 > m)
1567 w->q1 -= m;
1568 else
1569 w->q1 = 0;
1570 w->nr -= m;
1571 runemove(w->r, w->r+m, w->nr);
1572 q0 -= m;
1574 if(w->nr+n > w->maxr){
1576 * Minimize realloc breakage:
1577 * Allocate at least MinWater
1578 * Double allocation size each time
1579 * But don't go much above HiWater
1581 m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater;
1582 if(m > HiWater)
1583 m = max(HiWater+MinWater, w->nr+n);
1584 if(m > w->maxr){
1585 w->r = runerealloc(w->r, m);
1586 w->maxr = m;
1589 runemove(w->r+q0+n, w->r+q0, w->nr-q0);
1590 runemove(w->r+q0, r, n);
1591 w->nr += n;
1592 /* if output touches, advance selection, not qh; works best for keyboard and output */
1593 if(q0 <= w->q1)
1594 w->q1 += n;
1595 if(q0 <= w->q0)
1596 w->q0 += n;
1597 if(q0 < w->qh)
1598 w->qh += n;
1599 if(q0 < w->org)
1600 w->org += n;
1601 else if(q0 <= w->org+w->f.nchars)
1602 frinsert(&w->f, r, r+n, q0-w->org);
1603 return q0;
1606 void
1607 wfill(Window *w)
1609 Rune *rp;
1610 int i, n, m, nl;
1612 if(w->f.lastlinefull)
1613 return;
1614 rp = malloc(messagesize);
1615 do{
1616 n = w->nr-(w->org+w->f.nchars);
1617 if(n == 0)
1618 break;
1619 if(n > 2000) /* educated guess at reasonable amount */
1620 n = 2000;
1621 runemove(rp, w->r+(w->org+w->f.nchars), n);
1623 * it's expensive to frinsert more than we need, so
1624 * count newlines.
1626 nl = w->f.maxlines-w->f.nlines;
1627 m = 0;
1628 for(i=0; i<n; ){
1629 if(rp[i++] == '\n'){
1630 m++;
1631 if(m >= nl)
1632 break;
1635 frinsert(&w->f, rp, rp+i, w->f.nchars);
1636 }while(w->f.lastlinefull == FALSE);
1637 free(rp);
1640 char*
1641 wcontents(Window *w, int *ip)
1643 return runetobyte(w->r, w->nr, ip);