xref: /plan9/sys/src/libdraw/event.c (revision 14cc0f535177405a84c5b73603a98e5db6674719)
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <cursor.h>
5 #include <event.h>
6 
7 typedef struct	Slave Slave;
8 typedef struct	Ebuf Ebuf;
9 
10 struct Slave
11 {
12 	int	pid;
13 	Ebuf	*head;		/* queue of messages for this descriptor */
14 	Ebuf	*tail;
15 	int	(*fn)(int, Event*, uchar*, int);
16 };
17 
18 struct Ebuf
19 {
20 	Ebuf	*next;
21 	int	n;		/* number of bytes in buf */
22 	uchar	buf[EMAXMSG];
23 };
24 
25 static	Slave	eslave[MAXSLAVE];
26 static	int	Skeyboard = -1;
27 static	int	Smouse = -1;
28 static	int	Stimer = -1;
29 static	int	logfid;
30 
31 static	int	nslave;
32 static	int	parentpid;
33 static	int	epipe[2];
34 
35 static	int	eforkslave(ulong);
36 static	void	extract(void);
37 static	void	ekill(void);
38 static	int	enote(void *, char *);
39 
40 static	int	mousefd;
41 static	int	cursorfd;
42 
43 static
44 Ebuf*
ebread(Slave * s)45 ebread(Slave *s)
46 {
47 	Ebuf *eb;
48 	Dir *d;
49 	ulong l;
50 
51 	for(;;){
52 		d = dirfstat(epipe[0]);
53 		if(d == nil)
54 			drawerror(display, "events: eread stat error");
55 		l = d->length;
56 		free(d);
57 		if(s->head && l==0)
58 			break;
59 		extract();
60 	}
61 	eb = s->head;
62 	s->head = s->head->next;
63 	if(s->head == 0)
64 		s->tail = 0;
65 	return eb;
66 }
67 
68 ulong
event(Event * e)69 event(Event *e)
70 {
71 	return eread(~0UL, e);
72 }
73 
74 ulong
eread(ulong keys,Event * e)75 eread(ulong keys, Event *e)
76 {
77 	Ebuf *eb;
78 	int i, id;
79 
80 	if(keys == 0)
81 		return 0;
82 	for(;;){
83 		for(i=0; i<nslave; i++)
84 			if((keys & (1<<i)) && eslave[i].head){
85 				id = 1<<i;
86 				if(i == Smouse)
87 					e->mouse = emouse();
88 				else if(i == Skeyboard)
89 					e->kbdc = ekbd();
90 				else if(i == Stimer)
91 					eslave[i].head = 0;
92 				else{
93 					eb = ebread(&eslave[i]);
94 					e->n = eb->n;
95 					if(eslave[i].fn)
96 						id = (*eslave[i].fn)(id, e, eb->buf, eb->n);
97 					else
98 						memmove(e->data, eb->buf, eb->n);
99 					free(eb);
100 				}
101 				return id;
102 			}
103 		extract();
104 	}
105 }
106 
107 int
ecanmouse(void)108 ecanmouse(void)
109 {
110 	if(Smouse < 0)
111 		drawerror(display, "events: mouse not initialized");
112 	return ecanread(Emouse);
113 }
114 
115 int
ecankbd(void)116 ecankbd(void)
117 {
118 	if(Skeyboard < 0)
119 		drawerror(display, "events: keyboard not initialzed");
120 	return ecanread(Ekeyboard);
121 }
122 
123 int
ecanread(ulong keys)124 ecanread(ulong keys)
125 {
126 	Dir *d;
127 	int i;
128 	ulong l;
129 
130 	for(;;){
131 		for(i=0; i<nslave; i++)
132 			if((keys & (1<<i)) && eslave[i].head)
133 				return 1;
134 		d = dirfstat(epipe[0]);
135 		if(d == nil)
136 			drawerror(display, "events: ecanread stat error");
137 		l = d->length;
138 		free(d);
139 		if(l == 0)
140 			return 0;
141 		extract();
142 	}
143 }
144 
145 ulong
estartfn(ulong key,int fd,int n,int (* fn)(int,Event *,uchar *,int))146 estartfn(ulong key, int fd, int n, int (*fn)(int, Event*, uchar*, int))
147 {
148 	char buf[EMAXMSG+1];
149 	int i, r;
150 
151 	if(fd < 0)
152 		drawerror(display, "events: bad file descriptor");
153 	if(n <= 0 || n > EMAXMSG)
154 		n = EMAXMSG;
155 	i = eforkslave(key);
156 	if(i < MAXSLAVE){
157 		eslave[i].fn = fn;
158 		return 1<<i;
159 	}
160 	buf[0] = i - MAXSLAVE;
161 	while((r = read(fd, buf+1, n))>0)
162 		if(write(epipe[1], buf, r+1)!=r+1)
163 			break;
164 	buf[0] = MAXSLAVE;
165 	write(epipe[1], buf, 1);
166 	_exits(0);
167 	return 0;
168 }
169 
170 ulong
estart(ulong key,int fd,int n)171 estart(ulong key, int fd, int n)
172 {
173 	return estartfn(key, fd, n, nil);
174 }
175 
176 ulong
etimer(ulong key,int n)177 etimer(ulong key, int n)
178 {
179 	char t[2];
180 
181 	if(Stimer != -1)
182 		drawerror(display, "events: timer started twice");
183 	Stimer = eforkslave(key);
184 	if(Stimer < MAXSLAVE)
185 		return 1<<Stimer;
186 	if(n <= 0)
187 		n = 1000;
188 	t[0] = t[1] = Stimer - MAXSLAVE;
189 	do
190 		sleep(n);
191 	while(write(epipe[1], t, 2) == 2);
192 	t[0] = MAXSLAVE;
193 	write(epipe[1], t, 1);
194 	_exits(0);
195 	return 0;
196 }
197 
198 static void
ekeyslave(int fd)199 ekeyslave(int fd)
200 {
201 	Rune r;
202 	char t[3], k[10];
203 	int kr, kn, w;
204 
205 	if(eforkslave(Ekeyboard) < MAXSLAVE)
206 		return;
207 	kn = 0;
208 	t[0] = Skeyboard;
209 	for(;;){
210 		while(!fullrune(k, kn)){
211 			kr = read(fd, k+kn, sizeof k - kn);
212 			if(kr <= 0)
213 				goto breakout;
214 			kn += kr;
215 		}
216 		w = chartorune(&r, k);
217 		kn -= w;
218 		memmove(k, &k[w], kn);
219 		t[1] = r;
220 		t[2] = r>>8;
221 		if(write(epipe[1], t, 3) != 3)
222 			break;
223 	}
224 breakout:;
225 	t[0] = MAXSLAVE;
226 	write(epipe[1], t, 1);
227 	_exits(0);
228 }
229 
230 void
einit(ulong keys)231 einit(ulong keys)
232 {
233 	int ctl, fd;
234 	char buf[256];
235 
236 	parentpid = getpid();
237 	if(pipe(epipe) < 0)
238 		drawerror(display, "events: einit pipe");
239 	atexit(ekill);
240 	atnotify(enote, 1);
241 	snprint(buf, sizeof buf, "%s/mouse", display->devdir);
242 	mousefd = open(buf, ORDWR|OCEXEC);
243 	if(mousefd < 0)
244 		drawerror(display, "einit: can't open mouse\n");
245 	snprint(buf, sizeof buf, "%s/cursor", display->devdir);
246 	cursorfd = open(buf, ORDWR|OCEXEC);
247 	if(cursorfd < 0)
248 		drawerror(display, "einit: can't open cursor\n");
249 	if(keys&Ekeyboard){
250 		snprint(buf, sizeof buf, "%s/cons", display->devdir);
251 		fd = open(buf, OREAD);
252 		if(fd < 0)
253 			drawerror(display, "events: can't open console");
254 		snprint(buf, sizeof buf, "%s/consctl", display->devdir);
255 		ctl = open("/dev/consctl", OWRITE|OCEXEC);
256 		if(ctl < 0)
257 			drawerror(display, "events: can't open consctl");
258 		write(ctl, "rawon", 5);
259 		for(Skeyboard=0; Ekeyboard & ~(1<<Skeyboard); Skeyboard++)
260 			;
261 		ekeyslave(fd);
262 	}
263 	if(keys&Emouse){
264 		estart(Emouse, mousefd, 1+4*12);
265 		for(Smouse=0; Emouse & ~(1<<Smouse); Smouse++)
266 			;
267 	}
268 }
269 
270 static void
extract(void)271 extract(void)
272 {
273 	Slave *s;
274 	Ebuf *eb;
275 	int i, n;
276 	uchar ebuf[EMAXMSG+1];
277 
278 	/* avoid generating a message if there's nothing to show. */
279 	/* this test isn't perfect, though; could do flushimage(display, 0) then call extract */
280 	/* also: make sure we don't interfere if we're multiprocessing the display */
281 	if(display->locking){
282 		/* if locking is being done by program, this means it can't depend on automatic flush in emouse() etc. */
283 		if(canqlock(&display->qlock)){
284 			if(display->bufp > display->buf)
285 				flushimage(display, 1);
286 			unlockdisplay(display);
287 		}
288 	}else
289 		if(display->bufp > display->buf)
290 			flushimage(display, 1);
291 loop:
292 	if((n=read(epipe[0], ebuf, EMAXMSG+1)) < 0
293 	|| ebuf[0] >= MAXSLAVE)
294 		drawerror(display, "eof on event pipe");
295 	if(n == 0)
296 		goto loop;
297 	i = ebuf[0];
298 	if(i >= nslave || n <= 1)
299 		drawerror(display, "events: protocol error: short read");
300 	s = &eslave[i];
301 	if(i == Stimer){
302 		s->head = (Ebuf *)1;
303 		return;
304 	}
305 	if(i == Skeyboard && n != 3)
306 		drawerror(display, "events: protocol error: keyboard");
307 	if(i == Smouse){
308 		if(n < 1+1+2*12)
309 			drawerror(display, "events: protocol error: mouse");
310 		if(ebuf[1] == 'r')
311 			eresized(1);
312 		/* squash extraneous mouse events */
313 		if((eb=s->tail) && memcmp(eb->buf+1+2*12, ebuf+1+1+2*12, 12)==0){
314 			memmove(eb->buf, &ebuf[1], n - 1);
315 			return;
316 		}
317 	}
318 	/* try to save space by only allocating as much buffer as we need */
319 	eb = malloc(sizeof(*eb) - sizeof(eb->buf) + n - 1);
320 	if(eb == 0)
321 		drawerror(display, "events: protocol error 4");
322 	eb->n = n - 1;
323 	memmove(eb->buf, &ebuf[1], n - 1);
324 	eb->next = 0;
325 	if(s->head)
326 		s->tail = s->tail->next = eb;
327 	else
328 		s->head = s->tail = eb;
329 }
330 
331 static int
eforkslave(ulong key)332 eforkslave(ulong key)
333 {
334 	int i, pid;
335 
336 	for(i=0; i<MAXSLAVE; i++)
337 		if((key & ~(1<<i)) == 0 && eslave[i].pid == 0){
338 			if(nslave <= i)
339 				nslave = i + 1;
340 			/*
341 			 * share the file descriptors so the last child
342 			 * out closes all connections to the window server.
343 			 */
344 			switch(pid = rfork(RFPROC)){
345 			case 0:
346 				return MAXSLAVE+i;
347 			case -1:
348 				fprint(2, "events: fork error\n");
349 				exits("fork");
350 			}
351 			eslave[i].pid = pid;
352 			eslave[i].head = eslave[i].tail = 0;
353 			return i;
354 		}
355 	drawerror(display, "events: bad slave assignment");
356 	return 0;
357 }
358 
359 static int
enote(void * v,char * s)360 enote(void *v, char *s)
361 {
362 	char t[1];
363 	int i, pid;
364 
365 	USED(v, s);
366 	pid = getpid();
367 	if(pid != parentpid){
368 		for(i=0; i<nslave; i++){
369 			if(pid == eslave[i].pid){
370 				t[0] = MAXSLAVE;
371 				write(epipe[1], t, 1);
372 				break;
373 			}
374 		}
375 		return 0;
376 	}
377 	close(epipe[0]);
378 	epipe[0] = -1;
379 	close(epipe[1]);
380 	epipe[1] = -1;
381 	for(i=0; i<nslave; i++){
382 		if(pid == eslave[i].pid)
383 			continue;	/* don't kill myself */
384 		postnote(PNPROC, eslave[i].pid, "die");
385 	}
386 	return 0;
387 }
388 
389 static void
ekill(void)390 ekill(void)
391 {
392 	enote(0, 0);
393 }
394 
395 Mouse
emouse(void)396 emouse(void)
397 {
398 	Mouse m;
399 	Ebuf *eb;
400 	static but[2];
401 	int b;
402 
403 	if(Smouse < 0)
404 		drawerror(display, "events: mouse not initialized");
405 	eb = ebread(&eslave[Smouse]);
406 	m.xy.x = atoi((char*)eb->buf+1+0*12);
407 	m.xy.y = atoi((char*)eb->buf+1+1*12);
408 	b = atoi((char*)eb->buf+1+2*12);
409 	m.buttons = b;
410 	m.msec = atoi((char*)eb->buf+1+3*12);
411 	if (logfid)
412 		fprint(logfid, "b: %d xy: %P\n", m.buttons, m.xy);
413 	free(eb);
414 	return m;
415 }
416 
417 int
ekbd(void)418 ekbd(void)
419 {
420 	Ebuf *eb;
421 	int c;
422 
423 	if(Skeyboard < 0)
424 		drawerror(display, "events: keyboard not initialzed");
425 	eb = ebread(&eslave[Skeyboard]);
426 	c = eb->buf[0] + (eb->buf[1]<<8);
427 	free(eb);
428 	return c;
429 }
430 
431 void
emoveto(Point pt)432 emoveto(Point pt)
433 {
434 	char buf[2*12+2];
435 	int n;
436 
437 	n = sprint(buf, "m%d %d", pt.x, pt.y);
438 	write(mousefd, buf, n);
439 }
440 
441 void
esetcursor(Cursor * c)442 esetcursor(Cursor *c)
443 {
444 	uchar curs[2*4+2*2*16];
445 
446 	if(c == 0)
447 		write(cursorfd, curs, 0);
448 	else{
449 		BPLONG(curs+0*4, c->offset.x);
450 		BPLONG(curs+1*4, c->offset.y);
451 		memmove(curs+2*4, c->clr, 2*2*16);
452 		write(cursorfd, curs, sizeof curs);
453 	}
454 }
455 
456 int
ereadmouse(Mouse * m)457 ereadmouse(Mouse *m)
458 {
459 	int n;
460 	char buf[128];
461 
462 	do{
463 		n = read(mousefd, buf, sizeof(buf));
464 		if(n < 0)	/* probably interrupted */
465 			return -1;
466 		n = eatomouse(m, buf, n);
467 	}while(n == 0);
468 	return n;
469 }
470 
471 int
eatomouse(Mouse * m,char * buf,int n)472 eatomouse(Mouse *m, char *buf, int n)
473 {
474 	if(n != 1+4*12){
475 		werrstr("atomouse: bad count");
476 		return -1;
477 	}
478 
479 	if(buf[0] == 'r')
480 		eresized(1);
481 	m->xy.x = atoi(buf+1+0*12);
482 	m->xy.y = atoi(buf+1+1*12);
483 	m->buttons = atoi(buf+1+2*12);
484 	m->msec = atoi(buf+1+3*12);
485 	return n;
486 }
487