acme.c 20 KB

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