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