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 close(p[0]);
38 dup(p[1], 0);
39 dup(p[1], 1);
40 /* execl("strace", "strace", "-o", "drawsrv.out", "drawsrv", nil); */
41 /*
42 * The argv0 has no meaning to devdraw.
43 * Pass it along only so that the various
44 * devdraws in psu -a can be distinguished.
45 * The NOLIBTHREADDAEMONIZE keeps devdraw from
46 * forking before threadmain. OS X hates it when
47 * guis fork.
48 */
49 putenv("NOLIBTHREADDAEMONIZE", "1");
50 execl("devdraw", argv0, argv0, "(devdraw)", nil);
51 sysfatal("exec devdraw: %r");
52 }
53 close(p[1]);
54 d->srvfd = p[0];
55 return 0;
56 }
58 int
59 _displaymux(Display *d)
60 {
61 if((d->mux = mallocz(sizeof(*d->mux), 1)) == nil)
62 return -1;
64 d->mux->mintag = 1;
65 d->mux->maxtag = 255;
66 d->mux->send = drawsend;
67 d->mux->recv = drawrecv;
68 d->mux->nbrecv = drawnbrecv;
69 d->mux->gettag = drawgettag;
70 d->mux->settag = drawsettag;
71 d->mux->aux = d;
72 muxinit(d->mux);
74 return 0;
75 }
77 static int
78 drawsend(Mux *mux, void *vmsg)
79 {
80 int n;
81 uchar *msg;
82 Display *d;
84 msg = vmsg;
85 GET(msg, n);
86 d = mux->aux;
87 return write(d->srvfd, msg, n);
88 }
90 static int
91 _drawrecv(Mux *mux, int canblock, void **vp)
92 {
93 int n;
94 uchar buf[4], *p;
95 Display *d;
97 d = mux->aux;
98 *vp = nil;
99 if(!canblock && !canreadfd(d->srvfd))
100 return 0;
101 if((n=readn(d->srvfd, buf, 4)) != 4)
102 return 1;
103 GET(buf, n);
104 p = malloc(n);
105 if(p == nil){
106 fprint(2, "out of memory allocating %d in drawrecv\n", n);
107 return 1;
109 memmove(p, buf, 4);
110 if(readn(d->srvfd, p+4, n-4) != n-4){
111 free(p);
112 return 1;
114 *vp = p;
115 return 1;
118 static void*
119 drawrecv(Mux *mux)
121 void *p;
122 _drawrecv(mux, 1, &p);
123 return p;
126 static int
127 drawnbrecv(Mux *mux, void **vp)
129 return _drawrecv(mux, 0, vp);
132 static int
133 drawgettag(Mux *mux, void *vmsg)
135 uchar *msg;
136 USED(mux);
138 msg = vmsg;
139 return msg[4];
142 static int
143 drawsettag(Mux *mux, void *vmsg, uint tag)
145 uchar *msg;
146 USED(mux);
148 msg = vmsg;
149 msg[4] = tag;
150 return 0;
153 static int
154 displayrpc(Display *d, Wsysmsg *tx, Wsysmsg *rx, void **freep)
156 int n, nn;
157 void *tpkt, *rpkt;
159 n = sizeW2M(tx);
160 tpkt = malloc(n);
161 if(freep)
162 *freep = nil;
163 if(tpkt == nil)
164 return -1;
165 tx->tag = 0;
166 if(chattydrawclient)
167 fprint(2, "<- %W\n", tx);
168 nn = convW2M(tx, tpkt, n);
169 if(nn != n){
170 free(tpkt);
171 werrstr("drawclient: sizeW2M convW2M mismatch");
172 fprint(2, "%r\n");
173 return -1;
175 /*
176 * This is the only point where we might reschedule.
177 * Muxrpc might need to acquire d->mux->lk, which could
178 * be held by some other proc (e.g., the one reading from
179 * the keyboard via Trdkbd messages). If we need to wait
180 * for the lock, don't let other threads from this proc
181 * run. This keeps up the appearance that writes to /dev/draw
182 * don't cause rescheduling. If you *do* allow rescheduling
183 * here, then flushimage(display, 1) happening in two different
184 * threads in the same proc can cause a buffer of commands
185 * to be written out twice, leading to interesting results
186 * on the screen.
188 * Threadpin and threadunpin were added to the thread library
189 * to solve exactly this problem. Be careful! They are dangerous.
191 * _pin and _unpin are aliases for threadpin and threadunpin
192 * in a threaded program and are no-ops in unthreaded programs.
193 */
194 _pin();
195 rpkt = muxrpc(d->mux, tpkt);
196 _unpin();
197 free(tpkt);
198 if(rpkt == nil){
199 werrstr("muxrpc: %r");
200 return -1;
202 GET((uchar*)rpkt, n);
203 nn = convM2W(rpkt, n, rx);
204 if(nn != n){
205 free(rpkt);
206 werrstr("drawclient: convM2W packet size mismatch %d %d %.*H", n, nn, n, rpkt);
207 fprint(2, "%r\n");
208 return -1;
210 if(chattydrawclient)
211 fprint(2, "-> %W\n", rx);
212 if(rx->type == Rerror){
213 werrstr("%s", rx->error);
214 free(rpkt);
215 return -1;
217 if(rx->type != tx->type+1){
218 werrstr("packet type mismatch -- tx %d rx %d",
219 tx->type, rx->type);
220 free(rpkt);
221 return -1;
223 if(freep)
224 *freep = rpkt;
225 else
226 free(rpkt);
227 return 0;
230 int
231 _displayinit(Display *d, char *label, char *winsize)
233 Wsysmsg tx, rx;
235 tx.type = Tinit;
236 tx.label = label;
237 tx.winsize = winsize;
238 return displayrpc(d, &tx, &rx, nil);
241 int
242 _displayrdmouse(Display *d, Mouse *m, int *resized)
244 Wsysmsg tx, rx;
246 tx.type = Trdmouse;
247 if(displayrpc(d, &tx, &rx, nil) < 0)
248 return -1;
249 *m = rx.mouse;
250 *resized = rx.resized;
251 return 0;
254 int
255 _displayrdkbd(Display *d, Rune *r)
257 Wsysmsg tx, rx;
259 tx.type = Trdkbd;
260 if(displayrpc(d, &tx, &rx, nil) < 0)
261 return -1;
262 *r = rx.rune;
263 return 0;
266 int
267 _displaymoveto(Display *d, Point p)
269 Wsysmsg tx, rx;
271 tx.type = Tmoveto;
272 tx.mouse.xy = p;
273 return displayrpc(d, &tx, &rx, nil);
276 int
277 _displaycursor(Display *d, Cursor *c)
279 Wsysmsg tx, rx;
281 tx.type = Tcursor;
282 if(c == nil){
283 memset(&tx.cursor, 0, sizeof tx.cursor);
284 tx.arrowcursor = 1;
285 }else{
286 tx.arrowcursor = 0;
287 tx.cursor = *c;
289 return displayrpc(d, &tx, &rx, nil);
292 int
293 _displaybouncemouse(Display *d, Mouse *m)
295 Wsysmsg tx, rx;
297 tx.type = Tbouncemouse;
298 tx.mouse = *m;
299 return displayrpc(d, &tx, &rx, nil);
302 int
303 _displaylabel(Display *d, char *label)
305 Wsysmsg tx, rx;
307 tx.type = Tlabel;
308 tx.label = label;
309 return displayrpc(d, &tx, &rx, nil);
312 char*
313 _displayrdsnarf(Display *d)
315 void *p;
316 char *s;
317 Wsysmsg tx, rx;
319 tx.type = Trdsnarf;
320 if(displayrpc(d, &tx, &rx, &p) < 0)
321 return nil;
322 s = strdup(rx.snarf);
323 free(p);
324 return s;
327 int
328 _displaywrsnarf(Display *d, char *snarf)
330 Wsysmsg tx, rx;
332 tx.type = Twrsnarf;
333 tx.snarf = snarf;
334 return displayrpc(d, &tx, &rx, nil);
337 int
338 _displayrddraw(Display *d, void *v, int n)
340 void *p;
341 Wsysmsg tx, rx;
343 tx.type = Trddraw;
344 tx.count = n;
345 if(displayrpc(d, &tx, &rx, &p) < 0)
346 return -1;
347 memmove(v, rx.data, rx.count);
348 free(p);
349 return rx.count;
352 int
353 _displaywrdraw(Display *d, void *v, int n)
355 Wsysmsg tx, rx;
357 tx.type = Twrdraw;
358 tx.count = n;
359 tx.data = v;
360 if(displayrpc(d, &tx, &rx, nil) < 0)
361 return -1;
362 return rx.count;
365 int
366 _displaytop(Display *d)
368 Wsysmsg tx, rx;
370 tx.type = Ttop;
371 return displayrpc(d, &tx, &rx, nil);
374 int
375 _displayresize(Display *d, Rectangle r)
377 Wsysmsg tx, rx;
379 tx.type = Tresize;
380 tx.rect = r;
381 return displayrpc(d, &tx, &rx, nil);
384 static int
385 canreadfd(int fd)
387 fd_set rs, ws, xs;
388 struct timeval tv;
390 FD_ZERO(&rs);
391 FD_ZERO(&ws);
392 FD_ZERO(&xs);
393 FD_SET(fd, &rs);
394 FD_SET(fd, &xs);
395 tv.tv_sec = 0;
396 tv.tv_usec = 0;
397 if(select(fd+1, &rs, &ws, &xs, &tv) < 0)
398 return 0;
399 if(FD_ISSET(fd, &rs) || FD_ISSET(fd, &xs))
400 return 1;
401 return 0;