wind.c (34335B)
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" 15 16 #define MOVEIT if(0) 17 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 }; 24 25 static int topped; 26 static int id; 27 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; 37 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 } 45 46 Window* 47 wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl, int scrolling) 48 { 49 Window *w; 50 Rectangle r; 51 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 } 98 99 void 100 wsetname(Window *w) 101 { 102 int i, n; 103 char err[ERRMAX]; 104 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; 114 } 115 w->name[0] = 0; 116 fprint(2, "rio: setname failed: %s\n", err); 117 } 118 119 void 120 wresize(Window *w, Image *i, int move) 121 { 122 Rectangle r, or; 123 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; 131 } 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); 151 } 152 wborder(w, wscale(w, Selborder)); 153 w->topped = ++topped; 154 w->resized = TRUE; 155 w->mouse.counter++; 156 } 157 158 void 159 wrefresh(Window *w, Rectangle r) 160 { 161 /* USED(r); */ 162 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); 179 } 180 181 int 182 wclose(Window *w) 183 { 184 int i; 185 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; 195 } 196 197 198 void 199 winctl(void *arg) 200 { 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]; 216 217 w = arg; 218 snprint(buf, sizeof buf, "winctl-id%d", w->id); 219 threadsetname(buf); 220 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); 227 228 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; 251 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; 280 } 281 } 282 } 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++; 294 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; 305 } 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; 321 } 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); 334 } 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++; 357 } 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; 369 } 370 bp++; 371 } 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; 378 } 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; 409 } 410 } 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; 417 } 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; 433 } 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); 439 } 440 send(cwrm.c2, &pair); 441 continue; 442 } 443 if(!w->deleted) 444 flushimage(display, 1); 445 } 446 } 447 448 void 449 waddraw(Window *w, Rune *r, int nr) 450 { 451 w->raw = runerealloc(w->raw, w->nraw+nr); 452 runemove(w->raw+w->nraw, r, nr); 453 w->nraw += nr; 454 } 455 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) 462 { 463 int *notefd; 464 465 notefd = v; 466 write(*notefd, "interrupt", 9); 467 free(notefd); 468 } 469 470 int 471 windfilewidth(Window *w, uint q0, int oneelement) 472 { 473 uint q; 474 Rune r; 475 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; 484 } 485 return q0-q; 486 } 487 488 void 489 showcandidates(Window *w, Completion *c) 490 { 491 int i; 492 Fmt f; 493 Rune *rp; 494 uint nr, qline, q0; 495 char *s; 496 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]); 510 } 511 fmtprint(&f, "]\n"); 512 } 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--; 517 518 rp = runefmtstrflush(&f); 519 nr = runestrlen(rp); 520 521 q0 = w->q0; 522 q0 += winsert(w, rp, runestrlen(rp), qline) - qline; 523 free(rp); 524 wsetselect(w, q0+nr, q0+nr); 525 } 526 527 Rune* 528 namecomplete(Window *w) 529 { 530 int nstr, npath; 531 Rune *rp, *path, *str; 532 Completion *c; 533 char *s, *dir, *root; 534 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; 545 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); 557 } 558 dir = cleanname(dir); 559 560 s = smprint("%.*S", nstr, str); 561 c = complete(dir, s); 562 free(s); 563 if(c == nil) 564 goto Return; 565 566 if(!c->advance) 567 showcandidates(w, c); 568 569 if(c->advance) 570 rp = runesmprint("%s", c->string); 571 572 Return: 573 freecompletion(c); 574 free(dir); 575 free(path); 576 free(str); 577 return rp; 578 } 579 580 void 581 wkeyctl(Window *w, Rune r) 582 { 583 uint q0 ,q1; 584 int n, nb, nr; 585 Rune *rp; 586 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); 628 } 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); 635 } 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; 665 } 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; 682 } 683 if(!w->holding && w->rawing && (w->q0==w->nr || w->mouseopen)){ 684 waddraw(w, &r, 1); 685 return; 686 } 687 if(r == Kcmd+'x'){ 688 wsnarf(w); 689 wcut(w); 690 wscrdraw(w); 691 return; 692 } 693 if(r == Kcmd+'c'){ 694 wsnarf(w); 695 return; 696 } 697 if(r == Kcmd+'v'){ 698 riogetsnarf(); 699 wpaste(w); 700 wscrdraw(w); 701 return; 702 } 703 if(r != 0x7F){ 704 wsnarf(w); 705 wcut(w); 706 } 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; 742 } 743 if(nb > 0){ 744 wdelete(w, q0, q0+nb); 745 wsetselect(w, q0, q0); 746 } 747 w->iq1 = w->q0; 748 return; 749 } 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; 755 } 756 757 void 758 wsetcols(Window *w) 759 { 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; 770 } 771 772 void 773 wrepaint(Window *w) 774 { 775 wsetcols(w); 776 if(!w->mouseopen){ 777 frredraw(&w->f); 778 } 779 if(w == input){ 780 wborder(w, wscale(w, Selborder)); 781 wsetcursor(w, 0); 782 }else 783 wborder(w, wscale(w, Unselborder)); 784 } 785 786 int 787 wbswidth(Window *w, Rune c) 788 { 789 uint q, eq, stop; 790 Rune r; 791 int skipping; 792 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; 807 } 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; 814 } 815 --q; 816 } 817 return w->q0-q; 818 } 819 820 void 821 wsnarf(Window *w) 822 { 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(); 830 } 831 832 void 833 wcut(Window *w) 834 { 835 if(w->q1 == w->q0) 836 return; 837 wdelete(w, w->q0, w->q1); 838 wsetselect(w, w->q0, w->q0); 839 } 840 841 void 842 wpaste(Window *w) 843 { 844 uint q0; 845 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); 856 } 857 } 858 859 void 860 wplumb(Window *w) 861 { 862 Plumbmsg *m; 863 static CFid *fd; 864 char buf[32]; 865 uint p0, p1; 866 Cursor *c; 867 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); 888 } 889 if(p1-p0 > messagesize-1024){ 890 plumbfree(m); 891 return; /* too large for 9P */ 892 } 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); 899 } 900 plumbfree(m); 901 } 902 903 int 904 winborder(Window *w, Point xy) 905 { 906 return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->screenr, wscale(w, Selborder))); 907 } 908 909 void 910 wlook(Window *w) 911 { 912 int i, n, e; 913 914 i = w->q1; 915 n = i - w->q0; 916 e = w->nr - n; 917 if(n <= 0 || e < n) 918 return; 919 920 if(i > e) 921 i = 0; 922 923 while(runestrncmp(w->r+w->q0, w->r+i, n) != 0){ 924 if(i < e) 925 i++; 926 else 927 i = 0; 928 } 929 930 wsetselect(w, i, i+n); 931 wshow(w, i); 932 } 933 934 void 935 wmousectl(Window *w) 936 { 937 int but; 938 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; 951 } 952 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; 960 } 961 if(but == 1) 962 wselect(w); 963 /* else all is handled by main process */ 964 Return: 965 wclose(w); 966 } 967 968 void 969 wdelete(Window *w, uint q0, uint q1) 970 { 971 uint n, p0, p1; 972 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); 1001 } 1002 } 1003 1004 1005 static Window *clickwin; 1006 static uint clickmsec; 1007 static Window *selectwin; 1008 static uint selectq; 1009 1010 /* 1011 * called from frame library 1012 */ 1013 void 1014 framescroll(Frame *f, int dl) 1015 { 1016 if(f != &selectwin->f) 1017 error("frameselect not right frame"); 1018 wframescroll(selectwin, dl); 1019 } 1020 1021 void 1022 wframescroll(Window *w, int dl) 1023 { 1024 uint q0; 1025 1026 if(dl == 0){ 1027 wscrsleep(w, 100); 1028 return; 1029 } 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); 1044 } 1045 wsetorigin(w, q0, TRUE); 1046 } 1047 1048 void 1049 wselect(Window *w) 1050 { 1051 uint q0, q1; 1052 int b, x, y, first; 1053 1054 first = 1; 1055 selectwin = w; 1056 /* 1057 * Double-click immediately if it might make sense. 1058 */ 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 */ 1071 do 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; 1079 } 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; 1095 } 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; 1103 } 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(); 1119 } 1120 wpaste(w); 1121 } 1122 } 1123 wscrdraw(w); 1124 flushimage(display, 1); 1125 while(w->mc.m.buttons == b) 1126 readmouse(&w->mc); 1127 clickwin = nil; 1128 } 1129 } 1130 1131 void 1132 wsendctlmesg(Window *w, int type, Rectangle r, Image *image) 1133 { 1134 Wctlmesg wcm; 1135 1136 wcm.type = type; 1137 wcm.r = r; 1138 wcm.image = image; 1139 send(w->cctl, &wcm); 1140 } 1141 1142 int 1143 wctlmesg(Window *w, int m, Rectangle r, Image *i) 1144 { 1145 char buf[64]; 1146 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; 1158 } 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); 1190 } 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; 1221 } 1222 return m; 1223 } 1224 1225 /* 1226 * Convert back to physical coordinates 1227 */ 1228 void 1229 wmovemouse(Window *w, Point p) 1230 { 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); 1234 } 1235 1236 void 1237 wcurrent(Window *w) 1238 { 1239 Window *oi; 1240 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); 1250 } 1251 if(w != oi){ 1252 if(oi){ 1253 oi->wctlready = 1; 1254 wsendctlmesg(oi, Wakeup, ZR, nil); 1255 } 1256 if(w){ 1257 w->wctlready = 1; 1258 wsendctlmesg(w, Wakeup, ZR, nil); 1259 } 1260 } 1261 } 1262 1263 void 1264 wsetcursor(Window *w, int force) 1265 { 1266 Cursor *p; 1267 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); 1278 } 1279 1280 void 1281 riosetcursor(Cursor *p, int force) 1282 { 1283 if(!force && p==lastcursor) 1284 return; 1285 setcursor(mousectl, p); 1286 lastcursor = p; 1287 } 1288 1289 Window* 1290 wtop(Point pt) 1291 { 1292 Window *w; 1293 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; 1302 } 1303 return w; 1304 } 1305 1306 void 1307 wtopme(Window *w) 1308 { 1309 if(w!=nil && w->i!=nil && !w->deleted && w->topped!=topped){ 1310 topwindow(w->i); 1311 flushimage(display, 1); 1312 w->topped = ++ topped; 1313 } 1314 } 1315 1316 void 1317 wbottomme(Window *w) 1318 { 1319 if(w!=nil && w->i!=nil && !w->deleted){ 1320 bottomwindow(w->i); 1321 flushimage(display, 1); 1322 w->topped = 0; 1323 } 1324 } 1325 1326 Window* 1327 wlookid(int id) 1328 { 1329 int i; 1330 1331 for(i=0; i<nwindow; i++) 1332 if(window[i]->id == id) 1333 return window[i]; 1334 return nil; 1335 } 1336 1337 void 1338 wclosewin(Window *w) 1339 { 1340 Rectangle r; 1341 int i; 1342 1343 w->deleted = TRUE; 1344 if(w == input){ 1345 input = nil; 1346 wsetcursor(w, 0); 1347 } 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; 1355 } 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; 1367 } 1368 error("unknown window in closewin"); 1369 } 1370 1371 void 1372 wsetpid(Window *w, int pid, int dolabel) 1373 { 1374 char buf[128]; 1375 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); 1382 } 1383 } 1384 1385 static Rune left1[] = { 1386 '{', '[', '(', '<', 0xAB, 1387 0x207d, 0x2329, 0x27e6, 0x27e8, 0x27ea, 1388 0xfe59, 0xfe5b, 0xfe5d, 0xff08, 0xff3b, 0xff5b, 1389 0 1390 }; 1391 static Rune right1[] = { 1392 '}', ']', ')', '>', 0xBB, 1393 0x207e, 0x232a, 0x27e7, 0x27e9, 0x27eb, 1394 0xfe5a, 0xfe5c, 0xfe5e, 0xff09, 0xff3d, 0xff5d, 1395 0 1396 }; 1397 static Rune left2[] = { '\n', 0 }; 1398 static Rune left3[] = { '\'', '"', '`', 0 }; 1399 1400 Rune *left[] = { 1401 left1, 1402 left2, 1403 left3, 1404 nil 1405 }; 1406 Rune *right[] = { 1407 right1, 1408 left2, 1409 left3, 1410 nil 1411 }; 1412 1413 void 1414 wdoubleclick(Window *w, uint *q0, uint *q1) 1415 { 1416 int c, i; 1417 Rune *r, *l, *p; 1418 uint q; 1419 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; 1434 } 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)++; 1447 } 1448 return; 1449 } 1450 } 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)--; 1457 } 1458 1459 int 1460 wclickmatch(Window *w, int cl, int cr, int dir, uint *q) 1461 { 1462 Rune c; 1463 int nest; 1464 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]; 1477 } 1478 if(c == cr){ 1479 if(--nest==0) 1480 return 1; 1481 }else if(c == cl) 1482 nest++; 1483 } 1484 return cl=='\n' && nest==1; 1485 } 1486 1487 1488 uint 1489 wbacknl(Window *w, uint p, uint n) 1490 { 1491 int i, j; 1492 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; 1505 } 1506 return p; 1507 } 1508 1509 void 1510 wshow(Window *w, uint q0) 1511 { 1512 int qe; 1513 int nl; 1514 uint q; 1515 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); 1527 } 1528 } 1529 1530 void 1531 wsetorigin(Window *w, uint org, int exact) 1532 { 1533 int i, a, fixup; 1534 Rune *r; 1535 uint n; 1536 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; 1544 } 1545 org++; 1546 } 1547 } 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); 1567 } 1568 1569 void 1570 wsetselect(Window *w, uint q0, uint q1) 1571 { 1572 int p0, p1; 1573 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; 1596 } 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); 1604 } 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); 1611 } 1612 1613 Return: 1614 w->f.p0 = p0; 1615 w->f.p1 = p1; 1616 } 1617 1618 uint 1619 winsert(Window *w, Rune *r, int n, uint q0) 1620 { 1621 uint m; 1622 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; 1640 } 1641 if(w->nr+n > w->maxr){ 1642 /* 1643 * Minimize realloc breakage: 1644 * Allocate at least MinWater 1645 * Double allocation size each time 1646 * But don't go much above HiWater 1647 */ 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; 1654 } 1655 } 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; 1673 } 1674 1675 void 1676 wfill(Window *w) 1677 { 1678 Rune *rp; 1679 int i, n, m, nl; 1680 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); 1691 /* 1692 * it's expensive to frinsert more than we need, so 1693 * count newlines. 1694 */ 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; 1702 } 1703 } 1704 frinsert(&w->f, rp, rp+i, w->f.nchars); 1705 }while(w->f.lastlinefull == FALSE); 1706 free(rp); 1707 } 1708 1709 char* 1710 wcontents(Window *w, int *ip) 1711 { 1712 return runetobyte(w->r, w->nr, ip); 1713 }