Blob


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