Blob


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