1 /* Copyright (c) 2006 Russ Cox */
2 
3 #include <u.h>
4 #include <sys/select.h>
5 #include <libc.h>
6 #include <draw.h>
7 #include <mouse.h>
8 #include <cursor.h>
9 #include <drawfcall.h>
10 #include <mux.h>
11 
12 extern Mouse _drawmouse;
13 int chattydrawclient = 0;
14 
15 static int	drawgettag(Mux *mux, void *vmsg);
16 static void*	drawrecv(Mux *mux);
17 static int	drawnbrecv(Mux *mux, void**);
18 static int	drawsend(Mux *mux, void *vmsg);
19 static int	drawsettag(Mux *mux, void *vmsg, uint tag);
20 static int canreadfd(int);
21 
22 int
_displayconnect(Display * d)23 _displayconnect(Display *d)
24 {
25 	int pid, p[2], fd, nbuf, n;
26 	char *wsysid, *ns, *addr, *id;
27 	uchar *buf;
28 	Wsysmsg w;
29 
30 	fmtinstall('W', drawfcallfmt);
31 	fmtinstall('H', encodefmt);
32 
33 	wsysid = getenv("wsysid");
34 	if(wsysid != nil) {
35 		// Connect to running devdraw service.
36 		// wsysid=devdrawname/id
37 		id = strchr(wsysid, '/');
38 		if(id == nil) {
39 			werrstr("invalid $wsysid");
40 			return -1;
41 		}
42 		*id++ = '\0';
43 		if((ns = getns()) == nil)
44 			return -1;
45 		addr = smprint("unix!%s/%s", ns, wsysid);
46 		free(ns);
47 		if(addr == nil)
48 			return -1;
49 		fd = dial(addr, 0, 0, 0);
50 		free(addr);
51 		if(fd < 0)
52 			return -1;
53 		nbuf = strlen(id) + 500;
54 		buf = malloc(nbuf);
55 		if(buf == nil) {
56 			close(fd);
57 			return -1;
58 		}
59 		memset(&w, 0, sizeof w);
60 		w.type = Tctxt;
61 		w.id = id;
62 		n = convW2M(&w, buf, nbuf);
63 		if(write(fd, buf, n) != n) {
64 			close(fd);
65 			werrstr("wsys short write: %r");
66 			return -1;
67 		}
68 		n = readwsysmsg(fd, buf, nbuf);
69 		if(n < 0) {
70 			close(fd);
71 			werrstr("wsys short read: %r");
72 			return -1;
73 		}
74 		if(convM2W(buf, n, &w) <= 0) {
75 			close(fd);
76 			werrstr("wsys decode error");
77 			return -1;
78 		}
79 		if(w.type != Rctxt) {
80 			close(fd);
81 			if(w.type == Rerror)
82 				werrstr("%s", w.error);
83 			else
84 				werrstr("wsys rpc phase error (%d)", w.type);
85 			return -1;
86 		}
87 		d->srvfd = fd;
88 		return 0;
89 	}
90 
91 	if(pipe(p) < 0)
92 		return -1;
93 	if((pid=fork()) < 0){
94 		close(p[0]);
95 		close(p[1]);
96 		return -1;
97 	}
98 	if(pid == 0){
99 		char *devdraw;
100 
101 		devdraw = getenv("DEVDRAW");
102 		if(devdraw == nil)
103 			devdraw = "devdraw";
104 		close(p[0]);
105 		dup(p[1], 0);
106 		dup(p[1], 1);
107 		/* execl("strace", "strace", "-o", "drawsrv.out", "drawsrv", nil); */
108 		/*
109 		 * The argv0 has no meaning to devdraw.
110 		 * Pass it along only so that the various
111 		 * devdraws in psu -a can be distinguished.
112 		 * The NOLIBTHREADDAEMONIZE keeps devdraw from
113 		 * forking before threadmain. OS X hates it when
114 		 * guis fork.
115 		 *
116 		 * If client didn't use ARGBEGIN, argv0 == nil.
117 		 * Can't send nil through because OS X expects
118 		 * argv[0] to be non-nil.  Also, OS X apparently
119 		 * expects argv[0] to be a valid executable name,
120 		 * so "(argv0)" is not okay.  Use "devdraw"
121 		 * instead.
122 		 */
123 		putenv("NOLIBTHREADDAEMONIZE", "1");
124 		devdraw = getenv("DEVDRAW");
125 		if(devdraw == nil)
126 			devdraw = "devdraw";
127 		if(argv0 == nil)
128 			argv0 = devdraw;
129 		execl(devdraw, argv0, argv0, "(devdraw)", nil);
130 		sysfatal("exec devdraw: %r");
131 	}
132 	close(p[1]);
133 	d->srvfd = p[0];
134 	return 0;
135 }
136 
137 int
_displaymux(Display * d)138 _displaymux(Display *d)
139 {
140 	if((d->mux = mallocz(sizeof(*d->mux), 1)) == nil)
141 		return -1;
142 
143 	d->mux->mintag = 1;
144 	d->mux->maxtag = 255;
145 	d->mux->send = drawsend;
146 	d->mux->recv = drawrecv;
147 	d->mux->nbrecv = drawnbrecv;
148 	d->mux->gettag = drawgettag;
149 	d->mux->settag = drawsettag;
150 	d->mux->aux = d;
151 	muxinit(d->mux);
152 
153 	return 0;
154 }
155 
156 static int
drawsend(Mux * mux,void * vmsg)157 drawsend(Mux *mux, void *vmsg)
158 {
159 	int n;
160 	uchar *msg;
161 	Display *d;
162 
163 	msg = vmsg;
164 	GET(msg, n);
165 	d = mux->aux;
166 	return write(d->srvfd, msg, n);
167 }
168 
169 static int
_drawrecv(Mux * mux,int canblock,void ** vp)170 _drawrecv(Mux *mux, int canblock, void **vp)
171 {
172 	int n;
173 	uchar buf[4], *p;
174 	Display *d;
175 
176 	d = mux->aux;
177 	*vp = nil;
178 	if(!canblock && !canreadfd(d->srvfd))
179 		return 0;
180 	if((n=readn(d->srvfd, buf, 4)) != 4)
181 		return 1;
182 	GET(buf, n);
183 	p = malloc(n);
184 	if(p == nil){
185 		fprint(2, "out of memory allocating %d in drawrecv\n", n);
186 		return 1;
187 	}
188 	memmove(p, buf, 4);
189 	if(readn(d->srvfd, p+4, n-4) != n-4){
190 		free(p);
191 		return 1;
192 	}
193 	*vp = p;
194 	return 1;
195 }
196 
197 static void*
drawrecv(Mux * mux)198 drawrecv(Mux *mux)
199 {
200 	void *p;
201 	_drawrecv(mux, 1, &p);
202 	return p;
203 }
204 
205 static int
drawnbrecv(Mux * mux,void ** vp)206 drawnbrecv(Mux *mux, void **vp)
207 {
208 	return _drawrecv(mux, 0, vp);
209 }
210 
211 static int
drawgettag(Mux * mux,void * vmsg)212 drawgettag(Mux *mux, void *vmsg)
213 {
214 	uchar *msg;
215 	USED(mux);
216 
217 	msg = vmsg;
218 	return msg[4];
219 }
220 
221 static int
drawsettag(Mux * mux,void * vmsg,uint tag)222 drawsettag(Mux *mux, void *vmsg, uint tag)
223 {
224 	uchar *msg;
225 	USED(mux);
226 
227 	msg = vmsg;
228 	msg[4] = tag;
229 	return 0;
230 }
231 
232 static int
displayrpc(Display * d,Wsysmsg * tx,Wsysmsg * rx,void ** freep)233 displayrpc(Display *d, Wsysmsg *tx, Wsysmsg *rx, void **freep)
234 {
235 	int n, nn;
236 	void *tpkt, *rpkt;
237 
238 	n = sizeW2M(tx);
239 	tpkt = malloc(n);
240 	if(freep)
241 		*freep = nil;
242 	if(tpkt == nil)
243 		return -1;
244 	tx->tag = 0;
245 	if(chattydrawclient)
246 		fprint(2, "<- %W\n", tx);
247 	nn = convW2M(tx, tpkt, n);
248 	if(nn != n){
249 		free(tpkt);
250 		werrstr("drawclient: sizeW2M convW2M mismatch");
251 		fprint(2, "%r\n");
252 		return -1;
253 	}
254 	/*
255 	 * This is the only point where we might reschedule.
256 	 * Muxrpc might need to acquire d->mux->lk, which could
257 	 * be held by some other proc (e.g., the one reading from
258 	 * the keyboard via Trdkbd messages).  If we need to wait
259 	 * for the lock, don't let other threads from this proc
260 	 * run.  This keeps up the appearance that writes to /dev/draw
261 	 * don't cause rescheduling.  If you *do* allow rescheduling
262 	 * here, then flushimage(display, 1) happening in two different
263 	 * threads in the same proc can cause a buffer of commands
264 	 * to be written out twice, leading to interesting results
265 	 * on the screen.
266 	 *
267 	 * Threadpin and threadunpin were added to the thread library
268 	 * to solve exactly this problem.  Be careful!  They are dangerous.
269 	 *
270 	 * _pin and _unpin are aliases for threadpin and threadunpin
271 	 * in a threaded program and are no-ops in unthreaded programs.
272 	 */
273 	_pin();
274 	rpkt = muxrpc(d->mux, tpkt);
275 	_unpin();
276 	free(tpkt);
277 	if(rpkt == nil){
278 		werrstr("muxrpc: %r");
279 		return -1;
280 	}
281 	GET((uchar*)rpkt, n);
282 	nn = convM2W(rpkt, n, rx);
283 	if(nn != n){
284 		free(rpkt);
285 		werrstr("drawclient: convM2W packet size mismatch %d %d %.*H", n, nn, n, rpkt);
286 		fprint(2, "%r\n");
287 		return -1;
288 	}
289 	if(chattydrawclient)
290 		fprint(2, "-> %W\n", rx);
291 	if(rx->type == Rerror){
292 		werrstr("%s", rx->error);
293 		free(rpkt);
294 		return -1;
295 	}
296 	if(rx->type != tx->type+1){
297 		werrstr("packet type mismatch -- tx %d rx %d",
298 			tx->type, rx->type);
299 		free(rpkt);
300 		return -1;
301 	}
302 	if(freep)
303 		*freep = rpkt;
304 	else
305 		free(rpkt);
306 	return 0;
307 }
308 
309 int
_displayinit(Display * d,char * label,char * winsize)310 _displayinit(Display *d, char *label, char *winsize)
311 {
312 	Wsysmsg tx, rx;
313 
314 	tx.type = Tinit;
315 	tx.label = label;
316 	tx.winsize = winsize;
317 	return displayrpc(d, &tx, &rx, nil);
318 }
319 
320 int
_displayrdmouse(Display * d,Mouse * m,int * resized)321 _displayrdmouse(Display *d, Mouse *m, int *resized)
322 {
323 	Wsysmsg tx, rx;
324 
325 	tx.type = Trdmouse;
326 	if(displayrpc(d, &tx, &rx, nil) < 0)
327 		return -1;
328 	_drawmouse = rx.mouse;
329 	*m = rx.mouse;
330 	*resized = rx.resized;
331 	return 0;
332 }
333 
334 int
_displayrdkbd(Display * d,Rune * r)335 _displayrdkbd(Display *d, Rune *r)
336 {
337 	Wsysmsg tx, rx;
338 
339 	tx.type = Trdkbd4;
340 	if(displayrpc(d, &tx, &rx, nil) < 0)
341 		return -1;
342 	*r = rx.rune;
343 	return 0;
344 }
345 
346 int
_displaymoveto(Display * d,Point p)347 _displaymoveto(Display *d, Point p)
348 {
349 	Wsysmsg tx, rx;
350 
351 	tx.type = Tmoveto;
352 	tx.mouse.xy = p;
353 	if(displayrpc(d, &tx, &rx, nil) < 0)
354 		return -1;
355 	_drawmouse.xy = p;
356 	return flushimage(d, 1);
357 }
358 
359 int
_displaycursor(Display * d,Cursor * c,Cursor2 * c2)360 _displaycursor(Display *d, Cursor *c, Cursor2 *c2)
361 {
362 	Wsysmsg tx, rx;
363 
364 	tx.type = Tcursor2;
365 	if(c == nil){
366 		memset(&tx.cursor, 0, sizeof tx.cursor);
367 		memset(&tx.cursor2, 0, sizeof tx.cursor2);
368 		tx.arrowcursor = 1;
369 	}else{
370 		tx.arrowcursor = 0;
371 		tx.cursor = *c;
372 		if(c2 != nil)
373 			tx.cursor2 = *c2;
374 		else
375 			scalecursor(&tx.cursor2, c);
376 	}
377 	return displayrpc(d, &tx, &rx, nil);
378 }
379 
380 int
_displaybouncemouse(Display * d,Mouse * m)381 _displaybouncemouse(Display *d, Mouse *m)
382 {
383 	Wsysmsg tx, rx;
384 
385 	tx.type = Tbouncemouse;
386 	tx.mouse = *m;
387 	return displayrpc(d, &tx, &rx, nil);
388 }
389 
390 int
_displaylabel(Display * d,char * label)391 _displaylabel(Display *d, char *label)
392 {
393 	Wsysmsg tx, rx;
394 
395 	tx.type = Tlabel;
396 	tx.label = label;
397 	return displayrpc(d, &tx, &rx, nil);
398 }
399 
400 char*
_displayrdsnarf(Display * d)401 _displayrdsnarf(Display *d)
402 {
403 	void *p;
404 	char *s;
405 	Wsysmsg tx, rx;
406 
407 	tx.type = Trdsnarf;
408 	if(displayrpc(d, &tx, &rx, &p) < 0)
409 		return nil;
410 	s = strdup(rx.snarf);
411 	free(p);
412 	return s;
413 }
414 
415 int
_displaywrsnarf(Display * d,char * snarf)416 _displaywrsnarf(Display *d, char *snarf)
417 {
418 	Wsysmsg tx, rx;
419 
420 	tx.type = Twrsnarf;
421 	tx.snarf = snarf;
422 	return displayrpc(d, &tx, &rx, nil);
423 }
424 
425 int
_displayrddraw(Display * d,void * v,int n)426 _displayrddraw(Display *d, void *v, int n)
427 {
428 	void *p;
429 	Wsysmsg tx, rx;
430 
431 	tx.type = Trddraw;
432 	tx.count = n;
433 	if(displayrpc(d, &tx, &rx, &p) < 0)
434 		return -1;
435 	memmove(v, rx.data, rx.count);
436 	free(p);
437 	return rx.count;
438 }
439 
440 int
_displaywrdraw(Display * d,void * v,int n)441 _displaywrdraw(Display *d, void *v, int n)
442 {
443 	Wsysmsg tx, rx;
444 
445 	tx.type = Twrdraw;
446 	tx.count = n;
447 	tx.data = v;
448 	if(displayrpc(d, &tx, &rx, nil) < 0)
449 		return -1;
450 	return rx.count;
451 }
452 
453 int
_displaytop(Display * d)454 _displaytop(Display *d)
455 {
456 	Wsysmsg tx, rx;
457 
458 	tx.type = Ttop;
459 	return displayrpc(d, &tx, &rx, nil);
460 }
461 
462 int
_displayresize(Display * d,Rectangle r)463 _displayresize(Display *d, Rectangle r)
464 {
465 	Wsysmsg tx, rx;
466 
467 	tx.type = Tresize;
468 	tx.rect = r;
469 	return displayrpc(d, &tx, &rx, nil);
470 }
471 
472 static int
canreadfd(int fd)473 canreadfd(int fd)
474 {
475 	fd_set rs, ws, xs;
476 	struct timeval tv;
477 
478 	FD_ZERO(&rs);
479 	FD_ZERO(&ws);
480 	FD_ZERO(&xs);
481 	FD_SET(fd, &rs);
482 	FD_SET(fd, &xs);
483 	tv.tv_sec = 0;
484 	tv.tv_usec = 0;
485 	if(select(fd+1, &rs, &ws, &xs, &tv) < 0)
486 		return 0;
487 	if(FD_ISSET(fd, &rs) || FD_ISSET(fd, &xs))
488 		return 1;
489 	return 0;
490 }
491