1implement Rioimport; 2include "sys.m"; 3 sys: Sys; 4include "draw.m"; 5 draw: Draw; 6 Image, Point, Rect, Display, Screen: import draw; 7include "wmsrv.m"; 8 wmsrv: Wmsrv; 9include "sh.m"; 10 sh: Sh; 11include "string.m"; 12 str: String; 13 14Rioimport: module{ 15 init: fn(nil: ref Draw->Context, argv: list of string); 16}; 17 18Client: adt{ 19 ptrstarted: int; 20 kbdstarted: int; 21 state: int; # Hidden|Current 22 req: chan of (array of byte, Sys->Rwrite); 23 resize: chan of ref Riowin; 24 ptr: chan of ref Draw->Pointer; 25 riowctl: chan of (ref Riowin, int); 26 wins: list of ref Riowin; 27 winfd: ref Sys->FD; 28 sc: ref Wmsrv->Client; 29}; 30 31Riowin: adt { 32 tag: string; 33 img: ref Image; 34 dir: string; 35 state: int; 36 ptrpid: int; 37 kbdpid: int; 38 ctlpid: int; 39 ptrfd: ref Sys->FD; 40 ctlfd: ref Sys->FD; 41}; 42 43Hidden, Current: con 1<<iota; 44Ptrsize: con 1+4*12; # 'm' plus 4 12-byte decimal integers 45P9PATH: con "/n/local"; 46Borderwidth: con 4; # defined in /sys/include/draw.h 47 48display: ref Display; 49wsysseq := 0; 50screenr := Rect((0, 0), (640, 480)); # no way of getting this reliably from rio 51 52Minwinsize: con Point(100, 42); 53 54init(nil: ref Draw->Context, argv: list of string) 55{ 56 sys = load Sys Sys->PATH; 57 draw = load Draw Draw->PATH; 58 sh = load Sh Sh->PATH; 59 sh->initialise(); 60 str = load String String->PATH; 61 wmsrv = load Wmsrv Wmsrv->PATH; 62 63 wc := chan of (ref Draw->Context, string); 64 spawn rioproxy(wc); 65 (ctxt, err) := <-wc; 66 if(err != nil){ 67 sys->fprint(sys->fildes(2), "rioimport: %s\n", err); 68 raise "fail:no display"; 69 } 70 sh->run(ctxt, tl argv); 71} 72 73ebind(a, b: string, flag: int) 74{ 75 if(sys->bind(a, b, flag) == -1){ 76 sys->fprint(sys->fildes(2), "rioimport: cannot bind %q onto %q: %r\n", a, b); 77 raise "fail:error"; 78 } 79} 80 81rioproxy(wc: chan of (ref Draw->Context, string)) 82{ 83 { 84 rioproxy1(wc); 85 } exception e { 86 "fail:*" => 87 wc <-= (nil, e[5:]); 88 } 89} 90 91rioproxy1(wc: chan of (ref Draw->Context, string)) 92{ 93 sys->pctl(Sys->NEWFD, 0 :: 1 :: 2 :: nil); 94 95 ebind("#U*", P9PATH, Sys->MREPL); 96 display = Display.allocate(P9PATH + "/dev"); 97 if(display == nil) 98 raise sys->sprint("fail:cannot allocate display: %r"); 99 100 101 (wm, join, req) := wmsrv->init(); 102 if(wm == nil){ 103 wc <-= (nil, sys->sprint("%r")); 104 return; 105 } 106 readscreenr(); 107 wc <-= (ref Draw->Context(display, nil, wm), nil); 108 109 sys->pctl(Sys->FORKNS, nil); 110 ebind("#₪", "/srv", Sys->MREPL|Sys->MCREATE); 111 if(sys->bind(P9PATH+"/dev/draw", "/dev/draw", Sys->MREPL) == -1) 112 ebind(P9PATH+"/dev", "/dev", Sys->MAFTER); 113 sh->run(nil, "mount" :: "{mntgen}" :: "/mnt" :: nil); 114 115 clients: array of ref Client; 116 nc := 0; 117 for(;;) alt{ 118 (sc, rc) := <-join => 119 if(nc != 0) 120 rc <-= "only one client available"; 121 sync := chan of (ref Client, string); 122 spawn clientproc(sc,sync); 123 (c, err) := <-sync; 124 rc <-= err; 125 if(c != nil){ 126 if(sc.id >= len clients) 127 clients = (array[sc.id + 1] of ref Client)[0:] = clients; 128 clients[sc.id] = c; 129 } 130 (sc, data, rc) := <-req => 131 clients[sc.id].req <-= (data, rc); 132 if(rc == nil) 133 clients[sc.id] = nil; 134 } 135} 136zclient: Client; 137clientproc(sc: ref Wmsrv->Client, rc: chan of (ref Client, string)) 138{ 139 c := ref zclient; 140 c.req = chan of (array of byte, Sys->Rwrite); 141 c.resize = chan of ref Riowin; 142 c.ptr = chan of ref Draw->Pointer; 143 c.riowctl = chan of (ref Riowin, int); 144 c.sc = sc; 145 rc <-= (c, nil); 146 147loop: 148 for(;;) alt{ 149 (data, drc) := <-c.req => 150 if(drc == nil) 151 break loop; 152 err := handlerequest(c, data); 153 n := len data; 154 if(err != nil) 155 n = -1; 156 alt{ 157 drc <-= (n, err) =>; 158 * =>; 159 } 160 p := <-c.ptr => 161 sc.ptr <-= p; 162 w := <-c.resize => 163 if((c.state & Hidden) == 0) 164 sc.ctl <-= sys->sprint("!reshape %q -1 0 0 0 0 getwin", w.tag); 165 (w, state) := <-c.riowctl => 166 if((c.state^state)&Current) 167 sc.ctl <-= "haskbdfocus " + string ((state & Current)!=0); 168 if((c.state^state)&Hidden){ 169 s := "unhide"; 170 if(state&Hidden) 171 s = "hide"; 172 for(wl := c.wins; wl != nil; wl = tl wl){ 173 if(hd wl != w) 174 rioctl(hd wl, s); 175 if(c.state&Hidden) 176 sc.ctl <-= sys->sprint("!reshape %q -1 0 0 0 0 getwin", (hd wl).tag); 177 } 178 } 179 c.state = state; 180 w.state = state; 181 } 182 sc.stop <-= 1; 183 for(wl := c.wins; wl != nil; wl = tl wl) 184 delwin(hd wl); 185} 186 187handlerequest(c: ref Client, data: array of byte): string 188{ 189 req := string data; 190#sys->print("%d: %s\n", c.sc.id, req); 191 if(req == nil) 192 return "no request"; 193 args := str->unquoted(req); 194 n := len args; 195 case hd args { 196 "key" => 197 return "permission denied"; 198 "ptr" => 199 # ptr x y 200 if(n != 3) 201 return "bad arg count"; 202 if(c.ptrstarted == 0) 203 return "pointer not active"; 204 for(w := c.wins; w != nil; w = tl w){ 205 if((hd w).ptrfd != nil){ 206 sys->fprint((hd w).ptrfd, "m%11d %11d", int hd tl args, int hd tl tl args); 207 return nil; 208 } 209 } 210 return "no windows"; 211 "start" => 212 if(n != 2) 213 return "bad arg count"; 214 case hd tl args { 215 "ptr" or 216 "mouse" => 217 if(c.ptrstarted == -1) 218 return "already started"; 219 sync := chan of int; 220 for(w := c.wins; w != nil; w = tl w){ 221 spawn ptrproc(hd w, c.ptr, c.resize, sync); 222 (hd w).ptrpid = <-sync; 223 } 224 c.ptrstarted = 1; 225 return nil; 226 "kbd" => 227 if(c.kbdstarted == -1) 228 return "already started"; 229 sync := chan of int; 230 for(w := c.wins; w != nil; w = tl w){ 231 spawn kbdproc(hd w, c.sc.kbd, sync); 232 (hd w).kbdpid = <-sync; 233 } 234 return nil; 235 * => 236 return "unknown input source"; 237 } 238 "!reshape" => 239 # reshape tag reqid rect [how] 240 # XXX allow "how" to specify that the origin of the window is never 241 # changed - a new window will be created instead. 242 if(n < 7) 243 return "bad arg count"; 244 args = tl args; 245 tag := hd args; args = tl args; 246 args = tl args; # skip reqid 247 r: Rect; 248 r.min.x = int hd args; args = tl args; 249 r.min.y = int hd args; args = tl args; 250 r.max.x = int hd args; args = tl args; 251 r.max.y = int hd args; args = tl args; 252 if(r.dx() < Minwinsize.x) 253 r.max.x = r.min.x + Minwinsize.x; 254 if(r.dy() < Minwinsize.y) 255 r.max.y = r.min.y + Minwinsize.y; 256 257 spec := ""; 258 if(args != nil){ 259 case hd args{ 260 "onscreen" => 261 r = fitrect(r, screenr).inset(-Borderwidth); 262 spec = "-r " + r2s(r); 263 "place" => 264 r = fitrect(r, screenr).inset(-Borderwidth); 265 spec = "-dx " + string r.dx() + " -dy " + string r.dy(); 266 "exact" => 267 spec = "-r " + r2s(r.inset(-Borderwidth)); 268 "max" => 269 r = screenr; # XXX don't obscure toolbar? 270 spec = "-r " + r2s(r.inset(Borderwidth)); 271 "getwin" => 272 ; # just get the new image 273 * => 274 return "unkown placement method"; 275 } 276 }else 277 spec = "-r " + r2s(r.inset(-Borderwidth)); 278 return reshape(c, tag, spec); 279 "delete" => 280 # delete tag 281 if(tl args == nil) 282 return "tag required"; 283 tag := hd tl args; 284 nw: list of ref Riowin; 285 for(w := c.wins; w != nil; w = tl w){ 286 if((hd w).tag == tag){ 287 delwin(hd w); 288 wmsrv->c.sc.setimage(tag, nil); 289 }else 290 nw = hd w :: nw; 291 } 292 c.wins = nil; 293 for(; nw != nil; nw = tl nw) 294 c.wins = hd nw :: c.wins; 295 "label" => 296 if(n != 2) 297 return "bad arg count"; 298 for(w := c.wins; w != nil; w = tl w) 299 setlabel(hd w, hd tl args); 300 "raise" => 301 for(w := c.wins; w != nil; w = tl w){ 302 rioctl(hd w, "top"); 303 if(tl w == nil) 304 rioctl(hd w, "current"); 305 } 306 "lower" => 307 for(w := c.wins; w != nil; w = tl w) 308 rioctl(hd w, "bottom"); 309 "task" => 310 if(n != 2) 311 return "bad arg count"; 312 c.state |= Hidden; 313 for(w := c.wins; w != nil; w = tl w){ 314 setlabel(hd w, hd tl args); 315 rioctl(hd w, "hide"); 316 } 317 "untask" => 318 wins: list of ref Riowin; 319 for(w := c.wins; w != nil; w = tl w) 320 wins = hd w :: wins; 321 for(; wins != nil; wins = tl wins) 322 rioctl(hd wins, "unhide"); 323 "!move" => 324 # !move tag reqid startx starty 325 if(n != 5) 326 return "bad arg count"; 327 args = tl args; 328 tag := hd args; args = tl args; 329 args = tl args; 330 w := wmsrv->c.sc.window(tag); 331 if(w == nil) 332 return "no such tag"; 333 return dragwin(c.ptr, c, w, Point(int hd args, int hd tl args)); 334 "!size" => 335 return "nope"; 336 "kbdfocus" => 337 if(n != 2) 338 return "bad arg count"; 339 if(int hd tl args){ 340 if(c.wins != nil) 341 return rioctl(hd c.wins, "current"); 342 } 343 return nil; 344 * => 345 return "unknown request"; 346 } 347 return nil; 348} 349 350dragwin(ptr: chan of ref Draw->Pointer, c: ref Client, w: ref Wmsrv->Window, click: Point): string 351{ 352# if(buttons == 0) 353# return "too late"; 354 p: ref Draw->Pointer; 355 img := w.img.screen.image; 356 r := img.r; 357 off := click.sub(r.min); 358 do{ 359 p = <-ptr; 360 img.origin(r.min, p.xy.sub(off)); 361 } while (p.buttons != 0); 362 c.sc.ptr <-= p; 363# buttons = 0; 364 nr: Rect; 365 nr.min = p.xy.sub(off); 366 nr.max = nr.min.add(r.size()); 367 if(nr.eq(r)) 368 return "not moved"; 369 reshape(c, w.tag, "-r " + r2s(nr)); 370 return nil; 371} 372 373rioctl(w: ref Riowin, req: string): string 374{ 375 if(sys->fprint(w.ctlfd, "%s", req) == -1){ 376#sys->print("rioctl fail %s: %s: %r\n", w.dir, req); 377 return sys->sprint("%r"); 378} 379#sys->print("rioctl %s: %s\n", w.dir, req); 380 return nil; 381} 382 383reshape(c: ref Client, tag: string, spec: string): string 384{ 385 for(wl := c.wins; wl != nil; wl = tl wl) 386 if((hd wl).tag == tag) 387 break; 388 if(wl == nil){ 389 (w, e) := newwin(c, tag, spec); 390 if(w == nil){ 391sys->print("can't make new win (spec %q): %s\n", spec, e); 392 return e; 393 } 394 c.wins = w :: c.wins; 395 wmsrv->c.sc.setimage(tag, w.img); 396 sync := chan of int; 397 if(c.kbdstarted){ 398 spawn kbdproc(w, c.sc.kbd, sync); 399 w.kbdpid = <-sync; 400 } 401 if(c.ptrstarted){ 402 spawn ptrproc(w, c.ptr, c.resize, sync); 403 w.ptrpid = <-sync; 404 } 405 return nil; 406 } 407 w := hd wl; 408 if(spec != nil){ 409 e := rioctl(w, "resize " + spec); 410 if(e != nil) 411 return e; 412 } 413 getwin(w); 414 if(w.img == nil) 415 return "getwin failed"; 416 wmsrv->c.sc.setimage(tag, w.img); 417 return nil; 418} 419 420zriowin: Riowin; 421newwin(c: ref Client, tag, spec: string): (ref Riowin, string) 422{ 423 wsys := readfile(P9PATH + "/env/wsys"); 424 if(wsys == nil) 425 return (nil, "no $wsys"); 426 427 d := "/mnt/"+string wsysseq++; 428 fd := sys->open(wsys, Sys->ORDWR); 429 if(fd == nil) 430 return (nil, sys->sprint("cannot open %q: %r\n", wsys)); 431 # XXX this won't multiplex properly - srv9 should export attach files (actually that's what plan 9 should do) 432 if(sys->mount(fd, nil, d, Sys->MREPL, "new "+spec) == -1) 433 return (nil, sys->sprint("mount %q failed: %r", wsys)); 434 (ok, nil) := sys->stat(d + "/winname"); 435 if(ok == -1) 436 return (nil, "could not make window"); 437 w := ref zriowin; 438 w.tag = tag; 439 w.dir = d; 440 getwin(w); 441 w.ctlfd = sys->open(d + "/wctl", Sys->ORDWR); 442 setlabel(w, "inferno "+string sys->pctl(0, nil)+"."+tag); 443 sync := chan of int; 444 spawn ctlproc(w, c.riowctl, sync); 445 w.ctlpid = <-sync; 446 return (w, nil); 447} 448 449setlabel(w: ref Riowin, s: string) 450{ 451 fd := sys->open(w.dir + "/label", Sys->OWRITE); 452 if(fd != nil) 453 sys->fprint(fd, "%s", s); 454} 455 456ctlproc(w: ref Riowin, wctl: chan of (ref Riowin, int), sync: chan of int) 457{ 458 sync <-= sys->pctl(0, nil); 459 buf := array[1024] of byte; 460 for(;;){ 461 n := sys->read(w.ctlfd, buf, len buf); 462 if(n <= 0) 463 break; 464 if(n > 4*12){ 465 state := 0; 466 (nil, toks) := sys->tokenize(string buf[4*12:], " "); 467 if(hd toks == "current") 468 state |= Current; 469 if(hd tl toks == "hidden") 470 state |= Hidden; 471 wctl <-= (w, state); 472 } 473 } 474#sys->print("riowctl eof\n"); 475} 476 477delwin(w: ref Riowin) 478{ 479 sys->unmount(nil, w.dir); 480 kill(w.ptrpid, "kill"); 481 kill(w.kbdpid, "kill"); 482 kill(w.ctlpid, "kill"); 483} 484 485getwin(w: ref Riowin): int 486{ 487 s := readfile(w.dir + "/winname"); 488#sys->print("getwin %s\n", s); 489 i := display.namedimage(s); 490 if(i == nil) 491 return -1; 492 scr := Screen.allocate(i, display.white, 0); 493 if(scr == nil) 494 return -1; 495 wi := scr.newwindow(i.r.inset(Borderwidth), Draw->Refnone, Draw->Nofill); 496 if(wi == nil) 497 return -1; 498 w.img = wi; 499 return 0; 500} 501 502kbdproc(w: ref Riowin, keys: chan of int, sync: chan of int) 503{ 504 sys->pctl(Sys->NEWFD, nil); 505 cctl := sys->open(w.dir + "/consctl", Sys->OWRITE); 506 sys->fprint(cctl, "rawon"); 507 fd := sys->open(w.dir + "/cons", Sys->OREAD); 508 if(fd == nil){ 509 sync <-= -1; 510 return; 511 } 512 sync <-= sys->pctl(0, nil); 513 buf := array[12] of byte; 514 while((n := sys->read(fd, buf, len buf)) > 0){ 515 s := string buf[0:n]; 516 for(j := 0; j < len s; j++) 517 keys <-= int s[j]; 518 } 519#sys->print("eof on kbdproc\n"); 520} 521 522# fit a window rectangle to the available space. 523# try to preserve requested location if possible. 524# make sure that the window is no bigger than 525# the screen, and that its top and left-hand edges 526# will be visible at least. 527fitrect(w, r: Rect): Rect 528{ 529 if(w.dx() > r.dx()) 530 w.max.x = w.min.x + r.dx(); 531 if(w.dy() > r.dy()) 532 w.max.y = w.min.y + r.dy(); 533 size := w.size(); 534 if (w.max.x > r.max.x) 535 (w.min.x, w.max.x) = (r.min.x - size.x, r.max.x - size.x); 536 if (w.max.y > r.max.y) 537 (w.min.y, w.max.y) = (r.min.y - size.y, r.max.y - size.y); 538 if (w.min.x < r.min.x) 539 (w.min.x, w.max.x) = (r.min.x, r.min.x + size.x); 540 if (w.min.y < r.min.y) 541 (w.min.y, w.max.y) = (r.min.y, r.min.y + size.y); 542 return w; 543} 544 545ptrproc(w: ref Riowin, ptr: chan of ref Draw->Pointer, resize: chan of ref Riowin, sync: chan of int) 546{ 547 w.ptrfd = sys->open(w.dir + "/mouse", Sys->ORDWR); 548 if(w.ptrfd == nil){ 549 sync <-= -1; 550 return; 551 } 552 sync <-= sys->pctl(0, nil); 553 554 b:= array[Ptrsize] of byte; 555 while((n := sys->read(w.ptrfd, b, len b)) > 0){ 556 if(n > 0 && int b[0] == 'r'){ 557#sys->print("ptrproc got resize: %s\n", string b[0:n]); 558 resize <-= w; 559 }else{ 560 p := bytes2ptr(b); 561 if(p != nil) 562 ptr <-= p; 563 } 564 } 565#sys->print("eof on ptrproc\n"); 566} 567 568bytes2ptr(b: array of byte): ref Draw->Pointer 569{ 570 if(len b < Ptrsize || int b[0] != 'm') 571 return nil; 572 x := int string b[1:13]; 573 y := int string b[13:25]; 574 but := int string b[25:37]; 575 msec := int string b[37:49]; 576 return ref Draw->Pointer (but, (x, y), msec); 577} 578 579readfile(f: string): string 580{ 581 fd := sys->open(f, sys->OREAD); 582 if(fd == nil) 583 return nil; 584 585 buf := array[8192] of byte; 586 n := sys->read(fd, buf, len buf); 587 if(n < 0) 588 return nil; 589 590 return string buf[0:n]; 591} 592 593readscreenr() 594{ 595 fd := sys->open(P9PATH + "/dev/screen", Sys->OREAD); 596 if(fd == nil) 597 return ; 598 buf := array[5*12] of byte; 599 n := sys->read(fd, buf, len buf); 600 if(n <= len buf) 601 return; 602 screenr.min.x = int string buf[12:23]; 603 screenr.min.y = int string buf[24:35]; 604 screenr.max.x = int string buf[36:47]; 605 screenr.max.y = int string buf[48:]; 606} 607 608r2s(r: Rect): string 609{ 610 return string r.min.x + " " + string r.min.y + " " + 611 string r.max.x + " " + string r.max.y; 612} 613 614kill(pid: int, note: string): int 615{ 616 fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); 617 if(fd == nil || sys->fprint(fd, "%s", note) < 0) 618 return -1; 619 return 0; 620} 621