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 "flayer.h" 10 #include "samterm.h" 11 12 Text cmd; 13 Rune *scratch; 14 long nscralloc; 15 Cursor *cursor; 16 Flayer *which = 0; 17 Flayer *work = 0; 18 long snarflen; 19 long typestart = -1; 20 long typeend = -1; 21 long typeesc = -1; 22 long modified = 0; /* strange lookahead for menus */ 23 char hostlock = 1; 24 char hasunlocked = 0; 25 int maxtab = 8; 26 int autoindent; 27 28 void 29 threadmain(int argc, char *argv[]) 30 { 31 int i, got, scr; 32 Text *t; 33 Rectangle r; 34 Flayer *nwhich; 35 36 getscreen(argc, argv); 37 iconinit(); 38 initio(); 39 scratch = alloc(100*RUNESIZE); 40 nscralloc = 100; 41 r = screen->r; 42 r.max.y = r.min.y+Dy(r)/5; 43 flstart(screen->clipr); 44 rinit(&cmd.rasp); 45 flnew(&cmd.l[0], gettext, 1, &cmd); 46 flinit(&cmd.l[0], r, font, cmdcols); 47 cmd.nwin = 1; 48 which = &cmd.l[0]; 49 cmd.tag = Untagged; 50 outTs(Tversion, VERSION); 51 startnewfile(Tstartcmdfile, &cmd); 52 53 got = 0; 54 for(;;got = waitforio()){ 55 if(hasunlocked && RESIZED()) 56 resize(); 57 if(got&(1<<RHost)) 58 rcv(); 59 if(got&(1<<RPlumb)){ 60 for(i=0; cmd.l[i].textfn==0; i++) 61 ; 62 current(&cmd.l[i]); 63 flsetselect(which, cmd.rasp.nrunes, cmd.rasp.nrunes); 64 type(which, RPlumb); 65 } 66 if(got&(1<<RKeyboard)) 67 if(which) 68 type(which, RKeyboard); 69 else 70 kbdblock(); 71 if(got&(1<<RMouse)){ 72 if(hostlock==2 || !ptinrect(mousep->xy, screen->r)){ 73 mouseunblock(); 74 continue; 75 } 76 nwhich = flwhich(mousep->xy); 77 scr = which && ptinrect(mousep->xy, which->scroll); 78 if(mousep->buttons) 79 flushtyping(1); 80 if(mousep->buttons&1){ 81 if(nwhich){ 82 if(nwhich!=which) 83 current(nwhich); 84 else if(scr) 85 scroll(which, 1); 86 else{ 87 t=(Text *)which->user1; 88 if(flselect(which)){ 89 outTsl(Tdclick, t->tag, which->p0); 90 t->lock++; 91 }else if(t!=&cmd) 92 outcmd(); 93 } 94 } 95 }else if((mousep->buttons&2) && which){ 96 if(scr) 97 scroll(which, 2); 98 else 99 menu2hit(); 100 }else if((mousep->buttons&4)){ 101 if(scr) 102 scroll(which, 3); 103 else 104 menu3hit(); 105 } 106 mouseunblock(); 107 } 108 } 109 } 110 111 112 void 113 resize(void) 114 { 115 int i; 116 117 flresize(screen->clipr); 118 for(i = 0; i<nname; i++) 119 if(text[i]) 120 hcheck(text[i]->tag); 121 } 122 123 void 124 current(Flayer *nw) 125 { 126 Text *t; 127 128 if(which) 129 flborder(which, 0); 130 if(nw){ 131 flushtyping(1); 132 flupfront(nw); 133 flborder(nw, 1); 134 buttons(Up); 135 t = (Text *)nw->user1; 136 t->front = nw-&t->l[0]; 137 if(t != &cmd) 138 work = nw; 139 } 140 which = nw; 141 } 142 143 void 144 closeup(Flayer *l) 145 { 146 Text *t=(Text *)l->user1; 147 int m; 148 149 m = whichmenu(t->tag); 150 if(m < 0) 151 return; 152 flclose(l); 153 if(l == which){ 154 which = 0; 155 current(flwhich(Pt(0, 0))); 156 } 157 if(l == work) 158 work = 0; 159 if(--t->nwin == 0){ 160 rclear(&t->rasp); 161 free((uchar *)t); 162 text[m] = 0; 163 }else if(l == &t->l[t->front]){ 164 for(m=0; m<NL; m++) /* find one; any one will do */ 165 if(t->l[m].textfn){ 166 t->front = m; 167 return; 168 } 169 panic("close"); 170 } 171 } 172 173 Flayer * 174 findl(Text *t) 175 { 176 int i; 177 for(i = 0; i<NL; i++) 178 if(t->l[i].textfn==0) 179 return &t->l[i]; 180 return 0; 181 } 182 183 void 184 duplicate(Flayer *l, Rectangle r, Font *f, int close) 185 { 186 Text *t=(Text *)l->user1; 187 Flayer *nl = findl(t); 188 Rune *rp; 189 ulong n; 190 191 if(nl){ 192 flnew(nl, gettext, l->user0, (char *)t); 193 flinit(nl, r, f, l->f.cols); 194 nl->origin = l->origin; 195 rp = (*l->textfn)(l, l->f.nchars, &n); 196 flinsert(nl, rp, rp+n, l->origin); 197 flsetselect(nl, l->p0, l->p1); 198 if(close){ 199 flclose(l); 200 if(l==which) 201 which = 0; 202 }else 203 t->nwin++; 204 current(nl); 205 hcheck(t->tag); 206 } 207 setcursor(mousectl, cursor); 208 } 209 210 void 211 buttons(int updown) 212 { 213 while(((mousep->buttons&7)!=0) != updown) 214 getmouse(); 215 } 216 217 int 218 getr(Rectangle *rp) 219 { 220 Point p; 221 Rectangle r; 222 223 *rp = getrect(3, mousectl); 224 if(rp->max.x && rp->max.x-rp->min.x<=5 && rp->max.y-rp->min.y<=5){ 225 p = rp->min; 226 r = cmd.l[cmd.front].entire; 227 *rp = screen->r; 228 if(cmd.nwin==1){ 229 if (p.y <= r.min.y) 230 rp->max.y = r.min.y; 231 else if (p.y >= r.max.y) 232 rp->min.y = r.max.y; 233 if (p.x <= r.min.x) 234 rp->max.x = r.min.x; 235 else if (p.x >= r.max.x) 236 rp->min.x = r.max.x; 237 } 238 } 239 return rectclip(rp, screen->r) && 240 rp->max.x-rp->min.x>100 && rp->max.y-rp->min.y>40; 241 } 242 243 void 244 snarf(Text *t, int w) 245 { 246 Flayer *l = &t->l[w]; 247 248 if(l->p1>l->p0){ 249 snarflen = l->p1-l->p0; 250 outTsll(Tsnarf, t->tag, l->p0, l->p1); 251 } 252 } 253 254 void 255 cut(Text *t, int w, int save, int check) 256 { 257 long p0, p1; 258 Flayer *l; 259 260 l = &t->l[w]; 261 p0 = l->p0; 262 p1 = l->p1; 263 if(p0 == p1) 264 return; 265 if(p0 < 0) 266 panic("cut"); 267 if(save) 268 snarf(t, w); 269 outTsll(Tcut, t->tag, p0, p1); 270 flsetselect(l, p0, p0); 271 t->lock++; 272 hcut(t->tag, p0, p1-p0); 273 if(check) 274 hcheck(t->tag); 275 } 276 277 void 278 paste(Text *t, int w) 279 { 280 if(snarflen){ 281 cut(t, w, 0, 0); 282 t->lock++; 283 outTsl(Tpaste, t->tag, t->l[w].p0); 284 } 285 } 286 287 void 288 scrorigin(Flayer *l, int but, long p0) 289 { 290 Text *t=(Text *)l->user1; 291 292 switch(but){ 293 case 1: 294 outTsll(Torigin, t->tag, l->origin, p0); 295 break; 296 case 2: 297 outTsll(Torigin, t->tag, p0, 1L); 298 break; 299 case 3: 300 horigin(t->tag,p0); 301 } 302 } 303 304 int 305 alnum(int c) 306 { 307 /* 308 * Hard to get absolutely right. Use what we know about ASCII 309 * and assume anything above the Latin control characters is 310 * potentially an alphanumeric. 311 */ 312 if(c<=' ') 313 return 0; 314 if(0x7F<=c && c<=0xA0) 315 return 0; 316 if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c)) 317 return 0; 318 return 1; 319 } 320 321 int 322 raspc(Rasp *r, long p) 323 { 324 ulong n; 325 rload(r, p, p+1, &n); 326 if(n) 327 return scratch[0]; 328 return 0; 329 } 330 331 long 332 ctlw(Rasp *r, long o, long p) 333 { 334 int c; 335 336 if(--p < o) 337 return o; 338 if(raspc(r, p)=='\n') 339 return p; 340 for(; p>=o && !alnum(c=raspc(r, p)); --p) 341 if(c=='\n') 342 return p+1; 343 for(; p>o && alnum(raspc(r, p-1)); --p) 344 ; 345 return p>=o? p : o; 346 } 347 348 long 349 ctlu(Rasp *r, long o, long p) 350 { 351 if(--p < o) 352 return o; 353 if(raspc(r, p)=='\n') 354 return p; 355 for(; p-1>=o && raspc(r, p-1)!='\n'; --p) 356 ; 357 return p>=o? p : o; 358 } 359 360 int 361 center(Flayer *l, long a) 362 { 363 Text *t; 364 365 t = l->user1; 366 if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){ 367 if(a > t->rasp.nrunes) 368 a = t->rasp.nrunes; 369 outTsll(Torigin, t->tag, a, 2L); 370 return 1; 371 } 372 return 0; 373 } 374 375 int 376 onethird(Flayer *l, long a) 377 { 378 Text *t; 379 Rectangle s; 380 long lines; 381 382 t = l->user1; 383 if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){ 384 if(a > t->rasp.nrunes) 385 a = t->rasp.nrunes; 386 s = insetrect(l->scroll, 1); 387 lines = ((s.max.y-s.min.y)/l->f.font->height+1)/3; 388 if (lines < 2) 389 lines = 2; 390 outTsll(Torigin, t->tag, a, lines); 391 return 1; 392 } 393 return 0; 394 } 395 396 void 397 flushtyping(int clearesc) 398 { 399 Text *t; 400 ulong n; 401 402 if(clearesc) 403 typeesc = -1; 404 if(typestart == typeend) { 405 modified = 0; 406 return; 407 } 408 t = which->user1; 409 if(t != &cmd) 410 modified = 1; 411 rload(&t->rasp, typestart, typeend, &n); 412 scratch[n] = 0; 413 if(t==&cmd && typeend==t->rasp.nrunes && scratch[typeend-typestart-1]=='\n'){ 414 setlock(); 415 outcmd(); 416 } 417 outTslS(Ttype, t->tag, typestart, scratch); 418 typestart = -1; 419 typeend = -1; 420 } 421 422 #define BACKSCROLLKEY Kup 423 #define ENDKEY Kend 424 #define ESC 0x1B 425 #define HOMEKEY Khome 426 #define LEFTARROW Kleft 427 #define LINEEND 0x05 428 #define LINESTART 0x01 429 #define PAGEDOWN Kpgdown 430 #define PAGEUP Kpgup 431 #define RIGHTARROW Kright 432 #define SCROLLKEY Kdown 433 434 int 435 nontypingkey(int c) 436 { 437 switch(c){ 438 case BACKSCROLLKEY: 439 case ENDKEY: 440 case HOMEKEY: 441 case LEFTARROW: 442 case LINEEND: 443 case LINESTART: 444 case PAGEDOWN: 445 case PAGEUP: 446 case RIGHTARROW: 447 case SCROLLKEY: 448 return 1; 449 } 450 return 0; 451 } 452 453 454 void 455 type(Flayer *l, int res) /* what a bloody mess this is */ 456 { 457 Text *t = (Text *)l->user1; 458 Rune buf[100]; 459 Rune *p = buf; 460 int c, backspacing; 461 long a, a0; 462 int scrollkey; 463 464 scrollkey = 0; 465 if(res == RKeyboard) 466 scrollkey = nontypingkey(qpeekc()); /* ICK */ 467 468 if(hostlock || t->lock){ 469 kbdblock(); 470 return; 471 } 472 a = l->p0; 473 if(a!=l->p1 && !scrollkey){ 474 flushtyping(1); 475 cut(t, t->front, 1, 1); 476 return; /* it may now be locked */ 477 } 478 backspacing = 0; 479 while((c = kbdchar())>0){ 480 if(res == RKeyboard){ 481 if(nontypingkey(c) || c==ESC) 482 break; 483 /* backspace, ctrl-u, ctrl-w, del */ 484 if(c=='\b' || c==0x15 || c==0x17 || c==0x7F){ 485 backspacing = 1; 486 break; 487 } 488 } 489 *p++ = c; 490 if(autoindent) 491 if(c == '\n'){ 492 /* autoindent */ 493 int cursor, ch; 494 cursor = ctlu(&t->rasp, 0, a+(p-buf)-1); 495 while(p < buf+nelem(buf)){ 496 ch = raspc(&t->rasp, cursor++); 497 if(ch == ' ' || ch == '\t') 498 *p++ = ch; 499 else 500 break; 501 } 502 } 503 if(c == '\n' || p >= buf+sizeof(buf)/sizeof(buf[0])) 504 break; 505 } 506 if(p > buf){ 507 if(typestart < 0) 508 typestart = a; 509 if(typeesc < 0) 510 typeesc = a; 511 hgrow(t->tag, a, p-buf, 0); 512 t->lock++; /* pretend we Trequest'ed for hdatarune*/ 513 hdatarune(t->tag, a, buf, p-buf); 514 a += p-buf; 515 l->p0 = a; 516 l->p1 = a; 517 typeend = a; 518 if(c=='\n' || typeend-typestart>100) 519 flushtyping(0); 520 onethird(l, a); 521 } 522 if(c==SCROLLKEY || c==PAGEDOWN){ 523 flushtyping(0); 524 center(l, l->origin+l->f.nchars+1); 525 /* backspacing immediately after outcmd(): sorry */ 526 }else if(c==BACKSCROLLKEY || c==PAGEUP){ 527 flushtyping(0); 528 a0 = l->origin-l->f.nchars; 529 if(a0 < 0) 530 a0 = 0; 531 center(l, a0); 532 }else if(c == RIGHTARROW){ 533 flushtyping(0); 534 a0 = l->p0; 535 if(a0 < t->rasp.nrunes) 536 a0++; 537 flsetselect(l, a0, a0); 538 center(l, a0); 539 }else if(c == LEFTARROW){ 540 flushtyping(0); 541 a0 = l->p0; 542 if(a0 > 0) 543 a0--; 544 flsetselect(l, a0, a0); 545 center(l, a0); 546 }else if(c == HOMEKEY){ 547 flushtyping(0); 548 center(l, 0); 549 }else if(c == ENDKEY){ 550 flushtyping(0); 551 center(l, t->rasp.nrunes); 552 }else if(c == LINESTART || c == LINEEND){ 553 flushtyping(1); 554 if(c == LINESTART) 555 while(a > 0 && raspc(&t->rasp, a-1)!='\n') 556 a--; 557 else 558 while(a < t->rasp.nrunes && raspc(&t->rasp, a)!='\n') 559 a++; 560 l->p0 = l->p1 = a; 561 for(l=t->l; l<&t->l[NL]; l++) 562 if(l->textfn) 563 flsetselect(l, l->p0, l->p1); 564 }else if(backspacing && !hostlock){ 565 /* backspacing immediately after outcmd(): sorry */ 566 if(l->f.p0>0 && a>0){ 567 switch(c){ 568 case '\b': 569 case 0x7F: /* del */ 570 l->p0 = a-1; 571 break; 572 case 0x15: /* ctrl-u */ 573 l->p0 = ctlu(&t->rasp, l->origin, a); 574 break; 575 case 0x17: /* ctrl-w */ 576 l->p0 = ctlw(&t->rasp, l->origin, a); 577 break; 578 } 579 l->p1 = a; 580 if(l->p1 != l->p0){ 581 /* cut locally if possible */ 582 if(typestart<=l->p0 && l->p1<=typeend){ 583 t->lock++; /* to call hcut */ 584 hcut(t->tag, l->p0, l->p1-l->p0); 585 /* hcheck is local because we know rasp is contiguous */ 586 hcheck(t->tag); 587 }else{ 588 flushtyping(0); 589 cut(t, t->front, 0, 1); 590 } 591 } 592 if(typeesc >= l->p0) 593 typeesc = l->p0; 594 if(typestart >= 0){ 595 if(typestart >= l->p0) 596 typestart = l->p0; 597 typeend = l->p0; 598 if(typestart == typeend){ 599 typestart = -1; 600 typeend = -1; 601 modified = 0; 602 } 603 } 604 } 605 }else{ 606 if(c==ESC && typeesc>=0){ 607 l->p0 = typeesc; 608 l->p1 = a; 609 flushtyping(1); 610 } 611 for(l=t->l; l<&t->l[NL]; l++) 612 if(l->textfn) 613 flsetselect(l, l->p0, l->p1); 614 } 615 } 616 617 618 void 619 outcmd(void){ 620 if(work) 621 outTsll(Tworkfile, ((Text *)work->user1)->tag, work->p0, work->p1); 622 } 623 624 void 625 panic(char *s) 626 { 627 panic1(display, s); 628 } 629 630 void 631 panic1(Display*, char *s) 632 { 633 fprint(2, "samterm:panic: "); 634 perror(s); 635 abort(); 636 } 637 638 Rune* 639 gettext(Flayer *l, long n, ulong *np) 640 { 641 Text *t; 642 643 t = l->user1; 644 rload(&t->rasp, l->origin, l->origin+n, np); 645 return scratch; 646 } 647 648 long 649 scrtotal(Flayer *l) 650 { 651 return ((Text *)l->user1)->rasp.nrunes; 652 } 653 654 void* 655 alloc(ulong n) 656 { 657 void *p; 658 659 p = malloc(n); 660 if(p == 0) 661 panic("alloc"); 662 memset(p, 0, n); 663 return p; 664 } 665