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