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