PageRenderTime 58ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/appl/cmd/sh/tk.b

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