acme.c (24679B)
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 <fcall.h> 10 #include <plumb.h> 11 #include <libsec.h> 12 #include "dat.h" 13 #include "fns.h" 14 /* for generating syms in mkfile only: */ 15 #include <bio.h> 16 #include "edit.h" 17 18 void mousethread(void*); 19 void keyboardthread(void*); 20 void waitthread(void*); 21 void xfidallocthread(void*); 22 void newwindowthread(void*); 23 void plumbproc(void*); 24 int timefmt(Fmt*); 25 26 Reffont **fontcache; 27 int nfontcache; 28 char wdir[512] = "."; 29 Reffont *reffonts[2]; 30 int snarffd = -1; 31 int mainpid; 32 int swapscrollbuttons = FALSE; 33 char *mtpt; 34 35 enum{ 36 NSnarf = 1000 /* less than 1024, I/O buffer size */ 37 }; 38 Rune snarfrune[NSnarf+1]; 39 40 char *fontnames[2] = 41 { 42 "/lib/font/bit/lucsans/euro.8.font", 43 "/lib/font/bit/lucm/unicode.9.font" 44 }; 45 46 Command *command; 47 48 void shutdownthread(void*); 49 void acmeerrorinit(void); 50 void readfile(Column*, char*); 51 static int shutdown(void*, char*); 52 53 void 54 derror(Display *d, char *errorstr) 55 { 56 USED(d); 57 error(errorstr); 58 } 59 60 void 61 threadmain(int argc, char *argv[]) 62 { 63 int i; 64 char *p, *loadfile = alloca(128); 65 Column *c; 66 int ncol; 67 Display *d; 68 69 rfork(RFENVG|RFNAMEG); 70 71 ncol = -1; 72 73 strcpy(loadfile, ".acme.dump"); 74 ARGBEGIN{ 75 case 'D': 76 {extern int _threaddebuglevel; 77 _threaddebuglevel = ~0; 78 } 79 break; 80 case 'a': 81 globalautoindent = TRUE; 82 break; 83 case 'b': 84 bartflag = TRUE; 85 break; 86 case 'c': 87 p = ARGF(); 88 if(p == nil) 89 goto Usage; 90 ncol = atoi(p); 91 if(ncol <= 0) 92 goto Usage; 93 break; 94 case 'f': 95 fontnames[0] = ARGF(); 96 if(fontnames[0] == nil) 97 goto Usage; 98 break; 99 case 'F': 100 fontnames[1] = ARGF(); 101 if(fontnames[1] == nil) 102 goto Usage; 103 break; 104 case 'l': 105 loadfile = ARGF(); 106 if(loadfile == nil) 107 goto Usage; 108 break; 109 case 'm': 110 mtpt = ARGF(); 111 if(mtpt == nil) 112 goto Usage; 113 break; 114 case 'r': 115 swapscrollbuttons = TRUE; 116 break; 117 case 'W': 118 winsize = ARGF(); 119 if(winsize == nil) 120 goto Usage; 121 break; 122 default: 123 Usage: 124 fprint(2, "usage: acme -a -c ncol -f fontname -F fixedwidthfontname -l loadfile -W winsize\n"); 125 threadexitsall("usage"); 126 }ARGEND 127 128 fontnames[0] = estrdup(fontnames[0]); 129 fontnames[1] = estrdup(fontnames[1]); 130 131 quotefmtinstall(); 132 fmtinstall('t', timefmt); 133 134 cputype = getenv("cputype"); 135 objtype = getenv("objtype"); 136 home = getenv("HOME"); 137 acmeshell = getenv("acmeshell"); 138 if(acmeshell && *acmeshell == '\0') 139 acmeshell = nil; 140 p = getenv("tabstop"); 141 if(p != nil){ 142 maxtab = strtoul(p, nil, 0); 143 free(p); 144 } 145 if(maxtab == 0) 146 maxtab = 4; 147 if(loadfile) 148 rowloadfonts(loadfile); 149 putenv("font", fontnames[0]); 150 snarffd = open("/dev/snarf", OREAD|OCEXEC); 151 /* 152 if(cputype){ 153 sprint(buf, "/acme/bin/%s", cputype); 154 bind(buf, "/bin", MBEFORE); 155 } 156 bind("/acme/bin", "/bin", MBEFORE); 157 */ 158 getwd(wdir, sizeof wdir); 159 160 /* 161 if(geninitdraw(nil, derror, fontnames[0], "acme", nil, Refnone) < 0){ 162 fprint(2, "acme: can't open display: %r\n"); 163 threadexitsall("geninitdraw"); 164 } 165 */ 166 if(initdraw(derror, fontnames[0], "acme") < 0){ 167 fprint(2, "acme: can't open display: %r\n"); 168 threadexitsall("initdraw"); 169 } 170 171 d = display; 172 font = d->defaultfont; 173 /*assert(font); */ 174 175 reffont.f = font; 176 reffonts[0] = &reffont; 177 incref(&reffont.ref); /* one to hold up 'font' variable */ 178 incref(&reffont.ref); /* one to hold up reffonts[0] */ 179 fontcache = emalloc(sizeof(Reffont*)); 180 nfontcache = 1; 181 fontcache[0] = &reffont; 182 183 iconinit(); 184 timerinit(); 185 rxinit(); 186 187 display->white = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x181818FF); /* set the default bg color */ 188 189 190 cwait = threadwaitchan(); 191 ccommand = chancreate(sizeof(Command**), 0); 192 ckill = chancreate(sizeof(Rune*), 0); 193 cxfidalloc = chancreate(sizeof(Xfid*), 0); 194 cxfidfree = chancreate(sizeof(Xfid*), 0); 195 cnewwindow = chancreate(sizeof(Channel*), 0); 196 cerr = chancreate(sizeof(char*), 0); 197 cedit = chancreate(sizeof(int), 0); 198 cexit = chancreate(sizeof(int), 0); 199 cwarn = chancreate(sizeof(void*), 1); 200 if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil || cwarn==nil){ 201 fprint(2, "acme: can't create initial channels: %r\n"); 202 threadexitsall("channels"); 203 } 204 chansetname(ccommand, "ccommand"); 205 chansetname(ckill, "ckill"); 206 chansetname(cxfidalloc, "cxfidalloc"); 207 chansetname(cxfidfree, "cxfidfree"); 208 chansetname(cnewwindow, "cnewwindow"); 209 chansetname(cerr, "cerr"); 210 chansetname(cedit, "cedit"); 211 chansetname(cexit, "cexit"); 212 chansetname(cwarn, "cwarn"); 213 214 mousectl = initmouse(nil, screen); 215 if(mousectl == nil){ 216 fprint(2, "acme: can't initialize mouse: %r\n"); 217 threadexitsall("mouse"); 218 } 219 mouse = &mousectl->m; 220 keyboardctl = initkeyboard(nil); 221 if(keyboardctl == nil){ 222 fprint(2, "acme: can't initialize keyboard: %r\n"); 223 threadexitsall("keyboard"); 224 } 225 mainpid = getpid(); 226 startplumbing(); 227 /* 228 plumbeditfd = plumbopen("edit", OREAD|OCEXEC); 229 if(plumbeditfd < 0) 230 fprint(2, "acme: can't initialize plumber: %r\n"); 231 else{ 232 cplumb = chancreate(sizeof(Plumbmsg*), 0); 233 threadcreate(plumbproc, nil, STACK); 234 } 235 plumbsendfd = plumbopen("send", OWRITE|OCEXEC); 236 */ 237 238 fsysinit(); 239 240 #define WPERCOL 8 241 disk = diskinit(); 242 if(!loadfile || !rowload(&row, loadfile, TRUE)){ 243 rowinit(&row, screen->clipr); 244 if(ncol < 0){ 245 if(argc == 0) 246 ncol = 2; 247 else{ 248 ncol = (argc+(WPERCOL-1))/WPERCOL; 249 if(ncol < 2) 250 ncol = 2; 251 } 252 } 253 if(ncol == 0) 254 ncol = 2; 255 for(i=0; i<ncol; i++){ 256 c = rowadd(&row, nil, -1); 257 if(c==nil && i==0) 258 error("initializing columns"); 259 } 260 c = row.col[row.ncol-1]; 261 if(argc == 0) 262 readfile(c, wdir); 263 else 264 for(i=0; i<argc; i++){ 265 p = utfrrune(argv[i], '/'); 266 if((p!=nil && strcmp(p, "/guide")==0) || i/WPERCOL>=row.ncol) 267 readfile(c, argv[i]); 268 else 269 readfile(row.col[i/WPERCOL], argv[i]); 270 } 271 } 272 flushimage(display, 1); 273 274 acmeerrorinit(); 275 threadcreate(keyboardthread, nil, STACK); 276 threadcreate(mousethread, nil, STACK); 277 threadcreate(waitthread, nil, STACK); 278 threadcreate(xfidallocthread, nil, STACK); 279 threadcreate(newwindowthread, nil, STACK); 280 /* threadcreate(shutdownthread, nil, STACK); */ 281 threadnotify(shutdown, 1); 282 recvul(cexit); 283 killprocs(); 284 threadexitsall(nil); 285 } 286 287 void 288 readfile(Column *c, char *s) 289 { 290 Window *w; 291 Rune rb[256]; 292 int nr; 293 Runestr rs; 294 295 w = coladd(c, nil, nil, -1); 296 if(s[0] != '/') 297 runesnprint(rb, sizeof rb, "%s/%s", wdir, s); 298 else 299 runesnprint(rb, sizeof rb, "%s", s); 300 nr = runestrlen(rb); 301 rs = cleanrname(runestr(rb, nr)); 302 winsetname(w, rs.r, rs.nr); 303 textload(&w->body, 0, s, 1); 304 w->body.file->mod = FALSE; 305 w->dirty = FALSE; 306 winsettag(w); 307 winresize(w, w->r, FALSE, TRUE); 308 textscrdraw(&w->body); 309 textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc); 310 xfidlog(w, "new"); 311 } 312 313 char *ignotes[] = { 314 "sys: write on closed pipe", 315 "sys: ttin", 316 "sys: ttou", 317 "sys: tstp", 318 nil 319 }; 320 321 char *oknotes[] ={ 322 "delete", 323 "hangup", 324 "kill", 325 "exit", 326 nil 327 }; 328 329 int dumping; 330 331 static int 332 shutdown(void *v, char *msg) 333 { 334 int i; 335 336 USED(v); 337 338 for(i=0; ignotes[i]; i++) 339 if(strncmp(ignotes[i], msg, strlen(ignotes[i])) == 0) 340 return 1; 341 342 killprocs(); 343 if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){ 344 dumping = TRUE; 345 rowdump(&row, nil); 346 } 347 for(i=0; oknotes[i]; i++) 348 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0) 349 threadexitsall(msg); 350 print("acme: %s\n", msg); 351 return 0; 352 } 353 354 /* 355 void 356 shutdownthread(void *v) 357 { 358 char *msg; 359 Channel *c; 360 361 USED(v); 362 363 threadsetname("shutdown"); 364 c = threadnotechan(); 365 while((msg = recvp(c)) != nil) 366 shutdown(nil, msg); 367 } 368 */ 369 370 void 371 killprocs(void) 372 { 373 Command *c; 374 375 fsysclose(); 376 /* if(display) */ 377 /* flushimage(display, 1); */ 378 379 for(c=command; c; c=c->next) 380 postnote(PNGROUP, c->pid, "hangup"); 381 } 382 383 static int errorfd; 384 int erroutfd; 385 386 void 387 acmeerrorproc(void *v) 388 { 389 char *buf; 390 int n; 391 392 USED(v); 393 threadsetname("acmeerrorproc"); 394 buf = emalloc(8192+1); 395 while((n=read(errorfd, buf, 8192)) >= 0){ 396 buf[n] = '\0'; 397 sendp(cerr, estrdup(buf)); 398 } 399 free(buf); 400 } 401 402 void 403 acmeerrorinit(void) 404 { 405 int pfd[2]; 406 407 if(pipe(pfd) < 0) 408 error("can't create pipe"); 409 #if 0 410 sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid); 411 fd = create(acmeerrorfile, OWRITE, 0666); 412 if(fd < 0){ 413 remove(acmeerrorfile); 414 fd = create(acmeerrorfile, OWRITE, 0666); 415 if(fd < 0) 416 error("can't create acmeerror file"); 417 } 418 sprint(buf, "%d", pfd[0]); 419 write(fd, buf, strlen(buf)); 420 close(fd); 421 /* reopen pfd[1] close on exec */ 422 sprint(buf, "/fd/%d", pfd[1]); 423 errorfd = open(buf, OREAD|OCEXEC); 424 #endif 425 fcntl(pfd[0], F_SETFD, FD_CLOEXEC); 426 fcntl(pfd[1], F_SETFD, FD_CLOEXEC); 427 erroutfd = pfd[0]; 428 errorfd = pfd[1]; 429 if(errorfd < 0) 430 error("can't re-open acmeerror file"); 431 proccreate(acmeerrorproc, nil, STACK); 432 } 433 434 /* 435 void 436 plumbproc(void *v) 437 { 438 Plumbmsg *m; 439 440 USED(v); 441 threadsetname("plumbproc"); 442 for(;;){ 443 m = threadplumbrecv(plumbeditfd); 444 if(m == nil) 445 threadexits(nil); 446 sendp(cplumb, m); 447 } 448 } 449 */ 450 451 void 452 keyboardthread(void *v) 453 { 454 Rune r; 455 Timer *timer; 456 Text *t; 457 enum { KTimer, KKey, NKALT }; 458 static Alt alts[NKALT+1]; 459 460 USED(v); 461 alts[KTimer].c = nil; 462 alts[KTimer].v = nil; 463 alts[KTimer].op = CHANNOP; 464 alts[KKey].c = keyboardctl->c; 465 alts[KKey].v = &r; 466 alts[KKey].op = CHANRCV; 467 alts[NKALT].op = CHANEND; 468 469 timer = nil; 470 typetext = nil; 471 threadsetname("keyboardthread"); 472 for(;;){ 473 switch(alt(alts)){ 474 case KTimer: 475 timerstop(timer); 476 t = typetext; 477 if(t!=nil && t->what==Tag){ 478 winlock(t->w, 'K'); 479 wincommit(t->w, t); 480 winunlock(t->w); 481 flushimage(display, 1); 482 } 483 alts[KTimer].c = nil; 484 alts[KTimer].op = CHANNOP; 485 break; 486 case KKey: 487 casekeyboard: 488 typetext = rowtype(&row, r, mouse->xy); 489 t = typetext; 490 if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */ 491 activecol = t->col; 492 if(t!=nil && t->w!=nil) 493 t->w->body.file->curtext = &t->w->body; 494 if(timer != nil) 495 timercancel(timer); 496 if(t!=nil && t->what==Tag) { 497 timer = timerstart(500); 498 alts[KTimer].c = timer->c; 499 alts[KTimer].op = CHANRCV; 500 }else{ 501 timer = nil; 502 alts[KTimer].c = nil; 503 alts[KTimer].op = CHANNOP; 504 } 505 if(nbrecv(keyboardctl->c, &r) > 0) 506 goto casekeyboard; 507 flushimage(display, 1); 508 break; 509 } 510 } 511 } 512 513 void 514 mousethread(void *v) 515 { 516 Text *t, *argt; 517 int but; 518 uint q0, q1; 519 Window *w; 520 Plumbmsg *pm; 521 Mouse m; 522 char *act; 523 enum { MResize, MMouse, MPlumb, MWarnings, NMALT }; 524 enum { Shift = 5 }; 525 static Alt alts[NMALT+1]; 526 527 USED(v); 528 threadsetname("mousethread"); 529 alts[MResize].c = mousectl->resizec; 530 alts[MResize].v = nil; 531 alts[MResize].op = CHANRCV; 532 alts[MMouse].c = mousectl->c; 533 alts[MMouse].v = &mousectl->m; 534 alts[MMouse].op = CHANRCV; 535 alts[MPlumb].c = cplumb; 536 alts[MPlumb].v = ± 537 alts[MPlumb].op = CHANRCV; 538 alts[MWarnings].c = cwarn; 539 alts[MWarnings].v = nil; 540 alts[MWarnings].op = CHANRCV; 541 if(cplumb == nil) 542 alts[MPlumb].op = CHANNOP; 543 alts[NMALT].op = CHANEND; 544 545 for(;;){ 546 qlock(&row.lk); 547 flushwarnings(); 548 qunlock(&row.lk); 549 flushimage(display, 1); 550 switch(alt(alts)){ 551 case MResize: 552 if(getwindow(display, Refnone) < 0) 553 error("attach to window"); 554 draw(screen, screen->r, display->white, nil, ZP); 555 iconinit(); 556 scrlresize(); 557 rowresize(&row, screen->clipr); 558 break; 559 case MPlumb: 560 if(strcmp(pm->type, "text") == 0){ 561 act = plumblookup(pm->attr, "action"); 562 if(act==nil || strcmp(act, "showfile")==0) 563 plumblook(pm); 564 else if(strcmp(act, "showdata")==0) 565 plumbshow(pm); 566 } 567 plumbfree(pm); 568 break; 569 case MWarnings: 570 break; 571 case MMouse: 572 /* 573 * Make a copy so decisions are consistent; mousectl changes 574 * underfoot. Can't just receive into m because this introduces 575 * another race; see /sys/src/libdraw/mouse.c. 576 */ 577 m = mousectl->m; 578 qlock(&row.lk); 579 t = rowwhich(&row, m.xy); 580 581 if((t!=mousetext && t!=nil && t->w!=nil) && 582 (mousetext==nil || mousetext->w==nil || t->w->id!=mousetext->w->id)) { 583 xfidlog(t->w, "focus"); 584 } 585 586 if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){ 587 winlock(mousetext->w, 'M'); 588 mousetext->eq0 = ~0; 589 wincommit(mousetext->w, mousetext); 590 winunlock(mousetext->w); 591 } 592 mousetext = t; 593 if(t == nil) 594 goto Continue; 595 w = t->w; 596 if(t==nil || m.buttons==0) 597 goto Continue; 598 but = 0; 599 if(m.buttons == 1) 600 but = 1; 601 else if(m.buttons == 2) 602 but = 2; 603 else if(m.buttons == 4) 604 but = 3; 605 barttext = t; 606 if(t->what==Body && ptinrect(m.xy, t->scrollr)){ 607 if(but){ 608 if(swapscrollbuttons){ 609 if(but == 1) 610 but = 3; 611 else if(but == 3) 612 but = 1; 613 } 614 winlock(w, 'M'); 615 t->eq0 = ~0; 616 textscroll(t, but); 617 winunlock(w); 618 } 619 goto Continue; 620 } 621 /* scroll buttons, wheels, etc. */ 622 if(w != nil && (m.buttons & (8|16))){ 623 if(m.buttons & 8) 624 but = Kscrolloneup; 625 else 626 but = Kscrollonedown; 627 winlock(w, 'M'); 628 t->eq0 = ~0; 629 texttype(t, but); 630 winunlock(w); 631 goto Continue; 632 } 633 if(ptinrect(m.xy, t->scrollr)){ 634 if(but){ 635 if(t->what == Columntag) 636 rowdragcol(&row, t->col, but); 637 else if(t->what == Tag){ 638 coldragwin(t->col, t->w, but); 639 if(t->w) 640 barttext = &t->w->body; 641 } 642 if(t->col) 643 activecol = t->col; 644 } 645 goto Continue; 646 } 647 if(m.buttons){ 648 if(w) 649 winlock(w, 'M'); 650 t->eq0 = ~0; 651 if(w) 652 wincommit(w, t); 653 else 654 textcommit(t, TRUE); 655 if(m.buttons & 1){ 656 textselect(t); 657 if(w) 658 winsettag(w); 659 argtext = t; 660 seltext = t; 661 if(t->col) 662 activecol = t->col; /* button 1 only */ 663 if(t->w!=nil && t==&t->w->body) 664 activewin = t->w; 665 }else if(m.buttons & 2){ 666 if(textselect2(t, &q0, &q1, &argt)) 667 execute(t, q0, q1, FALSE, argt); 668 }else if(m.buttons & (4|(4<<Shift))){ 669 if(textselect3(t, &q0, &q1)) 670 look3(t, q0, q1, FALSE, (m.buttons&(4<<Shift))!=0); 671 } 672 if(w) 673 winunlock(w); 674 goto Continue; 675 } 676 Continue: 677 qunlock(&row.lk); 678 break; 679 } 680 } 681 } 682 683 /* 684 * There is a race between process exiting and our finding out it was ever created. 685 * This structure keeps a list of processes that have exited we haven't heard of. 686 */ 687 typedef struct Pid Pid; 688 struct Pid 689 { 690 int pid; 691 char msg[ERRMAX]; 692 Pid *next; 693 }; 694 695 void 696 waitthread(void *v) 697 { 698 Waitmsg *w; 699 Command *c, *lc; 700 uint pid; 701 int found, ncmd; 702 Rune *cmd; 703 char *err; 704 Text *t; 705 Pid *pids, *p, *lastp; 706 enum { WErr, WKill, WWait, WCmd, NWALT }; 707 Alt alts[NWALT+1]; 708 709 USED(v); 710 threadsetname("waitthread"); 711 pids = nil; 712 alts[WErr].c = cerr; 713 alts[WErr].v = &err; 714 alts[WErr].op = CHANRCV; 715 alts[WKill].c = ckill; 716 alts[WKill].v = &cmd; 717 alts[WKill].op = CHANRCV; 718 alts[WWait].c = cwait; 719 alts[WWait].v = &w; 720 alts[WWait].op = CHANRCV; 721 alts[WCmd].c = ccommand; 722 alts[WCmd].v = &c; 723 alts[WCmd].op = CHANRCV; 724 alts[NWALT].op = CHANEND; 725 726 command = nil; 727 for(;;){ 728 switch(alt(alts)){ 729 case WErr: 730 qlock(&row.lk); 731 warning(nil, "%s", err); 732 free(err); 733 flushimage(display, 1); 734 qunlock(&row.lk); 735 break; 736 case WKill: 737 found = FALSE; 738 ncmd = runestrlen(cmd); 739 for(c=command; c; c=c->next){ 740 /* -1 for blank */ 741 if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){ 742 if(postnote(PNGROUP, c->pid, "kill") < 0) 743 warning(nil, "kill %S: %r\n", cmd); 744 found = TRUE; 745 } 746 } 747 if(!found) 748 warning(nil, "Kill: no process %S\n", cmd); 749 free(cmd); 750 break; 751 case WWait: 752 pid = w->pid; 753 lc = nil; 754 for(c=command; c; c=c->next){ 755 if(c->pid == pid){ 756 if(lc) 757 lc->next = c->next; 758 else 759 command = c->next; 760 break; 761 } 762 lc = c; 763 } 764 qlock(&row.lk); 765 t = &row.tag; 766 textcommit(t, TRUE); 767 if(c == nil){ 768 /* helper processes use this exit status */ 769 if(strncmp(w->msg, "libthread", 9) != 0){ 770 p = emalloc(sizeof(Pid)); 771 p->pid = pid; 772 strncpy(p->msg, w->msg, sizeof(p->msg)); 773 p->next = pids; 774 pids = p; 775 } 776 }else{ 777 if(search(t, c->name, c->nname, FALSE)){ 778 textdelete(t, t->q0, t->q1, TRUE); 779 textsetselect(t, 0, 0); 780 } 781 if(w->msg[0]) 782 warning(c->md, "%.*S: exit %s\n", c->nname-1, c->name, w->msg); 783 flushimage(display, 1); 784 } 785 qunlock(&row.lk); 786 free(w); 787 Freecmd: 788 if(c){ 789 if(c->iseditcmd) 790 sendul(cedit, 0); 791 free(c->text); 792 free(c->name); 793 fsysdelid(c->md); 794 free(c); 795 } 796 break; 797 case WCmd: 798 /* has this command already exited? */ 799 lastp = nil; 800 for(p=pids; p!=nil; p=p->next){ 801 if(p->pid == c->pid){ 802 if(p->msg[0]) 803 warning(c->md, "%s\n", p->msg); 804 if(lastp == nil) 805 pids = p->next; 806 else 807 lastp->next = p->next; 808 free(p); 809 goto Freecmd; 810 } 811 lastp = p; 812 } 813 c->next = command; 814 command = c; 815 qlock(&row.lk); 816 t = &row.tag; 817 textcommit(t, TRUE); 818 textinsert(t, 0, c->name, c->nname, TRUE); 819 textsetselect(t, 0, 0); 820 flushimage(display, 1); 821 qunlock(&row.lk); 822 break; 823 } 824 } 825 } 826 827 void 828 xfidallocthread(void *v) 829 { 830 Xfid *xfree, *x; 831 enum { Alloc, Free, N }; 832 static Alt alts[N+1]; 833 834 USED(v); 835 threadsetname("xfidallocthread"); 836 alts[Alloc].c = cxfidalloc; 837 alts[Alloc].v = nil; 838 alts[Alloc].op = CHANRCV; 839 alts[Free].c = cxfidfree; 840 alts[Free].v = &x; 841 alts[Free].op = CHANRCV; 842 alts[N].op = CHANEND; 843 844 xfree = nil; 845 for(;;){ 846 switch(alt(alts)){ 847 case Alloc: 848 x = xfree; 849 if(x) 850 xfree = x->next; 851 else{ 852 x = emalloc(sizeof(Xfid)); 853 x->c = chancreate(sizeof(void(*)(Xfid*)), 0); 854 chansetname(x->c, "xc%p", x->c); 855 x->arg = x; 856 threadcreate(xfidctl, x->arg, STACK); 857 } 858 sendp(cxfidalloc, x); 859 break; 860 case Free: 861 x->next = xfree; 862 xfree = x; 863 break; 864 } 865 } 866 } 867 868 /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */ 869 void 870 newwindowthread(void *v) 871 { 872 Window *w; 873 874 USED(v); 875 threadsetname("newwindowthread"); 876 877 for(;;){ 878 /* only fsysproc is talking to us, so synchronization is trivial */ 879 recvp(cnewwindow); 880 w = makenewwindow(nil); 881 winsettag(w); 882 xfidlog(w, "new"); 883 sendp(cnewwindow, w); 884 } 885 } 886 887 Reffont* 888 rfget(int fix, int save, int setfont, char *name) 889 { 890 Reffont *r; 891 Font *f; 892 int i; 893 894 r = nil; 895 if(name == nil){ 896 name = fontnames[fix]; 897 r = reffonts[fix]; 898 } 899 if(r == nil){ 900 for(i=0; i<nfontcache; i++) 901 if(strcmp(name, fontcache[i]->f->name) == 0){ 902 r = fontcache[i]; 903 goto Found; 904 } 905 f = openfont(display, name); 906 if(f == nil){ 907 warning(nil, "can't open font file %s: %r\n", name); 908 return nil; 909 } 910 r = emalloc(sizeof(Reffont)); 911 r->f = f; 912 fontcache = erealloc(fontcache, (nfontcache+1)*sizeof(Reffont*)); 913 fontcache[nfontcache++] = r; 914 } 915 Found: 916 if(save){ 917 incref(&r->ref); 918 if(reffonts[fix]) 919 rfclose(reffonts[fix]); 920 reffonts[fix] = r; 921 if(name != fontnames[fix]){ 922 free(fontnames[fix]); 923 fontnames[fix] = estrdup(name); 924 } 925 } 926 if(setfont){ 927 reffont.f = r->f; 928 incref(&r->ref); 929 rfclose(reffonts[0]); 930 font = r->f; 931 reffonts[0] = r; 932 incref(&r->ref); 933 iconinit(); 934 } 935 incref(&r->ref); 936 return r; 937 } 938 939 void 940 rfclose(Reffont *r) 941 { 942 int i; 943 944 if(decref(&r->ref) == 0){ 945 for(i=0; i<nfontcache; i++) 946 if(r == fontcache[i]) 947 break; 948 if(i >= nfontcache) 949 warning(nil, "internal error: can't find font in cache\n"); 950 else{ 951 nfontcache--; 952 memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*)); 953 } 954 freefont(r->f); 955 free(r); 956 } 957 } 958 959 Cursor boxcursor = { 960 {-7, -7}, 961 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 962 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 963 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF, 964 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 965 {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 966 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 967 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 968 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00} 969 }; 970 971 Cursor2 boxcursor2 = { 972 {-15, -15}, 973 {0xFF, 0xFF, 0xFF, 0xFF, 974 0xFF, 0xFF, 0xFF, 0xFF, 975 0xFF, 0xFF, 0xFF, 0xFF, 976 0xFF, 0xFF, 0xFF, 0xFF, 977 0xFF, 0xFF, 0xFF, 0xFF, 978 0xFF, 0xFF, 0xFF, 0xFF, 979 0xFF, 0xFF, 0xFF, 0xFF, 980 0xFF, 0xFF, 0xFF, 0xFF, 981 0xFF, 0xFF, 0xFF, 0xFF, 982 0xFF, 0xFF, 0xFF, 0xFF, 983 0xFF, 0xC0, 0x03, 0xFF, 984 0xFF, 0xC0, 0x03, 0xFF, 985 0xFF, 0xC0, 0x03, 0xFF, 986 0xFF, 0xC0, 0x03, 0xFF, 987 0xFF, 0xC0, 0x03, 0xFF, 988 0xFF, 0xC0, 0x03, 0xFF, 989 0xFF, 0xC0, 0x03, 0xFF, 990 0xFF, 0xC0, 0x03, 0xFF, 991 0xFF, 0xC0, 0x03, 0xFF, 992 0xFF, 0xC0, 0x03, 0xFF, 993 0xFF, 0xC0, 0x03, 0xFF, 994 0xFF, 0xC0, 0x03, 0xFF, 995 0xFF, 0xFF, 0xFF, 0xFF, 996 0xFF, 0xFF, 0xFF, 0xFF, 997 0xFF, 0xFF, 0xFF, 0xFF, 998 0xFF, 0xFF, 0xFF, 0xFF, 999 0xFF, 0xFF, 0xFF, 0xFF, 1000 0xFF, 0xFF, 0xFF, 0xFF, 1001 0xFF, 0xFF, 0xFF, 0xFF, 1002 0xFF, 0xFF, 0xFF, 0xFF, 1003 0xFF, 0xFF, 0xFF, 0xFF, 1004 0xFF, 0xFF, 0xFF, 0xFF}, 1005 {0x00, 0x00, 0x00, 0x00, 1006 0x00, 0x00, 0x00, 0x00, 1007 0x3F, 0xFF, 0xFF, 0xFC, 1008 0x3F, 0xFF, 0xFF, 0xFC, 1009 0x3F, 0xFF, 0xFF, 0xFC, 1010 0x3F, 0xFF, 0xFF, 0xFC, 1011 0x3F, 0xFF, 0xFF, 0xFC, 1012 0x3F, 0xFF, 0xFF, 0xFC, 1013 0x3F, 0x00, 0x00, 0xFC, 1014 0x3F, 0x00, 0x00, 0xFC, 1015 0x3F, 0x00, 0x00, 0xFC, 1016 0x3F, 0x00, 0x00, 0xFC, 1017 0x3F, 0x00, 0x00, 0xFC, 1018 0x3F, 0x00, 0x00, 0xFC, 1019 0x3F, 0x00, 0x00, 0xFC, 1020 0x3F, 0x00, 0x00, 0xFC, 1021 0x3F, 0x00, 0x00, 0xFC, 1022 0x3F, 0x00, 0x00, 0xFC, 1023 0x3F, 0x00, 0x00, 0xFC, 1024 0x3F, 0x00, 0x00, 0xFC, 1025 0x3F, 0x00, 0x00, 0xFC, 1026 0x3F, 0x00, 0x00, 0xFC, 1027 0x3F, 0x00, 0x00, 0xFC, 1028 0x3F, 0x00, 0x00, 0xFC, 1029 0x3F, 0xFF, 0xFF, 0xFC, 1030 0x3F, 0xFF, 0xFF, 0xFC, 1031 0x3F, 0xFF, 0xFF, 0xFC, 1032 0x3F, 0xFF, 0xFF, 0xFC, 1033 0x3F, 0xFF, 0xFF, 0xFC, 1034 0x3F, 0xFF, 0xFF, 0xFC, 1035 0x00, 0x00, 0x00, 0x00, 1036 0x00, 0x00, 0x00, 0x00} 1037 }; 1038 1039 void 1040 iconinit(void) 1041 { 1042 Rectangle r; 1043 Image *tmp; 1044 1045 if(tagcols[BACK] == nil) { 1046 /* Blue */ 1047 tagcols[BACK] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x453D41FF); 1048 tagcols[HIGH] = allocimagemix(display, 0x453D41FF, 0xFFFFFF33); 1049 tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xD7AF00FF); 1050 tagcols[TEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xF4F4FFFF); 1051 tagcols[HTEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xF4F4FFFF); 1052 1053 /* Yellow */ 1054 textcols[BACK] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x181818FF); 1055 textcols[HIGH] = allocimagemix(display, 0x181818FF, 0xFFFFFF33); 1056 textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xD7AF00FF); 1057 textcols[TEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xF4F4FFFF); 1058 textcols[HTEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xF4F4FFFF); 1059 } 1060 1061 r = Rect(0, 0, Scrollwid, font->height+1); 1062 if(button && eqrect(r, button->r)) 1063 return; 1064 1065 if(button){ 1066 freeimage(button); 1067 freeimage(modbutton); 1068 freeimage(colbutton); 1069 } 1070 1071 button = allocimage(display, r, screen->chan, 0, DNofill); 1072 draw(button, r, tagcols[BACK], nil, r.min); 1073 border(button, r, ButtonBorder, tagcols[BORD], ZP); 1074 1075 r = button->r; 1076 modbutton = allocimage(display, r, screen->chan, 0, DNofill); 1077 draw(modbutton, r, tagcols[BACK], nil, r.min); 1078 border(modbutton, r, ButtonBorder, tagcols[BORD], ZP); 1079 r = insetrect(r, ButtonBorder); 1080 tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue); 1081 draw(modbutton, r, tmp, nil, ZP); 1082 freeimage(tmp); 1083 1084 r = button->r; 1085 colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue); 1086 1087 but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF); 1088 but3col = allocimage(display, r, screen->chan, 1, 0x006600FF); 1089 } 1090 1091 /* 1092 * /dev/snarf updates when the file is closed, so we must open our own 1093 * fd here rather than use snarffd 1094 */ 1095 1096 /* rio truncates larges snarf buffers, so this avoids using the 1097 * service if the string is huge */ 1098 1099 #define MAXSNARF 100*1024 1100 1101 void 1102 acmeputsnarf(void) 1103 { 1104 int i, n; 1105 Fmt f; 1106 char *s; 1107 1108 if(snarfbuf.nc==0) 1109 return; 1110 if(snarfbuf.nc > MAXSNARF) 1111 return; 1112 1113 fmtstrinit(&f); 1114 for(i=0; i<snarfbuf.nc; i+=n){ 1115 n = snarfbuf.nc-i; 1116 if(n >= NSnarf) 1117 n = NSnarf; 1118 bufread(&snarfbuf, i, snarfrune, n); 1119 if(fmtprint(&f, "%.*S", n, snarfrune) < 0) 1120 break; 1121 } 1122 s = fmtstrflush(&f); 1123 if(s && s[0]) 1124 putsnarf(s); 1125 free(s); 1126 } 1127 1128 void 1129 acmegetsnarf(void) 1130 { 1131 char *s; 1132 int nb, nr, nulls, len; 1133 Rune *r; 1134 1135 s = getsnarf(); 1136 if(s == nil || s[0]==0){ 1137 free(s); 1138 return; 1139 } 1140 1141 len = strlen(s); 1142 r = runemalloc(len+1); 1143 cvttorunes(s, len, r, &nb, &nr, &nulls); 1144 bufreset(&snarfbuf); 1145 bufinsert(&snarfbuf, 0, r, nr); 1146 free(r); 1147 free(s); 1148 } 1149 1150 int 1151 ismtpt(char *file) 1152 { 1153 int n; 1154 1155 if(mtpt == nil) 1156 return 0; 1157 1158 /* This is not foolproof, but it will stop a lot of them. */ 1159 n = strlen(mtpt); 1160 return strncmp(file, mtpt, n) == 0 && ((n > 0 && mtpt[n-1] == '/') || file[n] == '/' || file[n] == 0); 1161 } 1162 1163 int 1164 timefmt(Fmt *f) 1165 { 1166 Tm *tm; 1167 1168 tm = localtime(va_arg(f->args, ulong)); 1169 return fmtprint(f, "%04d/%02d/%02d %02d:%02d:%02d", 1170 tm->year+1900, tm->mon+1, tm->mday, tm->hour, tm->min, tm->sec); 1171 }