495 lines · 9.2 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <draw.h>#include <cursor.h>#include <event.h> typedef struct	Slave Slave;typedef struct	Ebuf Ebuf; struct Slave{	int	pid;	Ebuf	*head;		/* queue of messages for this descriptor */	Ebuf	*tail;	int	(*fn)(int, Event*, uchar*, int);}; struct Ebuf{	Ebuf	*next;	int	n;		/* number of bytes in buf */	uchar	buf[EMAXMSG];}; static	Slave	eslave[MAXSLAVE];static	int	Skeyboard = -1;static	int	Smouse = -1;static	int	Stimer = -1;static	int	logfid; static	int	nslave;static	int	parentpid;static	int	epipe[2]; static	int	eforkslave(ulong);static	void	extract(void);static	void	ekill(void);static	int	enote(void *, char *); static	int	mousefd;static	int	cursorfd; staticEbuf*ebread(Slave *s){	Ebuf *eb;	Dir *d;	ulong l; 	for(;;){		d = dirfstat(epipe[0]);		if(d == nil)			drawerror(display, "events: eread stat error");		l = d->length;		free(d);		if(s->head && l==0)			break;		extract();	}	eb = s->head;	s->head = s->head->next;	if(s->head == 0)		s->tail = 0;	return eb;} ulongevent(Event *e){	return eread(~0UL, e);} ulongeread(ulong keys, Event *e){	Ebuf *eb;	int i, id; 	if(keys == 0)		return 0;	for(;;){		for(i=0; i<nslave; i++)			if((keys & (1<<i)) && eslave[i].head){				id = 1<<i;				if(i == Smouse)					e->mouse = emouse();				else if(i == Skeyboard)					e->kbdc = ekbd();				else if(i == Stimer)					eslave[i].head = 0;				else{					eb = ebread(&eslave[i]);					e->n = eb->n;					if(eslave[i].fn)						id = (*eslave[i].fn)(id, e, eb->buf, eb->n);					else						memmove(e->data, eb->buf, eb->n);					free(eb);				}				return id;			}		extract();	}} intecanmouse(void){	if(Smouse < 0)		drawerror(display, "events: mouse not initialized");	return ecanread(Emouse);} intecankbd(void){	if(Skeyboard < 0)		drawerror(display, "events: keyboard not initialzed");	return ecanread(Ekeyboard);} intecanread(ulong keys){	Dir *d;	int i;	ulong l; 	for(;;){		for(i=0; i<nslave; i++)			if((keys & (1<<i)) && eslave[i].head)				return 1;		d = dirfstat(epipe[0]);		if(d == nil)			drawerror(display, "events: ecanread stat error");		l = d->length;		free(d);		if(l == 0)			return 0;		extract();	}} ulongestartfn(ulong key, int fd, int n, int (*fn)(int, Event*, uchar*, int)){	char buf[EMAXMSG+1];	int i, r; 	if(fd < 0)		drawerror(display, "events: bad file descriptor");	if(n <= 0 || n > EMAXMSG)		n = EMAXMSG;	i = eforkslave(key);	if(i < MAXSLAVE){		eslave[i].fn = fn;		return 1<<i;	}	buf[0] = i - MAXSLAVE;	while((r = read(fd, buf+1, n))>0)		if(write(epipe[1], buf, r+1)!=r+1)			break;	buf[0] = MAXSLAVE;	write(epipe[1], buf, 1);	_exits(0);	return 0;} ulongestart(ulong key, int fd, int n){	return estartfn(key, fd, n, nil);} ulongetimer(ulong key, int n){	char t[2]; 	if(Stimer != -1)		drawerror(display, "events: timer started twice");	Stimer = eforkslave(key);	if(Stimer < MAXSLAVE)		return 1<<Stimer;	if(n <= 0)		n = 1000;	t[0] = t[1] = Stimer - MAXSLAVE;	do		sleep(n);	while(write(epipe[1], t, 2) == 2);	t[0] = MAXSLAVE;	write(epipe[1], t, 1);	_exits(0);	return 0;} static voidekeyslave(int fd){	Rune r;	char t[3], k[10];	int kr, kn, w; 	if(eforkslave(Ekeyboard) < MAXSLAVE)		return;	kn = 0;	t[0] = Skeyboard;	for(;;){		while(!fullrune(k, kn)){			kr = read(fd, k+kn, sizeof k - kn);			if(kr <= 0)				goto breakout;			kn += kr;		}		w = chartorune(&r, k);		kn -= w;		memmove(k, &k[w], kn);		t[1] = r;		t[2] = r>>8;		if(write(epipe[1], t, 3) != 3)			break;	}breakout:;	t[0] = MAXSLAVE;	write(epipe[1], t, 1);	_exits(0);} voideinit(ulong keys){	int ctl, fd;	char buf[256]; 	parentpid = getpid();	if(pipe(epipe) < 0)		drawerror(display, "events: einit pipe");	atexit(ekill);	atnotify(enote, 1);	snprint(buf, sizeof buf, "%s/mouse", display->devdir);	mousefd = open(buf, ORDWR|OCEXEC);	if(mousefd < 0)		drawerror(display, "einit: can't open mouse\n");	snprint(buf, sizeof buf, "%s/cursor", display->devdir);	cursorfd = open(buf, ORDWR|OCEXEC);	if(cursorfd < 0)		drawerror(display, "einit: can't open cursor\n");	if(keys&Ekeyboard){		snprint(buf, sizeof buf, "%s/cons", display->devdir);		fd = open(buf, OREAD);		if(fd < 0)			drawerror(display, "events: can't open console");		snprint(buf, sizeof buf, "%s/consctl", display->devdir);		ctl = open("/dev/consctl", OWRITE|OCEXEC);		if(ctl < 0)			drawerror(display, "events: can't open consctl");		write(ctl, "rawon", 5);		for(Skeyboard=0; Ekeyboard & ~(1<<Skeyboard); Skeyboard++)			;		ekeyslave(fd);	}	if(keys&Emouse){		estart(Emouse, mousefd, 1+4*12);		for(Smouse=0; Emouse & ~(1<<Smouse); Smouse++)			;	}} static voidextract(void){	Slave *s;	Ebuf *eb;	int i, n;	uchar ebuf[EMAXMSG+1]; 	/* avoid generating a message if there's nothing to show. */	/* this test isn't perfect, though; could do flushimage(display, 0) then call extract */	/* also: make sure we don't interfere if we're multiprocessing the display */	if(display->locking){		/* if locking is being done by program, this means it can't depend on automatic flush in emouse() etc. */		if(canqlock(&display->qlock)){			if(display->bufp > display->buf)				flushimage(display, 1);			unlockdisplay(display);		}	}else		if(display->bufp > display->buf)			flushimage(display, 1);loop:	if((n=read(epipe[0], ebuf, EMAXMSG+1)) < 0	|| ebuf[0] >= MAXSLAVE)		drawerror(display, "eof on event pipe");	if(n == 0)		goto loop;	i = ebuf[0];	if(i >= nslave || n <= 1)		drawerror(display, "events: protocol error: short read");	s = &eslave[i];	if(i == Stimer){		s->head = (Ebuf *)1;		return;	}	if(i == Skeyboard && n != 3)		drawerror(display, "events: protocol error: keyboard");	if(i == Smouse){		if(n < 1+1+2*12)			drawerror(display, "events: protocol error: mouse");		if(ebuf[1] == 'r')			eresized(1);		/* squash extraneous mouse events */		if((eb=s->tail) && memcmp(eb->buf+1+2*12, ebuf+1+1+2*12, 12)==0){			memmove(eb->buf, &ebuf[1], n - 1);			return;		}	}	/* try to save space by only allocating as much buffer as we need */	eb = malloc(sizeof(*eb) - sizeof(eb->buf) + n - 1);	if(eb == 0)		drawerror(display, "events: protocol error 4");	eb->n = n - 1;	memmove(eb->buf, &ebuf[1], n - 1);	eb->next = 0;	if(s->head)		s->tail = s->tail->next = eb;	else		s->head = s->tail = eb;} static inteforkslave(ulong key){	int i, pid; 	for(i=0; i<MAXSLAVE; i++)		if((key & ~(1<<i)) == 0 && eslave[i].pid == 0){			if(nslave <= i)				nslave = i + 1;			/*			 * share the file descriptors so the last child			 * out closes all connections to the window server.			 */			switch(pid = rfork(RFPROC)){			case 0:				return MAXSLAVE+i;			case -1:				fprint(2, "events: fork error\n");				exits("fork");			}			eslave[i].pid = pid;			eslave[i].head = eslave[i].tail = 0;			return i;		}	drawerror(display, "events: bad slave assignment");	return 0;} static intenote(void *v, char *s){	char t[1];	int i, pid; 	USED(v, s);	pid = getpid();	if(pid != parentpid){		for(i=0; i<nslave; i++){			if(pid == eslave[i].pid){				t[0] = MAXSLAVE;				write(epipe[1], t, 1);				break;			}		}		return 0;	}	close(epipe[0]);	epipe[0] = -1;	close(epipe[1]);	epipe[1] = -1;	for(i=0; i<nslave; i++){		if(pid == eslave[i].pid)			continue;	/* don't kill myself */		postnote(PNPROC, eslave[i].pid, "die");	}	return 0;} static voidekill(void){	enote(0, 0);} Mouseemouse(void){	Mouse m;	Ebuf *eb;	static but[2];	int b; 	if(Smouse < 0)		drawerror(display, "events: mouse not initialized");	eb = ebread(&eslave[Smouse]);	m.xy.x = atoi((char*)eb->buf+1+0*12);	m.xy.y = atoi((char*)eb->buf+1+1*12);	b = atoi((char*)eb->buf+1+2*12);	m.buttons = b;	m.msec = atoi((char*)eb->buf+1+3*12);	if (logfid)		fprint(logfid, "b: %d xy: %P\n", m.buttons, m.xy);	free(eb);	return m;} intekbd(void){	Ebuf *eb;	int c; 	if(Skeyboard < 0)		drawerror(display, "events: keyboard not initialzed");	eb = ebread(&eslave[Skeyboard]);	c = eb->buf[0] + (eb->buf[1]<<8);	free(eb);	return c;} voidemoveto(Point pt){	char buf[2*12+2];	int n; 	n = sprint(buf, "m%d %d", pt.x, pt.y);	write(mousefd, buf, n);} voidesetcursor(Cursor *c){	uchar curs[2*4+2*2*16]; 	if(c == 0)		write(cursorfd, curs, 0);	else{		BPLONG(curs+0*4, c->offset.x);		BPLONG(curs+1*4, c->offset.y);		memmove(curs+2*4, c->clr, 2*2*16);		write(cursorfd, curs, sizeof curs);	}} intereadmouse(Mouse *m){	int n;	char buf[128]; 	do{		n = read(mousefd, buf, sizeof(buf));		if(n < 0)	/* probably interrupted */			return -1;		n = eatomouse(m, buf, n);	}while(n == 0);	return n;} inteatomouse(Mouse *m, char *buf, int n){	if(n != 1+4*12){		werrstr("atomouse: bad count");		return -1;	} 	if(buf[0] == 'r')		eresized(1);	m->xy.x = atoi(buf+1+0*12);	m->xy.y = atoi(buf+1+1*12);	m->buttons = atoi(buf+1+2*12);	m->msec = atoi(buf+1+3*12);	return n;}