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