drawclient.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.type); 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", "drawsrv", nil); */ 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 dangerous. 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 %.*H", n, nn, n, rpkt); 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 }