1implement Shellbuiltin; 2 3include "sys.m"; 4 sys: Sys; 5include "draw.m"; 6include "tk.m"; 7 tk: Tk; 8include "tkclient.m"; 9 tkclient: Tkclient; 10include "sh.m"; 11 sh: Sh; 12 Listnode, Context: import sh; 13 myself: Shellbuiltin; 14 15tklock: chan of int; 16 17chans := array[23] of list of (string, chan of string); 18wins := array[16] of list of (int, ref Tk->Toplevel); 19winid := 0; 20 21badmodule(ctxt: ref Context, p: string) 22{ 23 ctxt.fail("bad module", sys->sprint("tk: cannot load %s: %r", p)); 24} 25 26initbuiltin(ctxt: ref Context, shmod: Sh): string 27{ 28 sys = load Sys Sys->PATH; 29 sh = shmod; 30 31 myself = load Shellbuiltin "$self"; 32 if (myself == nil) badmodule(ctxt, "self"); 33 34 tk = load Tk Tk->PATH; 35 if (tk == nil) badmodule(ctxt, Tk->PATH); 36 37 tkclient = load Tkclient Tkclient->PATH; 38 if (tkclient == nil) badmodule(ctxt, Tkclient->PATH); 39 tkclient->init(); 40 41 tklock = chan[1] of int; 42 43 ctxt.addbuiltin("tk", myself); 44 ctxt.addbuiltin("chan", myself); 45 ctxt.addbuiltin("send", myself); 46 47 ctxt.addsbuiltin("tk", myself); 48 ctxt.addsbuiltin("recv", myself); 49 ctxt.addsbuiltin("alt", myself); 50 ctxt.addsbuiltin("tkquote", myself); 51 return nil; 52} 53 54whatis(nil: ref Sh->Context, nil: Sh, nil: string, nil: int): string 55{ 56 return nil; 57} 58 59getself(): Shellbuiltin 60{ 61 return myself; 62} 63 64runbuiltin(ctxt: ref Context, nil: Sh, 65 cmd: list of ref Listnode, nil: int): string 66{ 67 case (hd cmd).word { 68 "tk" => return builtin_tk(ctxt, cmd); 69 "chan" => return builtin_chan(ctxt, cmd); 70 "send" => return builtin_send(ctxt, cmd); 71 } 72 return nil; 73} 74 75runsbuiltin(ctxt: ref Context, nil: Sh, 76 cmd: list of ref Listnode): list of ref Listnode 77{ 78 case (hd cmd).word { 79 "tk" => return sbuiltin_tk(ctxt, cmd); 80 "recv" => return sbuiltin_recv(ctxt, cmd); 81 "alt" => return sbuiltin_alt(ctxt, cmd); 82 "tkquote" => return sbuiltin_tkquote(ctxt, cmd); 83 } 84 return nil; 85} 86 87builtin_tk(ctxt: ref Context, argv: list of ref Listnode): string 88{ 89 # usage: tk window _title_ _options_ 90 # tk wintitle _winid_ _title_ 91 # tk _winid_ _cmd_ 92 if (tl argv == nil) 93 ctxt.fail("usage", "usage: tk (<winid>|window|onscreen|winctlwintitle|del|namechan) args..."); 94 argv = tl argv; 95 w := (hd argv).word; 96 case w { 97 "window" => 98 remark(ctxt, string makewin(ctxt, tl argv)); 99 "wintitle" => 100 argv = tl argv; 101 # change the title of a window 102 if (len argv != 2 || !isnum((hd argv).word)) 103 ctxt.fail("usage", "usage: tk wintitle winid title"); 104 tkclient->settitle(egetwin(ctxt, hd argv), word(hd tl argv)); 105 "winctl" => 106 argv = tl argv; 107 if (len argv != 2 || !isnum((hd argv).word)) 108 ctxt.fail("usage", "usage: tk winctl winid cmd"); 109 wid := (hd argv).word; 110 win := egetwin(ctxt, hd argv); 111 rq := word(hd tl argv); 112 if (rq == "exit") { 113 delwin(int wid); 114 delchan(wid); 115 } 116 tkclient->wmctl(win, rq); 117 "onscreen" => 118 argv = tl argv; 119 if (len argv < 1 || !isnum((hd argv).word)) 120 ctxt.fail("usage", "usage: tk onscreen winid [how]"); 121 how := ""; 122 if(tl argv != nil) 123 how = word(hd tl argv); 124 win := egetwin(ctxt, hd argv); 125 tkclient->startinput(win, "ptr" :: "kbd" :: nil); 126 tkclient->onscreen(win, how); 127 "namechan" => 128 argv = tl argv; 129 n := len argv; 130 if (n < 2 || n > 3 || !isnum((hd argv).word)) 131 ctxt.fail("usage", "usage: tk namechan winid chan [name]"); 132 name: string; 133 if (n == 3) 134 name = word(hd tl tl argv); 135 else 136 name = word(hd tl argv); 137 tk->namechan(egetwin(ctxt, hd argv), egetchan(ctxt, hd tl argv), name); 138 139 "del" => 140 if (len argv < 2) 141 ctxt.fail("usage", "usage: tk del id..."); 142 for (argv = tl argv; argv != nil; argv = tl argv) { 143 id := (hd argv).word; 144 if (isnum(id)) 145 delwin(int id); 146 delchan(id); 147 } 148 * => 149 e := tkcmd(ctxt, argv); 150 if (e != nil) 151 remark(ctxt, e); 152 if (e != nil && e[0] == '!') 153 return e; 154 } 155 return nil; 156} 157 158remark(ctxt: ref Context, s: string) 159{ 160 if (ctxt.options() & ctxt.INTERACTIVE) 161 sys->print("%s\n", s); 162} 163 164# create a new window (and its associated channel) 165makewin(ctxt: ref Context, argv: list of ref Listnode): int 166{ 167 if (argv == nil) 168 ctxt.fail("usage", "usage: tk window title options"); 169 170 if (ctxt.drawcontext == nil) 171 ctxt.fail("no draw context", sys->sprint("tk: no graphics context available")); 172 173 (title, options) := (word(hd argv), concat(tl argv)); 174 (top, topchan) := tkclient->toplevel(ctxt.drawcontext, options, title, Tkclient->Appl); 175 newid := addwin(top); 176 addchan(string newid, topchan); 177 return newid; 178} 179 180builtin_chan(ctxt: ref Context, argv: list of ref Listnode): string 181{ 182 # create a new channel 183 argv = tl argv; 184 if (argv == nil) 185 ctxt.fail("usage", "usage: chan name...."); 186 for (; argv != nil; argv = tl argv) { 187 name := (hd argv).word; 188 if (name == nil || isnum(name)) 189 ctxt.fail("bad chan", "tk: bad channel name "+q(name)); 190 if (addchan(name, chan of string) == nil) 191 ctxt.fail("bad chan", "tk: channel "+q(name)+" already exists"); 192 } 193 return nil; 194} 195 196builtin_send(ctxt: ref Context, argv: list of ref Listnode): string 197{ 198 if (len argv != 3) 199 ctxt.fail("usage", "usage: send chan arg"); 200 argv = tl argv; 201 c := egetchan(ctxt, hd argv); 202 c <-= word(hd tl argv); 203 return nil; 204} 205 206 207sbuiltin_tk(ctxt: ref Context, argv: list of ref Listnode): list of ref Listnode 208{ 209 # usage: tk _winid_ _command_ 210 # tk window _title_ _options_ 211 argv = tl argv; 212 if (argv == nil) 213 ctxt.fail("usage", "tk (window|wid) args"); 214 case (hd argv).word { 215 "window" => 216 return ref Listnode(nil, string makewin(ctxt, tl argv)) :: nil; 217 "winids" => 218 ret: list of ref Listnode; 219 for (i := 0; i < len wins; i++) 220 for (wl := wins[i]; wl != nil; wl = tl wl) 221 ret = ref Listnode(nil, string (hd wl).t0) :: ret; 222 return ret; 223 * => 224 return ref Listnode(nil, tkcmd(ctxt, argv)) :: nil; 225 } 226} 227 228sbuiltin_alt(ctxt: ref Context, argv: list of ref Listnode): list of ref Listnode 229{ 230 # usage: alt chan ... 231 argv = tl argv; 232 if (argv == nil) 233 ctxt.fail("usage", "usage: alt chan..."); 234 nc := len argv; 235 kbd := array[nc] of chan of int; 236 ptr := array[nc] of chan of ref Draw->Pointer; 237 ca := array[nc * 3] of chan of string; 238 win := array[nc] of ref Tk->Toplevel; 239 240 cname := array[nc] of string; 241 i := 0; 242 for (; argv != nil; argv = tl argv) { 243 w := (hd argv).word; 244 ca[i*3] = egetchan(ctxt, hd argv); 245 cname[i] = w; 246 if(isnum(w)){ 247 win[i] = egetwin(ctxt, hd argv); 248 ca[i*3+1] = win[i].ctxt.ctl; 249 ca[i*3+2] = win[i].wreq; 250 ptr[i] = win[i].ctxt.ptr; 251 kbd[i] = win[i].ctxt.kbd; 252 } 253 i++; 254 } 255 for(;;) alt{ 256 (n, key) := <-kbd => 257 tk->keyboard(win[n], key); 258 (n, p) := <-ptr => 259 tk->pointer(win[n], *p); 260 (n, v) := <-ca => 261 return ref Listnode(nil, cname[n/3]) :: ref Listnode(nil, v) :: nil; 262 } 263} 264 265sbuiltin_recv(ctxt: ref Context, argv: list of ref Listnode): list of ref Listnode 266{ 267 # usage: recv chan 268 if (len argv != 2) 269 ctxt.fail("usage", "usage: recv chan"); 270 ch := hd tl argv; 271 c := egetchan(ctxt, ch); 272 if(!isnum(ch.word)) 273 return ref Listnode(nil, <-c) :: nil; 274 275 win := egetwin(ctxt, ch); 276 for(;;)alt{ 277 key := <-win.ctxt.kbd => 278 tk->keyboard(win, key); 279 p := <-win.ctxt.ptr => 280 tk->pointer(win, *p); 281 s := <-win.ctxt.ctl or 282 s = <-win.wreq or 283 s = <-c => 284 return ref Listnode(nil, s) :: nil; 285 } 286} 287 288sbuiltin_tkquote(ctxt: ref Context, argv: list of ref Listnode): list of ref Listnode 289{ 290 if (len argv != 2) 291 ctxt.fail("usage", "usage: tkquote arg"); 292 return ref Listnode(nil, tk->quote(word(hd tl argv))) :: nil; 293} 294 295tkcmd(ctxt: ref Context, argv: list of ref Listnode): string 296{ 297 if (argv == nil || !isnum((hd argv).word)) 298 ctxt.fail("usage", "usage: tk winid command"); 299 300 return tk->cmd(egetwin(ctxt, hd argv), concat(tl argv)); 301} 302 303hashfn(s: string, n: int): int 304{ 305 h := 0; 306 m := len s; 307 for(i:=0; i<m; i++){ 308 h = 65599*h+s[i]; 309 } 310 return (h & 16r7fffffff) % n; 311} 312 313q(s: string): string 314{ 315 return "'" + s + "'"; 316} 317 318egetchan(ctxt: ref Context, n: ref Listnode): chan of string 319{ 320 if ((c := getchan(n.word)) == nil) 321 ctxt.fail("bad chan", "tk: bad channel name "+ q(n.word)); 322 return c; 323} 324 325# assumes that n.word has been checked and found to be numeric. 326egetwin(ctxt: ref Context, n: ref Listnode): ref Tk->Toplevel 327{ 328 wid := int n.word; 329 if (wid < 0 || (top := getwin(wid)) == nil) 330 ctxt.fail("bad win", "tk: unknown window id " + q(n.word)); 331 return top; 332} 333 334getchan(name: string): chan of string 335{ 336 n := hashfn(name, len chans); 337 for (cl := chans[n]; cl != nil; cl = tl cl) { 338 (cname, c) := hd cl; 339 if (cname == name) 340 return c; 341 } 342 return nil; 343} 344 345addchan(name: string, c: chan of string): chan of string 346{ 347 n := hashfn(name, len chans); 348 tklock <-= 1; 349 if (getchan(name) == nil) 350 chans[n] = (name, c) :: chans[n]; 351 <-tklock; 352 return c; 353} 354 355delchan(name: string) 356{ 357 n := hashfn(name, len chans); 358 tklock <-= 1; 359 ncl: list of (string, chan of string); 360 for (cl := chans[n]; cl != nil; cl = tl cl) { 361 (cname, nil) := hd cl; 362 if (cname != name) 363 ncl = hd cl :: ncl; 364 } 365 chans[n] = ncl; 366 <-tklock; 367} 368 369addwin(top: ref Tk->Toplevel): int 370{ 371 tklock <-= 1; 372 id := winid++; 373 slot := id % len wins; 374 wins[slot] = (id, top) :: wins[slot]; 375 <-tklock; 376 return id; 377} 378 379delwin(id: int) 380{ 381 tklock <-= 1; 382 slot := id % len wins; 383 nwl: list of (int, ref Tk->Toplevel); 384 for (wl := wins[slot]; wl != nil; wl = tl wl) { 385 (wid, nil) := hd wl; 386 if (wid != id) 387 nwl = hd wl :: nwl; 388 } 389 wins[slot] = nwl; 390 <-tklock; 391} 392 393getwin(id: int): ref Tk->Toplevel 394{ 395 slot := id % len wins; 396 for (wl := wins[slot]; wl != nil; wl = tl wl) { 397 (wid, top) := hd wl; 398 if (wid == id) 399 return top; 400 } 401 return nil; 402} 403 404word(n: ref Listnode): string 405{ 406 if (n.word != nil) 407 return n.word; 408 if (n.cmd != nil) 409 n.word = sh->cmd2string(n.cmd); 410 return n.word; 411} 412 413isnum(s: string): int 414{ 415 for (i := 0; i < len s; i++) 416 if (s[i] > '9' || s[i] < '0') 417 return 0; 418 return 1; 419} 420 421concat(argv: list of ref Listnode): string 422{ 423 if (argv == nil) 424 return nil; 425 s := word(hd argv); 426 for (argv = tl argv; argv != nil; argv = tl argv) 427 s += " " + word(hd argv); 428 return s; 429} 430 431lockproc(c: chan of int) 432{ 433 sys->pctl(Sys->NEWFD|Sys->NEWNS, nil); 434 for(;;){ 435 c <-= 1; 436 <-c; 437 } 438} 439