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