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