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 default:
100 Usage:
101 fprint(2, "usage: acme -a -c ncol -f fontname -F fixedwidthfontname -l loadfile\n");
102 exits("usage");
103 }ARGEND
105 cputype = getenv("cputype");
106 objtype = getenv("objtype");
107 home = getenv("home");
108 p = getenv("tabstop");
109 if(p != nil){
110 maxtab = strtoul(p, nil, 0);
111 free(p);
113 if(maxtab == 0)
114 maxtab = 4;
115 if(loadfile)
116 rowloadfonts(loadfile);
117 putenv("font", fontnames[0]);
118 snarffd = open("/dev/snarf", OREAD|OCEXEC);
119 /*
120 if(cputype){
121 sprint(buf, "/acme/bin/%s", cputype);
122 bind(buf, "/bin", MBEFORE);
124 bind("/acme/bin", "/bin", MBEFORE);
125 */
126 getwd(wdir, sizeof wdir);
128 /*
129 if(geninitdraw(nil, derror, fontnames[0], "acme", nil, Refnone) < 0){
130 fprint(2, "acme: can't open display: %r\n");
131 exits("geninitdraw");
133 */
134 if(initdraw(derror, fontnames[0], "acme") < 0){
135 fprint(2, "acme: can't open display: %r\n");
136 exits("initdraw");
139 d = display;
140 font = d->defaultfont;
141 //assert(font);
143 reffont.f = font;
144 reffonts[0] = &reffont;
145 incref(&reffont.ref); /* one to hold up 'font' variable */
146 incref(&reffont.ref); /* one to hold up reffonts[0] */
147 fontcache = emalloc(sizeof(Reffont*));
148 nfontcache = 1;
149 fontcache[0] = &reffont;
151 iconinit();
152 timerinit();
153 rxinit();
155 cwait = threadwaitchan();
156 ccommand = chancreate(sizeof(Command**), 0);
157 ckill = chancreate(sizeof(Rune*), 0);
158 cxfidalloc = chancreate(sizeof(Xfid*), 0);
159 cxfidfree = chancreate(sizeof(Xfid*), 0);
160 cnewwindow = chancreate(sizeof(Channel*), 0);
161 cerr = chancreate(sizeof(char*), 0);
162 cedit = chancreate(sizeof(int), 0);
163 cexit = chancreate(sizeof(int), 0);
164 cwarn = chancreate(sizeof(void*), 1);
165 if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil || cwarn==nil){
166 fprint(2, "acme: can't create initial channels: %r\n");
167 exits("channels");
170 mousectl = initmouse(nil, screen);
171 if(mousectl == nil){
172 fprint(2, "acme: can't initialize mouse: %r\n");
173 exits("mouse");
175 mouse = &mousectl->m;
176 keyboardctl = initkeyboard(nil);
177 if(keyboardctl == nil){
178 fprint(2, "acme: can't initialize keyboard: %r\n");
179 exits("keyboard");
181 mainpid = getpid();
182 startplumbing();
183 /*
184 plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
185 if(plumbeditfd < 0)
186 fprint(2, "acme: can't initialize plumber: %r\n");
187 else{
188 cplumb = chancreate(sizeof(Plumbmsg*), 0);
189 threadcreate(plumbproc, nil, STACK);
191 plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
192 */
194 fsysinit();
196 #define WPERCOL 8
197 disk = diskinit();
198 if(loadfile)
199 rowload(&row, loadfile, TRUE);
200 else{
201 rowinit(&row, screen->clipr);
202 if(ncol < 0){
203 if(argc == 0)
204 ncol = 2;
205 else{
206 ncol = (argc+(WPERCOL-1))/WPERCOL;
207 if(ncol < 2)
208 ncol = 2;
211 if(ncol == 0)
212 ncol = 2;
213 for(i=0; i<ncol; i++){
214 c = rowadd(&row, nil, -1);
215 if(c==nil && i==0)
216 error("initializing columns");
218 c = row.col[row.ncol-1];
219 if(argc == 0)
220 readfile(c, wdir);
221 else
222 for(i=0; i<argc; i++){
223 p = utfrrune(argv[i], '/');
224 if((p!=nil && strcmp(p, "/guide")==0) || i/WPERCOL>=row.ncol)
225 readfile(c, argv[i]);
226 else
227 readfile(row.col[i/WPERCOL], argv[i]);
230 flushimage(display, 1);
232 acmeerrorinit();
233 threadcreate(keyboardthread, nil, STACK);
234 threadcreate(mousethread, nil, STACK);
235 threadcreate(waitthread, nil, STACK);
236 threadcreate(xfidallocthread, nil, STACK);
237 threadcreate(newwindowthread, nil, STACK);
239 threadnotify(shutdown, 1);
240 recvul(cexit);
241 killprocs();
242 threadexitsall(nil);
245 void
246 readfile(Column *c, char *s)
248 Window *w;
249 Rune rb[256];
250 int nb, nr;
251 Runestr rs;
253 w = coladd(c, nil, nil, -1);
254 cvttorunes(s, strlen(s), rb, &nb, &nr, nil);
255 rs = cleanrname(runestr(rb, nr));
256 winsetname(w, rs.r, rs.nr);
257 textload(&w->body, 0, s, 1);
258 w->body.file->mod = FALSE;
259 w->dirty = FALSE;
260 winsettag(w);
261 textscrdraw(&w->body);
262 textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc);
265 char *oknotes[] ={
266 "delete",
267 "hangup",
268 "kill",
269 "exit",
270 nil
271 };
273 int dumping;
275 static int
276 shutdown(void *v, char *msg)
278 int i;
280 if(strcmp(msg, "sys: write on closed pipe") == 0)
281 return 1;
283 USED(v);
284 killprocs();
285 if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){
286 dumping = TRUE;
287 rowdump(&row, nil);
289 for(i=0; oknotes[i]; i++)
290 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
291 threadexitsall(msg);
292 print("acme: %s\n", msg);
293 abort();
294 return 0;
297 void
298 killprocs(void)
300 Command *c;
302 fsysclose();
303 // if(display)
304 // flushimage(display, 1);
306 for(c=command; c; c=c->next)
307 postnote(PNGROUP, c->pid, "hangup");
310 static int errorfd;
311 int erroutfd;
313 void
314 acmeerrorproc(void *v)
316 char *buf;
317 int n;
319 USED(v);
320 threadsetname("acmeerrorproc");
321 buf = emalloc(8192+1);
322 while((n=threadread(errorfd, buf, 8192)) >= 0){
323 buf[n] = '\0';
324 sendp(cerr, estrdup(buf));
328 void
329 acmeerrorinit(void)
331 int pfd[2];
333 if(pipe(pfd) < 0)
334 error("can't create pipe");
335 #if 0
336 sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid);
337 fd = create(acmeerrorfile, OWRITE, 0666);
338 if(fd < 0){
339 remove(acmeerrorfile);
340 fd = create(acmeerrorfile, OWRITE, 0666);
341 if(fd < 0)
342 error("can't create acmeerror file");
344 sprint(buf, "%d", pfd[0]);
345 write(fd, buf, strlen(buf));
346 close(fd);
347 /* reopen pfd[1] close on exec */
348 sprint(buf, "/fd/%d", pfd[1]);
349 errorfd = open(buf, OREAD|OCEXEC);
350 #endif
351 fcntl(pfd[0], F_SETFD, FD_CLOEXEC);
352 fcntl(pfd[1], F_SETFD, FD_CLOEXEC);
353 erroutfd = pfd[0];
354 errorfd = pfd[1];
355 if(errorfd < 0)
356 error("can't re-open acmeerror file");
357 threadcreate(acmeerrorproc, nil, STACK);
360 /*
361 void
362 plumbproc(void *v)
364 Plumbmsg *m;
366 USED(v);
367 threadsetname("plumbproc");
368 for(;;){
369 m = threadplumbrecv(plumbeditfd);
370 if(m == nil)
371 threadexits(nil);
372 sendp(cplumb, m);
375 */
377 void
378 keyboardthread(void *v)
380 Rune r;
381 Timer *timer;
382 Text *t;
383 enum { KTimer, KKey, NKALT };
384 static Alt alts[NKALT+1];
386 USED(v);
387 alts[KTimer].c = nil;
388 alts[KTimer].v = nil;
389 alts[KTimer].op = CHANNOP;
390 alts[KKey].c = keyboardctl->c;
391 alts[KKey].v = &r;
392 alts[KKey].op = CHANRCV;
393 alts[NKALT].op = CHANEND;
395 timer = nil;
396 typetext = nil;
397 threadsetname("keyboardthread");
398 for(;;){
399 switch(alt(alts)){
400 case KTimer:
401 timerstop(timer);
402 t = typetext;
403 if(t!=nil && t->what==Tag){
404 winlock(t->w, 'K');
405 wincommit(t->w, t);
406 winunlock(t->w);
407 flushimage(display, 1);
409 alts[KTimer].c = nil;
410 alts[KTimer].op = CHANNOP;
411 break;
412 case KKey:
413 casekeyboard:
414 typetext = rowtype(&row, r, mouse->xy);
415 t = typetext;
416 if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */
417 activecol = t->col;
418 if(t!=nil && t->w!=nil)
419 t->w->body.file->curtext = &t->w->body;
420 if(timer != nil)
421 timercancel(timer);
422 if(t!=nil && t->what==Tag) {
423 timer = timerstart(500);
424 alts[KTimer].c = timer->c;
425 alts[KTimer].op = CHANRCV;
426 }else{
427 timer = nil;
428 alts[KTimer].c = nil;
429 alts[KTimer].op = CHANNOP;
431 if(nbrecv(keyboardctl->c, &r) > 0)
432 goto casekeyboard;
433 flushimage(display, 1);
434 break;
439 void
440 mousethread(void *v)
442 Text *t, *argt;
443 int but;
444 uint q0, q1;
445 Window *w;
446 Plumbmsg *pm;
447 Mouse m;
448 char *act;
449 enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
450 static Alt alts[NMALT+1];
452 USED(v);
453 threadsetname("mousethread");
454 alts[MResize].c = mousectl->resizec;
455 alts[MResize].v = nil;
456 alts[MResize].op = CHANRCV;
457 alts[MMouse].c = mousectl->c;
458 alts[MMouse].v = &mousectl->m;
459 alts[MMouse].op = CHANRCV;
460 alts[MPlumb].c = cplumb;
461 alts[MPlumb].v = &pm;
462 alts[MPlumb].op = CHANRCV;
463 alts[MWarnings].c = cwarn;
464 alts[MWarnings].v = nil;
465 alts[MWarnings].op = CHANRCV;
466 if(cplumb == nil)
467 alts[MPlumb].op = CHANNOP;
468 alts[NMALT].op = CHANEND;
470 for(;;){
471 qlock(&row.lk);
472 flushwarnings();
473 qunlock(&row.lk);
474 flushimage(display, 1);
475 switch(alt(alts)){
476 case MResize:
477 if(getwindow(display, Refnone) < 0)
478 error("attach to window");
479 draw(screen, screen->r, display->white, nil, ZP);
480 scrlresize();
481 rowresize(&row, screen->clipr);
482 break;
483 case MPlumb:
484 if(strcmp(pm->type, "text") == 0){
485 act = plumblookup(pm->attr, "action");
486 if(act==nil || strcmp(act, "showfile")==0)
487 plumblook(pm);
488 else if(strcmp(act, "showdata")==0)
489 plumbshow(pm);
491 plumbfree(pm);
492 break;
493 case MWarnings:
494 break;
495 case MMouse:
496 /*
497 * Make a copy so decisions are consistent; mousectl changes
498 * underfoot. Can't just receive into m because this introduces
499 * another race; see /sys/src/libdraw/mouse.c.
500 */
501 m = mousectl->m;
502 qlock(&row.lk);
503 t = rowwhich(&row, m.xy);
504 if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
505 winlock(mousetext->w, 'M');
506 mousetext->eq0 = ~0;
507 wincommit(mousetext->w, mousetext);
508 winunlock(mousetext->w);
510 mousetext = t;
511 if(t == nil)
512 goto Continue;
513 w = t->w;
514 if(t==nil || m.buttons==0)
515 goto Continue;
516 but = 0;
517 if(m.buttons == 1)
518 but = 1;
519 else if(m.buttons == 2)
520 but = 2;
521 else if(m.buttons == 4)
522 but = 3;
523 barttext = t;
524 if(t->what==Body && ptinrect(m.xy, t->scrollr)){
525 if(but){
526 winlock(w, 'M');
527 t->eq0 = ~0;
528 textscroll(t, but);
529 winunlock(w);
531 goto Continue;
533 if(ptinrect(m.xy, t->scrollr)){
534 if(but){
535 if(t->what == Columntag)
536 rowdragcol(&row, t->col, but);
537 else if(t->what == Tag){
538 coldragwin(t->col, t->w, but);
539 if(t->w)
540 barttext = &t->w->body;
542 if(t->col)
543 activecol = t->col;
545 goto Continue;
547 if(m.buttons){
548 if(w)
549 winlock(w, 'M');
550 t->eq0 = ~0;
551 if(w)
552 wincommit(w, t);
553 else
554 textcommit(t, TRUE);
555 if(m.buttons & 1){
556 textselect(t);
557 if(w)
558 winsettag(w);
559 argtext = t;
560 seltext = t;
561 if(t->col)
562 activecol = t->col; /* button 1 only */
563 if(t->w!=nil && t==&t->w->body)
564 activewin = t->w;
565 }else if(m.buttons & 2){
566 if(textselect2(t, &q0, &q1, &argt))
567 execute(t, q0, q1, FALSE, argt);
568 }else if(m.buttons & 4){
569 if(textselect3(t, &q0, &q1))
570 look3(t, q0, q1, FALSE);
572 if(w)
573 winunlock(w);
574 goto Continue;
576 Continue:
577 qunlock(&row.lk);
578 break;
583 /*
584 * There is a race between process exiting and our finding out it was ever created.
585 * This structure keeps a list of processes that have exited we haven't heard of.
586 */
587 typedef struct Pid Pid;
588 struct Pid
590 int pid;
591 char msg[ERRMAX];
592 Pid *next;
593 };
595 void
596 waitthread(void *v)
598 Waitmsg *w;
599 Command *c, *lc;
600 uint pid;
601 int found, ncmd;
602 Rune *cmd;
603 char *err;
604 Text *t;
605 Pid *pids, *p, *lastp;
606 enum { WErr, WKill, WWait, WCmd, NWALT };
607 Alt alts[NWALT+1];
609 USED(v);
610 threadsetname("waitthread");
611 pids = nil;
612 alts[WErr].c = cerr;
613 alts[WErr].v = &err;
614 alts[WErr].op = CHANRCV;
615 alts[WKill].c = ckill;
616 alts[WKill].v = &cmd;
617 alts[WKill].op = CHANRCV;
618 alts[WWait].c = cwait;
619 alts[WWait].v = &w;
620 alts[WWait].op = CHANRCV;
621 alts[WCmd].c = ccommand;
622 alts[WCmd].v = &c;
623 alts[WCmd].op = CHANRCV;
624 alts[NWALT].op = CHANEND;
626 command = nil;
627 for(;;){
628 switch(alt(alts)){
629 case WErr:
630 qlock(&row.lk);
631 warning(nil, "%s", err);
632 free(err);
633 flushimage(display, 1);
634 qunlock(&row.lk);
635 break;
636 case WKill:
637 found = FALSE;
638 ncmd = runestrlen(cmd);
639 for(c=command; c; c=c->next){
640 /* -1 for blank */
641 if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){
642 if(postnote(PNGROUP, c->pid, "kill") < 0)
643 warning(nil, "kill %S: %r\n", cmd);
644 found = TRUE;
647 if(!found)
648 warning(nil, "Kill: no process %S\n", cmd);
649 free(cmd);
650 break;
651 case WWait:
652 pid = w->pid;
653 lc = nil;
654 for(c=command; c; c=c->next){
655 if(c->pid == pid){
656 if(lc)
657 lc->next = c->next;
658 else
659 command = c->next;
660 break;
662 lc = c;
664 qlock(&row.lk);
665 t = &row.tag;
666 textcommit(t, TRUE);
667 if(c == nil){
668 /* helper processes use this exit status */
669 if(strncmp(w->msg, "libthread", 9) != 0){
670 p = emalloc(sizeof(Pid));
671 p->pid = pid;
672 strncpy(p->msg, w->msg, sizeof(p->msg));
673 p->next = pids;
674 pids = p;
676 }else{
677 if(search(t, c->name, c->nname)){
678 textdelete(t, t->q0, t->q1, TRUE);
679 textsetselect(t, 0, 0);
681 if(w->msg[0])
682 warning(c->md, "%S: %s\n", c->name, w->msg);
683 flushimage(display, 1);
685 qunlock(&row.lk);
686 free(w);
687 Freecmd:
688 if(c){
689 if(c->iseditcmd)
690 sendul(cedit, 0);
691 free(c->text);
692 free(c->name);
693 fsysdelid(c->md);
694 free(c);
696 break;
697 case WCmd:
698 /* has this command already exited? */
699 lastp = nil;
700 for(p=pids; p!=nil; p=p->next){
701 if(p->pid == c->pid){
702 if(p->msg[0])
703 warning(c->md, "%s\n", p->msg);
704 if(lastp == nil)
705 pids = p->next;
706 else
707 lastp->next = p->next;
708 free(p);
709 goto Freecmd;
711 lastp = p;
713 c->next = command;
714 command = c;
715 qlock(&row.lk);
716 t = &row.tag;
717 textcommit(t, TRUE);
718 textinsert(t, 0, c->name, c->nname, TRUE);
719 textsetselect(t, 0, 0);
720 flushimage(display, 1);
721 qunlock(&row.lk);
722 break;
727 void
728 xfidallocthread(void *v)
730 Xfid *xfree, *x;
731 enum { Alloc, Free, N };
732 static Alt alts[N+1];
734 USED(v);
735 threadsetname("xfidallocthread");
736 alts[Alloc].c = cxfidalloc;
737 alts[Alloc].v = nil;
738 alts[Alloc].op = CHANRCV;
739 alts[Free].c = cxfidfree;
740 alts[Free].v = &x;
741 alts[Free].op = CHANRCV;
742 alts[N].op = CHANEND;
744 xfree = nil;
745 for(;;){
746 switch(alt(alts)){
747 case Alloc:
748 x = xfree;
749 if(x)
750 xfree = x->next;
751 else{
752 x = emalloc(sizeof(Xfid));
753 x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
754 x->arg = x;
755 threadcreate(xfidctl, x->arg, STACK);
757 sendp(cxfidalloc, x);
758 break;
759 case Free:
760 x->next = xfree;
761 xfree = x;
762 break;
767 /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */
768 void
769 newwindowthread(void *v)
771 Window *w;
773 USED(v);
774 threadsetname("newwindowthread");
776 for(;;){
777 /* only fsysproc is talking to us, so synchronization is trivial */
778 recvp(cnewwindow);
779 w = makenewwindow(nil);
780 winsettag(w);
781 sendp(cnewwindow, w);
785 Reffont*
786 rfget(int fix, int save, int setfont, char *name)
788 Reffont *r;
789 Font *f;
790 int i;
792 r = nil;
793 if(name == nil){
794 name = fontnames[fix];
795 r = reffonts[fix];
797 if(r == nil){
798 for(i=0; i<nfontcache; i++)
799 if(strcmp(name, fontcache[i]->f->name) == 0){
800 r = fontcache[i];
801 goto Found;
803 f = openfont(display, name);
804 if(f == nil){
805 fprint(2, "can't open font file %s: %r\n", name);
806 warning(nil, "can't open font file %s: %r\n", name);
807 return nil;
809 r = emalloc(sizeof(Reffont));
810 r->f = f;
811 fontcache = realloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
812 fontcache[nfontcache++] = r;
814 Found:
815 if(save){
816 incref(&r->ref);
817 if(reffonts[fix])
818 rfclose(reffonts[fix]);
819 reffonts[fix] = r;
820 free(fontnames[fix]);
821 fontnames[fix] = name;
823 if(setfont){
824 reffont.f = r->f;
825 incref(&r->ref);
826 rfclose(reffonts[0]);
827 font = r->f;
828 reffonts[0] = r;
829 incref(&r->ref);
830 iconinit();
832 incref(&r->ref);
833 return r;
836 void
837 rfclose(Reffont *r)
839 int i;
841 if(decref(&r->ref) == 0){
842 for(i=0; i<nfontcache; i++)
843 if(r == fontcache[i])
844 break;
845 if(i >= nfontcache)
846 warning(nil, "internal error: can't find font in cache\n");
847 else{
848 nfontcache--;
849 memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*));
851 freefont(r->f);
852 free(r);
856 Cursor boxcursor = {
857 {-7, -7},
858 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
859 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
860 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
861 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
862 {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
863 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
864 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
865 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
866 };
868 void
869 iconinit(void)
871 Rectangle r;
872 Image *tmp;
874 /* Blue */
875 tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
876 tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
877 tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
878 tagcols[TEXT] = display->black;
879 tagcols[HTEXT] = display->black;
881 /* Yellow */
882 textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
883 textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
884 textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
885 textcols[TEXT] = display->black;
886 textcols[HTEXT] = display->black;
888 if(button){
889 freeimage(button);
890 freeimage(modbutton);
891 freeimage(colbutton);
894 r = Rect(0, 0, Scrollwid+2, font->height+1);
895 button = allocimage(display, r, screen->chan, 0, DNofill);
896 draw(button, r, tagcols[BACK], nil, r.min);
897 r.max.x -= 2;
898 border(button, r, 2, tagcols[BORD], ZP);
900 r = button->r;
901 modbutton = allocimage(display, r, screen->chan, 0, DNofill);
902 draw(modbutton, r, tagcols[BACK], nil, r.min);
903 r.max.x -= 2;
904 border(modbutton, r, 2, tagcols[BORD], ZP);
905 r = insetrect(r, 2);
906 tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
907 draw(modbutton, r, tmp, nil, ZP);
908 freeimage(tmp);
910 r = button->r;
911 colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
913 but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
914 but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
917 /*
918 * /dev/snarf updates when the file is closed, so we must open our own
919 * fd here rather than use snarffd
920 */
922 /* rio truncates larges snarf buffers, so this avoids using the
923 * service if the string is huge */
925 #define MAXSNARF 100*1024
927 void
928 acmeputsnarf(void)
930 int i, n;
931 Fmt f;
932 char *s;
934 if(snarfbuf.nc==0)
935 return;
936 if(snarfbuf.nc > MAXSNARF)
937 return;
939 fmtstrinit(&f);
940 for(i=0; i<snarfbuf.nc; i+=n){
941 n = snarfbuf.nc-i;
942 if(n >= NSnarf)
943 n = NSnarf;
944 bufread(&snarfbuf, i, snarfrune, n);
945 if(fmtprint(&f, "%.*S", n, snarfrune) < 0)
946 break;
948 s = fmtstrflush(&f);
949 if(s && s[0])
950 putsnarf(s);
951 free(s);
954 void
955 acmegetsnarf(void)
957 char *s;
958 int nb, nr, nulls, len;
959 Rune *r;
961 s = getsnarf();
962 if(s == nil || s[0]==0){
963 free(s);
964 return;
967 len = strlen(s);
968 r = runemalloc(len+1);
969 cvttorunes(s, len, r, &nb, &nr, &nulls);
970 bufreset(&snarfbuf);
971 bufinsert(&snarfbuf, 0, r, nr);
972 free(r);
973 free(s);