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 int chattydrawclient = 0;
14 static int drawgettag(Mux *mux, void *vmsg);
15 static void* drawrecv(Mux *mux);
16 static int drawnbrecv(Mux *mux, void**);
17 static int drawsend(Mux *mux, void *vmsg);
18 static int drawsettag(Mux *mux, void *vmsg, uint tag);
19 static int canreadfd(int);
21 int
22 _displayconnect(Display *d)
23 {
24 int pid, p[2];
26 fmtinstall('W', drawfcallfmt);
27 fmtinstall('H', encodefmt);
29 if(pipe(p) < 0)
30 return -1;
31 if((pid=fork()) < 0){
32 close(p[0]);
33 close(p[1]);
34 return -1;
35 }
36 if(pid == 0){
37 char *devdraw;
38 close(p[0]);
39 dup(p[1], 0);
40 dup(p[1], 1);
41 /* execl("strace", "strace", "-o", "drawsrv.out", "drawsrv", nil); */
42 /*
43 * The argv0 has no meaning to devdraw.
44 * Pass it along only so that the various
45 * devdraws in psu -a can be distinguished.
46 * The NOLIBTHREADDAEMONIZE keeps devdraw from
47 * forking before threadmain. OS X hates it when
48 * guis fork.
49 *
50 * If client didn't use ARGBEGIN, argv0 == nil.
51 * Can't send nil through because OS X expects
52 * argv[0] to be non-nil. Also, OS X apparently
53 * expects argv[0] to be a valid executable name,
54 * so "(argv0)" is not okay. Use "devdraw"
55 * instead.
56 */
57 putenv("NOLIBTHREADDAEMONIZE", "1");
58 devdraw = getenv("DEVDRAW");
59 if(devdraw == nil)
60 devdraw = "devdraw";
61 if(argv0 == nil)
62 argv0 = devdraw;
63 execl(devdraw, argv0, argv0, "(devdraw)", nil);
64 sysfatal("exec devdraw: %r");
65 }
66 close(p[1]);
67 d->srvfd = p[0];
68 return 0;
69 }
71 int
72 _displaymux(Display *d)
73 {
74 if((d->mux = mallocz(sizeof(*d->mux), 1)) == nil)
75 return -1;
77 d->mux->mintag = 1;
78 d->mux->maxtag = 255;
79 d->mux->send = drawsend;
80 d->mux->recv = drawrecv;
81 d->mux->nbrecv = drawnbrecv;
82 d->mux->gettag = drawgettag;
83 d->mux->settag = drawsettag;
84 d->mux->aux = d;
85 muxinit(d->mux);
87 return 0;
88 }
90 static int
91 drawsend(Mux *mux, void *vmsg)
92 {
93 int n;
94 uchar *msg;
95 Display *d;
97 msg = vmsg;
98 GET(msg, n);
99 d = mux->aux;
100 return write(d->srvfd, msg, n);
103 static int
104 _drawrecv(Mux *mux, int canblock, void **vp)
106 int n;
107 uchar buf[4], *p;
108 Display *d;
110 d = mux->aux;
111 *vp = nil;
112 if(!canblock && !canreadfd(d->srvfd))
113 return 0;
114 if((n=readn(d->srvfd, buf, 4)) != 4)
115 return 1;
116 GET(buf, n);
117 p = malloc(n);
118 if(p == nil){
119 fprint(2, "out of memory allocating %d in drawrecv\n", n);
120 return 1;
122 memmove(p, buf, 4);
123 if(readn(d->srvfd, p+4, n-4) != n-4){
124 free(p);
125 return 1;
127 *vp = p;
128 return 1;
131 static void*
132 drawrecv(Mux *mux)
134 void *p;
135 _drawrecv(mux, 1, &p);
136 return p;
139 static int
140 drawnbrecv(Mux *mux, void **vp)
142 return _drawrecv(mux, 0, vp);
145 static int
146 drawgettag(Mux *mux, void *vmsg)
148 uchar *msg;
149 USED(mux);
151 msg = vmsg;
152 return msg[4];
155 static int
156 drawsettag(Mux *mux, void *vmsg, uint tag)
158 uchar *msg;
159 USED(mux);
161 msg = vmsg;
162 msg[4] = tag;
163 return 0;
166 static int
167 displayrpc(Display *d, Wsysmsg *tx, Wsysmsg *rx, void **freep)
169 int n, nn;
170 void *tpkt, *rpkt;
172 n = sizeW2M(tx);
173 tpkt = malloc(n);
174 if(freep)
175 *freep = nil;
176 if(tpkt == nil)
177 return -1;
178 tx->tag = 0;
179 if(chattydrawclient)
180 fprint(2, "<- %W\n", tx);
181 nn = convW2M(tx, tpkt, n);
182 if(nn != n){
183 free(tpkt);
184 werrstr("drawclient: sizeW2M convW2M mismatch");
185 fprint(2, "%r\n");
186 return -1;
188 /*
189 * This is the only point where we might reschedule.
190 * Muxrpc might need to acquire d->mux->lk, which could
191 * be held by some other proc (e.g., the one reading from
192 * the keyboard via Trdkbd messages). If we need to wait
193 * for the lock, don't let other threads from this proc
194 * run. This keeps up the appearance that writes to /dev/draw
195 * don't cause rescheduling. If you *do* allow rescheduling
196 * here, then flushimage(display, 1) happening in two different
197 * threads in the same proc can cause a buffer of commands
198 * to be written out twice, leading to interesting results
199 * on the screen.
201 * Threadpin and threadunpin were added to the thread library
202 * to solve exactly this problem. Be careful! They are dangerous.
204 * _pin and _unpin are aliases for threadpin and threadunpin
205 * in a threaded program and are no-ops in unthreaded programs.
206 */
207 _pin();
208 rpkt = muxrpc(d->mux, tpkt);
209 _unpin();
210 free(tpkt);
211 if(rpkt == nil){
212 werrstr("muxrpc: %r");
213 return -1;
215 GET((uchar*)rpkt, n);
216 nn = convM2W(rpkt, n, rx);
217 if(nn != n){
218 free(rpkt);
219 werrstr("drawclient: convM2W packet size mismatch %d %d %.*H", n, nn, n, rpkt);
220 fprint(2, "%r\n");
221 return -1;
223 if(chattydrawclient)
224 fprint(2, "-> %W\n", rx);
225 if(rx->type == Rerror){
226 werrstr("%s", rx->error);
227 free(rpkt);
228 return -1;
230 if(rx->type != tx->type+1){
231 werrstr("packet type mismatch -- tx %d rx %d",
232 tx->type, rx->type);
233 free(rpkt);
234 return -1;
236 if(freep)
237 *freep = rpkt;
238 else
239 free(rpkt);
240 return 0;
243 int
244 _displayinit(Display *d, char *label, char *winsize)
246 Wsysmsg tx, rx;
248 tx.type = Tinit;
249 tx.label = label;
250 tx.winsize = winsize;
251 return displayrpc(d, &tx, &rx, nil);
254 int
255 _displayrdmouse(Display *d, Mouse *m, int *resized)
257 Wsysmsg tx, rx;
259 tx.type = Trdmouse;
260 if(displayrpc(d, &tx, &rx, nil) < 0)
261 return -1;
262 *m = rx.mouse;
263 *resized = rx.resized;
264 return 0;
267 int
268 _displayrdkbd(Display *d, Rune *r)
270 Wsysmsg tx, rx;
272 tx.type = Trdkbd;
273 if(displayrpc(d, &tx, &rx, nil) < 0)
274 return -1;
275 *r = rx.rune;
276 return 0;
279 int
280 _displaymoveto(Display *d, Point p)
282 Wsysmsg tx, rx;
284 tx.type = Tmoveto;
285 tx.mouse.xy = p;
286 return displayrpc(d, &tx, &rx, nil);
289 int
290 _displaycursor(Display *d, Cursor *c)
292 Wsysmsg tx, rx;
294 tx.type = Tcursor;
295 if(c == nil){
296 memset(&tx.cursor, 0, sizeof tx.cursor);
297 tx.arrowcursor = 1;
298 }else{
299 tx.arrowcursor = 0;
300 tx.cursor = *c;
302 return displayrpc(d, &tx, &rx, nil);
305 int
306 _displaybouncemouse(Display *d, Mouse *m)
308 Wsysmsg tx, rx;
310 tx.type = Tbouncemouse;
311 tx.mouse = *m;
312 return displayrpc(d, &tx, &rx, nil);
315 int
316 _displaylabel(Display *d, char *label)
318 Wsysmsg tx, rx;
320 tx.type = Tlabel;
321 tx.label = label;
322 return displayrpc(d, &tx, &rx, nil);
325 char*
326 _displayrdsnarf(Display *d)
328 void *p;
329 char *s;
330 Wsysmsg tx, rx;
332 tx.type = Trdsnarf;
333 if(displayrpc(d, &tx, &rx, &p) < 0)
334 return nil;
335 s = strdup(rx.snarf);
336 free(p);
337 return s;
340 int
341 _displaywrsnarf(Display *d, char *snarf)
343 Wsysmsg tx, rx;
345 tx.type = Twrsnarf;
346 tx.snarf = snarf;
347 return displayrpc(d, &tx, &rx, nil);
350 int
351 _displayrddraw(Display *d, void *v, int n)
353 void *p;
354 Wsysmsg tx, rx;
356 tx.type = Trddraw;
357 tx.count = n;
358 if(displayrpc(d, &tx, &rx, &p) < 0)
359 return -1;
360 memmove(v, rx.data, rx.count);
361 free(p);
362 return rx.count;
365 int
366 _displaywrdraw(Display *d, void *v, int n)
368 Wsysmsg tx, rx;
370 tx.type = Twrdraw;
371 tx.count = n;
372 tx.data = v;
373 if(displayrpc(d, &tx, &rx, nil) < 0)
374 return -1;
375 return rx.count;
378 int
379 _displaytop(Display *d)
381 Wsysmsg tx, rx;
383 tx.type = Ttop;
384 return displayrpc(d, &tx, &rx, nil);
387 int
388 _displayresize(Display *d, Rectangle r)
390 Wsysmsg tx, rx;
392 tx.type = Tresize;
393 tx.rect = r;
394 return displayrpc(d, &tx, &rx, nil);
397 static int
398 canreadfd(int fd)
400 fd_set rs, ws, xs;
401 struct timeval tv;
403 FD_ZERO(&rs);
404 FD_ZERO(&ws);
405 FD_ZERO(&xs);
406 FD_SET(fd, &rs);
407 FD_SET(fd, &xs);
408 tv.tv_sec = 0;
409 tv.tv_usec = 0;
410 if(select(fd+1, &rs, &ws, &xs, &tv) < 0)
411 return 0;
412 if(FD_ISSET(fd, &rs) || FD_ISSET(fd, &xs))
413 return 1;
414 return 0;