acme.c 19 KB

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