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];
27 fmtinstall('W', drawfcallfmt);
28 fmtinstall('H', encodefmt);
30 if(pipe(p) < 0)
31 return -1;
32 if((pid=fork()) < 0){
33 close(p[0]);
34 close(p[1]);
35 return -1;
36 }
37 if(pid == 0){
38 char *devdraw;
39 close(p[0]);
40 dup(p[1], 0);
41 dup(p[1], 1);
42 /* execl("strace", "strace", "-o", "drawsrv.out", "drawsrv", nil); */
43 /*
44 * The argv0 has no meaning to devdraw.
45 * Pass it along only so that the various
46 * devdraws in psu -a can be distinguished.
47 * The NOLIBTHREADDAEMONIZE keeps devdraw from
48 * forking before threadmain. OS X hates it when
49 * guis fork.
50 *
51 * If client didn't use ARGBEGIN, argv0 == nil.
52 * Can't send nil through because OS X expects
53 * argv[0] to be non-nil. Also, OS X apparently
54 * expects argv[0] to be a valid executable name,
55 * so "(argv0)" is not okay. Use "devdraw"
56 * instead.
57 */
58 putenv("NOLIBTHREADDAEMONIZE", "1");
59 devdraw = getenv("DEVDRAW");
60 if(devdraw == nil)
61 devdraw = "devdraw";
62 if(argv0 == nil)
63 argv0 = devdraw;
64 execl(devdraw, argv0, argv0, "(devdraw)", nil);
65 sysfatal("exec devdraw: %r");
66 }
67 close(p[1]);
68 d->srvfd = p[0];
69 return 0;
70 }
72 int
73 _displaymux(Display *d)
74 {
75 if((d->mux = mallocz(sizeof(*d->mux), 1)) == nil)
76 return -1;
78 d->mux->mintag = 1;
79 d->mux->maxtag = 255;
80 d->mux->send = drawsend;
81 d->mux->recv = drawrecv;
82 d->mux->nbrecv = drawnbrecv;
83 d->mux->gettag = drawgettag;
84 d->mux->settag = drawsettag;
85 d->mux->aux = d;
86 muxinit(d->mux);
88 return 0;
89 }
91 static int
92 drawsend(Mux *mux, void *vmsg)
93 {
94 int n;
95 uchar *msg;
96 Display *d;
98 msg = vmsg;
99 GET(msg, n);
100 d = mux->aux;
101 return write(d->srvfd, msg, n);
104 static int
105 _drawrecv(Mux *mux, int canblock, void **vp)
107 int n;
108 uchar buf[4], *p;
109 Display *d;
111 d = mux->aux;
112 *vp = nil;
113 if(!canblock && !canreadfd(d->srvfd))
114 return 0;
115 if((n=readn(d->srvfd, buf, 4)) != 4)
116 return 1;
117 GET(buf, n);
118 p = malloc(n);
119 if(p == nil){
120 fprint(2, "out of memory allocating %d in drawrecv\n", n);
121 return 1;
123 memmove(p, buf, 4);
124 if(readn(d->srvfd, p+4, n-4) != n-4){
125 free(p);
126 return 1;
128 *vp = p;
129 return 1;
132 static void*
133 drawrecv(Mux *mux)
135 void *p;
136 _drawrecv(mux, 1, &p);
137 return p;
140 static int
141 drawnbrecv(Mux *mux, void **vp)
143 return _drawrecv(mux, 0, vp);
146 static int
147 drawgettag(Mux *mux, void *vmsg)
149 uchar *msg;
150 USED(mux);
152 msg = vmsg;
153 return msg[4];
156 static int
157 drawsettag(Mux *mux, void *vmsg, uint tag)
159 uchar *msg;
160 USED(mux);
162 msg = vmsg;
163 msg[4] = tag;
164 return 0;
167 static int
168 displayrpc(Display *d, Wsysmsg *tx, Wsysmsg *rx, void **freep)
170 int n, nn;
171 void *tpkt, *rpkt;
173 n = sizeW2M(tx);
174 tpkt = malloc(n);
175 if(freep)
176 *freep = nil;
177 if(tpkt == nil)
178 return -1;
179 tx->tag = 0;
180 if(chattydrawclient)
181 fprint(2, "<- %W\n", tx);
182 nn = convW2M(tx, tpkt, n);
183 if(nn != n){
184 free(tpkt);
185 werrstr("drawclient: sizeW2M convW2M mismatch");
186 fprint(2, "%r\n");
187 return -1;
189 /*
190 * This is the only point where we might reschedule.
191 * Muxrpc might need to acquire d->mux->lk, which could
192 * be held by some other proc (e.g., the one reading from
193 * the keyboard via Trdkbd messages). If we need to wait
194 * for the lock, don't let other threads from this proc
195 * run. This keeps up the appearance that writes to /dev/draw
196 * don't cause rescheduling. If you *do* allow rescheduling
197 * here, then flushimage(display, 1) happening in two different
198 * threads in the same proc can cause a buffer of commands
199 * to be written out twice, leading to interesting results
200 * on the screen.
202 * Threadpin and threadunpin were added to the thread library
203 * to solve exactly this problem. Be careful! They are dangerous.
205 * _pin and _unpin are aliases for threadpin and threadunpin
206 * in a threaded program and are no-ops in unthreaded programs.
207 */
208 _pin();
209 rpkt = muxrpc(d->mux, tpkt);
210 _unpin();
211 free(tpkt);
212 if(rpkt == nil){
213 werrstr("muxrpc: %r");
214 return -1;
216 GET((uchar*)rpkt, n);
217 nn = convM2W(rpkt, n, rx);
218 if(nn != n){
219 free(rpkt);
220 werrstr("drawclient: convM2W packet size mismatch %d %d %.*H", n, nn, n, rpkt);
221 fprint(2, "%r\n");
222 return -1;
224 if(chattydrawclient)
225 fprint(2, "-> %W\n", rx);
226 if(rx->type == Rerror){
227 werrstr("%s", rx->error);
228 free(rpkt);
229 return -1;
231 if(rx->type != tx->type+1){
232 werrstr("packet type mismatch -- tx %d rx %d",
233 tx->type, rx->type);
234 free(rpkt);
235 return -1;
237 if(freep)
238 *freep = rpkt;
239 else
240 free(rpkt);
241 return 0;
244 int
245 _displayinit(Display *d, char *label, char *winsize)
247 Wsysmsg tx, rx;
249 tx.type = Tinit;
250 tx.label = label;
251 tx.winsize = winsize;
252 return displayrpc(d, &tx, &rx, nil);
255 int
256 _displayrdmouse(Display *d, Mouse *m, int *resized)
258 Wsysmsg tx, rx;
260 tx.type = Trdmouse;
261 if(displayrpc(d, &tx, &rx, nil) < 0)
262 return -1;
263 _drawmouse = rx.mouse;
264 *m = rx.mouse;
265 *resized = rx.resized;
266 return 0;
269 int
270 _displayrdkbd(Display *d, Rune *r)
272 Wsysmsg tx, rx;
274 tx.type = Trdkbd;
275 if(displayrpc(d, &tx, &rx, nil) < 0)
276 return -1;
277 *r = rx.rune;
278 return 0;
281 int
282 _displaymoveto(Display *d, Point p)
284 Wsysmsg tx, rx;
286 tx.type = Tmoveto;
287 tx.mouse.xy = p;
288 if(displayrpc(d, &tx, &rx, nil) < 0)
289 return -1;
290 _drawmouse.xy = p;
291 return flushimage(d, 1);
294 int
295 _displaycursor(Display *d, Cursor *c)
297 Wsysmsg tx, rx;
299 tx.type = Tcursor;
300 if(c == nil){
301 memset(&tx.cursor, 0, sizeof tx.cursor);
302 tx.arrowcursor = 1;
303 }else{
304 tx.arrowcursor = 0;
305 tx.cursor = *c;
307 return displayrpc(d, &tx, &rx, nil);
310 int
311 _displaybouncemouse(Display *d, Mouse *m)
313 Wsysmsg tx, rx;
315 tx.type = Tbouncemouse;
316 tx.mouse = *m;
317 return displayrpc(d, &tx, &rx, nil);
320 int
321 _displaylabel(Display *d, char *label)
323 Wsysmsg tx, rx;
325 tx.type = Tlabel;
326 tx.label = label;
327 return displayrpc(d, &tx, &rx, nil);
330 char*
331 _displayrdsnarf(Display *d)
333 void *p;
334 char *s;
335 Wsysmsg tx, rx;
337 tx.type = Trdsnarf;
338 if(displayrpc(d, &tx, &rx, &p) < 0)
339 return nil;
340 s = strdup(rx.snarf);
341 free(p);
342 return s;
345 int
346 _displaywrsnarf(Display *d, char *snarf)
348 Wsysmsg tx, rx;
350 tx.type = Twrsnarf;
351 tx.snarf = snarf;
352 return displayrpc(d, &tx, &rx, nil);
355 int
356 _displayrddraw(Display *d, void *v, int n)
358 void *p;
359 Wsysmsg tx, rx;
361 tx.type = Trddraw;
362 tx.count = n;
363 if(displayrpc(d, &tx, &rx, &p) < 0)
364 return -1;
365 memmove(v, rx.data, rx.count);
366 free(p);
367 return rx.count;
370 int
371 _displaywrdraw(Display *d, void *v, int n)
373 Wsysmsg tx, rx;
375 tx.type = Twrdraw;
376 tx.count = n;
377 tx.data = v;
378 if(displayrpc(d, &tx, &rx, nil) < 0)
379 return -1;
380 return rx.count;
383 int
384 _displaytop(Display *d)
386 Wsysmsg tx, rx;
388 tx.type = Ttop;
389 return displayrpc(d, &tx, &rx, nil);
392 int
393 _displayresize(Display *d, Rectangle r)
395 Wsysmsg tx, rx;
397 tx.type = Tresize;
398 tx.rect = r;
399 return displayrpc(d, &tx, &rx, nil);
402 static int
403 canreadfd(int fd)
405 fd_set rs, ws, xs;
406 struct timeval tv;
408 FD_ZERO(&rs);
409 FD_ZERO(&ws);
410 FD_ZERO(&xs);
411 FD_SET(fd, &rs);
412 FD_SET(fd, &xs);
413 tv.tv_sec = 0;
414 tv.tv_usec = 0;
415 if(select(fd+1, &rs, &ws, &xs, &tv) < 0)
416 return 0;
417 if(FD_ISSET(fd, &rs) || FD_ISSET(fd, &xs))
418 return 1;
419 return 0;