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 for(; p-1>=o && raspc(r, p-1)!='\n'; --p) 350 ; 351 return p>=o? p : o; 352 } 353 354 int 355 center(Flayer *l, long a) 356 { 357 Text *t; 358 359 t = l->user1; 360 if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){ 361 if(a > t->rasp.nrunes) 362 a = t->rasp.nrunes; 363 outTsll(Torigin, t->tag, a, 2L); 364 return 1; 365 } 366 return 0; 367 } 368 369 int 370 onethird(Flayer *l, long a) 371 { 372 Text *t; 373 Rectangle s; 374 long lines; 375 376 t = l->user1; 377 if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){ 378 if(a > t->rasp.nrunes) 379 a = t->rasp.nrunes; 380 s = insetrect(l->scroll, 1); 381 lines = ((s.max.y-s.min.y)/l->f.font->height+1)/3; 382 if (lines < 2) 383 lines = 2; 384 outTsll(Torigin, t->tag, a, lines); 385 return 1; 386 } 387 return 0; 388 } 389 390 void 391 flushtyping(int clearesc) 392 { 393 Text *t; 394 ulong n; 395 396 if(clearesc) 397 typeesc = -1; 398 if(typestart == typeend) { 399 modified = 0; 400 return; 401 } 402 t = which->user1; 403 if(t != &cmd) 404 modified = 1; 405 rload(&t->rasp, typestart, typeend, &n); 406 scratch[n] = 0; 407 if(t==&cmd && typeend==t->rasp.nrunes && scratch[typeend-typestart-1]=='\n'){ 408 setlock(); 409 outcmd(); 410 } 411 outTslS(Ttype, t->tag, typestart, scratch); 412 typestart = -1; 413 typeend = -1; 414 } 415 416 #define SCROLLKEY Kdown 417 #define BACKSCROLLKEY Kup 418 #define ESC 0x1B 419 420 void 421 type(Flayer *l, int res) /* what a bloody mess this is */ 422 { 423 Text *t = (Text *)l->user1; 424 Rune buf[100]; 425 Rune *p = buf; 426 int c, backspacing; 427 long a, a0; 428 int scrollkey; 429 430 scrollkey = 0; 431 if(res == RKeyboard) 432 scrollkey = (qpeekc()==SCROLLKEY || qpeekc()==BACKSCROLLKEY); /* ICK */ 433 434 if(hostlock || t->lock){ 435 kbdblock(); 436 return; 437 } 438 a = l->p0; 439 if(a!=l->p1 && !scrollkey){ 440 flushtyping(1); 441 cut(t, t->front, 1, 1); 442 return; /* it may now be locked */ 443 } 444 backspacing = 0; 445 while((c = kbdchar())>0){ 446 if(res == RKeyboard){ 447 if(c==SCROLLKEY || c==BACKSCROLLKEY || c==ESC) 448 break; 449 /* backspace, ctrl-u, ctrl-w, del */ 450 if(c=='\b' || c==0x15 || c==0x17 || c==0x7F){ 451 backspacing = 1; 452 break; 453 } 454 } 455 *p++ = c; 456 if(c == '\n' || p >= buf+sizeof(buf)/sizeof(buf[0])) 457 break; 458 } 459 if(p > buf){ 460 if(typestart < 0) 461 typestart = a; 462 if(typeesc < 0) 463 typeesc = a; 464 hgrow(t->tag, a, p-buf, 0); 465 t->lock++; /* pretend we Trequest'ed for hdatarune*/ 466 hdatarune(t->tag, a, buf, p-buf); 467 a += p-buf; 468 l->p0 = a; 469 l->p1 = a; 470 typeend = a; 471 if(c=='\n' || typeend-typestart>100) 472 flushtyping(0); 473 onethird(l, a); 474 } 475 if(c == SCROLLKEY){ 476 flushtyping(0); 477 center(l, l->origin+l->f.nchars+1); 478 /* backspacing immediately after outcmd(): sorry */ 479 }else if(c == BACKSCROLLKEY){ 480 flushtyping(0); 481 a0 = l->origin-l->f.nchars; 482 if(a0 < 0) 483 a0 = 0; 484 center(l, a0); 485 }else if(backspacing && !hostlock){ 486 if(l->f.p0>0 && a>0){ 487 switch(c){ 488 case '\b': 489 case 0x7F: /* del */ 490 l->p0 = a-1; 491 break; 492 case 0x15: /* ctrl-u */ 493 l->p0 = ctlu(&t->rasp, l->origin, a); 494 break; 495 case 0x17: /* ctrl-w */ 496 l->p0 = ctlw(&t->rasp, l->origin, a); 497 break; 498 } 499 l->p1 = a; 500 if(l->p1 != l->p0){ 501 /* cut locally if possible */ 502 if(typestart<=l->p0 && l->p1<=typeend){ 503 t->lock++; /* to call hcut */ 504 hcut(t->tag, l->p0, l->p1-l->p0); 505 /* hcheck is local because we know rasp is contiguous */ 506 hcheck(t->tag); 507 }else{ 508 flushtyping(0); 509 cut(t, t->front, 0, 1); 510 } 511 } 512 if(typeesc >= l->p0) 513 typeesc = l->p0; 514 if(typestart >= 0){ 515 if(typestart >= l->p0) 516 typestart = l->p0; 517 typeend = l->p0; 518 if(typestart == typeend){ 519 typestart = -1; 520 typeend = -1; 521 modified = 0; 522 } 523 } 524 } 525 }else{ 526 if(c==ESC && typeesc>=0){ 527 l->p0 = typeesc; 528 l->p1 = a; 529 flushtyping(1); 530 } 531 for(l=t->l; l<&t->l[NL]; l++) 532 if(l->textfn) 533 flsetselect(l, l->p0, l->p1); 534 } 535 } 536 537 538 void 539 outcmd(void){ 540 if(work) 541 outTsll(Tworkfile, ((Text *)work->user1)->tag, work->p0, work->p1); 542 } 543 544 void 545 panic(char *s) 546 { 547 panic1(display, s); 548 } 549 550 void 551 panic1(Display*, char *s) 552 { 553 fprint(2, "samterm:panic: "); 554 perror(s); 555 abort(); 556 } 557 558 Rune* 559 gettext(Flayer *l, long n, ulong *np) 560 { 561 Text *t; 562 563 t = l->user1; 564 rload(&t->rasp, l->origin, l->origin+n, np); 565 return scratch; 566 } 567 568 long 569 scrtotal(Flayer *l) 570 { 571 return ((Text *)l->user1)->rasp.nrunes; 572 } 573 574 void* 575 alloc(ulong n) 576 { 577 void *p; 578 579 p = malloc(n); 580 if(p == 0) 581 panic("alloc"); 582 memset(p, 0, n); 583 return p; 584 } 585