tdrawclient.c - plan9port - [fork] Plan 9 from user space | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
tdrawclient.c (8966B) | |
--- | |
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 | |
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.t… | |
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", "draws… | |
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 | |
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 | |
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 | |
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* | |
198 drawrecv(Mux *mux) | |
199 { | |
200 void *p; | |
201 _drawrecv(mux, 1, &p); | |
202 return p; | |
203 } | |
204 | |
205 static int | |
206 drawnbrecv(Mux *mux, void **vp) | |
207 { | |
208 return _drawrecv(mux, 0, vp); | |
209 } | |
210 | |
211 static int | |
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 | |
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 | |
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 dangero… | |
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 … | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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* | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 } |