acme.c 19 KB

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