1implement Ninewin; 2include "sys.m"; 3 sys: Sys; 4include "draw.m"; 5 draw: Draw; 6 Image, Display, Pointer: import draw; 7include "arg.m"; 8include "keyboard.m"; 9include "tk.m"; 10include "wmclient.m"; 11 wmclient: Wmclient; 12 Window: import wmclient; 13include "sh.m"; 14 sh: Sh; 15 16# run a p9 graphics program (default rio) under inferno wm, 17# making available to it: 18# /dev/winname - naming the current inferno window (changing on resize) 19# /dev/mouse - pointer file + resize events; write to change position 20# /dev/cursor - change appearance of cursor. 21# /dev/draw - inferno draw device 22# /dev/cons - read keyboard events, write to 9win stdout. 23 24Ninewin: module { 25 init: fn(ctxt: ref Draw->Context, argv: list of string); 26}; 27winname: string; 28 29init(ctxt: ref Draw->Context, argv: list of string) 30{ 31 size := Draw->Point(500, 500); 32 sys = load Sys Sys->PATH; 33 draw = load Draw Draw->PATH; 34 wmclient = load Wmclient Wmclient->PATH; 35 wmclient->init(); 36 sh = load Sh Sh->PATH; 37 38 buts := Wmclient->Resize; 39 if(ctxt == nil){ 40 ctxt = wmclient->makedrawcontext(); 41 buts = Wmclient->Plain; 42 } 43 arg := load Arg Arg->PATH; 44 arg->init(argv); 45 arg->setusage("9win [-s] [-x width] [-y height]"); 46 exportonly := 0; 47 while(((opt := arg->opt())) != 0){ 48 case opt { 49 's' => 50 exportonly = 1; 51 'x' => 52 size.x = int arg->earg(); 53 'y' => 54 size.y = int arg->earg(); 55 * => 56 arg->usage(); 57 } 58 } 59 if(size.x < 1 || size.y < 1) 60 arg->usage(); 61 argv = arg->argv(); 62 if(argv != nil && hd argv == "-s"){ 63 exportonly = 1; 64 argv = tl argv; 65 } 66 if(argv == nil && !exportonly) 67 argv = "rio" :: nil; 68 if(argv != nil && exportonly){ 69 sys->fprint(sys->fildes(2), "9win: no command allowed with -s flag\n"); 70 raise "fail:usage"; 71 } 72 title := "9win"; 73 if(!exportonly) 74 title += " " + hd argv; 75 w := wmclient->window(ctxt, title, buts); 76 w.reshape(((0, 0), size)); 77 w.onscreen(nil); 78 if(w.image == nil){ 79 sys->fprint(sys->fildes(2), "9win: cannot get image to draw on\n"); 80 raise "fail:no window"; 81 } 82 83 sys->pctl(Sys->FORKNS|Sys->NEWPGRP, nil); 84 ld := "/n/9win"; 85 if(sys->bind("#s", ld, Sys->MREPL) == -1 && 86 sys->bind("#s", ld = "/n/local", Sys->MREPL) == -1){ 87 sys->fprint(sys->fildes(2), "9win: cannot bind files: %r\n"); 88 raise "fail:error"; 89 } 90 w.startinput("kbd" :: "ptr" :: nil); 91 spawn ptrproc(rq := chan of Sys->Rread, ptr := chan[10] of ref Pointer, reshape := chan[1] of int); 92 93 94 fwinname := sys->file2chan(ld, "winname"); 95 fconsctl := sys->file2chan(ld, "consctl"); 96 fcons := sys->file2chan(ld, "cons"); 97 fmouse := sys->file2chan(ld, "mouse"); 98 fcursor := sys->file2chan(ld, "cursor"); 99 if(!exportonly){ 100 spawn run(sync := chan of string, w.ctl, ld, argv); 101 if((e := <-sync) != nil){ 102 sys->fprint(sys->fildes(2), "9win: %s", e); 103 raise "fail:error"; 104 } 105 } 106 spawn serveproc(w, rq, fwinname, fconsctl, fcons, fmouse, fcursor); 107 if(!exportonly){ 108 # handle events synchronously so that we don't get a "killed" message 109 # from the shell. 110 handleevents(w, ptr, reshape); 111 }else{ 112 spawn handleevents(w, ptr, reshape); 113 sys->bind(ld, "/dev", Sys->MBEFORE); 114 export(sys->fildes(0), w.ctl); 115 } 116} 117 118handleevents(w: ref Window, ptr: chan of ref Pointer, reshape: chan of int) 119{ 120 for(;;)alt{ 121 c := <-w.ctxt.ctl or 122 c = <-w.ctl => 123 e := w.wmctl(c); 124 if(e != nil) 125 sys->fprint(sys->fildes(2), "9win: ctl error: %s\n", e); 126 if(e == nil && c != nil && c[0] == '!'){ 127 alt{ 128 reshape <-= 1 => 129 ; 130 * => 131 ; 132 } 133 winname = nil; 134 } 135 p := <-w.ctxt.ptr => 136 if(w.pointer(*p) == 0){ 137 # XXX would block here if client isn't reading mouse... but we do want to 138 # extert back-pressure, which conflicts. 139 alt{ 140 ptr <-= p => 141 ; 142 * => 143 ; # sys->fprint(sys->fildes(2), "9win: discarding mouse event\n"); 144 } 145 } 146 } 147} 148 149serveproc(w: ref Window, mouserq: chan of Sys->Rread, fwinname, fconsctl, fcons, fmouse, fcursor: ref Sys->FileIO) 150{ 151 winid := 0; 152 krc: list of Sys->Rread; 153 ks: string; 154 155 for(;;)alt { 156 c := <-w.ctxt.kbd => 157 ks[len ks] = inf2p9key(c); 158 if(krc != nil){ 159 hd krc <-= (array of byte ks, nil); 160 ks = nil; 161 krc = tl krc; 162 } 163 (nil, d, nil, wc) := <-fcons.write => 164 if(wc != nil){ 165 sys->write(sys->fildes(1), d, len d); 166 wc <-= (len d, nil); 167 } 168 (nil, nil, nil, rc) := <-fcons.read => 169 if(rc != nil){ 170 if(ks != nil){ 171 rc <-= (array of byte ks, nil); 172 ks = nil; 173 }else 174 krc = rc :: krc; 175 } 176 (offset, nil, nil, rc) := <-fwinname.read => 177 if(rc != nil){ 178 if(winname == nil){ 179 winname = sys->sprint("noborder.9win.%d", winid++); 180 if(w.image.name(winname, 1) == -1){ 181 sys->fprint(sys->fildes(2), "9win: namewin %q failed: %r", winname); 182 rc <-= (nil, "namewin failure"); 183 break; 184 } 185 } 186 d := array of byte winname; 187 if(offset < len d) 188 d = d[offset:]; 189 else 190 d = nil; 191 rc <-= (d, nil); 192 } 193 (nil, nil, nil, wc) := <-fwinname.write => 194 if(wc != nil) 195 wc <-= (-1, "permission denied"); 196 (nil, nil, nil, rc) := <-fconsctl.read => 197 if(rc != nil) 198 rc <-= (nil, "permission denied"); 199 (nil, d, nil, wc) := <-fconsctl.write => 200 if(wc != nil){ 201 if(string d != "rawon") 202 wc <-= (-1, "cannot change console mode"); 203 else 204 wc <-= (len d, nil); 205 } 206 (nil, nil, nil, rc) := <-fmouse.read => 207 if(rc != nil) 208 mouserq <-= rc; 209 (nil, d, nil, wc) := <-fmouse.write => 210 if(wc != nil){ 211 e := cursorset(w, string d); 212 if(e == nil) 213 wc <-= (len d, nil); 214 else 215 wc <-= (-1, e); 216 } 217 (nil, nil, nil, rc) := <-fcursor.read => 218 if(rc != nil) 219 rc <-= (nil, "permission denied"); 220 (nil, d, nil, wc) := <-fcursor.write => 221 if(wc != nil){ 222 e := cursorswitch(w, d); 223 if(e == nil) 224 wc <-= (len d, nil); 225 else 226 wc <-= (-1, e); 227 } 228 } 229} 230 231ptrproc(rq: chan of Sys->Rread, ptr: chan of ref Pointer, reshape: chan of int) 232{ 233 rl: list of Sys->Rread; 234 c := ref Pointer(0, (0, 0), 0); 235 for(;;){ 236 ch: int; 237 alt{ 238 p := <-ptr => 239 ch = 'm'; 240 c = p; 241 <-reshape => 242 ch = 'r'; 243 rc := <-rq => 244 rl = rc :: rl; 245 continue; 246 } 247 if(rl == nil) 248 rl = <-rq :: rl; 249 hd rl <-= (sys->aprint("%c%11d %11d %11d %11d ", ch, c.xy.x, c.xy.y, c.buttons, c.msec), nil); 250 rl = tl rl; 251 } 252} 253 254cursorset(w: ref Window, m: string): string 255{ 256 if(m == nil || m[0] != 'm') 257 return "invalid mouse message"; 258 x := int m[1:]; 259 for(i := 1; i < len m; i++) 260 if(m[i] == ' '){ 261 while(m[i] == ' ') 262 i++; 263 break; 264 } 265 if(i == len m) 266 return "invalid mouse message"; 267 y := int m[i:]; 268 return w.wmctl(sys->sprint("ptr %d %d", x, y)); 269} 270 271cursorswitch(w: ref Window, d: array of byte): string 272{ 273 Hex: con "0123456789abcdef"; 274 if(len d != 2*4+64) 275 return w.wmctl("cursor"); 276 hot := Draw->Point(bglong(d, 0*4), bglong(d, 1*4)); 277 s := sys->sprint("cursor %d %d 16 32 ", hot.x, hot.y); 278 for(i := 2*4; i < len d; i++){ 279 c := int d[i]; 280 s[len s] = Hex[c >> 4]; 281 s[len s] = Hex[c & 16rf]; 282 } 283 return w.wmctl(s); 284} 285 286run(sync, ctl: chan of string, ld: string, argv: list of string) 287{ 288 Rcmeta: con "|<>&^*[]?();"; 289 sys->pctl(Sys->FORKNS, nil); 290 if(sys->bind("#₪", "/srv", Sys->MCREATE) == -1){ 291 sync <-= sys->sprint("cannot bind srv device: %r"); 292 exit; 293 } 294 srvname := "/srv/9win."+string sys->pctl(0, nil); # XXX do better. 295 fd := sys->create(srvname, Sys->ORDWR, 8r600); 296 if(fd == nil){ 297 sync <-= sys->sprint("cannot create %s: %r", srvname); 298 exit; 299 } 300 sync <-= nil; 301 spawn export(fd, ctl); 302 sh->run(nil, "os" :: 303 "rc" :: "-c" :: 304 "mount "+srvname+" /mnt/term;"+ 305 "rm "+srvname+";"+ 306 "bind -b /mnt/term"+ld+" /dev;"+ 307 "bind /mnt/term/dev/draw /dev/draw ||"+ 308 "bind -a /mnt/term/dev /dev;"+ 309 quotedc("cd"::"/mnt/term"+cwd()::nil, Rcmeta)+";"+ 310 quotedc(argv, Rcmeta)+";":: 311 nil 312 ); 313} 314 315export(fd: ref Sys->FD, ctl: chan of string) 316{ 317 sys->export(fd, "/", Sys->EXPWAIT); 318 ctl <-= "exit"; 319} 320 321inf2p9key(c: int): int 322{ 323 KF: import Keyboard; 324 325 P9KF: con 16rF000; 326 Spec: con 16rF800; 327 Khome: con P9KF|16r0D; 328 Kup: con P9KF|16r0E; 329 Kpgup: con P9KF|16r0F; 330 Kprint: con P9KF|16r10; 331 Kleft: con P9KF|16r11; 332 Kright: con P9KF|16r12; 333 Kdown: con Spec|16r00; 334 Kview: con Spec|16r00; 335 Kpgdown: con P9KF|16r13; 336 Kins: con P9KF|16r14; 337 Kend: con P9KF|16r18; 338 Kalt: con P9KF|16r15; 339 Kshift: con P9KF|16r16; 340 Kctl: con P9KF|16r17; 341 342 case c { 343 Keyboard->LShift => 344 return Kshift; 345 Keyboard->LCtrl => 346 return Kctl; 347 Keyboard->LAlt => 348 return Kalt; 349 Keyboard->Home => 350 return Khome; 351 Keyboard->End => 352 return Kend; 353 Keyboard->Up => 354 return Kup; 355 Keyboard->Down => 356 return Kdown; 357 Keyboard->Left => 358 return Kleft; 359 Keyboard->Right => 360 return Kright; 361 Keyboard->Pgup => 362 return Kpgup; 363 Keyboard->Pgdown => 364 return Kpgdown; 365 Keyboard->Ins => 366 return Kins; 367 368 # function keys 369 KF|1 or 370 KF|2 or 371 KF|3 or 372 KF|4 or 373 KF|5 or 374 KF|6 or 375 KF|7 or 376 KF|8 or 377 KF|9 or 378 KF|10 or 379 KF|11 or 380 KF|12 => 381 return (c - KF) + P9KF; 382 } 383 return c; 384} 385 386cwd(): string 387{ 388 return sys->fd2path(sys->open(".", Sys->OREAD)); 389} 390 391# from string.b, waiting for declaration to be uncommented. 392quotedc(argv: list of string, cl: string): string 393{ 394 s := ""; 395 while (argv != nil) { 396 arg := hd argv; 397 for (i := 0; i < len arg; i++) { 398 c := arg[i]; 399 if (c == ' ' || c == '\t' || c == '\n' || c == '\'' || in(c, cl)) 400 break; 401 } 402 if (i < len arg || arg == nil) { 403 s += "'" + arg[0:i]; 404 for (; i < len arg; i++) { 405 if (arg[i] == '\'') 406 s[len s] = '\''; 407 s[len s] = arg[i]; 408 } 409 s[len s] = '\''; 410 } else 411 s += arg; 412 if (tl argv != nil) 413 s[len s] = ' '; 414 argv = tl argv; 415 } 416 return s; 417} 418 419in(c: int, s: string): int 420{ 421 n := len s; 422 if(n == 0) 423 return 0; 424 ans := 0; 425 negate := 0; 426 if(s[0] == '^') { 427 negate = 1; 428 s = s[1:]; 429 n--; 430 } 431 for(i := 0; i < n; i++) { 432 if(s[i] == '-' && i > 0 && i < n-1) { 433 if(c >= s[i-1] && c <= s[i+1]) { 434 ans = 1; 435 break; 436 } 437 i++; 438 } 439 else 440 if(c == s[i]) { 441 ans = 1; 442 break; 443 } 444 } 445 if(negate) 446 ans = !ans; 447 448 # just to showcase labels 449skip: 450 return ans; 451} 452 453bglong(d: array of byte, i: int): int 454{ 455 return int d[i] | (int d[i+1]<<8) | (int d[i+2]<<16) | (int d[i+3]<<24); 456} 457