acme.c 19 KB

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