
After go1.16, go will use module mode by default, even when the repository is checked out under GOPATH or in a one-off directory. Add go.mod, go.sum to keep this repo buildable without opting out of the module mode. > go mod init github.com/mmcgrana/gobyexample > go mod tidy > go mod vendor In module mode, the 'vendor' directory is special and its contents will be actively maintained by the go command. pygments aren't the dependency the go will know about, so it will delete the contents from vendor directory. Move it to `third_party` directory now. And, vendor the blackfriday package. Note: the tutorial contents are not affected by the change in go1.16 because all the examples in this tutorial ask users to run the go command with the explicit list of files to be compiled (e.g. `go run hello-world.go` or `go build command-line-arguments.go`). When the source list is provided, the go command does not have to compute the build list and whether it's running in GOPATH mode or module mode becomes irrelevant.
457 lines
9.7 KiB
Brainfuck
457 lines
9.7 KiB
Brainfuck
implement Ninewin;
|
|
include "sys.m";
|
|
sys: Sys;
|
|
include "draw.m";
|
|
draw: Draw;
|
|
Image, Display, Pointer: import draw;
|
|
include "arg.m";
|
|
include "keyboard.m";
|
|
include "tk.m";
|
|
include "wmclient.m";
|
|
wmclient: Wmclient;
|
|
Window: import wmclient;
|
|
include "sh.m";
|
|
sh: Sh;
|
|
|
|
# run a p9 graphics program (default rio) under inferno wm,
|
|
# making available to it:
|
|
# /dev/winname - naming the current inferno window (changing on resize)
|
|
# /dev/mouse - pointer file + resize events; write to change position
|
|
# /dev/cursor - change appearance of cursor.
|
|
# /dev/draw - inferno draw device
|
|
# /dev/cons - read keyboard events, write to 9win stdout.
|
|
|
|
Ninewin: module {
|
|
init: fn(ctxt: ref Draw->Context, argv: list of string);
|
|
};
|
|
winname: string;
|
|
|
|
init(ctxt: ref Draw->Context, argv: list of string)
|
|
{
|
|
size := Draw->Point(500, 500);
|
|
sys = load Sys Sys->PATH;
|
|
draw = load Draw Draw->PATH;
|
|
wmclient = load Wmclient Wmclient->PATH;
|
|
wmclient->init();
|
|
sh = load Sh Sh->PATH;
|
|
|
|
buts := Wmclient->Resize;
|
|
if(ctxt == nil){
|
|
ctxt = wmclient->makedrawcontext();
|
|
buts = Wmclient->Plain;
|
|
}
|
|
arg := load Arg Arg->PATH;
|
|
arg->init(argv);
|
|
arg->setusage("9win [-s] [-x width] [-y height]");
|
|
exportonly := 0;
|
|
while(((opt := arg->opt())) != 0){
|
|
case opt {
|
|
's' =>
|
|
exportonly = 1;
|
|
'x' =>
|
|
size.x = int arg->earg();
|
|
'y' =>
|
|
size.y = int arg->earg();
|
|
* =>
|
|
arg->usage();
|
|
}
|
|
}
|
|
if(size.x < 1 || size.y < 1)
|
|
arg->usage();
|
|
argv = arg->argv();
|
|
if(argv != nil && hd argv == "-s"){
|
|
exportonly = 1;
|
|
argv = tl argv;
|
|
}
|
|
if(argv == nil && !exportonly)
|
|
argv = "rio" :: nil;
|
|
if(argv != nil && exportonly){
|
|
sys->fprint(sys->fildes(2), "9win: no command allowed with -s flag\n");
|
|
raise "fail:usage";
|
|
}
|
|
title := "9win";
|
|
if(!exportonly)
|
|
title += " " + hd argv;
|
|
w := wmclient->window(ctxt, title, buts);
|
|
w.reshape(((0, 0), size));
|
|
w.onscreen(nil);
|
|
if(w.image == nil){
|
|
sys->fprint(sys->fildes(2), "9win: cannot get image to draw on\n");
|
|
raise "fail:no window";
|
|
}
|
|
|
|
sys->pctl(Sys->FORKNS|Sys->NEWPGRP, nil);
|
|
ld := "/n/9win";
|
|
if(sys->bind("#s", ld, Sys->MREPL) == -1 &&
|
|
sys->bind("#s", ld = "/n/local", Sys->MREPL) == -1){
|
|
sys->fprint(sys->fildes(2), "9win: cannot bind files: %r\n");
|
|
raise "fail:error";
|
|
}
|
|
w.startinput("kbd" :: "ptr" :: nil);
|
|
spawn ptrproc(rq := chan of Sys->Rread, ptr := chan[10] of ref Pointer, reshape := chan[1] of int);
|
|
|
|
|
|
fwinname := sys->file2chan(ld, "winname");
|
|
fconsctl := sys->file2chan(ld, "consctl");
|
|
fcons := sys->file2chan(ld, "cons");
|
|
fmouse := sys->file2chan(ld, "mouse");
|
|
fcursor := sys->file2chan(ld, "cursor");
|
|
if(!exportonly){
|
|
spawn run(sync := chan of string, w.ctl, ld, argv);
|
|
if((e := <-sync) != nil){
|
|
sys->fprint(sys->fildes(2), "9win: %s", e);
|
|
raise "fail:error";
|
|
}
|
|
}
|
|
spawn serveproc(w, rq, fwinname, fconsctl, fcons, fmouse, fcursor);
|
|
if(!exportonly){
|
|
# handle events synchronously so that we don't get a "killed" message
|
|
# from the shell.
|
|
handleevents(w, ptr, reshape);
|
|
}else{
|
|
spawn handleevents(w, ptr, reshape);
|
|
sys->bind(ld, "/dev", Sys->MBEFORE);
|
|
export(sys->fildes(0), w.ctl);
|
|
}
|
|
}
|
|
|
|
handleevents(w: ref Window, ptr: chan of ref Pointer, reshape: chan of int)
|
|
{
|
|
for(;;)alt{
|
|
c := <-w.ctxt.ctl or
|
|
c = <-w.ctl =>
|
|
e := w.wmctl(c);
|
|
if(e != nil)
|
|
sys->fprint(sys->fildes(2), "9win: ctl error: %s\n", e);
|
|
if(e == nil && c != nil && c[0] == '!'){
|
|
alt{
|
|
reshape <-= 1 =>
|
|
;
|
|
* =>
|
|
;
|
|
}
|
|
winname = nil;
|
|
}
|
|
p := <-w.ctxt.ptr =>
|
|
if(w.pointer(*p) == 0){
|
|
# XXX would block here if client isn't reading mouse... but we do want to
|
|
# extert back-pressure, which conflicts.
|
|
alt{
|
|
ptr <-= p =>
|
|
;
|
|
* =>
|
|
; # sys->fprint(sys->fildes(2), "9win: discarding mouse event\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
serveproc(w: ref Window, mouserq: chan of Sys->Rread, fwinname, fconsctl, fcons, fmouse, fcursor: ref Sys->FileIO)
|
|
{
|
|
winid := 0;
|
|
krc: list of Sys->Rread;
|
|
ks: string;
|
|
|
|
for(;;)alt {
|
|
c := <-w.ctxt.kbd =>
|
|
ks[len ks] = inf2p9key(c);
|
|
if(krc != nil){
|
|
hd krc <-= (array of byte ks, nil);
|
|
ks = nil;
|
|
krc = tl krc;
|
|
}
|
|
(nil, d, nil, wc) := <-fcons.write =>
|
|
if(wc != nil){
|
|
sys->write(sys->fildes(1), d, len d);
|
|
wc <-= (len d, nil);
|
|
}
|
|
(nil, nil, nil, rc) := <-fcons.read =>
|
|
if(rc != nil){
|
|
if(ks != nil){
|
|
rc <-= (array of byte ks, nil);
|
|
ks = nil;
|
|
}else
|
|
krc = rc :: krc;
|
|
}
|
|
(offset, nil, nil, rc) := <-fwinname.read =>
|
|
if(rc != nil){
|
|
if(winname == nil){
|
|
winname = sys->sprint("noborder.9win.%d", winid++);
|
|
if(w.image.name(winname, 1) == -1){
|
|
sys->fprint(sys->fildes(2), "9win: namewin %q failed: %r", winname);
|
|
rc <-= (nil, "namewin failure");
|
|
break;
|
|
}
|
|
}
|
|
d := array of byte winname;
|
|
if(offset < len d)
|
|
d = d[offset:];
|
|
else
|
|
d = nil;
|
|
rc <-= (d, nil);
|
|
}
|
|
(nil, nil, nil, wc) := <-fwinname.write =>
|
|
if(wc != nil)
|
|
wc <-= (-1, "permission denied");
|
|
(nil, nil, nil, rc) := <-fconsctl.read =>
|
|
if(rc != nil)
|
|
rc <-= (nil, "permission denied");
|
|
(nil, d, nil, wc) := <-fconsctl.write =>
|
|
if(wc != nil){
|
|
if(string d != "rawon")
|
|
wc <-= (-1, "cannot change console mode");
|
|
else
|
|
wc <-= (len d, nil);
|
|
}
|
|
(nil, nil, nil, rc) := <-fmouse.read =>
|
|
if(rc != nil)
|
|
mouserq <-= rc;
|
|
(nil, d, nil, wc) := <-fmouse.write =>
|
|
if(wc != nil){
|
|
e := cursorset(w, string d);
|
|
if(e == nil)
|
|
wc <-= (len d, nil);
|
|
else
|
|
wc <-= (-1, e);
|
|
}
|
|
(nil, nil, nil, rc) := <-fcursor.read =>
|
|
if(rc != nil)
|
|
rc <-= (nil, "permission denied");
|
|
(nil, d, nil, wc) := <-fcursor.write =>
|
|
if(wc != nil){
|
|
e := cursorswitch(w, d);
|
|
if(e == nil)
|
|
wc <-= (len d, nil);
|
|
else
|
|
wc <-= (-1, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
ptrproc(rq: chan of Sys->Rread, ptr: chan of ref Pointer, reshape: chan of int)
|
|
{
|
|
rl: list of Sys->Rread;
|
|
c := ref Pointer(0, (0, 0), 0);
|
|
for(;;){
|
|
ch: int;
|
|
alt{
|
|
p := <-ptr =>
|
|
ch = 'm';
|
|
c = p;
|
|
<-reshape =>
|
|
ch = 'r';
|
|
rc := <-rq =>
|
|
rl = rc :: rl;
|
|
continue;
|
|
}
|
|
if(rl == nil)
|
|
rl = <-rq :: rl;
|
|
hd rl <-= (sys->aprint("%c%11d %11d %11d %11d ", ch, c.xy.x, c.xy.y, c.buttons, c.msec), nil);
|
|
rl = tl rl;
|
|
}
|
|
}
|
|
|
|
cursorset(w: ref Window, m: string): string
|
|
{
|
|
if(m == nil || m[0] != 'm')
|
|
return "invalid mouse message";
|
|
x := int m[1:];
|
|
for(i := 1; i < len m; i++)
|
|
if(m[i] == ' '){
|
|
while(m[i] == ' ')
|
|
i++;
|
|
break;
|
|
}
|
|
if(i == len m)
|
|
return "invalid mouse message";
|
|
y := int m[i:];
|
|
return w.wmctl(sys->sprint("ptr %d %d", x, y));
|
|
}
|
|
|
|
cursorswitch(w: ref Window, d: array of byte): string
|
|
{
|
|
Hex: con "0123456789abcdef";
|
|
if(len d != 2*4+64)
|
|
return w.wmctl("cursor");
|
|
hot := Draw->Point(bglong(d, 0*4), bglong(d, 1*4));
|
|
s := sys->sprint("cursor %d %d 16 32 ", hot.x, hot.y);
|
|
for(i := 2*4; i < len d; i++){
|
|
c := int d[i];
|
|
s[len s] = Hex[c >> 4];
|
|
s[len s] = Hex[c & 16rf];
|
|
}
|
|
return w.wmctl(s);
|
|
}
|
|
|
|
run(sync, ctl: chan of string, ld: string, argv: list of string)
|
|
{
|
|
Rcmeta: con "|<>&^*[]?();";
|
|
sys->pctl(Sys->FORKNS, nil);
|
|
if(sys->bind("#₪", "/srv", Sys->MCREATE) == -1){
|
|
sync <-= sys->sprint("cannot bind srv device: %r");
|
|
exit;
|
|
}
|
|
srvname := "/srv/9win."+string sys->pctl(0, nil); # XXX do better.
|
|
fd := sys->create(srvname, Sys->ORDWR, 8r600);
|
|
if(fd == nil){
|
|
sync <-= sys->sprint("cannot create %s: %r", srvname);
|
|
exit;
|
|
}
|
|
sync <-= nil;
|
|
spawn export(fd, ctl);
|
|
sh->run(nil, "os" ::
|
|
"rc" :: "-c" ::
|
|
"mount "+srvname+" /mnt/term;"+
|
|
"rm "+srvname+";"+
|
|
"bind -b /mnt/term"+ld+" /dev;"+
|
|
"bind /mnt/term/dev/draw /dev/draw ||"+
|
|
"bind -a /mnt/term/dev /dev;"+
|
|
quotedc("cd"::"/mnt/term"+cwd()::nil, Rcmeta)+";"+
|
|
quotedc(argv, Rcmeta)+";"::
|
|
nil
|
|
);
|
|
}
|
|
|
|
export(fd: ref Sys->FD, ctl: chan of string)
|
|
{
|
|
sys->export(fd, "/", Sys->EXPWAIT);
|
|
ctl <-= "exit";
|
|
}
|
|
|
|
inf2p9key(c: int): int
|
|
{
|
|
KF: import Keyboard;
|
|
|
|
P9KF: con 16rF000;
|
|
Spec: con 16rF800;
|
|
Khome: con P9KF|16r0D;
|
|
Kup: con P9KF|16r0E;
|
|
Kpgup: con P9KF|16r0F;
|
|
Kprint: con P9KF|16r10;
|
|
Kleft: con P9KF|16r11;
|
|
Kright: con P9KF|16r12;
|
|
Kdown: con Spec|16r00;
|
|
Kview: con Spec|16r00;
|
|
Kpgdown: con P9KF|16r13;
|
|
Kins: con P9KF|16r14;
|
|
Kend: con P9KF|16r18;
|
|
Kalt: con P9KF|16r15;
|
|
Kshift: con P9KF|16r16;
|
|
Kctl: con P9KF|16r17;
|
|
|
|
case c {
|
|
Keyboard->LShift =>
|
|
return Kshift;
|
|
Keyboard->LCtrl =>
|
|
return Kctl;
|
|
Keyboard->LAlt =>
|
|
return Kalt;
|
|
Keyboard->Home =>
|
|
return Khome;
|
|
Keyboard->End =>
|
|
return Kend;
|
|
Keyboard->Up =>
|
|
return Kup;
|
|
Keyboard->Down =>
|
|
return Kdown;
|
|
Keyboard->Left =>
|
|
return Kleft;
|
|
Keyboard->Right =>
|
|
return Kright;
|
|
Keyboard->Pgup =>
|
|
return Kpgup;
|
|
Keyboard->Pgdown =>
|
|
return Kpgdown;
|
|
Keyboard->Ins =>
|
|
return Kins;
|
|
|
|
# function keys
|
|
KF|1 or
|
|
KF|2 or
|
|
KF|3 or
|
|
KF|4 or
|
|
KF|5 or
|
|
KF|6 or
|
|
KF|7 or
|
|
KF|8 or
|
|
KF|9 or
|
|
KF|10 or
|
|
KF|11 or
|
|
KF|12 =>
|
|
return (c - KF) + P9KF;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
cwd(): string
|
|
{
|
|
return sys->fd2path(sys->open(".", Sys->OREAD));
|
|
}
|
|
|
|
# from string.b, waiting for declaration to be uncommented.
|
|
quotedc(argv: list of string, cl: string): string
|
|
{
|
|
s := "";
|
|
while (argv != nil) {
|
|
arg := hd argv;
|
|
for (i := 0; i < len arg; i++) {
|
|
c := arg[i];
|
|
if (c == ' ' || c == '\t' || c == '\n' || c == '\'' || in(c, cl))
|
|
break;
|
|
}
|
|
if (i < len arg || arg == nil) {
|
|
s += "'" + arg[0:i];
|
|
for (; i < len arg; i++) {
|
|
if (arg[i] == '\'')
|
|
s[len s] = '\'';
|
|
s[len s] = arg[i];
|
|
}
|
|
s[len s] = '\'';
|
|
} else
|
|
s += arg;
|
|
if (tl argv != nil)
|
|
s[len s] = ' ';
|
|
argv = tl argv;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
in(c: int, s: string): int
|
|
{
|
|
n := len s;
|
|
if(n == 0)
|
|
return 0;
|
|
ans := 0;
|
|
negate := 0;
|
|
if(s[0] == '^') {
|
|
negate = 1;
|
|
s = s[1:];
|
|
n--;
|
|
}
|
|
for(i := 0; i < n; i++) {
|
|
if(s[i] == '-' && i > 0 && i < n-1) {
|
|
if(c >= s[i-1] && c <= s[i+1]) {
|
|
ans = 1;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
else
|
|
if(c == s[i]) {
|
|
ans = 1;
|
|
break;
|
|
}
|
|
}
|
|
if(negate)
|
|
ans = !ans;
|
|
|
|
# just to showcase labels
|
|
skip:
|
|
return ans;
|
|
}
|
|
|
|
bglong(d: array of byte, i: int): int
|
|
{
|
|
return int d[i] | (int d[i+1]<<8) | (int d[i+2]<<16) | (int d[i+3]<<24);
|
|
}
|