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