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 = 64000000, /* 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 static int
39 wscale(Window *w, int n)
40 {
41 if(w == nil || w->i == nil)
42 return n;
43 return scalesize(w->i->display, n);
44 }
46 Window*
47 wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl, int scrolling)
48 {
49 Window *w;
50 Rectangle r;
52 if(cols[0] == nil){
53 /* greys are multiples of 0x11111100+0xFF, 14* being palest */
54 grey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
55 darkgrey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x666666FF);
56 cols[BACK] = display->white;
57 cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
58 cols[BORD] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x999999FF);
59 cols[TEXT] = display->black;
60 cols[HTEXT] = display->black;
61 titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreygreen);
62 lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreygreen);
63 holdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedblue);
64 lightholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreyblue);
65 paleholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreyblue);
66 }
67 w = emalloc(sizeof(Window));
68 w->screenr = i->r;
69 r = insetrect(i->r, wscale(w, Selborder)+wscale(w, 1));
70 w->i = i;
71 w->mc = *mc;
72 w->ck = ck;
73 w->cctl = cctl;
74 w->cursorp = nil;
75 w->conswrite = chancreate(sizeof(Conswritemesg), 0);
76 w->consread = chancreate(sizeof(Consreadmesg), 0);
77 w->mouseread = chancreate(sizeof(Mousereadmesg), 0);
78 w->wctlread = chancreate(sizeof(Consreadmesg), 0);
79 w->scrollr = r;
80 w->scrollr.max.x = r.min.x+wscale(w, Scrollwid);
81 w->lastsr = ZR;
82 r.min.x += wscale(w, Scrollwid)+wscale(w, Scrollgap);
83 frinit(&w->f, r, font, i, cols);
84 w->f.maxtab = maxtab*stringwidth(font, "0");
85 w->topped = ++topped;
86 w->id = ++id;
87 w->notefd = -1;
88 w->scrolling = scrolling;
89 w->dir = estrdup(startdir);
90 w->label = estrdup("<unnamed>");
91 r = insetrect(w->i->r, wscale(w, Selborder));
92 draw(w->i, r, cols[BACK], nil, w->f.entire.min);
93 wborder(w, wscale(w, Selborder));
94 wscrdraw(w);
95 incref(&w->ref); /* ref will be removed after mounting; avoids delete before ready to be deleted */
96 return w;
97 }
99 void
100 wsetname(Window *w)
102 int i, n;
103 char err[ERRMAX];
105 n = sprint(w->name, "window.%d.%d", w->id, w->namecount++);
106 for(i='A'; i<='Z'; i++){
107 if(nameimage(w->i, w->name, 1) > 0)
108 return;
109 errstr(err, sizeof err);
110 if(strcmp(err, "image name in use") != 0)
111 break;
112 w->name[n] = i;
113 w->name[n+1] = 0;
115 w->name[0] = 0;
116 fprint(2, "rio: setname failed: %s\n", err);
119 void
120 wresize(Window *w, Image *i, int move)
122 Rectangle r, or;
124 or = w->i->r;
125 if(move || (Dx(or)==Dx(i->r) && Dy(or)==Dy(i->r)))
126 draw(i, i->r, w->i, nil, w->i->r.min);
127 if(w->i != i){
128 fprint(2, "res %p %p\n", w->i, i);
129 freeimage(w->i);
130 w->i = i;
132 /* wsetname(w); */
133 /*XXX w->mc.image = i; */
134 r = insetrect(i->r, wscale(w, Selborder)+wscale(w, 1));
135 w->scrollr = r;
136 w->scrollr.max.x = r.min.x+wscale(w, Scrollwid);
137 w->lastsr = ZR;
138 r.min.x += wscale(w, Scrollwid)+wscale(w, Scrollgap);
139 if(move)
140 frsetrects(&w->f, r, w->i);
141 else{
142 frclear(&w->f, FALSE);
143 frinit(&w->f, r, w->f.font, w->i, cols);
144 wsetcols(w);
145 w->f.maxtab = maxtab*stringwidth(w->f.font, "0");
146 r = insetrect(w->i->r, wscale(w, Selborder));
147 draw(w->i, r, cols[BACK], nil, w->f.entire.min);
148 wfill(w);
149 wsetselect(w, w->q0, w->q1);
150 wscrdraw(w);
152 wborder(w, wscale(w, Selborder));
153 w->topped = ++topped;
154 w->resized = TRUE;
155 w->mouse.counter++;
158 void
159 wrefresh(Window *w, Rectangle r)
161 /* USED(r); */
163 /* BUG: rectangle is ignored */
164 if(w == input)
165 wborder(w, wscale(w, Selborder));
166 else
167 wborder(w, wscale(w, Unselborder));
168 if(w->mouseopen)
169 return;
170 draw(w->i, insetrect(w->i->r, Borderwidth), w->f.cols[BACK], nil, w->i->r.min);
171 w->f.ticked = 0;
172 if(w->f.p0 > 0)
173 frdrawsel(&w->f, frptofchar(&w->f, 0), 0, w->f.p0, 0);
174 if(w->f.p1 < w->f.nchars)
175 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, w->f.nchars, 0);
176 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 1);
177 w->lastsr = ZR;
178 wscrdraw(w);
181 int
182 wclose(Window *w)
184 int i;
186 i = decref(&w->ref);
187 if(i > 0)
188 return 0;
189 if(i < 0)
190 error("negative ref count");
191 if(!w->deleted)
192 wclosewin(w);
193 wsendctlmesg(w, Exited, ZR, nil);
194 return 1;
198 void
199 winctl(void *arg)
201 Rune *rp, *bp, *up, *kbdr;
202 uint qh;
203 int nr, nb, c, wid, i, npart, initial, lastb, scrolling;
204 char *s, *t, part[UTFmax];
205 Window *w;
206 Mousestate *mp, m;
207 enum { WKey, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT };
208 Alt alts[NWALT+1];
209 Mousereadmesg mrm;
210 Conswritemesg cwm;
211 Consreadmesg crm;
212 Consreadmesg cwrm;
213 Stringpair pair;
214 Wctlmesg wcm;
215 char buf[4*12+1];
217 w = arg;
218 snprint(buf, sizeof buf, "winctl-id%d", w->id);
219 threadsetname(buf);
221 mrm.cm = chancreate(sizeof(Mouse), 0);
222 cwm.cw = chancreate(sizeof(Stringpair), 0);
223 crm.c1 = chancreate(sizeof(Stringpair), 0);
224 crm.c2 = chancreate(sizeof(Stringpair), 0);
225 cwrm.c1 = chancreate(sizeof(Stringpair), 0);
226 cwrm.c2 = chancreate(sizeof(Stringpair), 0);
229 alts[WKey].c = w->ck;
230 alts[WKey].v = &kbdr;
231 alts[WKey].op = CHANRCV;
232 alts[WMouse].c = w->mc.c;
233 alts[WMouse].v = &w->mc.m;
234 alts[WMouse].op = CHANRCV;
235 alts[WMouseread].c = w->mouseread;
236 alts[WMouseread].v = &mrm;
237 alts[WMouseread].op = CHANSND;
238 alts[WCtl].c = w->cctl;
239 alts[WCtl].v = &wcm;
240 alts[WCtl].op = CHANRCV;
241 alts[WCwrite].c = w->conswrite;
242 alts[WCwrite].v = &cwm;
243 alts[WCwrite].op = CHANSND;
244 alts[WCread].c = w->consread;
245 alts[WCread].v = &crm;
246 alts[WCread].op = CHANSND;
247 alts[WWread].c = w->wctlread;
248 alts[WWread].v = &cwrm;
249 alts[WWread].op = CHANSND;
250 alts[NWALT].op = CHANEND;
252 npart = 0;
253 lastb = -1;
254 for(;;){
255 if(w->mouseopen && w->mouse.counter != w->mouse.lastcounter)
256 alts[WMouseread].op = CHANSND;
257 else
258 alts[WMouseread].op = CHANNOP;
259 // if(!w->scrolling && !w->mouseopen && w->qh>w->org+w->f.nchars)
260 // alts[WCwrite].op = CHANNOP;
261 // else
262 alts[WCwrite].op = CHANSND;
263 if(w->deleted || !w->wctlready)
264 alts[WWread].op = CHANNOP;
265 else
266 alts[WWread].op = CHANSND;
267 /* this code depends on NL and EOT fitting in a single byte */
268 /* kind of expensive for each loop; worth precomputing? */
269 if(w->holding)
270 alts[WCread].op = CHANNOP;
271 else if(npart || (w->rawing && w->nraw>0))
272 alts[WCread].op = CHANSND;
273 else{
274 alts[WCread].op = CHANNOP;
275 for(i=w->qh; i<w->nr; i++){
276 c = w->r[i];
277 if(c=='\n' || c=='\004'){
278 alts[WCread].op = CHANSND;
279 break;
283 switch(alt(alts)){
284 case WKey:
285 for(i=0; kbdr[i]!=L'\0'; i++)
286 wkeyctl(w, kbdr[i]);
287 /* wkeyctl(w, r); */
288 /* while(nbrecv(w->ck, &r)) */
289 /* wkeyctl(w, r); */
290 break;
291 case WMouse:
292 if(w->mouseopen) {
293 w->mouse.counter++;
295 /* queue click events */
296 if(!w->mouse.qfull && lastb != w->mc.m.buttons) { /* add to ring */
297 mp = &w->mouse.queue[w->mouse.wi];
298 if(++w->mouse.wi == nelem(w->mouse.queue))
299 w->mouse.wi = 0;
300 if(w->mouse.wi == w->mouse.ri)
301 w->mouse.qfull = TRUE;
302 mp->m = w->mc.m;
303 mp->counter = w->mouse.counter;
304 lastb = w->mc.m.buttons;
306 } else
307 wmousectl(w);
308 break;
309 case WMouseread:
310 /* send a queued event or, if the queue is empty, the current state */
311 /* if the queue has filled, we discard all the events it contained. */
312 /* the intent is to discard frantic clicking by the user during long latencies. */
313 w->mouse.qfull = FALSE;
314 if(w->mouse.wi != w->mouse.ri) {
315 m = w->mouse.queue[w->mouse.ri];
316 if(++w->mouse.ri == nelem(w->mouse.queue))
317 w->mouse.ri = 0;
318 } else {
319 m.m = w->mc.m;
320 m.counter = w->mouse.counter;
322 w->mouse.lastcounter = m.counter;
323 send(mrm.cm, &m.m);
324 continue;
325 case WCtl:
326 if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Exited){
327 chanfree(crm.c1);
328 chanfree(crm.c2);
329 chanfree(mrm.cm);
330 chanfree(cwm.cw);
331 chanfree(cwrm.c1);
332 chanfree(cwrm.c2);
333 threadexits(nil);
335 continue;
336 case WCwrite:
337 recv(cwm.cw, &pair);
338 rp = pair.s;
339 nr = pair.ns;
340 bp = rp;
341 up = rp;
342 initial = 0;
343 for(i=0; i<nr; i++){
344 switch(*bp){
345 case 0:
346 break;
347 case '\b':
348 if(up == rp)
349 initial++;
350 else
351 --up;
352 break;
353 case '\r':
354 while(i<nr-1 && *(bp+1) == '\r'){
355 bp++;
356 i++;
358 if(i<nr-1 && *(bp+1) != '\n'){
359 while(up > rp && *(up-1) != '\n')
360 up--;
361 if(up == rp)
362 initial = wbswidth(w, '\r');
363 }else if(i == nr-1)
364 *up++ = '\n';
365 break;
366 default:
367 *up++ = *bp;
368 break;
370 bp++;
372 if(initial){
373 if(initial > w->qh)
374 initial = w->qh;
375 qh = w->qh - initial;
376 wdelete(w, qh, qh+initial);
377 w->qh = qh;
379 nr = up - rp;
380 scrolling = w->scrolling && w->org <= w->qh && w->qh <= w->org + w->f.nchars;
381 w->qh = winsert(w, rp, nr, w->qh)+nr;
382 if(scrolling)
383 wshow(w, w->qh);
384 wsetselect(w, w->q0, w->q1);
385 wscrdraw(w);
386 free(rp);
387 break;
388 case WCread:
389 recv(crm.c1, &pair);
390 t = pair.s;
391 nb = pair.ns;
392 i = npart;
393 npart = 0;
394 if(i)
395 memmove(t, part, i);
396 while(i<nb && (w->qh<w->nr || w->nraw>0)){
397 if(w->qh == w->nr){
398 wid = runetochar(t+i, &w->raw[0]);
399 w->nraw--;
400 runemove(w->raw, w->raw+1, w->nraw);
401 }else
402 wid = runetochar(t+i, &w->r[w->qh++]);
403 c = t[i]; /* knows break characters fit in a byte */
404 i += wid;
405 if(!w->rawing && (c == '\n' || c=='\004')){
406 /* if(c == '\004') */
407 /* i--; */
408 break;
411 /* if(i==nb && w->qh<w->nr && w->r[w->qh]=='\004') */
412 /* w->qh++; */
413 if(i > nb){
414 npart = i-nb;
415 memmove(part, t+nb, npart);
416 i = nb;
418 pair.s = t;
419 pair.ns = i;
420 send(crm.c2, &pair);
421 continue;
422 case WWread:
423 w->wctlready = 0;
424 recv(cwrm.c1, &pair);
425 if(w->deleted || w->i==nil)
426 pair.ns = sprint(pair.s, "");
427 else{
428 s = "visible";
429 for(i=0; i<nhidden; i++)
430 if(hidden[i] == w){
431 s = "hidden";
432 break;
434 t = "notcurrent";
435 if(w == input)
436 t = "current";
437 pair.ns = snprint(pair.s, pair.ns, "%11d %11d %11d %11d %s %s ",
438 w->i->r.min.x, w->i->r.min.y, w->i->r.max.x, w->i->r.max.y, t, s);
440 send(cwrm.c2, &pair);
441 continue;
443 if(!w->deleted)
444 flushimage(display, 1);
448 void
449 waddraw(Window *w, Rune *r, int nr)
451 w->raw = runerealloc(w->raw, w->nraw+nr);
452 runemove(w->raw+w->nraw, r, nr);
453 w->nraw += nr;
456 /*
457 * Need to do this in a separate proc because if process we're interrupting
458 * is dying and trying to print tombstone, kernel is blocked holding p->debug lock.
459 */
460 void
461 interruptproc(void *v)
463 int *notefd;
465 notefd = v;
466 write(*notefd, "interrupt", 9);
467 free(notefd);
470 int
471 windfilewidth(Window *w, uint q0, int oneelement)
473 uint q;
474 Rune r;
476 q = q0;
477 while(q > 0){
478 r = w->r[q-1];
479 if(r<=' ')
480 break;
481 if(oneelement && r=='/')
482 break;
483 --q;
485 return q0-q;
488 void
489 showcandidates(Window *w, Completion *c)
491 int i;
492 Fmt f;
493 Rune *rp;
494 uint nr, qline, q0;
495 char *s;
497 runefmtstrinit(&f);
498 if (c->nmatch == 0)
499 s = "[no matches in ";
500 else
501 s = "[";
502 if(c->nfile > 32)
503 fmtprint(&f, "%s%d files]\n", s, c->nfile);
504 else{
505 fmtprint(&f, "%s", s);
506 for(i=0; i<c->nfile; i++){
507 if(i > 0)
508 fmtprint(&f, " ");
509 fmtprint(&f, "%s", c->filename[i]);
511 fmtprint(&f, "]\n");
513 /* place text at beginning of line before host point */
514 qline = w->qh;
515 while(qline>0 && w->r[qline-1] != '\n')
516 qline--;
518 rp = runefmtstrflush(&f);
519 nr = runestrlen(rp);
521 q0 = w->q0;
522 q0 += winsert(w, rp, runestrlen(rp), qline) - qline;
523 free(rp);
524 wsetselect(w, q0+nr, q0+nr);
527 Rune*
528 namecomplete(Window *w)
530 int nstr, npath;
531 Rune *rp, *path, *str;
532 Completion *c;
533 char *s, *dir, *root;
535 /* control-f: filename completion; works back to white space or / */
536 if(w->q0<w->nr && w->r[w->q0]>' ') /* must be at end of word */
537 return nil;
538 nstr = windfilewidth(w, w->q0, TRUE);
539 str = runemalloc(nstr);
540 runemove(str, w->r+(w->q0-nstr), nstr);
541 npath = windfilewidth(w, w->q0-nstr, FALSE);
542 path = runemalloc(npath);
543 runemove(path, w->r+(w->q0-nstr-npath), npath);
544 rp = nil;
546 /* is path rooted? if not, we need to make it relative to window path */
547 if(npath>0 && path[0]=='/'){
548 dir = malloc(UTFmax*npath+1);
549 sprint(dir, "%.*S", npath, path);
550 }else{
551 if(strcmp(w->dir, "") == 0)
552 root = ".";
553 else
554 root = w->dir;
555 dir = malloc(strlen(root)+1+UTFmax*npath+1);
556 sprint(dir, "%s/%.*S", root, npath, path);
558 dir = cleanname(dir);
560 s = smprint("%.*S", nstr, str);
561 c = complete(dir, s);
562 free(s);
563 if(c == nil)
564 goto Return;
566 if(!c->advance)
567 showcandidates(w, c);
569 if(c->advance)
570 rp = runesmprint("%s", c->string);
572 Return:
573 freecompletion(c);
574 free(dir);
575 free(path);
576 free(str);
577 return rp;
580 void
581 wkeyctl(Window *w, Rune r)
583 uint q0 ,q1;
584 int n, nb, nr;
585 Rune *rp;
587 if(r == 0)
588 return;
589 if(w->deleted)
590 return;
591 w->rawing = rawon();
592 /* navigation keys work only when mouse is not open */
593 if(!w->mouseopen)
594 switch(r){
595 case Kdown:
596 n = w->f.maxlines/3;
597 goto case_Down;
598 case Kscrollonedown:
599 n = mousescrollsize(w->f.maxlines);
600 if(n <= 0)
601 n = 1;
602 goto case_Down;
603 case Kpgdown:
604 n = 2*w->f.maxlines/3;
605 case_Down:
606 q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.min.y+n*w->f.font->height));
607 wsetorigin(w, q0, TRUE);
608 return;
609 case Kup:
610 n = w->f.maxlines/3;
611 goto case_Up;
612 case Kscrolloneup:
613 n = mousescrollsize(w->f.maxlines);
614 if(n <= 0)
615 n = 1;
616 goto case_Up;
617 case Kpgup:
618 n = 2*w->f.maxlines/3;
619 case_Up:
620 q0 = wbacknl(w, w->org, n);
621 wsetorigin(w, q0, TRUE);
622 return;
623 case Kleft:
624 if(w->q0 > 0){
625 q0 = w->q0-1;
626 wsetselect(w, q0, q0);
627 wshow(w, q0);
629 return;
630 case Kright:
631 if(w->q1 < w->nr){
632 q1 = w->q1+1;
633 wsetselect(w, q1, q1);
634 wshow(w, q1);
636 return;
637 case Khome:
638 if(w->org > w->iq1) {
639 q0 = wbacknl(w, w->iq1, 1);
640 wsetorigin(w, q0, TRUE);
641 } else
642 wshow(w, 0);
643 return;
644 case Kend:
645 if(w->iq1 > w->org+w->f.nchars) {
646 q0 = wbacknl(w, w->iq1, 1);
647 wsetorigin(w, q0, TRUE);
648 } else
649 wshow(w, w->nr);
650 return;
651 case 0x01: /* ^A: beginning of line */
652 if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\n')
653 return;
654 nb = wbswidth(w, 0x15 /* ^U */);
655 wsetselect(w, w->q0-nb, w->q0-nb);
656 wshow(w, w->q0);
657 return;
658 case 0x05: /* ^E: end of line */
659 q0 = w->q0;
660 while(q0 < w->nr && w->r[q0]!='\n')
661 q0++;
662 wsetselect(w, q0, q0);
663 wshow(w, w->q0);
664 return;
666 /*
667 * This if used to be below the if(w->rawing ...),
668 * but let's try putting it here. This will allow ESC-processing
669 * to toggle hold mode even in remote SSH connections.
670 * The drawback is that vi-style processing gets harder.
671 * If you find yourself in some weird readline mode, good
672 * luck getting out without ESC. Let's see who complains.
673 */
674 if(r==0x1B || (w->holding && r==0x7F)){ /* toggle hold */
675 if(w->holding)
676 --w->holding;
677 else
678 w->holding++;
679 wrepaint(w);
680 if(r == 0x1B)
681 return;
683 if(!w->holding && w->rawing && (w->q0==w->nr || w->mouseopen)){
684 waddraw(w, &r, 1);
685 return;
687 if(r == Kcmd+'x'){
688 wsnarf(w);
689 wcut(w);
690 wscrdraw(w);
691 return;
693 if(r == Kcmd+'c'){
694 wsnarf(w);
695 return;
697 if(r == Kcmd+'v'){
698 riogetsnarf();
699 wpaste(w);
700 wscrdraw(w);
701 return;
703 if(r != 0x7F){
704 wsnarf(w);
705 wcut(w);
707 switch(r){
708 case 0x03: /* maybe send interrupt */
709 /* since ^C is so commonly used as interrupt, special case it */
710 if (intrc() != 0x03)
711 break;
712 /* fall through */
713 case 0x7F: /* send interrupt */
714 w->qh = w->nr;
715 wshow(w, w->qh);
716 winterrupt(w);
717 w->iq1 = w->q0;
718 return;
719 case 0x06: /* ^F: file name completion */
720 case Kins: /* Insert: file name completion */
721 rp = namecomplete(w);
722 if(rp == nil)
723 return;
724 nr = runestrlen(rp);
725 q0 = w->q0;
726 q0 = winsert(w, rp, nr, q0);
727 wshow(w, q0+nr);
728 w->iq1 = w->q0;
729 free(rp);
730 return;
731 case 0x08: /* ^H: erase character */
732 case 0x15: /* ^U: erase line */
733 case 0x17: /* ^W: erase word */
734 if(w->q0==0 || w->q0==w->qh)
735 return;
736 nb = wbswidth(w, r);
737 q1 = w->q0;
738 q0 = q1-nb;
739 if(q0 < w->org){
740 q0 = w->org;
741 nb = q1-q0;
743 if(nb > 0){
744 wdelete(w, q0, q0+nb);
745 wsetselect(w, q0, q0);
747 w->iq1 = w->q0;
748 return;
750 /* otherwise ordinary character; just insert */
751 q0 = w->q0;
752 q0 = winsert(w, &r, 1, q0);
753 wshow(w, q0+1);
754 w->iq1 = w->q0;
757 void
758 wsetcols(Window *w)
760 if(w->holding)
761 if(w == input)
762 w->f.cols[TEXT] = w->f.cols[HTEXT] = holdcol;
763 else
764 w->f.cols[TEXT] = w->f.cols[HTEXT] = lightholdcol;
765 else
766 if(w == input)
767 w->f.cols[TEXT] = w->f.cols[HTEXT] = display->black;
768 else
769 w->f.cols[TEXT] = w->f.cols[HTEXT] = darkgrey;
772 void
773 wrepaint(Window *w)
775 wsetcols(w);
776 if(!w->mouseopen){
777 frredraw(&w->f);
779 if(w == input){
780 wborder(w, wscale(w, Selborder));
781 wsetcursor(w, 0);
782 }else
783 wborder(w, wscale(w, Unselborder));
786 int
787 wbswidth(Window *w, Rune c)
789 uint q, eq, stop;
790 Rune r;
791 int skipping;
793 /* there is known to be at least one character to erase */
794 if(c == 0x08) /* ^H: erase character */
795 return 1;
796 q = w->q0;
797 stop = 0;
798 if(q > w->qh)
799 stop = w->qh;
800 skipping = TRUE;
801 while(q > stop){
802 r = w->r[q-1];
803 if(r == '\n'){ /* eat at most one more character */
804 if(q == w->q0 && c != '\r') /* eat the newline */
805 --q;
806 break;
808 if(c == 0x17){
809 eq = isalnum(r);
810 if(eq && skipping) /* found one; stop skipping */
811 skipping = FALSE;
812 else if(!eq && !skipping)
813 break;
815 --q;
817 return w->q0-q;
820 void
821 wsnarf(Window *w)
823 if(w->q1 == w->q0)
824 return;
825 nsnarf = w->q1-w->q0;
826 snarf = runerealloc(snarf, nsnarf);
827 snarfversion++; /* maybe modified by parent */
828 runemove(snarf, w->r+w->q0, nsnarf);
829 rioputsnarf();
832 void
833 wcut(Window *w)
835 if(w->q1 == w->q0)
836 return;
837 wdelete(w, w->q0, w->q1);
838 wsetselect(w, w->q0, w->q0);
841 void
842 wpaste(Window *w)
844 uint q0;
846 if(nsnarf == 0)
847 return;
848 wcut(w);
849 q0 = w->q0;
850 if(w->rawing && !w->holding && q0==w->nr){
851 waddraw(w, snarf, nsnarf);
852 wsetselect(w, q0, q0);
853 }else{
854 q0 = winsert(w, snarf, nsnarf, w->q0);
855 wsetselect(w, q0, q0+nsnarf);
859 void
860 wplumb(Window *w)
862 Plumbmsg *m;
863 static CFid *fd;
864 char buf[32];
865 uint p0, p1;
866 Cursor *c;
868 if(fd == nil)
869 fd = plumbopenfid("send", OWRITE);
870 if(fd == nil)
871 return;
872 m = emalloc(sizeof(Plumbmsg));
873 m->src = estrdup("rio");
874 m->dst = nil;
875 m->wdir = estrdup(w->dir);
876 m->type = estrdup("text");
877 p0 = w->q0;
878 p1 = w->q1;
879 if(w->q1 > w->q0)
880 m->attr = nil;
881 else{
882 while(p0>0 && w->r[p0-1]!=' ' && w->r[p0-1]!='\t' && w->r[p0-1]!='\n')
883 p0--;
884 while(p1<w->nr && w->r[p1]!=' ' && w->r[p1]!='\t' && w->r[p1]!='\n')
885 p1++;
886 sprint(buf, "click=%d", w->q0-p0);
887 m->attr = plumbunpackattr(buf);
889 if(p1-p0 > messagesize-1024){
890 plumbfree(m);
891 return; /* too large for 9P */
893 m->data = runetobyte(w->r+p0, p1-p0, &m->ndata);
894 if(plumbsendtofid(fd, m) < 0){
895 c = lastcursor;
896 riosetcursor(&query, 1);
897 sleep(300);
898 riosetcursor(c, 1);
900 plumbfree(m);
903 int
904 winborder(Window *w, Point xy)
906 return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->screenr, wscale(w, Selborder)));
909 void
910 wlook(Window *w)
912 int i, n, e;
914 i = w->q1;
915 n = i - w->q0;
916 e = w->nr - n;
917 if(n <= 0 || e < n)
918 return;
920 if(i > e)
921 i = 0;
923 while(runestrncmp(w->r+w->q0, w->r+i, n) != 0){
924 if(i < e)
925 i++;
926 else
927 i = 0;
930 wsetselect(w, i, i+n);
931 wshow(w, i);
934 void
935 wmousectl(Window *w)
937 int but;
939 if(w->mc.m.buttons == 1)
940 but = 1;
941 else if(w->mc.m.buttons == 2)
942 but = 2;
943 else if(w->mc.m.buttons == 4)
944 but = 3;
945 else{
946 if(w->mc.m.buttons == 8)
947 wkeyctl(w, Kscrolloneup);
948 if(w->mc.m.buttons == 16)
949 wkeyctl(w, Kscrollonedown);
950 return;
953 incref(&w->ref); /* hold up window while we track */
954 if(w->deleted)
955 goto Return;
956 if(ptinrect(w->mc.m.xy, w->scrollr)){
957 if(but)
958 wscroll(w, but);
959 goto Return;
961 if(but == 1)
962 wselect(w);
963 /* else all is handled by main process */
964 Return:
965 wclose(w);
968 void
969 wdelete(Window *w, uint q0, uint q1)
971 uint n, p0, p1;
973 n = q1-q0;
974 if(n == 0)
975 return;
976 runemove(w->r+q0, w->r+q1, w->nr-q1);
977 w->nr -= n;
978 if(q0 < w->iq1)
979 w->iq1 -= min(n, w->iq1-q0);
980 if(q0 < w->q0)
981 w->q0 -= min(n, w->q0-q0);
982 if(q0 < w->q1)
983 w->q1 -= min(n, w->q1-q0);
984 if(q1 < w->qh)
985 w->qh -= n;
986 else if(q0 < w->qh)
987 w->qh = q0;
988 if(q1 <= w->org)
989 w->org -= n;
990 else if(q0 < w->org+w->f.nchars){
991 p1 = q1 - w->org;
992 if(p1 > w->f.nchars)
993 p1 = w->f.nchars;
994 if(q0 < w->org){
995 w->org = q0;
996 p0 = 0;
997 }else
998 p0 = q0 - w->org;
999 frdelete(&w->f, p0, p1);
1000 wfill(w);
1005 static Window *clickwin;
1006 static uint clickmsec;
1007 static Window *selectwin;
1008 static uint selectq;
1011 * called from frame library
1013 void
1014 framescroll(Frame *f, int dl)
1016 if(f != &selectwin->f)
1017 error("frameselect not right frame");
1018 wframescroll(selectwin, dl);
1021 void
1022 wframescroll(Window *w, int dl)
1024 uint q0;
1026 if(dl == 0){
1027 wscrsleep(w, 100);
1028 return;
1030 if(dl < 0){
1031 q0 = wbacknl(w, w->org, -dl);
1032 if(selectq > w->org+w->f.p0)
1033 wsetselect(w, w->org+w->f.p0, selectq);
1034 else
1035 wsetselect(w, selectq, w->org+w->f.p0);
1036 }else{
1037 if(w->org+w->f.nchars == w->nr)
1038 return;
1039 q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.min.y+dl*w->f.font->height));
1040 if(selectq >= w->org+w->f.p1)
1041 wsetselect(w, w->org+w->f.p1, selectq);
1042 else
1043 wsetselect(w, selectq, w->org+w->f.p1);
1045 wsetorigin(w, q0, TRUE);
1048 void
1049 wselect(Window *w)
1051 uint q0, q1;
1052 int b, x, y, first;
1054 first = 1;
1055 selectwin = w;
1057 * Double-click immediately if it might make sense.
1059 b = w->mc.m.buttons;
1060 q0 = w->q0;
1061 q1 = w->q1;
1062 selectq = w->org+frcharofpt(&w->f, w->mc.m.xy);
1063 if(clickwin==w && w->mc.m.msec-clickmsec<500)
1064 if(q0==q1 && selectq==w->q0){
1065 wdoubleclick(w, &q0, &q1);
1066 wsetselect(w, q0, q1);
1067 flushimage(display, 1);
1068 x = w->mc.m.xy.x;
1069 y = w->mc.m.xy.y;
1070 /* stay here until something interesting happens */
1072 readmouse(&w->mc);
1073 while(w->mc.m.buttons==b && abs(w->mc.m.xy.x-x)<3 && abs(w->mc.m.xy.y-y)<3);
1074 w->mc.m.xy.x = x; /* in case we're calling frselect */
1075 w->mc.m.xy.y = y;
1076 q0 = w->q0; /* may have changed */
1077 q1 = w->q1;
1078 selectq = q0;
1080 if(w->mc.m.buttons == b){
1081 w->f.scroll = framescroll;
1082 frselect(&w->f, &w->mc);
1083 /* horrible botch: while asleep, may have lost selection altogether */
1084 if(selectq > w->nr)
1085 selectq = w->org + w->f.p0;
1086 w->f.scroll = nil;
1087 if(selectq < w->org)
1088 q0 = selectq;
1089 else
1090 q0 = w->org + w->f.p0;
1091 if(selectq > w->org+w->f.nchars)
1092 q1 = selectq;
1093 else
1094 q1 = w->org+w->f.p1;
1096 if(q0 == q1){
1097 if(q0==w->q0 && clickwin==w && w->mc.m.msec-clickmsec<500){
1098 wdoubleclick(w, &q0, &q1);
1099 clickwin = nil;
1100 }else{
1101 clickwin = w;
1102 clickmsec = w->mc.m.msec;
1104 }else
1105 clickwin = nil;
1106 wsetselect(w, q0, q1);
1107 flushimage(display, 1);
1108 while(w->mc.m.buttons){
1109 w->mc.m.msec = 0;
1110 b = w->mc.m.buttons;
1111 if(b & 6){
1112 if(b & 2){
1113 wsnarf(w);
1114 wcut(w);
1115 }else{
1116 if(first){
1117 first = 0;
1118 riogetsnarf();
1120 wpaste(w);
1123 wscrdraw(w);
1124 flushimage(display, 1);
1125 while(w->mc.m.buttons == b)
1126 readmouse(&w->mc);
1127 clickwin = nil;
1131 void
1132 wsendctlmesg(Window *w, int type, Rectangle r, Image *image)
1134 Wctlmesg wcm;
1136 wcm.type = type;
1137 wcm.r = r;
1138 wcm.image = image;
1139 send(w->cctl, &wcm);
1142 int
1143 wctlmesg(Window *w, int m, Rectangle r, Image *i)
1145 char buf[64];
1147 switch(m){
1148 default:
1149 error("unknown control message");
1150 break;
1151 case Wakeup:
1152 break;
1153 case Moved:
1154 case Reshaped:
1155 if(w->deleted){
1156 freeimage(i);
1157 break;
1159 w->screenr = r;
1160 strcpy(buf, w->name);
1161 wresize(w, i, m==Moved);
1162 w->wctlready = 1;
1163 if(Dx(r) > 0){
1164 if(w != input)
1165 wcurrent(w);
1166 }else if(w == input)
1167 wcurrent(nil);
1168 flushimage(display, 1);
1169 break;
1170 case Refresh:
1171 if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r))
1172 break;
1173 if(!w->mouseopen)
1174 wrefresh(w, r);
1175 flushimage(display, 1);
1176 break;
1177 case Movemouse:
1178 if(sweeping || !ptinrect(r.min, w->i->r))
1179 break;
1180 wmovemouse(w, r.min);
1181 case Rawon:
1182 break;
1183 case Rawoff:
1184 if(w->deleted)
1185 break;
1186 while(w->nraw > 0){
1187 wkeyctl(w, w->raw[0]);
1188 --w->nraw;
1189 runemove(w->raw, w->raw+1, w->nraw);
1191 break;
1192 case Holdon:
1193 case Holdoff:
1194 if(w->deleted)
1195 break;
1196 wrepaint(w);
1197 flushimage(display, 1);
1198 break;
1199 case Deleted:
1200 if(w->deleted)
1201 break;
1202 write(w->notefd, "hangup", 6);
1203 wclosewin(w);
1204 break;
1205 case Exited:
1206 frclear(&w->f, TRUE);
1207 close(w->notefd);
1208 chanfree(w->mc.c);
1209 chanfree(w->ck);
1210 chanfree(w->cctl);
1211 chanfree(w->conswrite);
1212 chanfree(w->consread);
1213 chanfree(w->mouseread);
1214 chanfree(w->wctlread);
1215 free(w->raw);
1216 free(w->r);
1217 free(w->dir);
1218 free(w->label);
1219 free(w);
1220 break;
1222 return m;
1226 * Convert back to physical coordinates
1228 void
1229 wmovemouse(Window *w, Point p)
1231 p.x += w->screenr.min.x-w->i->r.min.x;
1232 p.y += w->screenr.min.y-w->i->r.min.y;
1233 moveto(mousectl, p);
1236 void
1237 wcurrent(Window *w)
1239 Window *oi;
1241 if(wkeyboard!=nil && w==wkeyboard)
1242 return;
1243 oi = input;
1244 input = w;
1245 if(oi!=w && oi!=nil)
1246 wrepaint(oi);
1247 if(w !=nil){
1248 wrepaint(w);
1249 wsetcursor(w, 0);
1251 if(w != oi){
1252 if(oi){
1253 oi->wctlready = 1;
1254 wsendctlmesg(oi, Wakeup, ZR, nil);
1256 if(w){
1257 w->wctlready = 1;
1258 wsendctlmesg(w, Wakeup, ZR, nil);
1263 void
1264 wsetcursor(Window *w, int force)
1266 Cursor *p;
1268 if(w==nil || /*w!=input || */ w->i==nil || Dx(w->screenr)<=0)
1269 p = nil;
1270 else if(wpointto(mouse->xy) == w){
1271 p = w->cursorp;
1272 if(p==nil && w->holding)
1273 p = &whitearrow;
1274 }else
1275 p = nil;
1276 if(!menuing)
1277 riosetcursor(p, force && !menuing);
1280 void
1281 riosetcursor(Cursor *p, int force)
1283 if(!force && p==lastcursor)
1284 return;
1285 setcursor(mousectl, p);
1286 lastcursor = p;
1289 Window*
1290 wtop(Point pt)
1292 Window *w;
1294 w = wpointto(pt);
1295 if(w){
1296 if(w->topped == topped)
1297 return nil;
1298 topwindow(w->i);
1299 wcurrent(w);
1300 flushimage(display, 1);
1301 w->topped = ++topped;
1303 return w;
1306 void
1307 wtopme(Window *w)
1309 if(w!=nil && w->i!=nil && !w->deleted && w->topped!=topped){
1310 topwindow(w->i);
1311 flushimage(display, 1);
1312 w->topped = ++ topped;
1316 void
1317 wbottomme(Window *w)
1319 if(w!=nil && w->i!=nil && !w->deleted){
1320 bottomwindow(w->i);
1321 flushimage(display, 1);
1322 w->topped = 0;
1326 Window*
1327 wlookid(int id)
1329 int i;
1331 for(i=0; i<nwindow; i++)
1332 if(window[i]->id == id)
1333 return window[i];
1334 return nil;
1337 void
1338 wclosewin(Window *w)
1340 Rectangle r;
1341 int i;
1343 w->deleted = TRUE;
1344 if(w == input){
1345 input = nil;
1346 wsetcursor(w, 0);
1348 if(w == wkeyboard)
1349 wkeyboard = nil;
1350 for(i=0; i<nhidden; i++)
1351 if(hidden[i] == w){
1352 --nhidden;
1353 memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof(hidden[0]));
1354 break;
1356 for(i=0; i<nwindow; i++)
1357 if(window[i] == w){
1358 --nwindow;
1359 memmove(window+i, window+i+1, (nwindow-i)*sizeof(Window*));
1360 w->deleted = TRUE;
1361 r = w->i->r;
1362 /* move it off-screen to hide it, in case client is slow in letting it go */
1363 MOVEIT originwindow(w->i, r.min, view->r.max);
1364 freeimage(w->i);
1365 w->i = nil;
1366 return;
1368 error("unknown window in closewin");
1371 void
1372 wsetpid(Window *w, int pid, int dolabel)
1374 char buf[128];
1376 w->pid = pid;
1377 if(dolabel){
1378 sprint(buf, "rc %d", pid);
1379 free(w->label);
1380 w->label = estrdup(buf);
1381 drawsetlabel(w->label);
1385 static Rune left1[] = {
1386 '{', '[', '(', '<', 0xAB,
1387 0x207d, 0x2329, 0x27e6, 0x27e8, 0x27ea,
1388 0xfe59, 0xfe5b, 0xfe5d, 0xff08, 0xff3b, 0xff5b,
1391 static Rune right1[] = {
1392 '}', ']', ')', '>', 0xBB,
1393 0x207e, 0x232a, 0x27e7, 0x27e9, 0x27eb,
1394 0xfe5a, 0xfe5c, 0xfe5e, 0xff09, 0xff3d, 0xff5d,
1397 static Rune left2[] = { '\n', 0 };
1398 static Rune left3[] = { '\'', '"', '`', 0 };
1400 Rune *left[] = {
1401 left1,
1402 left2,
1403 left3,
1404 nil
1406 Rune *right[] = {
1407 right1,
1408 left2,
1409 left3,
1410 nil
1413 void
1414 wdoubleclick(Window *w, uint *q0, uint *q1)
1416 int c, i;
1417 Rune *r, *l, *p;
1418 uint q;
1420 for(i=0; left[i]!=nil; i++){
1421 q = *q0;
1422 l = left[i];
1423 r = right[i];
1424 /* try matching character to left, looking right */
1425 if(q == 0)
1426 c = '\n';
1427 else
1428 c = w->r[q-1];
1429 p = strrune(l, c);
1430 if(p != nil){
1431 if(wclickmatch(w, c, r[p-l], 1, &q))
1432 *q1 = q-(c!='\n');
1433 return;
1435 /* try matching character to right, looking left */
1436 if(q == w->nr)
1437 c = '\n';
1438 else
1439 c = w->r[q];
1440 p = strrune(r, c);
1441 if(p != nil){
1442 if(wclickmatch(w, c, l[p-r], -1, &q)){
1443 *q1 = *q0+(*q0<w->nr && c=='\n');
1444 *q0 = q;
1445 if(c!='\n' || q!=0 || w->r[0]=='\n')
1446 (*q0)++;
1448 return;
1451 /* try filling out word to right */
1452 while(*q1<w->nr && isalnum(w->r[*q1]))
1453 (*q1)++;
1454 /* try filling out word to left */
1455 while(*q0>0 && isalnum(w->r[*q0-1]))
1456 (*q0)--;
1459 int
1460 wclickmatch(Window *w, int cl, int cr, int dir, uint *q)
1462 Rune c;
1463 int nest;
1465 nest = 1;
1466 for(;;){
1467 if(dir > 0){
1468 if(*q == w->nr)
1469 break;
1470 c = w->r[*q];
1471 (*q)++;
1472 }else{
1473 if(*q == 0)
1474 break;
1475 (*q)--;
1476 c = w->r[*q];
1478 if(c == cr){
1479 if(--nest==0)
1480 return 1;
1481 }else if(c == cl)
1482 nest++;
1484 return cl=='\n' && nest==1;
1488 uint
1489 wbacknl(Window *w, uint p, uint n)
1491 int i, j;
1493 /* look for start of this line if n==0 */
1494 if(n==0 && p>0 && w->r[p-1]!='\n')
1495 n = 1;
1496 i = n;
1497 while(i-->0 && p>0){
1498 --p; /* it's at a newline now; back over it */
1499 if(p == 0)
1500 break;
1501 /* at 128 chars, call it a line anyway */
1502 for(j=128; --j>0 && p>0; p--)
1503 if(w->r[p-1]=='\n')
1504 break;
1506 return p;
1509 void
1510 wshow(Window *w, uint q0)
1512 int qe;
1513 int nl;
1514 uint q;
1516 qe = w->org+w->f.nchars;
1517 if(w->org<=q0 && (q0<qe || (q0==qe && qe==w->nr)))
1518 wscrdraw(w);
1519 else{
1520 nl = 4*w->f.maxlines/5;
1521 q = wbacknl(w, q0, nl);
1522 /* avoid going backwards if trying to go forwards - long lines! */
1523 if(!(q0>w->org && q<w->org))
1524 wsetorigin(w, q, TRUE);
1525 while(q0 > w->org+w->f.nchars)
1526 wsetorigin(w, w->org+1, FALSE);
1530 void
1531 wsetorigin(Window *w, uint org, int exact)
1533 int i, a, fixup;
1534 Rune *r;
1535 uint n;
1537 if(org>0 && !exact){
1538 /* org is an estimate of the char posn; find a newline */
1539 /* don't try harder than 256 chars */
1540 for(i=0; i<256 && org<w->nr; i++){
1541 if(w->r[org] == '\n'){
1542 org++;
1543 break;
1545 org++;
1548 a = org-w->org;
1549 fixup = 0;
1550 if(a>=0 && a<w->f.nchars){
1551 frdelete(&w->f, 0, a);
1552 fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
1553 }else if(a<0 && -a<w->f.nchars){
1554 n = w->org - org;
1555 r = runemalloc(n);
1556 runemove(r, w->r+org, n);
1557 frinsert(&w->f, r, r+n, 0);
1558 free(r);
1559 }else
1560 frdelete(&w->f, 0, w->f.nchars);
1561 w->org = org;
1562 wfill(w);
1563 wscrdraw(w);
1564 wsetselect(w, w->q0, w->q1);
1565 if(fixup && w->f.p1 > w->f.p0)
1566 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1-1), w->f.p1-1, w->f.p1, 1);
1569 void
1570 wsetselect(Window *w, uint q0, uint q1)
1572 int p0, p1;
1574 /* w->f.p0 and w->f.p1 are always right; w->q0 and w->q1 may be off */
1575 w->q0 = q0;
1576 w->q1 = q1;
1577 /* compute desired p0,p1 from q0,q1 */
1578 p0 = q0-w->org;
1579 p1 = q1-w->org;
1580 if(p0 < 0)
1581 p0 = 0;
1582 if(p1 < 0)
1583 p1 = 0;
1584 if(p0 > w->f.nchars)
1585 p0 = w->f.nchars;
1586 if(p1 > w->f.nchars)
1587 p1 = w->f.nchars;
1588 if(p0==w->f.p0 && p1==w->f.p1)
1589 return;
1590 /* screen disagrees with desired selection */
1591 if(w->f.p1<=p0 || p1<=w->f.p0 || p0==p1 || w->f.p1==w->f.p0){
1592 /* no overlap or too easy to bother trying */
1593 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 0);
1594 frdrawsel(&w->f, frptofchar(&w->f, p0), p0, p1, 1);
1595 goto Return;
1597 /* overlap; avoid unnecessary painting */
1598 if(p0 < w->f.p0){
1599 /* extend selection backwards */
1600 frdrawsel(&w->f, frptofchar(&w->f, p0), p0, w->f.p0, 1);
1601 }else if(p0 > w->f.p0){
1602 /* trim first part of selection */
1603 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, p0, 0);
1605 if(p1 > w->f.p1){
1606 /* extend selection forwards */
1607 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, p1, 1);
1608 }else if(p1 < w->f.p1){
1609 /* trim last part of selection */
1610 frdrawsel(&w->f, frptofchar(&w->f, p1), p1, w->f.p1, 0);
1613 Return:
1614 w->f.p0 = p0;
1615 w->f.p1 = p1;
1618 uint
1619 winsert(Window *w, Rune *r, int n, uint q0)
1621 uint m;
1623 if(n == 0)
1624 return q0;
1625 if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){
1626 m = min(HiWater-LoWater, min(w->org, w->qh));
1627 w->org -= m;
1628 w->qh -= m;
1629 if(w->q0 > m)
1630 w->q0 -= m;
1631 else
1632 w->q0 = 0;
1633 if(w->q1 > m)
1634 w->q1 -= m;
1635 else
1636 w->q1 = 0;
1637 w->nr -= m;
1638 runemove(w->r, w->r+m, w->nr);
1639 q0 -= m;
1641 if(w->nr+n > w->maxr){
1643 * Minimize realloc breakage:
1644 * Allocate at least MinWater
1645 * Double allocation size each time
1646 * But don't go much above HiWater
1648 m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater;
1649 if(m > HiWater)
1650 m = max(HiWater+MinWater, w->nr+n);
1651 if(m > w->maxr){
1652 w->r = runerealloc(w->r, m);
1653 w->maxr = m;
1656 runemove(w->r+q0+n, w->r+q0, w->nr-q0);
1657 runemove(w->r+q0, r, n);
1658 w->nr += n;
1659 /* if output touches, advance selection, not qh; works best for keyboard and output */
1660 if(q0 <= w->q1)
1661 w->q1 += n;
1662 if(q0 <= w->q0)
1663 w->q0 += n;
1664 if(q0 < w->qh)
1665 w->qh += n;
1666 if(q0 < w->iq1)
1667 w->iq1 += n;
1668 if(q0 < w->org)
1669 w->org += n;
1670 else if(q0 <= w->org+w->f.nchars)
1671 frinsert(&w->f, r, r+n, q0-w->org);
1672 return q0;
1675 void
1676 wfill(Window *w)
1678 Rune *rp;
1679 int i, n, m, nl;
1681 if(w->f.lastlinefull)
1682 return;
1683 rp = malloc(messagesize);
1684 do{
1685 n = w->nr-(w->org+w->f.nchars);
1686 if(n == 0)
1687 break;
1688 if(n > 2000) /* educated guess at reasonable amount */
1689 n = 2000;
1690 runemove(rp, w->r+(w->org+w->f.nchars), n);
1692 * it's expensive to frinsert more than we need, so
1693 * count newlines.
1695 nl = w->f.maxlines-w->f.nlines;
1696 m = 0;
1697 for(i=0; i<n; ){
1698 if(rp[i++] == '\n'){
1699 m++;
1700 if(m >= nl)
1701 break;
1704 frinsert(&w->f, rp, rp+i, w->f.nchars);
1705 }while(w->f.lastlinefull == FALSE);
1706 free(rp);
1709 char*
1710 wcontents(Window *w, int *ip)
1712 return runetobyte(w->r, w->nr, ip);